diff --git a/Makefile.am b/Makefile.am index 282f63642b..44ec7a852e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ EXTRA_DIST = -SUBDIRS = . src build tests/unit +SUBDIRS = . src build AUTOMAKE_OPTIONS = foreign subdir-objects NAME = freeswitch @@ -775,3 +775,17 @@ support: @cp support-d/.screenrc ~ @cp support-d/.bashrc ~ @test -f ~/.cc-mode-installed || sh support-d/install-cc-mode.sh && touch ~/.cc-mode-installed + +# Using a non-recursive Makefile structure for the automated tests so that the tests have visibility into +# targets in the rest of the FreeSWITCH tree. This greatly simplifies dependency tracking at the expense +# of longer test target names. Since the tests are expected to be run easily and rapidly after minor source +# changes this is the most effective structure. + +check_PROGRAMS = + +include tests/unit/unit.mk + +TESTS = $(check_PROGRAMS) + +tests: $(check_PROGRAMS) + diff --git a/build/debpkgs.sh b/build/debpkgs.sh new file mode 100755 index 0000000000..5cf4387547 --- /dev/null +++ b/build/debpkgs.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +a='amd64 i386' +c='wheezy jessie stretch sid' +n='1' +T='/tmp/fs.sources.list' +K='/tmp/fs.asc' + +while getopts "a:c:n:" flag +do + case $flag in + a) a=$OPTARG;; + c) c=$OPTARG;; + n) n=$OPTARG;; + T) T=$OPTARG;; + K) K=$OPTARG;; + esac +done + +if [ "$EUID" -ne 0 ] +then + echo "Build script must be run as root or under sudo" + exit 1 +fi + +echo "./build/debpkgs.sh script is building FreeSWITCH Debian packages" + +VERSION=`cat ./build/next-release.txt` +echo "This Version: $VERSION" + +HASH=`git log -n 1 --oneline |cut -d ' ' -f 1` +echo "Commit hash $HASH" + +# Use the FreeSWITCH release repo for dependency testing +# The release codename here does not matter, since the util.sh script +# will adapt to the release being built +if [ ! -r "$T" ] +then + echo "deb http://files.freeswitch.org/repo/deb/debian/ jessie main" >> "$T" +fi + +# Use the FreeSWITCH release repo key +if [ ! -r "$K" ] +then + cat << EOF > "$K" +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.12 (GNU/Linux) + +mQGiBE8jEfIRBAC+Cca0fPQxhyhn0NMsPaMQJgTvqhWb5/f4Mel++kosmUQQ4fJq +4U9NFvpfNyLp5MoHpnlDfAb+e57B2sr47NOJLTh83yQIAnvU+8O0Q4kvMaiiesX5 +CisApLBs6Vx28y7VWmLsY3vWu8mC7M+PORKfpBV8DWy/7569wQPx2SCsIwCgzv2T +8YsnYsSVRrrmh46J1o4/ngsD/13ETX4ws/wNN+82RdqUxu7fjc0fNbUAb6XYddAb +1hrw5npQulgUNWkpnVmIDRHDXLNMeT8nZDkxsA8AsT+u7ACfPFa2o3R8w9zOPSO+ +oSO0+Puhop2+z1gm6lmfMKq9HpeXG3yt/8zsEVUmOYT9m+vYEVghfpXtACVYheDq +LzUuA/9E9HBiNPVhJ/mEpOk9bZ1gpwr3mjlpUbvX5aGwTJJ+YoTfZOCL7go3uQHn +/sT35WoJ23wJCRlW0SYTFJqCoris9AhI+qw7xRTw9wb+txSI96uhafUUMCn6GLkN ++yAixqDwNHKkdax3GSGJtLB0t67QoBDIpcGog7ZfRMvWP3QLNLQ4RnJlZVNXSVRD +SCBQYWNrYWdlIFNpZ25pbmcgS2V5IDxwYWNrYWdlc0BmcmVlc3dpdGNoLm9yZz6I +YgQTEQIAIgUCTyMR8gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ127c +dyXgEM879ACffY0HFi+mACtfFYmX/Uk/qGELSP4An1B8D5L4dLFFr1zV9YawQUbz +O9/MuQENBE8jEfIQBAC7vnn855YDuz1gTsUMYDxfIRH5KPmDDEAf1WXoD3QG4qOQ +xVW5nhp/bolh2CacAxdOjZePdhGkkdNOBpcu9NlTNRru0myGN8etbnzP3O5dq0io +VMf23C5u9KPbxwRWS+WFtC4CRFn6DafDI1qa3Gv3CkiBWtKR0Wid2SQLzl3mVwAF +EQP9HlwGjhBfFA26LlSMPhSo0Ll+sdcOJupJ21zmGeg7c0GpBnzDzyyJg04gbahs +xWtW3Y/+B4LGM97o6lnu0OQI7MX5gY1G4Jgu6pgYv8tQd5XyU/CAJUA5VWTxUMIi +JP6qlzm1bz4AAPmGw4mkS1u4N+vai21Zl4iyFIQFeiuU/K2ISQQYEQIACQUCTyMR +8gIbDAAKCRDXbtx3JeAQzxReAJ4uvms1n7xV3CcJPQlM7ndX5MZU3QCgxp8zubcL +/SsMvw7XApSHFs5ooYc= +=Xc8P +-----END PGP PUBLIC KEY BLOCK----- +EOF +fi + + +./debian/util.sh build-all -a "$a" -c "$c" -T $T -K $K -f ./build/modules.conf.most -j -bn -z9 -v$VERSION-$n~$HASH + +if [ $(ls -al ../freeswitch-mod* | wc -l) -lt 10 ]; then false; else true; fi + diff --git a/conf/testing/autoload_configs/conference_layouts.conf.xml b/conf/testing/autoload_configs/conference_layouts.conf.xml index 444b63471b..a5fc9b1995 100644 --- a/conf/testing/autoload_configs/conference_layouts.conf.xml +++ b/conf/testing/autoload_configs/conference_layouts.conf.xml @@ -12,6 +12,7 @@ + @@ -315,6 +316,10 @@ + + + + diff --git a/conf/testing/autoload_configs/switch.conf.xml b/conf/testing/autoload_configs/switch.conf.xml index 4ffe878563..8e66e7159f 100644 --- a/conf/testing/autoload_configs/switch.conf.xml +++ b/conf/testing/autoload_configs/switch.conf.xml @@ -150,7 +150,7 @@ - + diff --git a/conf/testing/autoload_configs/verto.conf.xml b/conf/testing/autoload_configs/verto.conf.xml index 772c679402..8b839dedf8 100644 --- a/conf/testing/autoload_configs/verto.conf.xml +++ b/conf/testing/autoload_configs/verto.conf.xml @@ -5,11 +5,9 @@ - + - - @@ -18,17 +16,38 @@ - - - + + + - + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/testing/dialplan/default/0019_conf.xml b/conf/testing/dialplan/default/0019_conf.xml index a19729ab43..daacf3f419 100644 --- a/conf/testing/dialplan/default/0019_conf.xml +++ b/conf/testing/dialplan/default/0019_conf.xml @@ -7,6 +7,15 @@ + + + + + + + + + diff --git a/conf/vanilla/autoload_configs/conference_layouts.conf.xml b/conf/vanilla/autoload_configs/conference_layouts.conf.xml index 243deb980d..14022a0db2 100644 --- a/conf/vanilla/autoload_configs/conference_layouts.conf.xml +++ b/conf/vanilla/autoload_configs/conference_layouts.conf.xml @@ -316,6 +316,10 @@ + + + + diff --git a/configure.ac b/configure.ac index 03ec965092..4fc4480cbe 100644 --- a/configure.ac +++ b/configure.ac @@ -834,6 +834,12 @@ AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS([sys/types.h sys/resource.h sched.h wchar.h sys/filio.h sys/ioctl.h sys/prctl.h sys/select.h netdb.h execinfo.h sys/time.h]) +# Solaris 11 privilege management +AS_CASE([$host], + [*-*-solaris2.11], [AC_CHECK_HEADER([priv.h], [AC_DEFINE([SOLARIS_PRIVILEGES],[1],[Solaris 11 privilege management])])] +) + + if test x"$ac_cv_header_wchar_h" = xyes; then HAVE_WCHAR_H_DEFINE=1 else @@ -1665,7 +1671,6 @@ ac_cv_file_dbd_apr_dbd_mysql_c=no AC_CONFIG_FILES([Makefile build/Makefile src/Makefile - tests/unit/Makefile src/mod/Makefile src/mod/applications/mod_abstraction/Makefile src/mod/applications/mod_avmd/Makefile diff --git a/debian/freeswitch.postinst b/debian/freeswitch.postinst index b19277fe8b..c08fd07e31 100644 --- a/debian/freeswitch.postinst +++ b/debian/freeswitch.postinst @@ -30,9 +30,12 @@ case "$1" in chown freeswitch $x done if [ ! -d "/etc/freeswitch" ]; then + mkdir -p /etc/freeswitch/ + cp -a /usr/share/freeswitch/conf/vanilla/* /etc/freeswitch/ + fi + if [ ! -d "/etc/freeswitch/tls" ]; then mkdir -p /etc/freeswitch/tls/ chown freeswitch:freeswitch /etc/freeswitch/tls - cp -a /usr/share/freeswitch/conf/vanilla/* /etc/freeswitch/ fi ;; abort-upgrade|abort-remove|abort-deconfigure) diff --git a/debian/freeswitch.prerm b/debian/freeswitch.prerm index 9f34f0b371..145275dd3d 100644 --- a/debian/freeswitch.prerm +++ b/debian/freeswitch.prerm @@ -3,9 +3,6 @@ set -e case "$1" in remove|upgrade|deconfigure) - if [ -d /etc/freeswitch ]; then - echo "We're about to remove a configured FreeSWITCH..." >&2 - fi ;; failed-upgrade) ;; diff --git a/debian/util.sh b/debian/util.sh index 075de7cdd5..fd37c33f71 100755 --- a/debian/util.sh +++ b/debian/util.sh @@ -137,10 +137,9 @@ create_orig () { { set -e local OPTIND OPTARG - local uver="" hrev="" bundle_deps=false modules_list="" zl=9e + local uver="" hrev="" bundle_deps=true modules_list="" zl=9e while getopts 'bm:nv:z:' o "$@"; do case "$o" in - b) bundle_deps=true;; m) modules_list="$OPTARG";; n) uver="nightly";; v) uver="$OPTARG";; @@ -275,8 +274,9 @@ build_debs () { { set -e local OPTIND OPTARG debug_hook=false hookdir="" cow_build_opts="" - local keep_pbuilder_config=false keyring="" custom_keyring="" - local use_custom_sources=false + local keep_pbuilder_config=false keyring="" custom_keyring="/tmp/fs.asc" + local use_custom_sources=true + local custom_sources_file="/tmp/fs.sources.list" while getopts 'BbdK:kT:t' o "$@"; do case "$o" in B) cow_build_opts="--debbuildopts '-B'";; @@ -284,11 +284,56 @@ build_debs () { d) debug_hook=true;; k) keep_pbuilder_config=true;; K) custom_keyring="$OPTARG";; - t) use_custom_sources=true; custom_sources_file="/etc/apt/sources.list";; - T) use_custom_sources=true; custom_sources_file="$OPTARG";; + t) custom_sources_file="/etc/apt/sources.list";; + T) custom_sources_file="$OPTARG";; esac done shift $(($OPTIND-1)) + if [ "$custom_sources_file" == "/etc/apt/sources.list" ]; then + # If you are using the system sources, then it is reasonable that you expect to use all of the supplementary repos too + cat /etc/apt/sources.list > /tmp/fs.sources.list + for X in /etc/apt/sources.list.d/*; do cat $X >> /tmp/fs.sources.list; done + custom_sources_file="/tmp/fs.sources.list" + apt-key exportall > "/tmp/fs.asc" + custom_keyring="/tmp/fs.asc" + fi + if [ "$custom_sources_file" == "" ]; then + # Caller has explicitly set the custom sources file to empty string. They must intend to not use additional mirrors. + use_custom_sources=false + fi + if [[ "$custom_source_file" == "/tmp/fs.sources.list" && ! -e "/tmp/fs.sources.list" ]]; then + echo "deb http://files.freeswitch.org/repo/deb/debian/ jessie main" >> "/tmp/fs.sources.list" + fi + if [[ "$custom_keyring" == "/tmp/fs.asc" && ! -r "/tmp/fs.asc" ]]; then + cat << EOF > "/tmp/fs.asc" +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.12 (GNU/Linux) + +mQGiBE8jEfIRBAC+Cca0fPQxhyhn0NMsPaMQJgTvqhWb5/f4Mel++kosmUQQ4fJq +4U9NFvpfNyLp5MoHpnlDfAb+e57B2sr47NOJLTh83yQIAnvU+8O0Q4kvMaiiesX5 +CisApLBs6Vx28y7VWmLsY3vWu8mC7M+PORKfpBV8DWy/7569wQPx2SCsIwCgzv2T +8YsnYsSVRrrmh46J1o4/ngsD/13ETX4ws/wNN+82RdqUxu7fjc0fNbUAb6XYddAb +1hrw5npQulgUNWkpnVmIDRHDXLNMeT8nZDkxsA8AsT+u7ACfPFa2o3R8w9zOPSO+ +oSO0+Puhop2+z1gm6lmfMKq9HpeXG3yt/8zsEVUmOYT9m+vYEVghfpXtACVYheDq +LzUuA/9E9HBiNPVhJ/mEpOk9bZ1gpwr3mjlpUbvX5aGwTJJ+YoTfZOCL7go3uQHn +/sT35WoJ23wJCRlW0SYTFJqCoris9AhI+qw7xRTw9wb+txSI96uhafUUMCn6GLkN ++yAixqDwNHKkdax3GSGJtLB0t67QoBDIpcGog7ZfRMvWP3QLNLQ4RnJlZVNXSVRD +SCBQYWNrYWdlIFNpZ25pbmcgS2V5IDxwYWNrYWdlc0BmcmVlc3dpdGNoLm9yZz6I +YgQTEQIAIgUCTyMR8gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ127c +dyXgEM879ACffY0HFi+mACtfFYmX/Uk/qGELSP4An1B8D5L4dLFFr1zV9YawQUbz +O9/MuQENBE8jEfIQBAC7vnn855YDuz1gTsUMYDxfIRH5KPmDDEAf1WXoD3QG4qOQ +xVW5nhp/bolh2CacAxdOjZePdhGkkdNOBpcu9NlTNRru0myGN8etbnzP3O5dq0io +VMf23C5u9KPbxwRWS+WFtC4CRFn6DafDI1qa3Gv3CkiBWtKR0Wid2SQLzl3mVwAF +EQP9HlwGjhBfFA26LlSMPhSo0Ll+sdcOJupJ21zmGeg7c0GpBnzDzyyJg04gbahs +xWtW3Y/+B4LGM97o6lnu0OQI7MX5gY1G4Jgu6pgYv8tQd5XyU/CAJUA5VWTxUMIi +JP6qlzm1bz4AAPmGw4mkS1u4N+vai21Zl4iyFIQFeiuU/K2ISQQYEQIACQUCTyMR +8gIbDAAKCRDXbtx3JeAQzxReAJ4uvms1n7xV3CcJPQlM7ndX5MZU3QCgxp8zubcL +/SsMvw7XApSHFs5ooYc= +=Xc8P +-----END PGP PUBLIC KEY BLOCK----- +EOF + fi + local distro="$(find_distro $1)" dsc="$2" arch="$3" if [ -z "$distro" ] || [ "$distro" = "auto" ]; then if ! (echo "$dsc" | grep -e '-[0-9]*~[a-z]*+[0-9]*'); then @@ -309,11 +354,13 @@ build_debs () { fi cow () { if ! $use_custom_sources; then + echo "Using system sources $keyring $distro $custom_sources_file" cowbuilder "$@" \ --distribution $distro \ --architecture $arch \ --basepath $cow_img else + echo "Using custom sources $keyring $distro $custom_sources_file" cowbuilder "$@" \ --distribution $distro \ --architecture $arch \ @@ -459,7 +506,6 @@ commands: [ This must be run as root! ] -a Specify architectures - -b Bundle downloaded libraries in source package -c Specify distributions -d Enable cowbuilder debug hook -f @@ -480,7 +526,7 @@ commands: Include otherwise avoided module -s [ paranoid | reckless ] Set FS bootstrap/build -j flags - -t Use system /etc/apt/sources.list in build environment + -t Use system /etc/apt/sources.list in build environment(does not include /etc/apt/sources.list.d/*.list) -T [/path/to/sources.list] Use custom /etc/apt/sources.list in build environment -u @@ -521,7 +567,6 @@ commands: create-orig - -b Bundle downloaded libraries in source package -m [ quicktest | non-dfsg ] Choose custom list of modules to build -n Nightly build diff --git a/html5/verto/js/src/jquery.FSRTC.js b/html5/verto/js/src/jquery.FSRTC.js index b06951e312..d0bf7bf24c 100644 --- a/html5/verto/js/src/jquery.FSRTC.js +++ b/html5/verto/js/src/jquery.FSRTC.js @@ -316,7 +316,14 @@ if(typeof self.localStream.stop == 'function') { self.localStream.stop(); } else { - self.localStream.active = false; + if (self.localStream.active){ + var tracks = self.localStream.getTracks(); + console.error(tracks); + tracks.forEach(function(track, index){ + console.log(track); + track.stop(); + }) + } } self.localStream = null; } @@ -334,7 +341,14 @@ if(typeof self.options.localVideoStream.stop == 'function') { self.options.localVideoStream.stop(); } else { - self.options.localVideoStream.active = false; + if (self.localVideoStream.active){ + var tracks = self.localVideoStream.getTracks(); + console.error(tracks); + tracks.forEach(function(track, index){ + console.log(track); + track.stop(); + }) + } } } @@ -453,7 +467,10 @@ var audio; - if (obj.options.videoParams && obj.options.screenShare) {//obj.options.videoParams.chromeMediaSource == 'desktop') { + if (obj.options.useMic && obj.options.useMic === "none") { + console.log("Microphone Disabled"); + audio = false; + } else if (obj.options.videoParams && obj.options.screenShare) {//obj.options.videoParams.chromeMediaSource == 'desktop') { //obj.options.videoParams = { // chromeMediaSource: 'screen', @@ -523,6 +540,7 @@ } } else { + console.log("Camera Disabled"); video = false; useVideo = false; } @@ -590,17 +608,20 @@ console.log("Audio constraints", mediaParams.audio); console.log("Video constraints", mediaParams.video); + if (mediaParams.audio || mediaParams.video) { - getUserMedia({ - constraints: { - audio: mediaParams.audio, + getUserMedia({ + constraints: { + audio: mediaParams.audio, video: mediaParams.video - }, - video: mediaParams.useVideo, - onsuccess: onSuccess, - onerror: onError - }); - + }, + video: mediaParams.useVideo, + onsuccess: onSuccess, + onerror: onError + }); + } else { + onSuccess(null); + } @@ -1086,12 +1107,8 @@ video: video }, onsuccess: function(e) { - if(typeof e.stop == 'function') { - e.stop(); - } else { - e.active = false; - } - console.info(w + "x" + h + " supported."); $.FSRTC.validRes.push([w, h]); checkRes(cam, func);}, + e.getTracks().forEach(function(track) {track.stop();}); + console.info(w + "x" + h + " supported."); $.FSRTC.validRes.push([w, h]); checkRes(cam, func);}, onerror: function(e) {console.error( w + "x" + h + " not supported."); checkRes(cam, func);} }); } @@ -1127,15 +1144,12 @@ video: check_video, }, onsuccess: function(e) { - if(typeof e.stop == 'function') { - e.stop(); - } else { - e.active = false; - } - console.info("media perm init complete"); - if (runtime) { - setTimeout(runtime, 100, true); - } + e.getTracks().forEach(function(track) {track.stop();}); + + console.info("media perm init complete"); + if (runtime) { + setTimeout(runtime, 100, true); + } }, onerror: function(e) { if (check_video && check_audio) { diff --git a/html5/verto/js/src/jquery.verto.js b/html5/verto/js/src/jquery.verto.js index ee88a8f507..779ba79125 100644 --- a/html5/verto/js/src/jquery.verto.js +++ b/html5/verto/js/src/jquery.verto.js @@ -85,9 +85,6 @@ if (verto.options.deviceParams.useCamera) { $.FSRTC.getValidRes(verto.options.deviceParams.useCamera, verto.options.deviceParams.onResCheck); - } else { - verto.options.deviceParams.useCamera = "any"; - $.FSRTC.getValidRes(undefined, undefined); } if (!verto.options.deviceParams.useMic) { @@ -2549,7 +2546,7 @@ console.info("Audio Devices", $.verto.audioInDevices); console.info("Video Devices", $.verto.videoDevices); - runtime(); + runtime(true); }); } else { /* of course it's a totally different API CALL with different element names for the same exact thing */ @@ -2586,12 +2583,12 @@ console.info("Audio IN Devices", $.verto.audioInDevices); console.info("Audio Out Devices", $.verto.audioOutDevices); console.info("Video Devices", $.verto.videoDevices); - runtime(); + runtime(true); }) .catch(function(err) { console.log(" Device Enumeration ERROR: " + err.name + ": " + err.message); - runtime(); + runtime(false); }); } @@ -2602,9 +2599,24 @@ } $.verto.init = function(obj, runtime) { - $.FSRTC.checkPerms(function() { + if (!obj) { + obj = {}; + } + + if (!obj.skipPermCheck && !obj.skipDeviceCheck) { + $.FSRTC.checkPerms(function(status) { + checkDevices(runtime); + }, true, true); + } else if (obj.skipPermCheck && !obj.skipDeviceCheck) { checkDevices(runtime); - }, true, true); + } else if (!obj.skipPermCheck && obj.skipDeviceCheck) { + $.FSRTC.checkPerms(function(status) { + runtime(status); + }, true, true); + } else { + runtime(null); + } + } $.verto.genUUID = function () { diff --git a/html5/verto/verto_communicator/.gitignore b/html5/verto/verto_communicator/.gitignore index 7274d6169c..b45b61e6c1 100644 --- a/html5/verto/verto_communicator/.gitignore +++ b/html5/verto/verto_communicator/.gitignore @@ -2,3 +2,4 @@ dist/ .tmp/ bower_components/ node_modules/ +src/vertoControllers/controllers/AboutController.js diff --git a/html5/verto/verto_communicator/Gruntfile.js b/html5/verto/verto_communicator/Gruntfile.js index 13ad96523d..4d2174ac9d 100644 --- a/html5/verto/verto_communicator/Gruntfile.js +++ b/html5/verto/verto_communicator/Gruntfile.js @@ -24,15 +24,16 @@ module.exports = function (grunt) { var debug = grunt.option('debug'); var uglify_config = { + options: { + sourceMap: true, + sourceMapIncludeSources:true + } }; + if (debug) { - uglify_config = { - options: { - beautify: debug ? true : false, - compress: debug ? false : true, - mangle: debug ? false : true - } - }; + uglify_config['options']['mangle'] = debug ? false : true; + uglify_config['options']['beautify'] = debug ? false : true; + uglify_config['options']['compress'] = debug ? false : true; } // Project configuration. grunt.initConfig({ @@ -62,6 +63,26 @@ module.exports = function (grunt) { } }, + revision: { + options: { + property: 'meta.revision', + ref: 'HEAD', + short: true + } + }, + + preprocess: { + options: { + context: { + revision: '<%= meta.revision %>' + } + }, + js: { + src: 'src/vertoControllers/controllers/AboutController.source.js', + dest: 'src/vertoControllers/controllers/AboutController.js' + }, + }, + postcss: { options: { map: true, @@ -129,6 +150,8 @@ module.exports = function (grunt) { ], routes: { '/partials': 'src/partials', + '/config.json': 'src/config.json', + '/contributors.txt': 'src/contributors.txt', '/bower_components': './bower_components', '/js/src': '../js/src', '/js': './js' @@ -283,6 +306,7 @@ module.exports = function (grunt) { '*.html', '*.json', 'partials/**/*.html', + 'img/*.png', 'images/{,*/}*.{webp}', 'css/fonts/{,*/}*.*', 'sounds/*.*' @@ -324,6 +348,9 @@ module.exports = function (grunt) { }, }); + grunt.loadNpmTasks('grunt-git-revision'); + grunt.loadNpmTasks('grunt-preprocess'); + grunt.registerTask('serve', function (target) { var tasks = [ 'wiredep', @@ -336,9 +363,13 @@ module.exports = function (grunt) { grunt.task.run(tasks); }); + + grunt.registerTask('default', ['build']); grunt.registerTask('build', [ 'clean:dist', + 'revision', + 'preprocess', 'wiredep', 'useminPrepare', 'concurrent:dist', diff --git a/html5/verto/verto_communicator/package.json b/html5/verto/verto_communicator/package.json index 34b861bf47..b2b0502a55 100644 --- a/html5/verto/verto_communicator/package.json +++ b/html5/verto/verto_communicator/package.json @@ -19,6 +19,8 @@ "grunt-contrib-jshint": "^0.11.0", "grunt-contrib-uglify": "^0.7.0", "grunt-contrib-watch": "latest", + "grunt-preprocess": "latest", + "grunt-git-revision": "latest", "grunt-filerev": "^2.1.2", "grunt-newer": "^1.1.0", "grunt-ng-annotate": "^0.9.2", diff --git a/html5/verto/verto_communicator/src/contributors.txt b/html5/verto/verto_communicator/src/contributors.txt index bc309438b2..7665f76cb0 100644 --- a/html5/verto/verto_communicator/src/contributors.txt +++ b/html5/verto/verto_communicator/src/contributors.txt @@ -3,5 +3,6 @@ "Ítalo Rossi ", "Stefan Yohansson ", "João Mesquita ", - "Ken Rice " + "Ken Rice ", + "Brian West " ] diff --git a/html5/verto/verto_communicator/src/css/verto.css b/html5/verto/verto_communicator/src/css/verto.css index df7efea2f7..9bc3a8b70c 100644 --- a/html5/verto/verto_communicator/src/css/verto.css +++ b/html5/verto/verto_communicator/src/css/verto.css @@ -8,6 +8,18 @@ body { padding-top: 60px; } +.ellipsis { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 160px; + display: inline-block; +} + +.clickable { + cursor: pointer; +} + .inline-block { display: inline-block; } @@ -110,6 +122,10 @@ button.btn i { color: #F45A5A; } +.mdi-navigation-more-vert.dark { + color: #333; +} + .incall-number { font-weight: bold; padding-top: 22px; @@ -223,6 +239,29 @@ button.btn i { } } +/* --- Splash Screen --- */ +.splash-errors { + background: rgba(249, 21, 21, 0.55); + color: white; + padding: 8px; + margin-top: 11px; +} + +.splash-errors ul { + padding-start: 0em; + -moz-padding-start: 0em; + -webkit-padding-start: 0em; + padding-start: 0em; +} + +.splash-errors li { + background-color: rgba(154, 36, 36, 0.28); + padding: 8px; + font-weight: bold; + list-style: none; +} +/* --- End of Splash Screen --- */ + /* --- Modal settings page --- */ @@ -231,6 +270,10 @@ body .modal-body .btn-group .btn.active { color: #EEE; } +.dedicated_encoder { + color: #0B3A84; +} + /* --- End of Modal settings page --- */ @@ -410,7 +453,7 @@ body .modal-body .btn-group .btn.active { } #dialpad .date { - margin-top: 15px; + margin-top: 15px; display: block; font-size: 11px; color: #CCC; @@ -529,7 +572,7 @@ body .modal-body .btn-group .btn.active { } #incall .phone li button .big-icon { - font-size: 36px; + font-size: 36px; } #incall .video-wrapper { @@ -857,10 +900,31 @@ body .modal-body .btn-group .btn.active { width: 160px; overflow: hidden; text-overflow: ellipsis; - margin-top: 5px; white-space: nowrap; } +.members-number { + font-size: 10px; +} + +.members-badges { + font-size: 10px; + text-transform: uppercase; + margin-top: -2px; +} + +.badge-floor span { + display: inline-block; +} + +.lock-floor { + position: relative; + top: -3px; + display: inline-block; + font-size: 10px; + color: #FFF; +} + .chat-members .chat-member-item { padding: 8px 16px; height: 56px; @@ -892,7 +956,8 @@ body .modal-body .btn-group .btn.active { margin: 0; font-size: 16px; display: inline-block; - line-height: 18px; + line-height: 16px; + margin-top: -3px; } .chat-members .chat-members-status i { @@ -1206,11 +1271,11 @@ body:-webkit-full-screen #incall .video-footer { body { overflow-x: hidden; } - + #dialpad .call-history li { overflow-x: hidden; } - + #sidebar-wrapper { margin-right: -750px; } @@ -1260,7 +1325,7 @@ body:-webkit-full-screen #incall .video-footer { margin: 0 auto; float: none; } - + #incall .phone li button { padding: 10px; } @@ -1285,8 +1350,8 @@ body:-webkit-full-screen #incall .video-footer { } } -@media screen - and (min-device-width: 320px) +@media screen + and (min-device-width: 320px) and (max-device-width: 780px) { body { @@ -1354,7 +1419,7 @@ body:-webkit-full-screen #incall .video-footer { margin: 0 auto; float: none; } - + #incall.centered-block-frame { display: block; justify-content: none; @@ -1375,7 +1440,7 @@ body:-webkit-full-screen #incall .video-footer { padding-top: calc(50% - 25%); padding-bottom: calc(50% - 25%); } - + .contributors { -webkit-padding-start: 0px; text-align: center; diff --git a/html5/verto/verto_communicator/src/img/fs_logo_small.png b/html5/verto/verto_communicator/src/img/fs_logo_small.png new file mode 100644 index 0000000000..a875564480 Binary files /dev/null and b/html5/verto/verto_communicator/src/img/fs_logo_small.png differ diff --git a/html5/verto/verto_communicator/src/img/logo.png b/html5/verto/verto_communicator/src/img/logo.png deleted file mode 100644 index 04bce2b5b2..0000000000 Binary files a/html5/verto/verto_communicator/src/img/logo.png and /dev/null differ diff --git a/html5/verto/verto_communicator/src/img/logo_big.png b/html5/verto/verto_communicator/src/img/logo_big.png deleted file mode 100644 index 99184e9565..0000000000 Binary files a/html5/verto/verto_communicator/src/img/logo_big.png and /dev/null differ diff --git a/html5/verto/verto_communicator/src/img/logo_med.png b/html5/verto/verto_communicator/src/img/logo_med.png deleted file mode 100644 index 1b47ebfacc..0000000000 Binary files a/html5/verto/verto_communicator/src/img/logo_med.png and /dev/null differ diff --git a/html5/verto/verto_communicator/src/img/vc_logo.png b/html5/verto/verto_communicator/src/img/vc_logo.png new file mode 100644 index 0000000000..27972c5a9b Binary files /dev/null and b/html5/verto/verto_communicator/src/img/vc_logo.png differ diff --git a/html5/verto/verto_communicator/src/index.html b/html5/verto/verto_communicator/src/index.html index 5945f72fe5..a1594f8a7f 100644 --- a/html5/verto/verto_communicator/src/index.html +++ b/html5/verto/verto_communicator/src/index.html @@ -98,15 +98,18 @@ + + + @@ -118,10 +121,13 @@ + + + diff --git a/html5/verto/verto_communicator/src/partials/about.html b/html5/verto/verto_communicator/src/partials/about.html new file mode 100644 index 0000000000..97ff126660 --- /dev/null +++ b/html5/verto/verto_communicator/src/partials/about.html @@ -0,0 +1,22 @@ + + + + diff --git a/html5/verto/verto_communicator/src/partials/chat.html b/html5/verto/verto_communicator/src/partials/chat.html index 06eee1da7d..6fb10a878f 100644 --- a/html5/verto/verto_communicator/src/partials/chat.html +++ b/html5/verto/verto_communicator/src/partials/chat.html @@ -19,13 +19,23 @@
- + -

{{ member.name }}
({{ member.number }})

+ +

+
{{ member.name }}
+ ({{ member.number }}) + +
+
Floor
+
Presenter
+
+

+
- - + +
diff --git a/html5/verto/verto_communicator/src/partials/login.html b/html5/verto/verto_communicator/src/partials/login.html index 5a0c03c7dd..1d20188885 100644 --- a/html5/verto/verto_communicator/src/partials/login.html +++ b/html5/verto/verto_communicator/src/partials/login.html @@ -28,6 +28,11 @@ +
+ + +
+
@@ -58,9 +58,15 @@ diff --git a/html5/verto/verto_communicator/src/partials/modal_logininfo.html b/html5/verto/verto_communicator/src/partials/modal_logininfo.html index e41eef4de1..efb964f447 100644 --- a/html5/verto/verto_communicator/src/partials/modal_logininfo.html +++ b/html5/verto/verto_communicator/src/partials/modal_logininfo.html @@ -23,6 +23,10 @@ +
+ + +
-
- -
+ + +

Dedicated Remote Encoder

+
Select a non default bandwidth to use a dedicated remote encoder.
+ +
+

Dedicated Remote Encoder enabled. +

+
@@ -107,6 +111,7 @@ diff --git a/html5/verto/verto_communicator/src/partials/splash_screen.html b/html5/verto/verto_communicator/src/partials/splash_screen.html new file mode 100644 index 0000000000..993999ec3a --- /dev/null +++ b/html5/verto/verto_communicator/src/partials/splash_screen.html @@ -0,0 +1,21 @@ +
+
+
+
+

Loading

+
+
+
+
+ +
+

Errors

+
    +
  • {{ ::error }}
  • +
+
+
+
+
+
+ diff --git a/html5/verto/verto_communicator/src/partials/ws_reconnect.html b/html5/verto/verto_communicator/src/partials/ws_reconnect.html new file mode 100644 index 0000000000..7879b587f7 --- /dev/null +++ b/html5/verto/verto_communicator/src/partials/ws_reconnect.html @@ -0,0 +1,9 @@ + + + + diff --git a/html5/verto/verto_communicator/src/storageService/services/splash_screen.js b/html5/verto/verto_communicator/src/storageService/services/splash_screen.js new file mode 100644 index 0000000000..da7a156f63 --- /dev/null +++ b/html5/verto/verto_communicator/src/storageService/services/splash_screen.js @@ -0,0 +1,235 @@ +'use strict'; + + angular + .module('storageService') + .service('splashscreen', ['$rootScope', '$q', 'storage', 'config', 'verto', + function($rootScope, $q, storage, config, verto) { + + var checkBrowser = function() { + return $q(function(resolve, reject) { + var activity = 'browser-upgrade'; + var result = { + 'activity': activity, + 'soft': false, + 'status': 'success', + 'message': 'Checking browser compability.' + }; + + navigator.getUserMedia = navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia; + + if (!navigator.getUserMedia) { + result['status'] = 'error'; + result['message'] = 'Error: browser doesn\'t support WebRTC.'; + reject(result); + } + + resolve(result); + + }); + }; + + var checkMediaPerm = function() { + return $q(function(resolve, reject) { + var activity = 'media-perm'; + var result = { + 'activity': activity, + 'soft': false, + 'status': 'success', + 'message': 'Checking media permissions' + }; + + verto.mediaPerm(function(status) { + if(!status) { + result['status'] = 'error'; + result['message'] = 'Error: Media Permission Denied'; + verto.data.mediaPerm = false; + reject(result); + } + verto.data.mediaPerm = true; + resolve(result); + }); + }); + }; + + var refreshMediaDevices = function() { + return $q(function(resolve, reject) { + var activity = 'refresh-devices'; + var result = { + 'status': 'success', + 'soft': true, + 'activity': activity, + 'message': 'Refresh Media Devices.' + }; + + verto.refreshDevices(function(status) { + verto.refreshDevicesCallback(function() { + resolve(result); + }); + }); + + }); + }; + + var provisionConfig = function() { + return $q(function(resolve, reject) { + var activity = 'provision-config'; + var result = { + 'status': 'promise', + 'soft': true, + 'activity': activity, + 'message': 'Provisioning configuration.' + }; + + var configResponse = config.configure(); + + var configPromise = configResponse.then( + function(response) { + /** + * from angular docs: + * A response status code between 200 and 299 is considered a success status and will result in the success callback being called + */ + if(response.status >= 200 && response.status <= 299) { + return result; + } else { + result['status'] = 'error'; + result['message'] = 'Error: Provision failed.'; + return result; + } + }); + + result['promise'] = configPromise; + + resolve(result); + }); + }; + + var checkLogin = function() { + return $q(function(resolve, reject) { + var activity = 'check-login'; + var result = { + 'status': 'success', + 'soft': true, + 'activity': activity, + 'message': 'Checking login.' + }; + + if(verto.data.connecting || verto.data.connected) { + resolve(result); + return; + }; + + var checkUserStored = function() { + /** + * if user data saved, use stored data for logon and not connecting + * not connecting prevent two connects + */ + if (storage.data.ui_connected && storage.data.ws_connected && !verto.data.connecting) { + verto.data.name = storage.data.name; + verto.data.email = storage.data.email; + verto.data.login = storage.data.login; + verto.data.password = storage.data.password; + + verto.data.connecting = true; + verto.connect(function(v, connected) { + verto.data.connecting = false; + resolve(result); + }); + }; + }; + + if(storage.data.ui_connected && storage.data.ws_connected) { + checkUserStored(); + } else { + resolve(result); + }; + }); + }; + + var progress = [ + checkBrowser, + checkMediaPerm, + refreshMediaDevices, + provisionConfig, + checkLogin + ]; + + var progress_message = [ + 'Checking browser compability.', + 'Checking media permissions', + 'Refresh Media Devices.', + 'Provisioning configuration.', + 'Checking login.' + ]; + + var getProgressMessage = function(current_progress) { + if(progress_message[current_progress] != undefined) { + return progress_message[current_progress]; + } else { + return 'Please wait...'; + } + }; + + var current_progress = -1; + var progress_percentage = 0; + + var calculateProgress = function(index) { + var _progress; + + _progress = index + 1; + progress_percentage = (_progress / progress.length) * 100; + return progress_percentage; + }; + + var nextProgress = function() { + var fn, fn_return, status, interrupt, activity, soft, message, promise; + interrupt = false; + current_progress++; + + if(current_progress >= progress.length) { + $rootScope.$emit('progress.complete', current_progress); + return; + } + + fn = progress[current_progress]; + fn_return = fn(); + + var emitNextProgress = function(fn_return) { + if(fn_return['promise'] != undefined) { + promise = fn_return['promise']; + } + + status = fn_return['status']; + soft = fn_return['soft']; + activity = fn_return['activity']; + message = fn_return['message']; + + if(status != 'success') { + interrupt = true; + } + + $rootScope.$emit('progress.next', current_progress, status, promise, activity, soft, interrupt, message); + + }; + + fn_return.then( + function(fn_return) { + emitNextProgress(fn_return); + }, + function(fn_return) { + emitNextProgress(fn_return); + } + ); + + }; + + return { + 'next': nextProgress, + 'getProgressMessage': getProgressMessage, + 'progress_percentage': progress_percentage, + 'calculate': calculateProgress + }; + + }]); + diff --git a/html5/verto/verto_communicator/src/storageService/services/storage.js b/html5/verto/verto_communicator/src/storageService/services/storage.js index 7120db18de..f0b8bb1b80 100644 --- a/html5/verto/verto_communicator/src/storageService/services/storage.js +++ b/html5/verto/verto_communicator/src/storageService/services/storage.js @@ -53,6 +53,8 @@ data.userStatus = 'disconnected'; }, factoryReset: function() { + localStorage.clear(); + // set defaultSettings again data.$reset(defaultSettings); }, }; diff --git a/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js b/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js index 2c7cffc381..0952ed1dce 100644 --- a/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js +++ b/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js @@ -19,6 +19,11 @@ vertoApp.config(['$routeProvider', 'gravatarServiceProvider', function($routeProvider, gravatarServiceProvider) { $routeProvider. + when('/', { + title: 'Loading', + templateUrl: 'partials/splash_screen.html', + controller: 'SplashScreenController' + }). when('/login', { title: 'Login', templateUrl: 'partials/login.html', @@ -40,7 +45,7 @@ controller: 'BrowserUpgradeController' }). otherwise({ - redirectTo: '/login' + redirectTo: '/' }); gravatarServiceProvider.defaults = { @@ -49,8 +54,19 @@ } ]); - vertoApp.run(['$rootScope', '$location', 'toastr', 'prompt', - function($rootScope, $location, toastr, prompt) { + vertoApp.run(['$rootScope', '$location', 'toastr', 'prompt', 'verto', + function($rootScope, $location, toastr, prompt, verto) { + + $rootScope.$on( "$routeChangeStart", function(event, next, current) { + if (!verto.data.connected) { + if ( next.templateUrl === "partials/login.html") { + // pass + } else { + $location.path("/"); + } + } + }); + $rootScope.$on('$routeChangeSuccess', function(event, current, previous) { $rootScope.title = current.$$route.title; }); @@ -61,17 +77,7 @@ $rootScope.safeProtocol = true; } - $rootScope.checkBrowser = function() { - navigator.getUserMedia = navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia; - - if (!navigator.getUserMedia) { - $location.path('/browser-upgrade'); - } - - }; - + $rootScope.promptInput = function(title, message, label, callback) { var ret = prompt({ title: title, diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/AboutController.source.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/AboutController.source.js new file mode 100644 index 0000000000..83b8a489cb --- /dev/null +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/AboutController.source.js @@ -0,0 +1,23 @@ +(function() { + 'use strict'; + + angular + .module('vertoControllers') + .controller('AboutController', ['$scope', '$http', + 'toastr', + function($scope, $http, toastr) { + var githash = '/* @echo revision */' || 'something is not right'; + $scope.githash = githash; + + /* leave this here for later, but its not needed right now + $http.get(window.location.pathname + '/contributors.txt') + .success(function(data) { + + }) + .error(function() { + toastr.error('contributors not found.'); + }); + */ + } + ]); +})(); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js index 0bb3f6f6a0..cd8fff2021 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js @@ -122,7 +122,7 @@ }); $rootScope.$on('members.clear', function(event) { - $scope.$apply(function() { + $scope.$applyAsync(function() { clearConferenceChat(); $scope.closeChat(); }); @@ -146,13 +146,17 @@ }; $scope.confMuteMic = function(memberID) { - console.log('$scope.confMuteMic'); - verto.data.conf.muteMic(memberID); + if(verto.data.confRole == 'moderator') { + console.log('$scope.confMuteMic'); + verto.data.conf.muteMic(memberID); + } }; $scope.confMuteVideo = function(memberID) { - console.log('$scope.confMuteVideo'); - verto.data.conf.muteVideo(memberID); + if(verto.data.confRole == 'moderator') { + console.log('$scope.confMuteVideo'); + verto.data.conf.muteVideo(memberID); + } }; $scope.confPresenter = function(memberID) { @@ -167,7 +171,22 @@ $scope.confBanner = function(memberID) { console.log('$scope.confBanner'); - var text = 'New Banner'; + + prompt({ + title: 'Please insert the banner text', + input: true, + label: '', + value: '', + }).then(function(text) { + if (text) { + verto.data.conf.banner(memberID, text); + } + }); + }; + + $scope.confResetBanner = function(memberID) { + console.log('$scope.confResetBanner'); + var text = 'reset'; verto.data.conf.banner(memberID, text); }; diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/ContributorsController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/ContributorsController.js index c66f421346..aa00f64e04 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/ContributorsController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/ContributorsController.js @@ -6,7 +6,8 @@ .controller('ContributorsController', ['$scope', '$http', 'toastr', function($scope, $http, toastr) { - $http.get(window.location.pathname + '/contributors.txt') + var url = window.location.origin + window.location.pathname; + $http.get(url + 'contributors.txt') .success(function(data) { var contributors = []; @@ -29,4 +30,4 @@ }); } ]); -})(); \ No newline at end of file +})(); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js index 8c7589c93d..2944cae46d 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js @@ -4,10 +4,12 @@ angular .module('vertoControllers') .controller('DialPadController', ['$rootScope', '$scope', - '$http', '$location', 'toastr', 'verto', 'storage', 'CallHistory', - function($rootScope, $scope, $http, $location, toastr, verto, storage, CallHistory) { + '$http', '$location', 'toastr', 'verto', 'storage', 'CallHistory', 'eventQueue', + function($rootScope, $scope, $http, $location, toastr, verto, storage, CallHistory, eventQueue) { console.debug('Executing DialPadController.'); - $scope.checkBrowser(); + + eventQueue.process(); + $scope.call_history = CallHistory.all(); $scope.history_control = CallHistory.all_control(); $scope.has_history = Object.keys($scope.call_history).length; diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js index 47eb987a28..84d8894b28 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js @@ -10,7 +10,6 @@ console.debug('Executing InCallController.'); $scope.layout = null; - $scope.checkBrowser(); $rootScope.dialpadNumber = ''; $scope.callTemplate = 'partials/phone_call.html'; $scope.dialpadTemplate = ''; diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/LoginController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/LoginController.js index bae60ac31e..d6f1d0366f 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/LoginController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/LoginController.js @@ -1,60 +1,23 @@ (function() { - 'use strict'; + 'use strict'; - angular - .module('vertoControllers') - .controller('LoginController', ['$scope', '$http', '$location', 'verto', - function($scope, $http, $location, verto) { - $scope.checkBrowser(); + angular + .module('vertoControllers') + .controller('LoginController', ['$scope', '$http', '$location', 'verto', + function($scope, $http, $location, verto) { + var preRoute = function() { + if(verto.data.connected) { + $location.path('/dialpad'); + } + } + preRoute(); + + verto.data.name = $scope.storage.data.name; + verto.data.email = $scope.storage.data.email; - /* - * Load the Configs before logging in - * with cache buster - */ - - $http.get(window.location.pathname + '/config.json?cachebuster=' + Math.floor((Math.random()*1000000)+1)) - .success(function(data) { - - /* save these for later as we're about to possibly over write them */ - var name = verto.data.name; - var email = verto.data.email; - - console.debug("googlelogin: " + data.googlelogin); - if (data.googlelogin){ - $scope.googlelogin = data.googlelogin; - $scope.googleclientid = data.googleclientid; - } - - angular.extend(verto.data, data); - - /** - * use stored data (localStorage) for login, allow config.json to take precedence - */ - - if (name != '' && data.name == '') { - verto.data.name = name; - } - if (email != '' && data.email == '') { - verto.data.email = email; - } - if (verto.data.login == '' && verto.data.password == '' && $scope.storage.data.login != '' && $scope.storage.data.password != '') { - verto.data.login = $scope.storage.data.login; - verto.data.password = $scope.storage.data.password; - } - - if (verto.data.autologin == "true" && !verto.data.autologin_done) { - console.debug("auto login per config.json"); - verto.data.autologin_done = true; - $scope.login(); - } - }); - - verto.data.name = $scope.storage.data.name; - verto.data.email = $scope.storage.data.email; - - console.debug('Executing LoginController.'); - } - ]); + console.debug('Executing LoginController.'); + } + ]); })(); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js index 0363d91f27..daa9b95b69 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js @@ -4,7 +4,7 @@ angular .module('vertoControllers') .controller('MainController', - function($scope, $rootScope, $location, $modal, $timeout, verto, storage, CallHistory, toastr, Fullscreen, prompt) { + function($scope, $rootScope, $location, $modal, $timeout, $q, verto, storage, CallHistory, toastr, Fullscreen, prompt, eventQueue) { console.debug('Executing MainController.'); @@ -24,58 +24,42 @@ * @type {string} */ $rootScope.dialpadNumber = ''; - - - /** - * if user data saved, use stored data for logon - */ - if (storage.data.ui_connected && storage.data.ws_connected) { - $scope.verto.data.name = storage.data.name; - $scope.verto.data.email = storage.data.email; - $scope.verto.data.login = storage.data.login; - $scope.verto.data.password = storage.data.password; - - verto.connect(function(v, connected) { - $scope.$apply(function() { - if (connected) { - toastr.success('Nice to see you again.', 'Welcome back'); - $location.path('/dialpad'); - } - }); - }); - - } - + // If verto is not connected, redirects to login page. if (!verto.data.connected) { console.debug('MainController: WebSocket not connected. Redirecting to login.'); - $location.path('/login'); + $location.path('/'); } - + + $rootScope.$on('config.http.success', function(ev) { + $scope.login(false); + }); /** * Login the user to verto server and * redirects him to dialpad page. */ - $scope.login = function() { + $scope.login = function(redirect) { + if(redirect == undefined) { + redirect = true; + } var connectCallback = function(v, connected) { $scope.$apply(function() { - if (connected) { - storage.data.ui_connected = verto.data.connected; - storage.data.ws_connected = verto.data.connected; - storage.data.name = verto.data.name; - storage.data.email = verto.data.email; - storage.data.login = verto.data.login; - storage.data.password = verto.data.password; - - console.debug('Redirecting to dialpad page.'); - toastr.success('Login successful.', 'Welcome'); + verto.data.connecting = false; + if (connected) { + storage.data.ui_connected = verto.data.connected; + storage.data.ws_connected = verto.data.connected; + storage.data.name = verto.data.name; + storage.data.email = verto.data.email; + storage.data.login = verto.data.login; + storage.data.password = verto.data.password; + if (redirect) { $location.path('/dialpad'); - } else { - toastr.error('There was an error while trying to login. Please try again.', 'Error'); } + } }); }; - + + verto.data.connecting = true; verto.connect(connectCallback); }; @@ -144,12 +128,16 @@ ); }; - $rootScope.openModal = function(templateUrl, controller) { - var modalInstance = $modal.open({ + $rootScope.openModal = function(templateUrl, controller, _options) { + var options = { animation: $scope.animationsEnabled, templateUrl: templateUrl, controller: controller, - }); + }; + + angular.extend(options, _options); + + var modalInstance = $modal.open(options); modalInstance.result.then( function(result) { @@ -165,7 +153,37 @@ jQuery.material.init(); } ); + + return modalInstance; + }; + $rootScope.$on('ws.close', onWSClose); + $rootScope.$on('ws.login', onWSLogin); + + var ws_modalInstance; + + function onWSClose(ev, data) { + if(ws_modalInstance) { + return; + }; + var options = { + backdrop: 'static', + keyboard: false + }; + ws_modalInstance = $scope.openModal('partials/ws_reconnect.html', 'ModalWsReconnectController', options); + }; + + function onWSLogin(ev, data) { + if(!ws_modalInstance) { + return; + }; + + ws_modalInstance.close(); + ws_modalInstance = null; + }; + + $scope.showAbout = function() { + $scope.openModal('partials/about.html', 'AboutController'); }; $scope.showContributors = function() { @@ -261,22 +279,27 @@ }); $rootScope.$on('page.incall', function(event, data) { - if (storage.data.askRecoverCall) { - prompt({ - title: 'Oops, Active Call in Course.', - message: 'It seems you were in a call before leaving the last time. Wanna go back to that?' - }).then(function() { - console.log('redirect to incall page'); - $location.path('/incall'); - }, function() { - storage.data.userStatus = 'connecting'; - verto.hangup(); + var page_incall = function() { + return $q(function(resolve, reject) { + if (storage.data.askRecoverCall) { + prompt({ + title: 'Oops, Active Call in Course.', + message: 'It seems you were in a call before leaving the last time. Wanna go back to that?' + }).then(function() { + console.log('redirect to incall page'); + $location.path('/incall'); + }, function() { + storage.data.userStatus = 'connecting'; + verto.hangup(); + }); + } else { + console.log('redirect to incall page'); + $location.path('/incall'); + } + resolve(); }); - } else { - console.log('redirect to incall page'); - $location.path('/incall'); - } - + }; + eventQueue.events.push(page_incall); }); $scope.$on('event:google-plus-signin-success', function (event,authResult) { @@ -313,7 +336,7 @@ console.log('Google+ Login Failure'); }); - $rootScope.callActive = function(data) { + $rootScope.callActive = function(data, params) { verto.data.mutedMic = storage.data.mutedMic; verto.data.mutedVideo = storage.data.mutedVideo; @@ -331,10 +354,16 @@ storage.data.calling = false; storage.data.cur_call = 1; + + $location.path('/incall'); + + if(params.useVideo) { + $rootScope.$emit('call.video', 'video'); + } }; - $rootScope.$on('call.active', function(event, data) { - $rootScope.callActive(data); + $rootScope.$on('call.active', function(event, data, params) { + $rootScope.callActive(data, params); }); $rootScope.$on('call.calling', function(event, data) { @@ -360,11 +389,11 @@ $scope.answerCall(); storage.data.called_number = data; - CallHistory.add(number, 'inbound', true); + CallHistory.add(data, 'inbound', true); $location.path('/incall'); }, function() { $scope.declineCall(); - CallHistory.add(number, 'inbound', false); + CallHistory.add(data, 'inbound', false); }); }); @@ -380,6 +409,7 @@ if (!verto.data.call) { toastr.warning('There is no call to hangup.'); $location.path('/dialpad'); + return; } //var hangupCallback = function(v, hangup) { @@ -394,7 +424,10 @@ if (verto.data.shareCall) { verto.screenshareHangup(); } + verto.hangup(); + + $location.path('/dialpad'); }; $scope.answerCall = function() { @@ -403,6 +436,7 @@ verto.data.call.answer({ useStereo: storage.data.useStereo, useCamera: storage.data.selectedVideo, + useVideo: storage.data.useVideo, useMic: storage.data.useMic, callee_id_name: verto.data.name, callee_id_number: verto.data.login diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js index b144d3026e..2b70948bbe 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js @@ -34,6 +34,14 @@ window.location.reload(); }; }; + + $scope.checkUseDedRemoteEncoder = function(option) { + if ($scope.mydata.incomingBandwidth != 'default' || $scope.mydata.outgoingBandwidth != 'default') { + $scope.mydata.useDedenc = true; + } else { + $scope.mydata.useDedenc = false; + } + }; } ]); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalWsReconnectController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalWsReconnectController.js new file mode 100644 index 0000000000..374ad18c8d --- /dev/null +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalWsReconnectController.js @@ -0,0 +1,15 @@ +(function() { + 'use strict'; + + angular + .module('vertoControllers') + .controller('ModalWsReconnectController', ModalWsReconnectController); + + ModalWsReconnectController.$inject = ['$scope', 'storage', 'verto']; + + function ModalWsReconnectController($scope, storage, verto) { + console.debug('Executing ModalWsReconnectController'); + }; + + +})(); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/SplashScreenController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/SplashScreenController.js new file mode 100644 index 0000000000..fdc96efd59 --- /dev/null +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/SplashScreenController.js @@ -0,0 +1,88 @@ +(function() { + 'use strict'; + + angular + .module('vertoControllers') + .controller('SplashScreenController', ['$scope', '$rootScope', '$location', '$timeout', 'splashscreen', 'prompt', 'verto', + function($scope, $rootScope, $location, $timeout, splashscreen, prompt, verto) { + console.debug('Executing SplashScreenController.'); + + $scope.progress_percentage = splashscreen.progress_percentage; + $scope.message = ''; + $scope.interrupt_next = false; + $scope.errors = []; + + var redirectTo = function(link, activity) { + if(activity) { + if(activity == 'browser-upgrade') { + link = activity; + } + } + + $location.path(link); + } + + var checkProgressState = function(current_progress, status, promise, activity, soft, interrupt, message) { + $scope.progress_percentage = splashscreen.calculate(current_progress); + $scope.message = message; + + if(interrupt && status == 'error') { + $scope.errors.push(message); + if(!soft) { + redirectTo('', activity); + return; + } else { + message = message + '. Continue?'; + }; + + if(!confirm(message)) { + $scope.interrupt_next = true; + }; + }; + + if($scope.interrupt_next) { + return; + }; + + $scope.message = splashscreen.getProgressMessage(current_progress+1); + + return true; + }; + + $rootScope.$on('progress.next', function(ev, current_progress, status, promise, activity, soft, interrupt, message) { + $timeout(function() { + if(promise) { + promise.then(function(response) { + message = response['message']; + status = response['status']; + if(checkProgressState(current_progress, status, promise, activity, soft, interrupt, message)) { + splashscreen.next(); + }; + }); + + return; + } + + if(!checkProgressState(current_progress, status, promise, activity, soft, interrupt, message)) { + return; + } + + splashscreen.next(); + }, 400); + }); + + $rootScope.$on('progress.complete', function(ev, current_progress) { + $scope.message = 'Complete'; + if(verto.data.connected) { + redirectTo('/dialpad'); + } else { + redirectTo('/login'); + $location.path('/login'); + } + }); + + splashscreen.next(); + + }]); + +})(); diff --git a/html5/verto/verto_communicator/src/vertoControllers/vertoControllers.module.js b/html5/verto/verto_communicator/src/vertoControllers/vertoControllers.module.js index f27923b322..a20bb2cfee 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/vertoControllers.module.js +++ b/html5/verto/verto_communicator/src/vertoControllers/vertoControllers.module.js @@ -2,10 +2,10 @@ 'use strict'; var vertoControllers = angular.module('vertoControllers', [ - 'ui.bootstrap', + 'ui.bootstrap', 'vertoService', 'storageService', 'ui.gravatar' ]); -})(); \ No newline at end of file +})(); diff --git a/html5/verto/verto_communicator/src/vertoDirectives/directives/videoTag.js b/html5/verto/verto_communicator/src/vertoDirectives/directives/videoTag.js index 2e85168569..38b50552f7 100644 --- a/html5/verto/verto_communicator/src/vertoDirectives/directives/videoTag.js +++ b/html5/verto/verto_communicator/src/vertoDirectives/directives/videoTag.js @@ -18,7 +18,7 @@ console.log('Moving the video to element.'); jQuery('video').removeClass('hide').appendTo(element); jQuery('video').css('display', 'block'); - scope.callActive(); + scope.callActive("", {useVideo: true}); element.on('$destroy', function() { // Move the video back to the body. diff --git a/html5/verto/verto_communicator/src/vertoService/services/configService.js b/html5/verto/verto_communicator/src/vertoService/services/configService.js new file mode 100644 index 0000000000..105a20b324 --- /dev/null +++ b/html5/verto/verto_communicator/src/vertoService/services/configService.js @@ -0,0 +1,82 @@ +'use strict'; + +var vertoService = angular.module('vertoService'); + +vertoService.service('config', ['$rootScope', '$http', '$location', 'storage', 'verto', + function($rootScope, $http, $location, storage, verto) { + var configure = function() { + /** + * Load stored user info into verto service + */ + if(storage.data.name) { + verto.data.name = storage.data.name; + } + if(storage.data.email) { + verto.data.email = storage.data.email; + } + if(storage.data.login) { + verto.data.login = storage.data.login; + } + if(storage.data.password) { + verto.data.password = storage.data.password; + } + + /* + * Load the Configs before logging in + * with cache buster + */ + var url = window.location.origin + window.location.pathname; + var httpRequest = $http.get(url + 'config.json?cachebuster=' + Math.floor((Math.random()*1000000)+1)); + + var httpReturn = httpRequest.then(function(response) { + var data = response.data; + + /* save these for later as we're about to possibly over write them */ + var name = verto.data.name; + var email = verto.data.email; + + console.debug("googlelogin: " + data.googlelogin); + if (data.googlelogin){ + verto.data.googlelogin = data.googlelogin; + verto.data.googleclientid = data.googleclientid; + } + + angular.extend(verto.data, data); + + /** + * use stored data (localStorage) for login, allow config.json to take precedence + */ + + if (name != '' && data.name == '') { + verto.data.name = name; + } + if (email != '' && data.email == '') { + verto.data.email = email; + } + if (verto.data.login == '' && verto.data.password == '' && storage.data.login != '' && storage.data.password != '') { + verto.data.login = storage.data.login; + verto.data.password = storage.data.password; + } + + if (verto.data.autologin == "true" && !verto.data.autologin_done) { + console.debug("auto login per config.json"); + verto.data.autologin_done = true; + } + + if(verto.data.autologin && storage.data.name.length && storage.data.email.length && storage.data.login.length && storage.data.password.length) { + $rootScope.$emit('config.http.success', data); + }; + return response; + }, function(response) { + $rootScope.$emit('config.http.error', response); + return response; + }); + + return httpReturn; + }; + + return { + 'configure': configure + }; + }]); + diff --git a/html5/verto/verto_communicator/src/vertoService/services/eventQueueService.js b/html5/verto/verto_communicator/src/vertoService/services/eventQueueService.js new file mode 100644 index 0000000000..71c48bcefa --- /dev/null +++ b/html5/verto/verto_communicator/src/vertoService/services/eventQueueService.js @@ -0,0 +1,50 @@ +'use strict'; + + angular + .module('vertoService') + .service('eventQueue', ['$rootScope', '$q', 'storage', 'verto', + function($rootScope, $q, storage, verto) { + + var events = []; + + var next = function() { + var fn, fn_return; + + fn = events.shift(); + + if (fn == undefined) { + $rootScope.$emit('eventqueue.complete'); + return; + } + fn_return = fn(); + + var emitNextProgress = function() { + $rootScope.$emit('eventqueue.next'); + }; + + fn_return.then( + function() { + emitNextProgress(); + }, + function() { + emitNextProgress(); + } + ); + }; + + var process = function() { + $rootScope.$on('eventqueue.next', function (ev){ + next(); + }); + + next(); + }; + + return { + 'next': next, + 'process': process, + 'events': events + }; + + }]); + diff --git a/html5/verto/verto_communicator/src/vertoService/services/vertoService.js b/html5/verto/verto_communicator/src/vertoService/services/vertoService.js index 940ae1059d..652d5aa0da 100644 --- a/html5/verto/verto_communicator/src/vertoService/services/vertoService.js +++ b/html5/verto/verto_communicator/src/vertoService/services/vertoService.js @@ -1,8 +1,8 @@ 'use strict'; /* Controllers */ - -var videoQuality = [{ +var videoQuality = []; +var videoQualitySource = [{ id: 'qvga', label: 'QVGA 320x240', width: 320, @@ -143,8 +143,8 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora $rootScope.$emit('page.incall', 'call'); } - function callActive(last_state) { - $rootScope.$emit('call.active', last_state); + function callActive(last_state, params) { + $rootScope.$emit('call.active', last_state, params); } function calling() { @@ -158,28 +158,28 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora function updateResolutions(supportedResolutions) { console.debug('Attempting to sync supported and available resolutions'); - var removed = 0; + //var removed = 0; - angular.forEach(videoQuality, function(resolution, id) { - var supported = false; + console.debug("VQ length: " + videoQualitySource.length); + console.debug(supportedResolutions); + + angular.forEach(videoQualitySource, function(resolution, id) { angular.forEach(supportedResolutions, function(res) { var width = res[0]; var height = res[1]; if(resolution.width == width && resolution.height == height) { - supported = true; + videoQuality.push(resolution); } }); - - if(!supported) { - delete videoQuality[id]; - ++removed; - } }); - videoQuality.length = videoQuality.length - removed; + // videoQuality.length = videoQuality.length - removed; + console.debug("VQ length 2: " + videoQuality.length); data.videoQuality = videoQuality; + console.debug(videoQuality); data.vidQual = (videoQuality.length > 0) ? videoQuality[videoQuality.length - 1].id : null; + console.debug(data.vidQual); return videoQuality; }; @@ -198,7 +198,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora videoResolution: videoResolution, bandwidth: bandwidth, - refreshDevicesCallback : function refreshDevicesCallback() { + refreshDevicesCallback : function refreshDevicesCallback(callback) { data.videoDevices = [{ id: 'none', label: 'No Camera' @@ -278,11 +278,19 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora } else { data.canVideo = true; } + + if(angular.isFunction(callback)) { + callback(); + } }, refreshDevices: function(callback) { console.debug('Attempting to refresh the devices.'); - jQuery.verto.refreshDevices(this.refreshDevicesCallback); + if(callback) { + jQuery.verto.refreshDevices(callback); + } else { + jQuery.verto.refreshDevices(this.refreshDevicesCallback); + } }, /** @@ -364,8 +372,11 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora } }); - console.log('>>> conf.listVideoLayouts();'); - conf.listVideoLayouts(); + if (data.confRole == "moderator") { + console.log('>>> conf.listVideoLayouts();'); + conf.listVideoLayouts(); + } + data.conf = conf; data.liveArray = new $.verto.liveArray( @@ -425,15 +436,20 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora console.log('Has data.liveArray.'); $rootScope.$emit('members.clear'); data.liveArray = null; - } else { console.log('Doesn\'t found data.liveArray.'); } + + if (data.conf) { + data.conf.destroy(); + data.conf = null; + } } var callbacks = { onWSLogin: function(v, success) { data.connected = success; + $rootScope.$emit('ws.login', success); console.debug('Connected to verto server:', success); if (angular.isFunction(callback)) { @@ -450,6 +466,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora switch (params.pvtData.action) { case "conference-liveArray-join": console.log("conference-liveArray-join"); + stopConference(); startConference(v, dialog, params.pvtData); break; case "conference-liveArray-part": @@ -472,6 +489,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora }); break; default: + console.warn('Got a not implemented message:', msg, dialog, params); break; } }, @@ -479,9 +497,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora onDialogState: function(d) { if (!data.call) { data.call = d; - if (d.state.name !== 'ringing') { - inCall(); - } + } console.debug('onDialogState:', d); @@ -501,7 +517,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora case "active": console.debug('Talking to:', d.cidString()); data.callState = 'active'; - callActive(d.lastState.name); + callActive(d.lastState.name, d.params); break; case "hangup": console.debug('Call ended with cause: ' + d.cause); @@ -512,21 +528,22 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora if (d.params.screenShare) { cleanShareCall(that); } else { - if (data.liveArray) { - data.liveArray.destroy(); + stopConference(); + if (!that.reloaded) { + cleanCall(); } - - if (data.conf) { - data.conf.destroy(); - } - cleanCall(); } break; + default: + console.warn('Got a not implemented state:', d); + break; } }, onWSClose: function(v, success) { console.debug('onWSClose:', success); + + $rootScope.$emit('ws.close', success); }, onEvent: function(v, e) { @@ -538,10 +555,11 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora function ourBootstrap() { // Checking if we have a failed connection attempt before // connecting again. - that.refreshDevicesCallback(); if (data.instance && !data.instance.rpcClient.socketReady()) { clearTimeout(data.instance.rpcClient.to); data.instance.logout(); + data.instance.login(); + return; }; data.instance = new jQuery.verto({ login: data.login + '@' + data.hostname, @@ -551,22 +569,36 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora ringFile: "sounds/bell_ring2.wav", // TODO: Add options for this. audioParams: { - googEchoCancellation: storage.data.googEchoCancellation || false, - googNoiseSuppression: storage.data.googNoiseSuppression || false, - googHighpassFilter: storage.data.googHighpassFilter || false + googEchoCancellation: storage.data.googEchoCancellation || true, + googNoiseSuppression: storage.data.googNoiseSuppression || true, + googHighpassFilter: storage.data.googHighpassFilter || true }, iceServers: storage.data.useSTUN }, callbacks); - data.instance.deviceParams({ - useCamera: storage.data.selectedVideo, - useMic: storage.data.selectedAudio, - onResCheck: that.refreshVideoResolution + // We need to know when user reloaded page and not react to + // verto events in order to not stop the reload and redirect user back + // to the dialpad. + that.reloaded = false; + jQuery.verto.unloadJobs.push(function() { + that.reloaded = true; }); - + data.instance.deviceParams({ + useCamera: storage.data.selectedVideo, + useMic: storage.data.selectedAudio, + onResCheck: that.refreshVideoResolution + }); } - $.verto.init({}, ourBootstrap); + if (data.mediaPerm) { + ourBootstrap(); + } else { + $.FSRTC.checkPerms(ourBootstrap, true, true); + } + }, + + mediaPerm: function(callback) { + $.FSRTC.checkPerms(callback, true, true); }, /** @@ -616,7 +648,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora var call = data.instance.newCall({ destination_number: destination, caller_id_name: data.name, - caller_id_number: data.login, + caller_id_number: data.callerid ? data.callerid : data.email, outgoingBandwidth: storage.data.outgoingBandwidth, incomingBandwidth: storage.data.incomingBandwidth, useVideo: storage.data.useVideo, diff --git a/html5/verto/video_demo/js/verto-min.js b/html5/verto/video_demo/js/verto-min.js index 622cfa10a4..da28e4dd7f 100644 --- a/html5/verto/video_demo/js/verto-min.js +++ b/html5/verto/video_demo/js/verto-min.js @@ -30,9 +30,10 @@ self.options.useAudio.play();self.remoteStream=stream;} function onOfferSDP(self,sdp){self.mediaData.SDP=self.stereoHack(sdp.sdp);console.log("Offer SDP");doCallback(self,"onOfferSDP");} $.FSRTC.prototype.answer=function(sdp,onSuccess,onError){this.peer.addAnswerSDP({type:"answer",sdp:sdp},onSuccess,onError);};$.FSRTC.prototype.stopPeer=function(){if(self.peer){console.log("stopping peer");self.peer.stop();}} $.FSRTC.prototype.stop=function(){var self=this;if(self.options.useVideo){self.options.useVideo.style.display='none';if(moz){self.options.useVideo['mozSrcObject']=null;}else{self.options.useVideo['src']='';}} -if(self.localStream){self.localStream.stop();self.localStream=null;} +if(self.localStream){if(typeof self.localStream.stop=='function'){self.localStream.stop();}else{if(self.localStream.active){var tracks=self.localStream.getTracks();console.error(tracks);tracks.forEach(function(track,index){console.log(track);track.stop();})}} +self.localStream=null;} if(self.options.localVideo){self.options.localVideo.style.display='none';if(moz){self.options.localVideo['mozSrcObject']=null;}else{self.options.localVideo['src']='';}} -if(self.options.localVideoStream){self.options.localVideoStream.stop();} +if(self.options.localVideoStream){if(typeof self.options.localVideoStream.stop=='function'){self.options.localVideoStream.stop();}else{if(self.localVideoStream.active){var tracks=self.localVideoStream.getTracks();console.error(tracks);tracks.forEach(function(track,index){console.log(track);track.stop();})}}} if(self.peer){console.log("stopping peer");self.peer.stop();}};$.FSRTC.prototype.getMute=function(){var self=this;return self.enabled;} $.FSRTC.prototype.setMute=function(what){var self=this;var audioTracks=self.localStream.getAudioTracks();for(var i=0,len=audioTracks.length;iremaining_samples != INT_MAX) { @@ -151,7 +151,7 @@ SPAN_DECLARE(int) silence_gen_free(silence_gen_state_t *s) /* The following dummy routines, to absorb data, don't really have a proper home, so they have been put here. */ -SPAN_DECLARE_NONSTD(int) span_dummy_rx(void *user_data, const int16_t amp[], int len) +SPAN_DECLARE(int) span_dummy_rx(void *user_data, const int16_t amp[], int len) { return 0; } @@ -163,7 +163,7 @@ SPAN_DECLARE(int) span_dummy_mod(void *user_data, int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) span_dummy_rx_fillin(void *user_data, int len) +SPAN_DECLARE(int) span_dummy_rx_fillin(void *user_data, int len) { return 0; } diff --git a/libs/spandsp/src/spandsp/async.h b/libs/spandsp/src/spandsp/async.h index eabb41d3f1..290b3acc1d 100644 --- a/libs/spandsp/src/spandsp/async.h +++ b/libs/spandsp/src/spandsp/async.h @@ -156,7 +156,7 @@ SPAN_DECLARE(const char *) signal_status_to_str(int status); - SIG_STATUS_TRAINING_SUCCEEDED - SIG_STATUS_TRAINING_FAILED - SIG_STATUS_END_OF_DATA */ -SPAN_DECLARE_NONSTD(void) async_rx_put_bit(void *user_data, int bit); +SPAN_DECLARE(void) async_rx_put_bit(void *user_data, int bit); /*! Initialise an asynchronous data receiver context. \brief Initialise an asynchronous data receiver context. @@ -190,7 +190,7 @@ SPAN_DECLARE(void) async_tx_presend_bits(async_tx_state_t *s, int bits); \brief Get the next bit of a transmitted serial bit stream. \param user_data An opaque point which must point to a transmitter context. \return the next bit, or PUTBIT_END_OF_DATA to indicate the data stream has ended. */ -SPAN_DECLARE_NONSTD(int) async_tx_get_bit(void *user_data); +SPAN_DECLARE(int) async_tx_get_bit(void *user_data); /*! Initialise an asynchronous data transmit context. \brief Initialise an asynchronous data transmit context. diff --git a/libs/spandsp/src/spandsp/fax.h b/libs/spandsp/src/spandsp/fax.h index 499b2b3b25..de6d52da0e 100644 --- a/libs/spandsp/src/spandsp/fax.h +++ b/libs/spandsp/src/spandsp/fax.h @@ -50,7 +50,7 @@ extern "C" \return The number of samples unprocessed. This should only be non-zero if the software has reached the end of the FAX call. */ -SPAN_DECLARE_NONSTD(int) fax_rx(fax_state_t *s, int16_t *amp, int len); +SPAN_DECLARE(int) fax_rx(fax_state_t *s, int16_t *amp, int len); /*! Apply fake T.30 receive processing when a block of audio samples is missing (e.g due to packet loss). @@ -60,7 +60,7 @@ SPAN_DECLARE_NONSTD(int) fax_rx(fax_state_t *s, int16_t *amp, int len); \return The number of samples unprocessed. This should only be non-zero if the software has reached the end of the FAX call. */ -SPAN_DECLARE_NONSTD(int) fax_rx_fillin(fax_state_t *s, int len); +SPAN_DECLARE(int) fax_rx_fillin(fax_state_t *s, int len); /*! Apply T.30 transmit processing to generate a block of audio samples. \brief Apply T.30 transmit processing to generate a block of audio samples. @@ -70,7 +70,7 @@ SPAN_DECLARE_NONSTD(int) fax_rx_fillin(fax_state_t *s, int len); \return The number of samples actually generated. This will be zero when there is nothing to send. */ -SPAN_DECLARE_NONSTD(int) fax_tx(fax_state_t *s, int16_t *amp, int max_len); +SPAN_DECLARE(int) fax_tx(fax_state_t *s, int16_t *amp, int max_len); /*! Select whether silent audio will be sent when FAX transmit is idle. \brief Select whether silent audio will be sent when FAX transmit is idle. diff --git a/libs/spandsp/src/spandsp/fax_modems.h b/libs/spandsp/src/spandsp/fax_modems.h index a076c0332f..15cc802474 100644 --- a/libs/spandsp/src/spandsp/fax_modems.h +++ b/libs/spandsp/src/spandsp/fax_modems.h @@ -64,7 +64,7 @@ extern "C" #endif /* TEMPORARY FUDGE */ -SPAN_DECLARE_NONSTD(void) fax_modems_hdlc_accept(void *user_data, const uint8_t *msg, int len, int ok); +SPAN_DECLARE(void) fax_modems_hdlc_accept(void *user_data, const uint8_t *msg, int len, int ok); /*! Convert a FAX modem type to a short text description. \brief Convert a FAX modem type to a short text description. @@ -73,14 +73,14 @@ SPAN_DECLARE_NONSTD(void) fax_modems_hdlc_accept(void *user_data, const uint8_t SPAN_DECLARE(const char *) fax_modem_to_str(int modem); /* N.B. the following are currently a work in progress */ -SPAN_DECLARE_NONSTD(int) fax_modems_v17_v21_rx(void *user_data, const int16_t amp[], int len); -SPAN_DECLARE_NONSTD(int) fax_modems_v27ter_v21_rx(void *user_data, const int16_t amp[], int len); -SPAN_DECLARE_NONSTD(int) fax_modems_v29_v21_rx(void *user_data, const int16_t amp[], int len); -SPAN_DECLARE_NONSTD(int) fax_modems_v17_v21_rx_fillin(void *user_data, int len); -SPAN_DECLARE_NONSTD(int) fax_modems_v27ter_v21_rx_fillin(void *user_data, int len); -SPAN_DECLARE_NONSTD(int) fax_modems_v29_v21_rx_fillin(void *user_data, int len); +SPAN_DECLARE(int) fax_modems_v17_v21_rx(void *user_data, const int16_t amp[], int len); +SPAN_DECLARE(int) fax_modems_v27ter_v21_rx(void *user_data, const int16_t amp[], int len); +SPAN_DECLARE(int) fax_modems_v29_v21_rx(void *user_data, const int16_t amp[], int len); +SPAN_DECLARE(int) fax_modems_v17_v21_rx_fillin(void *user_data, int len); +SPAN_DECLARE(int) fax_modems_v27ter_v21_rx_fillin(void *user_data, int len); +SPAN_DECLARE(int) fax_modems_v29_v21_rx_fillin(void *user_data, int len); -SPAN_DECLARE_NONSTD(void) fax_modems_hdlc_tx_frame(void *user_data, const uint8_t *msg, int len); +SPAN_DECLARE(void) fax_modems_hdlc_tx_frame(void *user_data, const uint8_t *msg, int len); SPAN_DECLARE(void) fax_modems_hdlc_tx_flags(fax_modems_state_t *s, int flags); diff --git a/libs/spandsp/src/spandsp/fsk.h b/libs/spandsp/src/spandsp/fsk.h index b65cd7842f..c016eca8a5 100644 --- a/libs/spandsp/src/spandsp/fsk.h +++ b/libs/spandsp/src/spandsp/fsk.h @@ -187,7 +187,7 @@ SPAN_DECLARE(void) fsk_tx_set_modem_status_handler(fsk_tx_state_t *s, modem_stat \param len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) fsk_tx(fsk_tx_state_t *s, int16_t amp[], int len); +SPAN_DECLARE(int) fsk_tx(fsk_tx_state_t *s, int16_t amp[], int len); /*! Get the current received signal power. \param s The modem context. @@ -228,7 +228,7 @@ SPAN_DECLARE(int) fsk_rx_free(fsk_rx_state_t *s); \param len The number of samples in the buffer. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) fsk_rx(fsk_rx_state_t *s, const int16_t *amp, int len); +SPAN_DECLARE(int) fsk_rx(fsk_rx_state_t *s, const int16_t *amp, int len); /*! Fake processing of a missing block of received FSK modem audio samples (e.g due to packet loss). @@ -237,7 +237,7 @@ SPAN_DECLARE_NONSTD(int) fsk_rx(fsk_rx_state_t *s, const int16_t *amp, int len); \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) fsk_rx_fillin(fsk_rx_state_t *s, int len); +SPAN_DECLARE(int) fsk_rx_fillin(fsk_rx_state_t *s, int len); SPAN_DECLARE(void) fsk_rx_set_put_bit(fsk_rx_state_t *s, put_bit_func_t put_bit, void *user_data); diff --git a/libs/spandsp/src/spandsp/hdlc.h b/libs/spandsp/src/spandsp/hdlc.h index a9369475c1..1349a57a04 100644 --- a/libs/spandsp/src/spandsp/hdlc.h +++ b/libs/spandsp/src/spandsp/hdlc.h @@ -160,20 +160,20 @@ SPAN_DECLARE(int) hdlc_rx_get_stats(hdlc_rx_state_t *s, \param s A pointer to an HDLC receiver context. \param new_bit The bit. */ -SPAN_DECLARE_NONSTD(void) hdlc_rx_put_bit(hdlc_rx_state_t *s, int new_bit); +SPAN_DECLARE(void) hdlc_rx_put_bit(hdlc_rx_state_t *s, int new_bit); /*! \brief Put a byte of data to an HDLC receiver. \param s A pointer to an HDLC receiver context. \param new_byte The byte of data. */ -SPAN_DECLARE_NONSTD(void) hdlc_rx_put_byte(hdlc_rx_state_t *s, int new_byte); +SPAN_DECLARE(void) hdlc_rx_put_byte(hdlc_rx_state_t *s, int new_byte); /*! \brief Put a series of bytes of data to an HDLC receiver. \param s A pointer to an HDLC receiver context. \param buf The buffer of data. \param len The length of the data in the buffer. */ -SPAN_DECLARE_NONSTD(void) hdlc_rx_put(hdlc_rx_state_t *s, const uint8_t buf[], int len); +SPAN_DECLARE(void) hdlc_rx_put(hdlc_rx_state_t *s, const uint8_t buf[], int len); /*! Initialise an HDLC transmitter context. \brief Initialise an HDLC transmitter context. @@ -250,13 +250,13 @@ SPAN_DECLARE(int) hdlc_tx_abort(hdlc_tx_state_t *s); \param s A pointer to an HDLC transmitter context. \return The next bit for transmission. */ -SPAN_DECLARE_NONSTD(int) hdlc_tx_get_bit(hdlc_tx_state_t *s); +SPAN_DECLARE(int) hdlc_tx_get_bit(hdlc_tx_state_t *s); /*! \brief Get the next byte for transmission. \param s A pointer to an HDLC transmitter context. \return The next byte for transmission. */ -SPAN_DECLARE_NONSTD(int) hdlc_tx_get_byte(hdlc_tx_state_t *s); +SPAN_DECLARE(int) hdlc_tx_get_byte(hdlc_tx_state_t *s); /*! \brief Get the next sequence of bytes for transmission. \param s A pointer to an HDLC transmitter context. @@ -264,7 +264,7 @@ SPAN_DECLARE_NONSTD(int) hdlc_tx_get_byte(hdlc_tx_state_t *s); \param max_len The number of bytes to get. \return The number of bytes actually got. */ -SPAN_DECLARE_NONSTD(int) hdlc_tx_get(hdlc_tx_state_t *s, uint8_t buf[], size_t max_len); +SPAN_DECLARE(int) hdlc_tx_get(hdlc_tx_state_t *s, uint8_t buf[], size_t max_len); #if defined(__cplusplus) } diff --git a/libs/spandsp/src/spandsp/modem_connect_tones.h b/libs/spandsp/src/spandsp/modem_connect_tones.h index 713f813bbb..bbfc6f3b97 100644 --- a/libs/spandsp/src/spandsp/modem_connect_tones.h +++ b/libs/spandsp/src/spandsp/modem_connect_tones.h @@ -127,9 +127,9 @@ SPAN_DECLARE(int) modem_connect_tones_tx_free(modem_connect_tones_tx_state_t *s) \param len The number of samples to generate. \return The number of samples generated. */ -SPAN_DECLARE_NONSTD(int) modem_connect_tones_tx(modem_connect_tones_tx_state_t *s, - int16_t amp[], - int len); +SPAN_DECLARE(int) modem_connect_tones_tx(modem_connect_tones_tx_state_t *s, + int16_t amp[], + int len); /*! \brief Process a block of samples through an instance of the modem connect tones detector. @@ -138,9 +138,9 @@ SPAN_DECLARE_NONSTD(int) modem_connect_tones_tx(modem_connect_tones_tx_state_t * \param len The number of samples in the array. \return The number of unprocessed samples. */ -SPAN_DECLARE_NONSTD(int) modem_connect_tones_rx(modem_connect_tones_rx_state_t *s, - const int16_t amp[], - int len); +SPAN_DECLARE(int) modem_connect_tones_rx(modem_connect_tones_rx_state_t *s, + const int16_t amp[], + int len); /*! Fake processing of a missing block of received modem connect tone samples (e.g due to packet loss). @@ -149,7 +149,7 @@ SPAN_DECLARE_NONSTD(int) modem_connect_tones_rx(modem_connect_tones_rx_state_t * \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) modem_connect_tones_rx_fillin(modem_connect_tones_rx_state_t *s, int len); +SPAN_DECLARE(int) modem_connect_tones_rx_fillin(modem_connect_tones_rx_state_t *s, int len); /*! \brief Test if a modem_connect tone has been detected. \param s The context. diff --git a/libs/spandsp/src/spandsp/silence_gen.h b/libs/spandsp/src/spandsp/silence_gen.h index df90ec0e9e..94357b7247 100644 --- a/libs/spandsp/src/spandsp/silence_gen.h +++ b/libs/spandsp/src/spandsp/silence_gen.h @@ -41,7 +41,7 @@ extern "C" \return The number of samples actually generated. This will be zero when there is nothing to send. */ -SPAN_DECLARE_NONSTD(int) silence_gen(silence_gen_state_t *s, int16_t *amp, int max_len); +SPAN_DECLARE(int) silence_gen(silence_gen_state_t *s, int16_t *amp, int max_len); /*! Set a silence generator context to output continuous silence. \brief Set a silence generator context to output continuous silence. @@ -110,7 +110,7 @@ SPAN_DECLARE(int) silence_gen_free(silence_gen_state_t *s); \param len The length of the signal buffer \return 0. */ -SPAN_DECLARE_NONSTD(int) span_dummy_rx(void *user_data, const int16_t amp[], int len); +SPAN_DECLARE(int) span_dummy_rx(void *user_data, const int16_t amp[], int len); /*! A dummy routine to use as a signal modifier callback, when we aren't really trying to process the signal. It just returns without affecting @@ -131,7 +131,7 @@ SPAN_DECLARE(int) span_dummy_mod(void *user_data, int16_t amp[], int len); \param len The length of the signal buffer \return 0. */ -SPAN_DECLARE_NONSTD(int) span_dummy_rx_fillin(void *user_data, int len); +SPAN_DECLARE(int) span_dummy_rx_fillin(void *user_data, int len); #if defined(__cplusplus) } diff --git a/libs/spandsp/src/spandsp/stdbool.h b/libs/spandsp/src/spandsp/stdbool.h index ce1dc387b5..faceaac3f9 100644 --- a/libs/spandsp/src/spandsp/stdbool.h +++ b/libs/spandsp/src/spandsp/stdbool.h @@ -32,14 +32,16 @@ #if !defined(_STDBOOL_H) #define _STDBOOL_H + #ifdef _MSC_VER #pragma warning (disable: 4005) #endif + #if !defined(__cplusplus) #define _Bool int -#define false 0 #define bool int +#define false 0 #define true (!false) #else diff --git a/libs/spandsp/src/spandsp/t30.h b/libs/spandsp/src/spandsp/t30.h index b3c0b6ad36..00749004f2 100644 --- a/libs/spandsp/src/spandsp/t30.h +++ b/libs/spandsp/src/spandsp/t30.h @@ -576,7 +576,7 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status); \brief Get a bit of received non-ECM image data. \param user_data An opaque pointer, which must point to the T.30 context. \return The next bit to transmit. */ -SPAN_DECLARE_NONSTD(int) t30_non_ecm_get_bit(void *user_data); +SPAN_DECLARE(int) t30_non_ecm_get_bit(void *user_data); /*! Get a chunk of received non-ECM image data. \brief Get a bit of received non-ECM image data. @@ -590,7 +590,7 @@ SPAN_DECLARE(int) t30_non_ecm_get(void *user_data, uint8_t buf[], int max_len); \brief Process a bit of received non-ECM image data \param user_data An opaque pointer, which must point to the T.30 context. \param bit The received bit. */ -SPAN_DECLARE_NONSTD(void) t30_non_ecm_put_bit(void *user_data, int bit); +SPAN_DECLARE(void) t30_non_ecm_put_bit(void *user_data, int bit); /*! Process a chunk of received non-ECM image data. \brief Process a chunk of received non-ECM image data @@ -605,7 +605,7 @@ SPAN_DECLARE(void) t30_non_ecm_put(void *user_data, const uint8_t buf[], int len \param msg The HDLC message. \param len The length of the message, in octets. \param ok True if the frame was received without error. */ -SPAN_DECLARE_NONSTD(void) t30_hdlc_accept(void *user_data, const uint8_t msg[], int len, int ok); +SPAN_DECLARE(void) t30_hdlc_accept(void *user_data, const uint8_t msg[], int len, int ok); /*! Report the passage of time to the T.30 engine. \brief Report the passage of time to the T.30 engine. diff --git a/libs/spandsp/src/spandsp/t31.h b/libs/spandsp/src/spandsp/t31.h index b61a4142f9..45237c4e7c 100644 --- a/libs/spandsp/src/spandsp/t31.h +++ b/libs/spandsp/src/spandsp/t31.h @@ -65,7 +65,7 @@ SPAN_DECLARE(int) t31_at_rx(t31_state_t *s, const char *t, int len); \param amp The audio sample buffer. \param len The number of samples in the buffer. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) t31_rx(t31_state_t *s, int16_t amp[], int len); +SPAN_DECLARE(int) t31_rx(t31_state_t *s, int16_t amp[], int len); /*! Fake processing of a missing block of received T.31 modem audio samples (e.g due to packet loss). @@ -73,7 +73,7 @@ SPAN_DECLARE_NONSTD(int) t31_rx(t31_state_t *s, int16_t amp[], int len); \param s The T.31 modem context. \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) t31_rx_fillin(t31_state_t *s, int len); +SPAN_DECLARE(int) t31_rx_fillin(t31_state_t *s, int len); /*! Generate a block of T.31 modem audio samples. \brief Generate a block of T.31 modem audio samples. @@ -82,7 +82,7 @@ SPAN_DECLARE_NONSTD(int) t31_rx_fillin(t31_state_t *s, int len); \param max_len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) t31_tx(t31_state_t *s, int16_t amp[], int max_len); +SPAN_DECLARE(int) t31_tx(t31_state_t *s, int16_t amp[], int max_len); SPAN_DECLARE(int) t31_t38_send_timeout(t31_state_t *s, int samples); diff --git a/libs/spandsp/src/spandsp/t38_core.h b/libs/spandsp/src/spandsp/t38_core.h index 86e45089ac..3d3d83da51 100644 --- a/libs/spandsp/src/spandsp/t38_core.h +++ b/libs/spandsp/src/spandsp/t38_core.h @@ -288,7 +288,7 @@ SPAN_DECLARE(int) t38_core_send_data_multi_field(t38_core_state_t *s, int data_t \param len The length of the packet contents. \param seq_no The packet sequence number. \return 0 for OK, else -1. */ -SPAN_DECLARE_NONSTD(int) t38_core_rx_ifp_packet(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t seq_no); +SPAN_DECLARE(int) t38_core_rx_ifp_packet(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t seq_no); /*! \brief Process a received T.38 IFP packet from a reliable stream (e.g. TCP). \param s The T.38 context. @@ -296,7 +296,7 @@ SPAN_DECLARE_NONSTD(int) t38_core_rx_ifp_packet(t38_core_state_t *s, const uint8 \param len The length of the packet contents. \param seq_no The packet sequence number, used for logging purposes. \return The length of the packet processed, or -1 if there is an error in the packet, or too few bytes of data to complete it. */ -SPAN_DECLARE_NONSTD(int) t38_core_rx_ifp_stream(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t log_seq_no); +SPAN_DECLARE(int) t38_core_rx_ifp_stream(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t log_seq_no); /*! Set the method to be used for data rate management, as per the T.38 spec. \param s The T.38 context. diff --git a/libs/spandsp/src/spandsp/t38_gateway.h b/libs/spandsp/src/spandsp/t38_gateway.h index 8d7f2e0500..c6f4a876f3 100644 --- a/libs/spandsp/src/spandsp/t38_gateway.h +++ b/libs/spandsp/src/spandsp/t38_gateway.h @@ -102,7 +102,7 @@ SPAN_DECLARE(int) t38_gateway_free(t38_gateway_state_t *s); \param amp The audio sample buffer. \param len The number of samples in the buffer. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len); +SPAN_DECLARE(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len); /*! Apply fake processing when a block of audio samples is missing (e.g due to packet loss). @@ -112,7 +112,7 @@ SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], i \return The number of samples unprocessed. This should only be non-zero if the software has reached the end of the FAX call. */ -SPAN_DECLARE_NONSTD(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len); +SPAN_DECLARE(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len); /*! Generate a block of FAX audio samples. \brief Generate a block of FAX audio samples. @@ -121,7 +121,7 @@ SPAN_DECLARE_NONSTD(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len); \param max_len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) t38_gateway_tx(t38_gateway_state_t *s, int16_t amp[], int max_len); +SPAN_DECLARE(int) t38_gateway_tx(t38_gateway_state_t *s, int16_t amp[], int max_len); /*! Control whether error correcting mode (ECM) is allowed. \brief Control whether error correcting mode (ECM) is allowed. diff --git a/libs/spandsp/src/spandsp/t38_non_ecm_buffer.h b/libs/spandsp/src/spandsp/t38_non_ecm_buffer.h index 3ca28c3d12..a841177d60 100644 --- a/libs/spandsp/src/spandsp/t38_non_ecm_buffer.h +++ b/libs/spandsp/src/spandsp/t38_non_ecm_buffer.h @@ -124,7 +124,7 @@ SPAN_DECLARE(void) t38_non_ecm_buffer_report_output_status(t38_non_ecm_buffer_st /*! \brief Get the next bit of data from a T.38 rate adapting non-ECM buffer context. \param user_data The buffer context, cast to a void pointer. \return The next bit, or one of the values indicating a change of modem status. */ -SPAN_DECLARE_NONSTD(int) t38_non_ecm_buffer_get_bit(void *user_data); +SPAN_DECLARE(int) t38_non_ecm_buffer_get_bit(void *user_data); #if defined(__cplusplus) } diff --git a/libs/spandsp/src/spandsp/telephony.h b/libs/spandsp/src/spandsp/telephony.h index 58a8e29c6a..0d0f306fe1 100644 --- a/libs/spandsp/src/spandsp/telephony.h +++ b/libs/spandsp/src/spandsp/telephony.h @@ -28,21 +28,17 @@ #if defined(_M_IX86) || defined(_M_X64) #if defined(LIBSPANDSP_EXPORTS) -#define SPAN_DECLARE(type) __declspec(dllexport) type __stdcall -#define SPAN_DECLARE_NONSTD(type) __declspec(dllexport) type __cdecl +#define SPAN_DECLARE(type) __declspec(dllexport) type #define SPAN_DECLARE_DATA __declspec(dllexport) #else -#define SPAN_DECLARE(type) __declspec(dllimport) type __stdcall -#define SPAN_DECLARE_NONSTD(type) __declspec(dllimport) type __cdecl +#define SPAN_DECLARE(type) __declspec(dllimport) type #define SPAN_DECLARE_DATA __declspec(dllimport) #endif #elif defined(SPANDSP_USE_EXPORT_CAPABILITY) && (defined(__GNUC__) || defined(__SUNCC__)) #define SPAN_DECLARE(type) __attribute__((visibility("default"))) type -#define SPAN_DECLARE_NONSTD(type) __attribute__((visibility("default"))) type #define SPAN_DECLARE_DATA __attribute__((visibility("default"))) #else #define SPAN_DECLARE(type) /**/ type -#define SPAN_DECLARE_NONSTD(type) /**/ type #define SPAN_DECLARE_DATA /**/ #endif diff --git a/libs/spandsp/src/spandsp/tone_generate.h b/libs/spandsp/src/spandsp/tone_generate.h index 7fe140c216..a3d1b5a893 100644 --- a/libs/spandsp/src/spandsp/tone_generate.h +++ b/libs/spandsp/src/spandsp/tone_generate.h @@ -88,7 +88,7 @@ SPAN_DECLARE(tone_gen_descriptor_t *) tone_gen_descriptor_init(tone_gen_descript SPAN_DECLARE(void) tone_gen_descriptor_free(tone_gen_descriptor_t *s); -SPAN_DECLARE_NONSTD(int) tone_gen(tone_gen_state_t *s, int16_t amp[], int max_samples); +SPAN_DECLARE(int) tone_gen(tone_gen_state_t *s, int16_t amp[], int max_samples); SPAN_DECLARE(tone_gen_state_t *) tone_gen_init(tone_gen_state_t *s, tone_gen_descriptor_t *t); diff --git a/libs/spandsp/src/spandsp/v17rx.h b/libs/spandsp/src/spandsp/v17rx.h index 8aebe85263..8f6c34993d 100644 --- a/libs/spandsp/src/spandsp/v17rx.h +++ b/libs/spandsp/src/spandsp/v17rx.h @@ -284,7 +284,7 @@ SPAN_DECLARE(void) v17_rx_set_modem_status_handler(v17_rx_state_t *s, modem_stat \param len The number of samples in the buffer. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len); +SPAN_DECLARE(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len); /*! Fake processing of a missing block of received V.17 modem audio samples. (e.g due to packet loss). @@ -293,7 +293,7 @@ SPAN_DECLARE_NONSTD(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len) \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v17_rx_fillin(v17_rx_state_t *s, int len); +SPAN_DECLARE(int) v17_rx_fillin(v17_rx_state_t *s, int len); /*! Get a snapshot of the current equalizer coefficients. \brief Get a snapshot of the current equalizer coefficients. diff --git a/libs/spandsp/src/spandsp/v17tx.h b/libs/spandsp/src/spandsp/v17tx.h index 02fb7309ad..79d65629fc 100644 --- a/libs/spandsp/src/spandsp/v17tx.h +++ b/libs/spandsp/src/spandsp/v17tx.h @@ -155,7 +155,7 @@ SPAN_DECLARE(void) v17_tx_set_modem_status_handler(v17_tx_state_t *s, modem_stat \param len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) v17_tx(v17_tx_state_t *s, int16_t amp[], int len); +SPAN_DECLARE(int) v17_tx(v17_tx_state_t *s, int16_t amp[], int len); #if defined(__cplusplus) } diff --git a/libs/spandsp/src/spandsp/v18.h b/libs/spandsp/src/spandsp/v18.h index 04c0db7c15..50aa5ab260 100644 --- a/libs/spandsp/src/spandsp/v18.h +++ b/libs/spandsp/src/spandsp/v18.h @@ -143,7 +143,7 @@ SPAN_DECLARE(int) v18_free(v18_state_t *s); \param max_len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) v18_tx(v18_state_t *s, int16_t amp[], int max_len); +SPAN_DECLARE(int) v18_tx(v18_state_t *s, int16_t amp[], int max_len); /*! Process a block of received V.18 audio samples. \brief Process a block of received V.18 audio samples. @@ -152,7 +152,7 @@ SPAN_DECLARE_NONSTD(int) v18_tx(v18_state_t *s, int16_t amp[], int max_len); \param len The number of samples in the buffer. \return The number of unprocessed samples. */ -SPAN_DECLARE_NONSTD(int) v18_rx(v18_state_t *s, const int16_t amp[], int len); +SPAN_DECLARE(int) v18_rx(v18_state_t *s, const int16_t amp[], int len); /*! Fake processing of a missing block of received V.18 audio samples. (e.g due to packet loss). @@ -161,7 +161,7 @@ SPAN_DECLARE_NONSTD(int) v18_rx(v18_state_t *s, const int16_t amp[], int len); \param len The number of samples to fake. \return The number of unprocessed samples. */ -SPAN_DECLARE_NONSTD(int) v18_rx_fillin(v18_state_t *s, int len); +SPAN_DECLARE(int) v18_rx_fillin(v18_state_t *s, int len); /*! \brief Put a string to a V.18 context's input buffer. \param s The V.18 context. diff --git a/libs/spandsp/src/spandsp/v22bis.h b/libs/spandsp/src/spandsp/v22bis.h index 50b2fb4467..fd93373cec 100644 --- a/libs/spandsp/src/spandsp/v22bis.h +++ b/libs/spandsp/src/spandsp/v22bis.h @@ -78,7 +78,7 @@ extern "C" \param amp The audio sample buffer. \param len The number of samples in the buffer. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int len); +SPAN_DECLARE(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int len); /*! Fake processing of a missing block of received V.22bis modem audio samples. (e.g due to packet loss). @@ -86,7 +86,7 @@ SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int l \param s The modem context. \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v22bis_rx_fillin(v22bis_state_t *s, int len); +SPAN_DECLARE(int) v22bis_rx_fillin(v22bis_state_t *s, int len); /*! Get a snapshot of the current equalizer coefficients. \brief Get a snapshot of the current equalizer coefficients. @@ -130,7 +130,7 @@ SPAN_DECLARE(void) v22bis_rx_set_qam_report_handler(v22bis_state_t *s, qam_repor \param amp The audio sample buffer. \param len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) v22bis_tx(v22bis_state_t *s, int16_t amp[], int len); +SPAN_DECLARE(int) v22bis_tx(v22bis_state_t *s, int16_t amp[], int len); /*! Adjust a V.22bis modem transmit context's power output. \brief Adjust a V.22bis modem transmit context's output power. diff --git a/libs/spandsp/src/spandsp/v27ter_rx.h b/libs/spandsp/src/spandsp/v27ter_rx.h index 80fdb3655f..cf8eaa99f7 100644 --- a/libs/spandsp/src/spandsp/v27ter_rx.h +++ b/libs/spandsp/src/spandsp/v27ter_rx.h @@ -117,7 +117,7 @@ SPAN_DECLARE(void) v27ter_rx_set_modem_status_handler(v27ter_rx_state_t *s, mode \param len The number of samples in the buffer. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], int len); +SPAN_DECLARE(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], int len); /*! Fake processing of a missing block of received V.27ter modem audio samples. (e.g due to packet loss). @@ -126,7 +126,7 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v27ter_rx_fillin(v27ter_rx_state_t *s, int len); +SPAN_DECLARE(int) v27ter_rx_fillin(v27ter_rx_state_t *s, int len); /*! Get a snapshot of the current equalizer coefficients. \brief Get a snapshot of the current equalizer coefficients. diff --git a/libs/spandsp/src/spandsp/v27ter_tx.h b/libs/spandsp/src/spandsp/v27ter_tx.h index fba064fea0..96a0df5f54 100644 --- a/libs/spandsp/src/spandsp/v27ter_tx.h +++ b/libs/spandsp/src/spandsp/v27ter_tx.h @@ -136,7 +136,7 @@ SPAN_DECLARE(void) v27ter_tx_set_modem_status_handler(v27ter_tx_state_t *s, mode \param len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) v27ter_tx(v27ter_tx_state_t *s, int16_t amp[], int len); +SPAN_DECLARE(int) v27ter_tx(v27ter_tx_state_t *s, int16_t amp[], int len); #if defined(__cplusplus) } diff --git a/libs/spandsp/src/spandsp/v29rx.h b/libs/spandsp/src/spandsp/v29rx.h index 378d4fe955..9ef548e4c7 100644 --- a/libs/spandsp/src/spandsp/v29rx.h +++ b/libs/spandsp/src/spandsp/v29rx.h @@ -196,7 +196,7 @@ SPAN_DECLARE(void) v29_rx_set_modem_status_handler(v29_rx_state_t *s, modem_stat \param amp The audio sample buffer. \param len The number of samples in the buffer. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len); +SPAN_DECLARE(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len); /*! Fake processing of a missing block of received V.29 modem audio samples. (e.g due to packet loss). @@ -204,7 +204,7 @@ SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len) \param s The modem context. \param len The number of samples to fake. \return The number of samples unprocessed. */ -SPAN_DECLARE_NONSTD(int) v29_rx_fillin(v29_rx_state_t *s, int len); +SPAN_DECLARE(int) v29_rx_fillin(v29_rx_state_t *s, int len); /*! Get a snapshot of the current equalizer coefficients. \brief Get a snapshot of the current equalizer coefficients. diff --git a/libs/spandsp/src/spandsp/v29tx.h b/libs/spandsp/src/spandsp/v29tx.h index 18bcf66b6a..6bdb5de9e7 100644 --- a/libs/spandsp/src/spandsp/v29tx.h +++ b/libs/spandsp/src/spandsp/v29tx.h @@ -167,7 +167,7 @@ SPAN_DECLARE(void) v29_tx_set_modem_status_handler(v29_tx_state_t *s, modem_stat \param len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) v29_tx(v29_tx_state_t *s, int16_t amp[], int len); +SPAN_DECLARE(int) v29_tx(v29_tx_state_t *s, int16_t amp[], int len); #if defined(__cplusplus) } diff --git a/libs/spandsp/src/spandsp/v42.h b/libs/spandsp/src/spandsp/v42.h index 8002d43eed..fa1d7c1b5f 100644 --- a/libs/spandsp/src/spandsp/v42.h +++ b/libs/spandsp/src/spandsp/v42.h @@ -45,7 +45,7 @@ extern "C" SPAN_DECLARE(const char *) lapm_status_to_str(int status); -SPAN_DECLARE_NONSTD(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok); +SPAN_DECLARE(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok); SPAN_DECLARE(void) v42_start(v42_state_t *s); diff --git a/libs/spandsp/src/spandsp/v8.h b/libs/spandsp/src/spandsp/v8.h index 9ded271aec..ab3d1466c5 100644 --- a/libs/spandsp/src/spandsp/v8.h +++ b/libs/spandsp/src/spandsp/v8.h @@ -173,7 +173,7 @@ SPAN_DECLARE(logging_state_t *) v8_get_logging_state(v8_state_t *s); \param max_len The number of samples to be generated. \return The number of samples actually generated. */ -SPAN_DECLARE_NONSTD(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len); +SPAN_DECLARE(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len); /*! Process a block of received V.8 audio samples. \brief Process a block of received V.8 audio samples. @@ -181,7 +181,7 @@ SPAN_DECLARE_NONSTD(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len); \param amp The audio sample buffer. \param len The number of samples in the buffer. */ -SPAN_DECLARE_NONSTD(int) v8_rx(v8_state_t *s, const int16_t *amp, int len); +SPAN_DECLARE(int) v8_rx(v8_state_t *s, const int16_t *amp, int len); /*! Log the list of supported modulations. \brief Log the list of supported modulations. diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index 3a31893e30..f13d4eb879 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -6401,7 +6401,7 @@ static void t30_non_ecm_rx_status(void *user_data, int status) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(void) t30_non_ecm_put_bit(void *user_data, int bit) +SPAN_DECLARE(void) t30_non_ecm_put_bit(void *user_data, int bit) { t30_state_t *s; int res; @@ -6499,7 +6499,7 @@ SPAN_DECLARE(void) t30_non_ecm_put(void *user_data, const uint8_t buf[], int len } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t30_non_ecm_get_bit(void *user_data) +SPAN_DECLARE(int) t30_non_ecm_get_bit(void *user_data) { int bit; t30_state_t *s; @@ -6705,7 +6705,7 @@ static void t30_hdlc_rx_status(void *user_data, int status) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(void) t30_hdlc_accept(void *user_data, const uint8_t *msg, int len, int ok) +SPAN_DECLARE(void) t30_hdlc_accept(void *user_data, const uint8_t *msg, int len, int ok) { t30_state_t *s; diff --git a/libs/spandsp/src/t31.c b/libs/spandsp/src/t31.c index e63eb229e8..91e1e8063c 100644 --- a/libs/spandsp/src/t31.c +++ b/libs/spandsp/src/t31.c @@ -2772,7 +2772,7 @@ static int initial_timed_rx(void *user_data, const int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t31_rx(t31_state_t *s, int16_t amp[], int len) +SPAN_DECLARE(int) t31_rx(t31_state_t *s, int16_t amp[], int len) { int i; int32_t power; @@ -2822,7 +2822,7 @@ SPAN_DECLARE_NONSTD(int) t31_rx(t31_state_t *s, int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t31_rx_fillin(t31_state_t *s, int len) +SPAN_DECLARE(int) t31_rx_fillin(t31_state_t *s, int len) { /* To mitigate the effect of lost packets on a packet network we should try to sustain the status quo. If there is no receive modem running, keep @@ -2848,7 +2848,7 @@ SPAN_DECLARE_NONSTD(int) t31_rx_fillin(t31_state_t *s, int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t31_tx(t31_state_t *s, int16_t amp[], int max_len) +SPAN_DECLARE(int) t31_tx(t31_state_t *s, int16_t amp[], int max_len) { int len; diff --git a/libs/spandsp/src/t38_core.c b/libs/spandsp/src/t38_core.c index de13af3e11..347549549f 100644 --- a/libs/spandsp/src/t38_core.c +++ b/libs/spandsp/src/t38_core.c @@ -347,7 +347,7 @@ static __inline__ int classify_seq_no_offset(int expected, int actual) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t38_core_rx_ifp_stream(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t log_seq_no) +SPAN_DECLARE(int) t38_core_rx_ifp_stream(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t log_seq_no) { int i; int t30_indicator; @@ -668,7 +668,7 @@ SPAN_DECLARE_NONSTD(int) t38_core_rx_ifp_stream(t38_core_state_t *s, const uint8 } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t38_core_rx_ifp_packet(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t seq_no) +SPAN_DECLARE(int) t38_core_rx_ifp_packet(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t seq_no) { int log_seq_no; int ptr; diff --git a/libs/spandsp/src/t38_gateway.c b/libs/spandsp/src/t38_gateway.c index 4467a615ad..53c877e591 100644 --- a/libs/spandsp/src/t38_gateway.c +++ b/libs/spandsp/src/t38_gateway.c @@ -2134,7 +2134,7 @@ static void update_rx_timing(t38_gateway_state_t *s, int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len) +SPAN_DECLARE(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], int len) { int i; @@ -2153,7 +2153,7 @@ SPAN_DECLARE_NONSTD(int) t38_gateway_rx(t38_gateway_state_t *s, int16_t amp[], i } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len) +SPAN_DECLARE(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len) { /* To mitigate the effect of lost packets on a packet network we should try to sustain the status quo. If there is no receive modem running, keep @@ -2181,7 +2181,7 @@ SPAN_DECLARE_NONSTD(int) t38_gateway_rx_fillin(t38_gateway_state_t *s, int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t38_gateway_tx(t38_gateway_state_t *s, int16_t amp[], int max_len) +SPAN_DECLARE(int) t38_gateway_tx(t38_gateway_state_t *s, int16_t amp[], int max_len) { int len; #if defined(LOG_FAX_AUDIO) diff --git a/libs/spandsp/src/t38_non_ecm_buffer.c b/libs/spandsp/src/t38_non_ecm_buffer.c index b5b2c8244b..99fa456a96 100644 --- a/libs/spandsp/src/t38_non_ecm_buffer.c +++ b/libs/spandsp/src/t38_non_ecm_buffer.c @@ -85,7 +85,7 @@ static void restart_buffer(t38_non_ecm_buffer_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) t38_non_ecm_buffer_get_bit(void *user_data) +SPAN_DECLARE(int) t38_non_ecm_buffer_get_bit(void *user_data) { t38_non_ecm_buffer_state_t *s; int bit; diff --git a/libs/spandsp/src/tone_generate.c b/libs/spandsp/src/tone_generate.c index be3f6854a4..c01c970674 100644 --- a/libs/spandsp/src/tone_generate.c +++ b/libs/spandsp/src/tone_generate.c @@ -119,7 +119,7 @@ SPAN_DECLARE(void) tone_gen_descriptor_free(tone_gen_descriptor_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) tone_gen(tone_gen_state_t *s, int16_t amp[], int max_samples) +SPAN_DECLARE(int) tone_gen(tone_gen_state_t *s, int16_t amp[], int max_samples) { int samples; int limit; diff --git a/libs/spandsp/src/v17rx.c b/libs/spandsp/src/v17rx.c index aea4e5913d..cda544990e 100644 --- a/libs/spandsp/src/v17rx.c +++ b/libs/spandsp/src/v17rx.c @@ -1229,7 +1229,7 @@ static __inline__ int signal_detect(v17_rx_state_t *s, int16_t amp) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len) +SPAN_DECLARE(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len) { int i; int step; @@ -1341,7 +1341,7 @@ SPAN_DECLARE_NONSTD(int) v17_rx(v17_rx_state_t *s, const int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v17_rx_fillin(v17_rx_state_t *s, int len) +SPAN_DECLARE(int) v17_rx_fillin(v17_rx_state_t *s, int len) { int i; diff --git a/libs/spandsp/src/v17tx.c b/libs/spandsp/src/v17tx.c index 4d3d8f9990..a57bc9f705 100644 --- a/libs/spandsp/src/v17tx.c +++ b/libs/spandsp/src/v17tx.c @@ -296,7 +296,7 @@ static __inline__ complexf_t getbaud(v17_tx_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v17_tx(v17_tx_state_t *s, int16_t amp[], int len) +SPAN_DECLARE(int) v17_tx(v17_tx_state_t *s, int16_t amp[], int len) { #if defined(SPANDSP_USE_FIXED_POINT) complexi16_t v; diff --git a/libs/spandsp/src/v18.c b/libs/spandsp/src/v18.c index 3b72b296e1..0f34415720 100644 --- a/libs/spandsp/src/v18.c +++ b/libs/spandsp/src/v18.c @@ -978,7 +978,7 @@ static void v18_textphone_put_async_byte(void *user_data, int byte) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v18_tx(v18_state_t *s, int16_t *amp, int max_len) +SPAN_DECLARE(int) v18_tx(v18_state_t *s, int16_t *amp, int max_len) { int len; int lenx; @@ -1006,7 +1006,7 @@ SPAN_DECLARE_NONSTD(int) v18_tx(v18_state_t *s, int16_t *amp, int max_len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v18_rx(v18_state_t *s, const int16_t amp[], int len) +SPAN_DECLARE(int) v18_rx(v18_state_t *s, const int16_t amp[], int len) { if (s->rx_suppression > 0) { @@ -1037,7 +1037,7 @@ SPAN_DECLARE_NONSTD(int) v18_rx(v18_state_t *s, const int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v18_rx_fillin(v18_state_t *s, int len) +SPAN_DECLARE(int) v18_rx_fillin(v18_state_t *s, int len) { if (s->rx_suppression > 0) { diff --git a/libs/spandsp/src/v22bis_rx.c b/libs/spandsp/src/v22bis_rx.c index af922882a6..cfb4e1378a 100644 --- a/libs/spandsp/src/v22bis_rx.c +++ b/libs/spandsp/src/v22bis_rx.c @@ -776,7 +776,7 @@ static __inline__ void process_half_baud(v22bis_state_t *s, const complexf_t *sa } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int len) +SPAN_DECLARE(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int len) { int i; int step; @@ -919,7 +919,7 @@ SPAN_DECLARE_NONSTD(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int l } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v22bis_rx_fillin(v22bis_state_t *s, int len) +SPAN_DECLARE(int) v22bis_rx_fillin(v22bis_state_t *s, int len) { int i; diff --git a/libs/spandsp/src/v22bis_tx.c b/libs/spandsp/src/v22bis_tx.c index 430ebc5d84..41afff55c7 100644 --- a/libs/spandsp/src/v22bis_tx.c +++ b/libs/spandsp/src/v22bis_tx.c @@ -469,7 +469,7 @@ static complexf_t getbaud(v22bis_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v22bis_tx(v22bis_state_t *s, int16_t amp[], int len) +SPAN_DECLARE(int) v22bis_tx(v22bis_state_t *s, int16_t amp[], int len) { #if defined(SPANDSP_USE_FIXED_POINT) complexi16_t v; diff --git a/libs/spandsp/src/v27ter_rx.c b/libs/spandsp/src/v27ter_rx.c index 93d3b4680d..9383d46af0 100644 --- a/libs/spandsp/src/v27ter_rx.c +++ b/libs/spandsp/src/v27ter_rx.c @@ -828,7 +828,7 @@ static __inline__ int signal_detect(v27ter_rx_state_t *s, int16_t amp) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], int len) +SPAN_DECLARE(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], int len) { int i; int step; @@ -978,7 +978,7 @@ SPAN_DECLARE_NONSTD(int) v27ter_rx(v27ter_rx_state_t *s, const int16_t amp[], in } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v27ter_rx_fillin(v27ter_rx_state_t *s, int len) +SPAN_DECLARE(int) v27ter_rx_fillin(v27ter_rx_state_t *s, int len) { int i; diff --git a/libs/spandsp/src/v27ter_tx.c b/libs/spandsp/src/v27ter_tx.c index f6b69917bb..e0f2aaf1af 100644 --- a/libs/spandsp/src/v27ter_tx.c +++ b/libs/spandsp/src/v27ter_tx.c @@ -241,7 +241,7 @@ static complexf_t getbaud(v27ter_tx_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v27ter_tx(v27ter_tx_state_t *s, int16_t amp[], int len) +SPAN_DECLARE(int) v27ter_tx(v27ter_tx_state_t *s, int16_t amp[], int len) { #if defined(SPANDSP_USE_FIXED_POINT) complexi16_t v; diff --git a/libs/spandsp/src/v29rx.c b/libs/spandsp/src/v29rx.c index da1ef0c909..4bed319246 100644 --- a/libs/spandsp/src/v29rx.c +++ b/libs/spandsp/src/v29rx.c @@ -910,7 +910,7 @@ static __inline__ int signal_detect(v29_rx_state_t *s, int16_t amp) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len) +SPAN_DECLARE(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len) { int i; int step; @@ -1026,7 +1026,7 @@ SPAN_DECLARE_NONSTD(int) v29_rx(v29_rx_state_t *s, const int16_t amp[], int len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v29_rx_fillin(v29_rx_state_t *s, int len) +SPAN_DECLARE(int) v29_rx_fillin(v29_rx_state_t *s, int len) { int i; diff --git a/libs/spandsp/src/v29tx.c b/libs/spandsp/src/v29tx.c index c36027701d..bef54364e8 100644 --- a/libs/spandsp/src/v29tx.c +++ b/libs/spandsp/src/v29tx.c @@ -212,7 +212,7 @@ static __inline__ complexf_t getbaud(v29_tx_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v29_tx(v29_tx_state_t *s, int16_t amp[], int len) +SPAN_DECLARE(int) v29_tx(v29_tx_state_t *s, int16_t amp[], int len) { #if defined(SPANDSP_USE_FIXED_POINT) complexi16_t v; diff --git a/libs/spandsp/src/v42.c b/libs/spandsp/src/v42.c index 44e6bd01d9..4dac118f65 100644 --- a/libs/spandsp/src/v42.c +++ b/libs/spandsp/src/v42.c @@ -1065,7 +1065,7 @@ static void lapm_hdlc_underflow(void *user_data) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok) +SPAN_DECLARE(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok) { lapm_state_t *s; v42_state_t *ss; diff --git a/libs/spandsp/src/v8.c b/libs/spandsp/src/v8.c index 40cc6feac1..4c5dd43244 100644 --- a/libs/spandsp/src/v8.c +++ b/libs/spandsp/src/v8.c @@ -734,7 +734,7 @@ static void send_cm_jm(v8_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len) +SPAN_DECLARE(int) v8_tx(v8_state_t *s, int16_t *amp, int max_len) { int len; @@ -852,7 +852,7 @@ static void handle_modem_connect_tone(v8_state_t *s, int tone) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE_NONSTD(int) v8_rx(v8_state_t *s, const int16_t *amp, int len) +SPAN_DECLARE(int) v8_rx(v8_state_t *s, const int16_t *amp, int len) { int residual_samples; int tone; diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am index 47ee06aec3..55d84e4d99 100644 --- a/libs/spandsp/tests/Makefile.am +++ b/libs/spandsp/tests/Makefile.am @@ -225,7 +225,7 @@ echo_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspand fax_decode_SOURCES = fax_decode.c fax_decode_LDADD = $(LIBDIR) -lspandsp -fax_tests_SOURCES = fax_tests.c fax_utils.c media_monitor.cpp +fax_tests_SOURCES = fax_tests.c fax_utils.c media_monitor.cpp fax_tester.c fax_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp fsk_tests_SOURCES = fsk_tests.c diff --git a/libs/spandsp/tests/fax_tester.c b/libs/spandsp/tests/fax_tester.c index fb5dcc1ac1..8885957972 100644 --- a/libs/spandsp/tests/fax_tester.c +++ b/libs/spandsp/tests/fax_tester.c @@ -85,7 +85,7 @@ struct xml_node_parms_s xmlChar *compression; }; -static struct +struct { const char *tag; int code; @@ -162,16 +162,278 @@ static void timer_update(faxtester_state_t *s, int len) if (s->timer > s->timeout) { s->timeout = 0x7FFFFFFFFFFFFFFFLL; - if (s->front_end_step_timeout_handler) - s->front_end_step_timeout_handler(s, s->front_end_step_timeout_user_data); + span_log(&s->logging, SPAN_LOG_FLOW, "FAX tester step timed out\n"); + printf("Test failed\n"); + exit(2); } } /*- End of function --------------------------------------------------------*/ static void front_end_step_complete(faxtester_state_t *s) { - if (s->front_end_step_complete_handler) - s->front_end_step_complete_handler(s, s->front_end_step_complete_user_data); + while (faxtester_next_step(s) == 0) + ; + /*endwhile*/ +} +/*- End of function --------------------------------------------------------*/ + +static int faxtester_phase_b_handler(void *user_data, int result) +{ + int ch; + int status; + faxtester_state_t *s; + const char *u; + + s = (faxtester_state_t *) user_data; + ch = s->far_tag; + status = T30_ERR_OK; + if ((u = t30_get_rx_ident(s->far_t30))) + { + printf("%c: Phase B: remote ident '%s'\n", ch, u); + if (s->expected_rx_info.ident[0] && strcmp(s->expected_rx_info.ident, u)) + { + printf("%c: Phase B: remote ident incorrect! - expected '%s'\n", ch, s->expected_rx_info.ident); + status = T30_ERR_IDENT_UNACCEPTABLE; + } + } + else + { + if (s->expected_rx_info.ident[0]) + { + printf("%c: Phase B: remote ident missing!\n", ch); + status = T30_ERR_IDENT_UNACCEPTABLE; + } + } + if ((u = t30_get_rx_sub_address(s->far_t30))) + { + printf("%c: Phase B: remote sub-address '%s'\n", ch, u); + if (s->expected_rx_info.sub_address[0] && strcmp(s->expected_rx_info.sub_address, u)) + { + printf("%c: Phase B: remote sub-address incorrect! - expected '%s'\n", ch, s->expected_rx_info.sub_address); + status = T30_ERR_SUB_UNACCEPTABLE; + } + } + else + { + if (s->expected_rx_info.sub_address[0]) + { + printf("%c: Phase B: remote sub-address missing!\n", ch); + status = T30_ERR_SUB_UNACCEPTABLE; + } + } + if ((u = t30_get_rx_polled_sub_address(s->far_t30))) + { + printf("%c: Phase B: remote polled sub-address '%s'\n", ch, u); + if (s->expected_rx_info.polled_sub_address[0] && strcmp(s->expected_rx_info.polled_sub_address, u)) + { + printf("%c: Phase B: remote polled sub-address incorrect! - expected '%s'\n", ch, s->expected_rx_info.polled_sub_address); + status = T30_ERR_PSA_UNACCEPTABLE; + } + } + else + { + if (s->expected_rx_info.polled_sub_address[0]) + { + printf("%c: Phase B: remote polled sub-address missing!\n", ch); + status = T30_ERR_PSA_UNACCEPTABLE; + } + } + if ((u = t30_get_rx_selective_polling_address(s->far_t30))) + { + printf("%c: Phase B: remote selective polling address '%s'\n", ch, u); + if (s->expected_rx_info.selective_polling_address[0] && strcmp(s->expected_rx_info.selective_polling_address, u)) + { + printf("%c: Phase B: remote selective polling address incorrect! - expected '%s'\n", ch, s->expected_rx_info.selective_polling_address); + status = T30_ERR_SEP_UNACCEPTABLE; + } + } + else + { + if (s->expected_rx_info.selective_polling_address[0]) + { + printf("%c: Phase B: remote selective polling address missing!\n", ch); + status = T30_ERR_SEP_UNACCEPTABLE; + } + } + if ((u = t30_get_rx_sender_ident(s->far_t30))) + { + printf("%c: Phase B: remote sender ident '%s'\n", ch, u); + if (s->expected_rx_info.sender_ident[0] && strcmp(s->expected_rx_info.sender_ident, u)) + { + printf("%c: Phase B: remote sender ident incorrect! - expected '%s'\n", ch, s->expected_rx_info.sender_ident); + status = T30_ERR_SID_UNACCEPTABLE; + } + } + else + { + if (s->expected_rx_info.sender_ident[0]) + { + printf("%c: Phase B: remote sender ident missing!\n", ch); + status = T30_ERR_SID_UNACCEPTABLE; + } + } + if ((u = t30_get_rx_password(s->far_t30))) + { + printf("%c: Phase B: remote password '%s'\n", ch, u); + if (s->expected_rx_info.password[0] && strcmp(s->expected_rx_info.password, u)) + { + printf("%c: Phase B: remote password incorrect! - expected '%s'\n", ch, s->expected_rx_info.password); + status = T30_ERR_PWD_UNACCEPTABLE; + } + } + else + { + if (s->expected_rx_info.password[0]) + { + printf("%c: Phase B: remote password missing!\n", ch); + status = T30_ERR_PWD_UNACCEPTABLE; + } + } + printf("%c: Phase B handler on channel %c - (0x%X) %s\n", ch, ch, result, t30_frametype(result)); + return status; +} +/*- End of function --------------------------------------------------------*/ + +static int faxtester_phase_d_handler(void *user_data, int result) +{ + int i; + int ch; + faxtester_state_t *s; + char tag[20]; + + s = (faxtester_state_t *) user_data; + ch = s->far_tag; + i = 0; + snprintf(tag, sizeof(tag), "%c: Phase D", ch); + printf("%c: Phase D handler on channel %c - (0x%X) %s\n", ch, ch, result, t30_frametype(result)); + fax_log_page_transfer_statistics(s->far_t30, tag); + fax_log_tx_parameters(s->far_t30, tag); + fax_log_rx_parameters(s->far_t30, tag); + + if (s->use_receiver_not_ready) + t30_set_receiver_not_ready(s->far_t30, 3); + + if (s->test_local_interrupt) + { + if (i == 0) + { + printf("%c: Initiating interrupt request\n", ch); + t30_local_interrupt_request(s->far_t30, true); + } + else + { + switch (result) + { + case T30_PIP: + case T30_PRI_MPS: + case T30_PRI_EOM: + case T30_PRI_EOP: + printf("%c: Accepting interrupt request\n", ch); + t30_local_interrupt_request(s->far_t30, true); + break; + case T30_PIN: + break; + } + } + } + return T30_ERR_OK; +} +/*- End of function --------------------------------------------------------*/ + +static void faxtester_phase_e_handler(void *user_data, int result) +{ + int ch; + faxtester_state_t *s; + char tag[20]; + + s = (faxtester_state_t *) user_data; + ch = s->far_tag; + snprintf(tag, sizeof(tag), "%c: Phase E", ch); + printf("%c: Phase E handler on channel %c - (%d) %s\n", ch, ch, result, t30_completion_code_to_str(result)); + fax_log_final_transfer_statistics(s->far_t30, tag); + fax_log_tx_parameters(s->far_t30, tag); + fax_log_rx_parameters(s->far_t30, tag); +} +/*- End of function --------------------------------------------------------*/ + +static void t30_real_time_frame_handler(void *user_data, + bool incoming, + const uint8_t *msg, + int len) +{ + if (msg == NULL) + { + } + else + { + fprintf(stderr, + "T.30: Real time frame handler - %s, %s, length = %d\n", + (incoming) ? "line->T.30" : "T.30->line", + t30_frametype(msg[2]), + len); + } +} +/*- End of function --------------------------------------------------------*/ + +static int faxtester_document_handler(void *user_data, int event) +{ + int ch; + faxtester_state_t *s; + t30_state_t *t; + + s = (faxtester_state_t *) user_data; + ch = s->far_tag; + t = s->far_t30; + fprintf(stderr, "%c: Document handler on channel %c - event %d\n", ch, ch, event); + if (s->next_tx_file[0]) + { + t30_set_tx_file(t, s->next_tx_file, -1, -1); + s->next_tx_file[0] = '\0'; + return true; + } + return false; +} +/*- End of function --------------------------------------------------------*/ + +static void faxtester_real_time_frame_handler(faxtester_state_t *s, + int direction, + const uint8_t *msg, + int len) +{ + if (msg == NULL) + { + while (faxtester_next_step(s) == 0) + ; + /*endwhile*/ + } + else + { + fprintf(stderr, + "TST: Real time frame handler - %s, %s, length = %d\n", + (direction) ? "line->tester" : "tester->line", + t30_frametype(msg[2]), + len); + if (direction && msg[1] == s->awaited[1]) + { + if ((s->awaited_len >= 0 && len != abs(s->awaited_len)) + || + (s->awaited_len < 0 && len < abs(s->awaited_len)) + || + memcmp(msg, s->awaited, abs(s->awaited_len)) != 0) + { + span_log_buf(&s->logging, SPAN_LOG_FLOW, "Expected", s->awaited, abs(s->awaited_len)); + span_log_buf(&s->logging, SPAN_LOG_FLOW, "Received", msg, len); + printf("Test failed\n"); + exit(2); + } + } + if (msg[1] == s->awaited[1]) + { + while (faxtester_next_step(s) == 0) + ; + /*endwhile*/ + } + } } /*- End of function --------------------------------------------------------*/ @@ -295,29 +557,17 @@ static int non_ecm_get_bit(void *user_data) } /*- End of function --------------------------------------------------------*/ -void faxtester_set_non_ecm_image_buffer(faxtester_state_t *s, const uint8_t *buf, int len) +static void faxtester_set_ecm_image_buffer(faxtester_state_t *s, int block, int frame_size, int crc_hit) { - s->image_ptr = 0; - s->image_bit_ptr = 8; - s->image_len = len; - s->image_buffer = buf; -} -/*- End of function --------------------------------------------------------*/ - -void faxtester_set_ecm_image_buffer(faxtester_state_t *s, const uint8_t *buf, int len, int block, int frame_size, int crc_hit) -{ - int start; - - start = 256*frame_size*block; - if (len > start + 256*frame_size) - len = start + 256*frame_size; + s->image_ptr = 256*frame_size*block; + if (s->image_len > s->image_ptr + 256*frame_size) + s->image_len = s->image_ptr + 256*frame_size; s->ecm_frame_size = frame_size; - s->image_ptr = start; s->image_bit_ptr = 8; - s->image_len = len; - s->image_buffer = buf; s->corrupt_crc = crc_hit; + s->image_buffer = s->image; + /* Send the first frame */ hdlc_underflow_handler(s); } @@ -343,10 +593,7 @@ static void non_ecm_rx_status(void *user_data, int status) break; case SIG_STATUS_CARRIER_DOWN: if (s->modems.rx_trained) - { - if (s->real_time_frame_handler) - s->real_time_frame_handler(s, s->real_time_frame_user_data, true, NULL, 0); - } + faxtester_real_time_frame_handler(s, true, NULL, 0); s->modems.rx_signal_present = false; s->modems.rx_trained = false; break; @@ -400,8 +647,7 @@ static void hdlc_accept(void *user_data, const uint8_t *msg, int len, int ok) return; } s = (faxtester_state_t *) user_data; - if (s->real_time_frame_handler) - s->real_time_frame_handler(s, s->real_time_frame_user_data, true, msg, len); + faxtester_real_time_frame_handler(s, true, msg, len); } /*- End of function --------------------------------------------------------*/ @@ -546,7 +792,7 @@ void faxtester_set_tx_type(void *user_data, int type, int bit_rate, int short_tr break; case T30_MODEM_CED: case T30_MODEM_CNG: - tone = (type == T30_MODEM_CED) ? MODEM_CONNECT_TONES_FAX_CED : MODEM_CONNECT_TONES_FAX_CNG; + tone = (type == T30_MODEM_CED) ? FAX_MODEM_CED_TONE_TX : FAX_MODEM_CNG_TONE_TX; fax_modems_start_slow_modem(t, tone); s->transmit = true; break; @@ -613,34 +859,996 @@ void faxtester_set_tep_mode(faxtester_state_t *s, int use_tep) } /*- End of function --------------------------------------------------------*/ -void faxtester_set_real_time_frame_handler(faxtester_state_t *s, faxtester_real_time_frame_handler_t handler, void *user_data) +static void corrupt_image(faxtester_state_t *s, const char *bad_rows) { - s->real_time_frame_handler = handler; - s->real_time_frame_user_data = user_data; + int i; + int j; + int k; + uint32_t bits; + uint32_t bitsx; + int list[1000]; + int x; + int row; + const char *t; + + /* Form the list of rows to be hit */ + x = 0; + t = bad_rows; + while (*t) + { + while (isspace((int) *t)) + t++; + if (sscanf(t, "%d", &list[x]) < 1) + break; + x++; + while (isdigit((int) *t)) + t++; + if (*t == ',') + t++; + } + + /* Go through the image, and corrupt the first bit of every listed row */ + bits = 0x7FF; + bitsx = 0x7FF; + row = 0; + for (i = 0; i < s->image_len; i++) + { + bits ^= (s->image[i] << 11); + bitsx ^= (s->image[i] << 11); + for (j = 0; j < 8; j++) + { + if ((bits & 0xFFF) == 0x800) + { + /* We are at an EOL. Is this row in the list of rows to be corrupted? */ + row++; + for (k = 0; k < x; k++) + { + if (list[k] == row) + { + /* Corrupt this row. TSB85 says to hit the first bit after the EOL */ + bitsx ^= 0x1000; + } + } + } + bits >>= 1; + bitsx >>= 1; + } + s->image[i] = (bitsx >> 3) & 0xFF; + } + span_log(&s->logging, SPAN_LOG_FLOW, "%d rows found. %d corrupted\n", row, x); } /*- End of function --------------------------------------------------------*/ -void faxtester_set_front_end_step_complete_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t handler, void *user_data) +static int string_to_msg(uint8_t msg[], uint8_t mask[], const char buf[]) { - s->front_end_step_complete_handler = handler; - s->front_end_step_complete_user_data = user_data; + int i; + int x; + const char *t; + + msg[0] = 0; + mask[0] = 0xFF; + i = 0; + t = (char *) buf; + while (*t) + { + /* Skip white space */ + while (isspace((int) *t)) + t++; + /* If we find ... we allow arbitrary additional info beyond this point in the message */ + if (t[0] == '.' && t[1] == '.' && t[2] == '.') + { + return -i; + } + else if (isxdigit((int) *t)) + { + for ( ; isxdigit((int) *t); t++) + { + x = *t; + if (x >= 'a') + x -= 0x20; + if (x >= 'A') + x -= ('A' - 10); + else + x -= '0'; + msg[i] = (msg[i] << 4) | x; + } + mask[i] = 0xFF; + if (*t == '/') + { + /* There is a mask following the byte */ + mask[i] = 0; + for (t++; isxdigit((int) *t); t++) + { + x = *t; + if (x >= 'a') + x -= 0x20; + if (x >= 'A') + x -= ('A' - 10); + else + x -= '0'; + mask[i] = (mask[i] << 4) | x; + } + } + if (*t && !isspace((int) *t)) + { + /* Bad string */ + return 0; + } + i++; + } + } + return i; } /*- End of function --------------------------------------------------------*/ -void faxtester_set_front_end_step_timeout_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t handler, void *user_data) +void faxtester_set_flush_handler(faxtester_state_t *s, faxtester_flush_handler_t handler, void *user_data) { - s->front_end_step_timeout_handler = handler; - s->front_end_step_timeout_user_data = user_data; + s->flush_handler = handler; + s->flush_user_data = user_data; } /*- End of function --------------------------------------------------------*/ -faxtester_state_t *faxtester_init(faxtester_state_t *s, int calling_party) +static void fax_prepare(faxtester_state_t *s) +{ + if (s->far_fax) + { + fax_set_transmit_on_idle(s->far_fax, true); + fax_set_tep_mode(s->far_fax, true); + } +#if 0 + t30_set_tx_ident(s->far_t30, "1234567890"); + t30_set_tx_sub_address(s->far_t30, "Sub-address"); + t30_set_tx_sender_ident(s->far_t30, "Sender ID"); + t30_set_tx_password(s->far_t30, "Password"); + t30_set_tx_polled_sub_address(s->far_t30, "Polled sub-address"); + t30_set_tx_selective_polling_address(s->far_t30, "Sel polling address"); +#endif + t30_set_tx_nsf(s->far_t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSF\x00", 16); + //t30_set_tx_nss(s->far_t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSS\x00", 16); + t30_set_tx_nsc(s->far_t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSC\x00", 16); + t30_set_ecm_capability(s->far_t30, true); + t30_set_supported_t30_features(s->far_t30, + T30_SUPPORT_IDENTIFICATION + | T30_SUPPORT_SELECTIVE_POLLING + | T30_SUPPORT_SUB_ADDRESSING); + t30_set_supported_image_sizes(s->far_t30, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_WIDTH_255MM + | T4_SUPPORT_WIDTH_303MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED); + t30_set_supported_bilevel_resolutions(s->far_t30, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200); + t30_set_supported_colour_resolutions(s->far_t30, 0); + t30_set_supported_modems(s->far_t30, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17); + t30_set_supported_compressions(s->far_t30, T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6); + t30_set_phase_b_handler(s->far_t30, faxtester_phase_b_handler, (void *) s); + t30_set_phase_d_handler(s->far_t30, faxtester_phase_d_handler, (void *) s); + t30_set_phase_e_handler(s->far_t30, faxtester_phase_e_handler, (void *) s); + t30_set_real_time_frame_handler(s->far_t30, t30_real_time_frame_handler, (void *) s); + t30_set_document_handler(s->far_t30, faxtester_document_handler, (void *) s); +} +/*- End of function --------------------------------------------------------*/ + +static void get_node_parms(struct xml_node_parms_s *parms, xmlNodePtr node) +{ + parms->dir = xmlGetProp(node, (const xmlChar *) "dir"); + parms->type = xmlGetProp(node, (const xmlChar *) "type"); + parms->modem = xmlGetProp(node, (const xmlChar *) "modem"); + parms->value = xmlGetProp(node, (const xmlChar *) "value"); + parms->tag = xmlGetProp(node, (const xmlChar *) "tag"); + parms->bad_rows = xmlGetProp(node, (const xmlChar *) "bad_rows"); + parms->crc_error = xmlGetProp(node, (const xmlChar *) "crc_error"); + parms->pattern = xmlGetProp(node, (const xmlChar *) "pattern"); + parms->timein = xmlGetProp(node, (const xmlChar *) "timein"); + parms->timeout = xmlGetProp(node, (const xmlChar *) "timeout"); + parms->min_bits = xmlGetProp(node, (const xmlChar *) "min_bits"); + parms->frame_size = xmlGetProp(node, (const xmlChar *) "frame_size"); + parms->block = xmlGetProp(node, (const xmlChar *) "block"); + parms->compression = xmlGetProp(node, (const xmlChar *) "compression"); +} +/*- End of function --------------------------------------------------------*/ + +static void free_node_parms(struct xml_node_parms_s *parms) +{ + if (parms->dir) + xmlFree(parms->dir); + if (parms->type) + xmlFree(parms->type); + if (parms->modem) + xmlFree(parms->modem); + if (parms->value) + xmlFree(parms->value); + if (parms->tag) + xmlFree(parms->tag); + if (parms->bad_rows) + xmlFree(parms->bad_rows); + if (parms->crc_error) + xmlFree(parms->crc_error); + if (parms->pattern) + xmlFree(parms->pattern); + if (parms->timein) + xmlFree(parms->timein); + if (parms->timeout) + xmlFree(parms->timeout); + if (parms->min_bits) + xmlFree(parms->min_bits); + if (parms->frame_size) + xmlFree(parms->frame_size); + if (parms->block) + xmlFree(parms->block); + if (parms->compression) + xmlFree(parms->compression); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) faxtester_next_step(faxtester_state_t *s) +{ + int delay; + int flags; + struct xml_node_parms_s parms; + uint8_t buf[1000]; + uint8_t mask[1000]; + char path[1024]; + int i; + int j; + int hdlc; + int short_train; + int min_row_bits; + int ecm_frame_size; + int ecm_block; + int compression_type; + xmlChar *min; + xmlChar *max; + t4_tx_state_t t4_tx_state; + t30_stats_t t30_stats; + + s->test_for_call_clear = false; + if (s->cur == NULL) + { + if (!s->final_delayed) + { + /* Add a bit of waiting at the end, to ensure everything gets flushed through, + any timers can expire, etc. */ + faxtester_set_timeout(s, -1); + faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); + faxtester_set_tx_type(s, T30_MODEM_PAUSE, 0, 120000, false); + s->final_delayed = true; + return 1; + } + /* Finished */ + printf("Test passed\n"); + exit(0); + } + for (;;) + { + if (s->cur == NULL) + { + if (s->repeat_parent == NULL) + { + /* Finished */ + printf("Test passed\n"); + exit(0); + } + if (++s->repeat_count > s->repeat_max) + { + /* Finished */ + printf("Too many repeats\n"); + printf("Test failed\n"); + exit(0); + } + if (s->repeat_count < s->repeat_min) + { + s->cur = s->repeat_start; + } + else + { + s->cur = s->repeat_parent->next; + s->repeat_parent = NULL; + } + } + if (xmlStrcmp(s->cur->name, (const xmlChar *) "step") == 0) + { + break; + } + if (s->repeat_parent == NULL && xmlStrcmp(s->cur->name, (const xmlChar *) "repeat") == 0) + { + min = xmlGetProp(s->cur, (const xmlChar *) "min"); + max = xmlGetProp(s->cur, (const xmlChar *) "max"); + s->repeat_min = min ? atoi((const char *) min) : 0; + s->repeat_max = max ? atoi((const char *) max) : INT_MAX; + s->repeat_count = 0; + if (min) + xmlFree(min); + if (max) + xmlFree(max); + if (s->repeat_min > 0) + { + s->repeat_parent = s->cur; + s->repeat_start = + s->cur = s->cur->xmlChildrenNode; + continue; + } + } + s->cur = s->cur->next; + } + + get_node_parms(&parms, s->cur); + + s->cur = s->cur->next; + + span_log(&s->logging, + SPAN_LOG_FLOW, + "Dir - %s, type - %s, modem - %s, value - %s, timein - %s, timeout - %s, tag - %s\n", + (parms.dir) ? (const char *) parms.dir : " ", + (parms.type) ? (const char *) parms.type : "", + (parms.modem) ? (const char *) parms.modem : "", + (parms.value) ? (const char *) parms.value : "", + (parms.timein) ? (const char *) parms.timein : "", + (parms.timeout) ? (const char *) parms.timeout : "", + (parms.tag) ? (const char *) parms.tag : ""); + if (parms.type == NULL) + { + free_node_parms(&parms); + return 1; + } + s->timein_x = (parms.timein) ? atoi((const char *) parms.timein) : -1; + s->timeout_x = (parms.timeout) ? atoi((const char *) parms.timeout) : -1; + + if (parms.dir && strcasecmp((const char *) parms.dir, "R") == 0) + { + /* Receive always has a timeout applied. */ + if (s->timeout_x < 0) + s->timeout_x = 7000; + faxtester_set_timeout(s, s->timeout_x); + if (parms.modem) + { + hdlc = (strcasecmp((const char *) parms.type, "PREAMBLE") == 0); + short_train = (strcasecmp((const char *) parms.type, "TCF") != 0); + faxtester_set_tx_type(s, T30_MODEM_NONE, 0, false, false); + if (strcasecmp((const char *) parms.modem, "V.21") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V21, 300, false, true); + } + else if (strcasecmp((const char *) parms.modem, "V.17/14400") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V17, 14400, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.17/12000") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V17, 12000, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.17/9600") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V17, 9600, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.17/7200") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V17, 7200, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.29/9600") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V29, 9600, false, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.29/7200") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V29, 7200, false, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.27ter/4800") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V27TER, 4800, false, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.27ter/2400") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_V27TER, 2400, false, hdlc); + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised modem\n"); + } + } + + if (strcasecmp((const char *) parms.type, "SET") == 0) + { + if (strcasecmp((const char *) parms.tag, "IDENT") == 0) + strcpy(s->expected_rx_info.ident, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "SUB") == 0) + strcpy(s->expected_rx_info.sub_address, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "SEP") == 0) + strcpy(s->expected_rx_info.selective_polling_address, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "PSA") == 0) + strcpy(s->expected_rx_info.polled_sub_address, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "SID") == 0) + strcpy(s->expected_rx_info.sender_ident, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "PWD") == 0) + strcpy(s->expected_rx_info.password, (const char *) parms.value); + free_node_parms(&parms); + return 0; + } + else if (strcasecmp((const char *) parms.type, "CNG") == 0) + { + /* Look for CNG */ + faxtester_set_rx_type(s, T30_MODEM_CNG, 0, false, false); + faxtester_set_tx_type(s, T30_MODEM_NONE, 0, false, false); + } + else if (strcasecmp((const char *) parms.type, "CED") == 0) + { + /* Look for CED */ + faxtester_set_rx_type(s, T30_MODEM_CED, 0, false, false); + faxtester_set_tx_type(s, T30_MODEM_NONE, 0, false, false); + } + else if (strcasecmp((const char *) parms.type, "HDLC") == 0) + { + i = string_to_msg(buf, mask, (const char *) parms.value); + bit_reverse(s->awaited, buf, abs(i)); + s->awaited_len = i; + } + else if (strcasecmp((const char *) parms.type, "TCF") == 0) + { + } + else if (strcasecmp((const char *) parms.type, "MSG") == 0) + { + } + else if (strcasecmp((const char *) parms.type, "PP") == 0) + { + } + else if (strcasecmp((const char *) parms.type, "SILENCE") == 0) + { + faxtest_set_rx_silence(s); + } + else if (strcasecmp((const char *) parms.type, "CLEAR") == 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Far end should drop the call\n"); + s->test_for_call_clear = true; + s->call_clear_timer = 0; + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised type '%s'\n", (const char *) parms.type); + free_node_parms(&parms); + return 0; + } + } + else + { + faxtester_set_timeout(s, s->timeout_x); + if (parms.modem) + { + hdlc = (strcasecmp((const char *) parms.type, "PREAMBLE") == 0); + short_train = (strcasecmp((const char *) parms.type, "TCF") != 0); + faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); + if (strcasecmp((const char *) parms.modem, "V.21") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V21, 300, false, true); + } + else if (strcasecmp((const char *) parms.modem, "V.17/14400") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V17, 14400, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.17/12000") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V17, 12000, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.17/9600") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V17, 9600, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.17/7200") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V17, 7200, short_train, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.29/9600") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V29, 9600, false, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.29/7200") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V29, 7200, false, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.27ter/4800") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V27TER, 4800, false, hdlc); + } + else if (strcasecmp((const char *) parms.modem, "V.27ter/2400") == 0) + { + faxtester_set_tx_type(s, T30_MODEM_V27TER, 2400, false, hdlc); + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised modem\n"); + } + } + + if (strcasecmp((const char *) parms.type, "SET") == 0) + { + if (strcasecmp((const char *) parms.tag, "IDENT") == 0) + t30_set_tx_ident(s->far_t30, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "SUB") == 0) + t30_set_tx_sub_address(s->far_t30, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "SEP") == 0) + t30_set_tx_selective_polling_address(s->far_t30, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "PSA") == 0) + t30_set_tx_polled_sub_address(s->far_t30, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "SID") == 0) + t30_set_tx_sender_ident(s->far_t30, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "PWD") == 0) + t30_set_tx_password(s->far_t30, (const char *) parms.value); + else if (strcasecmp((const char *) parms.tag, "RXFILE") == 0) + { + if (parms.value) + t30_set_rx_file(s->far_t30, (const char *) parms.value, -1); + else + t30_set_rx_file(s->far_t30, output_tiff_file_name, -1); + } + else if (strcasecmp((const char *) parms.tag, "TXFILE") == 0) + { + sprintf(s->next_tx_file, "%s/%s", s->image_path, (const char *) parms.value); + printf("Push '%s'\n", s->next_tx_file); + } + free_node_parms(&parms); + return 0; + } + else if (strcasecmp((const char *) parms.type, "CALL") == 0) + { + if (s->far_fax) + fax_restart(s->far_fax, false); + else + t38_terminal_restart(s->far_t38, false); + fax_prepare(s); + s->next_tx_file[0] = '\0'; + t30_set_rx_file(s->far_t30, output_tiff_file_name, -1); + /* Avoid libtiff 3.8.2 and earlier bug on complex 2D lines. */ + t30_set_supported_output_compressions(s->far_t30, T4_COMPRESSION_T4_1D); + if (parms.value) + { + sprintf(path, "%s/%s", s->image_path, (const char *) parms.value); + t30_set_tx_file(s->far_t30, path, -1, -1); + } + free_node_parms(&parms); + return 0; + } + else if (strcasecmp((const char *) parms.type, "ANSWER") == 0) + { + if (s->far_fax) + fax_restart(s->far_fax, true); + else + t38_terminal_restart(s->far_t38, true); + fax_prepare(s); + s->next_tx_file[0] = '\0'; + /* Avoid libtiff 3.8.2 and earlier bug on complex 2D lines. */ + t30_set_supported_output_compressions(s->far_t30, T4_COMPRESSION_T4_1D); + if (parms.value) + { + sprintf(path, "%s/%s", s->image_path, (const char *) parms.value); + t30_set_tx_file(s->far_t30, path, -1, -1); + } + free_node_parms(&parms); + return 0; + } + else if (strcasecmp((const char *) parms.type, "CNG") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); + faxtester_set_tx_type(s, T30_MODEM_CNG, 0, false, false); + } + else if (strcasecmp((const char *) parms.type, "CED") == 0) + { + faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); + faxtester_set_tx_type(s, T30_MODEM_CED, 0, false, false); + } + else if (strcasecmp((const char *) parms.type, "WAIT") == 0) + { + delay = (parms.value) ? atoi((const char *) parms.value) : 1; + faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); + faxtester_set_tx_type(s, T30_MODEM_PAUSE, 0, delay, false); + } + else if (strcasecmp((const char *) parms.type, "PREAMBLE") == 0) + { + flags = (parms.value) ? atoi((const char *) parms.value) : 37; + faxtester_send_hdlc_flags(s, flags); + } + else if (strcasecmp((const char *) parms.type, "POSTAMBLE") == 0) + { + flags = (parms.value) ? atoi((const char *) parms.value) : 5; + faxtester_send_hdlc_flags(s, flags); + } + else if (strcasecmp((const char *) parms.type, "HDLC") == 0) + { + i = string_to_msg(buf, mask, (const char *) parms.value); + bit_reverse(buf, buf, abs(i)); + if (parms.crc_error && strcasecmp((const char *) parms.crc_error, "0") == 0) + faxtester_send_hdlc_msg(s, buf, abs(i), false); + else + faxtester_send_hdlc_msg(s, buf, abs(i), true); + } + else if (strcasecmp((const char *) parms.type, "TCF") == 0) + { + i = (parms.value) ? atoi((const char *) parms.value) : 450; + if (parms.pattern) + { + /* TODO: implement proper patterns */ + j = atoi((const char *) parms.pattern); + memset(s->image, 0x55, j); + if (i > j) + memset(s->image + j, 0, i - j); + } + else + { + memset(s->image, 0, i); + } + s->image_ptr = 0; + s->image_bit_ptr = 8; + s->image_buffer = s->image; + s->image_len = i; + } + else if (strcasecmp((const char *) parms.type, "MSG") == 0) + { + /* A non-ECM page */ + min_row_bits = (parms.min_bits) ? atoi((const char *) parms.min_bits) : 0; + sprintf(path, "%s/%s", s->image_path, (const char *) parms.value); + if (t4_tx_init(&t4_tx_state, path, -1, -1) == NULL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to init T.4 send\n"); + printf("Test failed\n"); + exit(2); + } + t4_tx_set_header_info(&t4_tx_state, NULL); + compression_type = T4_COMPRESSION_T4_1D; + if (parms.compression) + { + if (strcasecmp((const char *) parms.compression, "T.4 1D") == 0) + compression_type = T4_COMPRESSION_T4_1D; + else if (strcasecmp((const char *) parms.compression, "T.4 2D") == 0) + compression_type = T4_COMPRESSION_T4_2D; + else if (strcasecmp((const char *) parms.compression, "T.6") == 0) + compression_type = T4_COMPRESSION_T6; + else if (strcasecmp((const char *) parms.compression, "T.85") == 0) + compression_type = T4_COMPRESSION_T85; + } + if (t4_tx_set_tx_image_format(&t4_tx_state, + compression_type, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200, + T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200) < 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); + printf("Test failed\n"); + exit(2); + } + t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); + if (t4_tx_start_page(&t4_tx_state)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to start T.4 send\n"); + printf("Test failed\n"); + exit(2); + } + s->image_len = t4_tx_get(&t4_tx_state, s->image, sizeof(s->image)); + if (parms.bad_rows) + { + span_log(&s->logging, SPAN_LOG_FLOW, "We need to corrupt the image\n"); + corrupt_image(s, (const char *) parms.bad_rows); + } + t4_tx_release(&t4_tx_state); + span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM image is %d bytes (min row bits %d)\n", s->image_len, min_row_bits); + s->image_ptr = 0; + s->image_bit_ptr = 8; + s->image_buffer = s->image; + } + else if (strcasecmp((const char *) parms.type, "PP") == 0) + { + min_row_bits = (parms.min_bits) ? atoi((const char *) parms.min_bits) : 0; + ecm_block = (parms.block) ? atoi((const char *) parms.block) : 0; + ecm_frame_size = (parms.frame_size) ? atoi((const char *) parms.frame_size) : 64; + i = (parms.crc_error) ? atoi((const char *) parms.crc_error) : -1; + sprintf(path, "%s/%s", s->image_path, (const char *) parms.value); + if (t4_tx_init(&t4_tx_state, path, -1, -1) == NULL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to init T.4 send\n"); + printf("Test failed\n"); + exit(2); + } + t4_tx_set_header_info(&t4_tx_state, NULL); + compression_type = T4_COMPRESSION_T4_1D; + if (parms.compression) + { + if (strcasecmp((const char *) parms.compression, "T.4 1D") == 0) + compression_type = T4_COMPRESSION_T4_1D; + else if (strcasecmp((const char *) parms.compression, "T.4 2D") == 0) + compression_type = T4_COMPRESSION_T4_2D; + else if (strcasecmp((const char *) parms.compression, "T.6") == 0) + compression_type = T4_COMPRESSION_T6; + else if (strcasecmp((const char *) parms.compression, "T.85") == 0) + compression_type = T4_COMPRESSION_T85; + } + if (t4_tx_set_tx_image_format(&t4_tx_state, + compression_type, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200, + T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200) < 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); + printf("Test failed\n"); + exit(2); + } + t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); + if (t4_tx_start_page(&t4_tx_state)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to start T.4 send\n"); + printf("Test failed\n"); + exit(2); + } + /*endif*/ + s->image_len = t4_tx_get(&t4_tx_state, s->image, sizeof(s->image)); + if (parms.bad_rows) + { + span_log(&s->logging, SPAN_LOG_FLOW, "We need to corrupt the image\n"); + corrupt_image(s, (const char *) parms.bad_rows); + } + /*endif*/ + t4_tx_release(&t4_tx_state); + span_log(&s->logging, SPAN_LOG_FLOW, "ECM image is %d bytes (min row bits %d)\n", s->image_len, min_row_bits); + faxtester_set_ecm_image_buffer(s, ecm_block, ecm_frame_size, i); + } + else if (strcasecmp((const char *) parms.type, "CLEAR") == 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Time to drop the call\n"); + t30_terminate(s->far_t30); + free_node_parms(&parms); + return 0; + } + else if (strcasecmp((const char *) parms.type, "STATUS") == 0) + { + if (parms.value) + { + for (i = 0; t30_status[i].code >= 0; i++) + { + if (strcmp(t30_status[i].tag, (const char *) parms.value) == 0) + break; + } + if (t30_status[i].code >= 0) + delay = t30_status[i].code; + else + delay = atoi((const char *) parms.value); + t30_get_transfer_statistics(s->far_t30, &t30_stats); + if (delay == t30_stats.current_status) + span_log(&s->logging, SPAN_LOG_FLOW, "Expected status (%s) found\n", t30_status[i].tag); + else + span_log(&s->logging, SPAN_LOG_FLOW, "Expected status %s, but found %s (%d)\n", t30_status[i].tag, t30_status[t30_stats.current_status].tag, t30_stats.current_status); + if (delay != t30_stats.current_status) + { + printf("Test failed\n"); + exit(2); + } + } + free_node_parms(&parms); + return 0; + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised type '%s'\n", (const char *) parms.type); + free_node_parms(&parms); + return 0; + } + /*endif*/ + } + /*endif*/ + free_node_parms(&parms); + return 1; +} +/*- End of function --------------------------------------------------------*/ + +static int parse_config(faxtester_state_t *s, xmlNodePtr cur) +{ + xmlChar *x; + xmlChar *y; + + while (cur) + { + if (xmlStrcmp(cur->name, (const xmlChar *) "path") == 0) + { + x = NULL; + y = NULL; + if ((x = xmlGetProp(cur, (const xmlChar *) "type")) + && + (y = xmlGetProp(cur, (const xmlChar *) "value"))) + { + if (strcasecmp((const char *) x, "IMAGE") == 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Found '%s' '%s'\n", (char *) x, (char *) y); + strcpy(s->image_path, (const char *) y); + } + /*endif*/ + } + /*endif*/ + if (x) + xmlFree(x); + /*endif*/ + if (y) + xmlFree(y); + /*endif*/ + } + /*endif*/ + cur = cur->next; + } + /*endwhile*/ + return -1; +} +/*- End of function --------------------------------------------------------*/ + +static int parse_test_group(faxtester_state_t *s, xmlNodePtr cur, const char *test) +{ + xmlChar *x; + + while (cur) + { + if (xmlStrcmp(cur->name, (const xmlChar *) "test") == 0) + { + if ((x = xmlGetProp(cur, (const xmlChar *) "name"))) + { + if (xmlStrcmp(x, (const xmlChar *) test) == 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Found '%s'\n", (char *) x); + s->cur = cur->xmlChildrenNode; + xmlFree(x); + return 0; + } + /*endif*/ + xmlFree(x); + } + /*endif*/ + } + /*endif*/ + cur = cur->next; + } + /*endwhile*/ + return -1; +} +/*- End of function --------------------------------------------------------*/ + +static int get_test_set(faxtester_state_t *s, const char *test_file, const char *test) +{ + xmlParserCtxtPtr ctxt; + xmlNodePtr cur; + + if ((ctxt = xmlNewParserCtxt()) == NULL) + { + fprintf(stderr, "Failed to allocate XML parser context\n"); + return -1; + } + /* parse the file, activating the DTD validation option */ + if ((s->doc = xmlCtxtReadFile(ctxt, test_file, NULL, XML_PARSE_XINCLUDE | XML_PARSE_DTDVALID)) == NULL) + { + fprintf(stderr, "Failed to read the XML document\n"); + return -1; + } + if (ctxt->valid == 0) + { + fprintf(stderr, "Failed to validate the XML document\n"); + xmlFreeDoc(s->doc); + s->doc = NULL; + xmlFreeParserCtxt(ctxt); + return -1; + } + xmlFreeParserCtxt(ctxt); + + /* Check the document is of the right kind */ + if ((cur = xmlDocGetRootElement(s->doc)) == NULL) + { + xmlFreeDoc(s->doc); + s->doc = NULL; + fprintf(stderr, "Empty document\n"); + return -1; + } + /*endif*/ + if (xmlStrcmp(cur->name, (const xmlChar *) "fax-tests")) + { + xmlFreeDoc(s->doc); + s->doc = NULL; + fprintf(stderr, "Document of the wrong type, root node != fax-tests\n"); + return -1; + } + /*endif*/ + cur = cur->xmlChildrenNode; + while (cur && xmlIsBlankNode(cur)) + cur = cur->next; + /*endwhile*/ + if (cur == NULL) + { + fprintf(stderr, "XML test not found\n"); + return -1; + } + /*endif*/ + xmlCleanupParser(); + while (cur) + { + if (xmlStrcmp(cur->name, (const xmlChar *) "config") == 0) + parse_config(s, cur->xmlChildrenNode); + /*endif*/ + if (xmlStrcmp(cur->name, (const xmlChar *) "test-group") == 0) + { + if (parse_test_group(s, cur->xmlChildrenNode, test) == 0) + return 0; + /*endif*/ + } + /*endif*/ + cur = cur->next; + } + /*endwhile*/ + fprintf(stderr, "XML test not found\n"); + return -1; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(logging_state_t *) faxtester_get_logging_state(faxtester_state_t *s) +{ + return &s->logging; +} +/*- End of function --------------------------------------------------------*/ + +faxtester_state_t *faxtester_init(faxtester_state_t *s, const char *test_file, const char *test) { if (s == NULL) { if ((s = (faxtester_state_t *) malloc(sizeof(*s))) == NULL) return NULL; } + /*endif*/ memset(s, 0, sizeof(*s)); span_log_init(&s->logging, SPAN_LOG_NONE, NULL); @@ -656,29 +1864,38 @@ faxtester_state_t *faxtester_init(faxtester_state_t *s, int calling_party) fax_modems_set_tep_mode(&s->modems, false); fax_modems_set_rx_active(&s->modems, true); faxtester_set_timeout(s, -1); + s->timein_x = -1; + s->timeout_x = -1; faxtester_set_tx_type(s, T30_MODEM_NONE, 0, false, false); - + strcpy(s->image_path, "."); + s->next_tx_file[0] = '\0'; + if (get_test_set(s, test_file, test) < 0) + { + /* TODO: free the state, if it was allocated. */ + return NULL; + } + /*endif*/ + memset(&s->expected_rx_info, 0, sizeof(s->expected_rx_info)); return s; } /*- End of function --------------------------------------------------------*/ int faxtester_release(faxtester_state_t *s) { + if (s->doc) + { + xmlFreeDoc(s->doc); + s->doc = NULL; + } return 0; } /*- End of function --------------------------------------------------------*/ int faxtester_free(faxtester_state_t *s) { + faxtester_release(s); free(s); return 0; } /*- End of function --------------------------------------------------------*/ - -void faxtester_set_flush_handler(faxtester_state_t *s, faxtester_flush_handler_t handler, void *user_data) -{ - s->flush_handler = handler; - s->flush_user_data = user_data; -} -/*- End of function --------------------------------------------------------*/ /*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/tests/fax_tester.h b/libs/spandsp/tests/fax_tester.h index 48308f7329..412c1fc340 100644 --- a/libs/spandsp/tests/fax_tester.h +++ b/libs/spandsp/tests/fax_tester.h @@ -39,23 +39,6 @@ typedef struct faxtester_state_s faxtester_state_t; typedef void (*faxtester_flush_handler_t)(faxtester_state_t *s, void *user_data, int which); -/*! - FAX tester real time frame handler. - \brief FAX tester real time frame handler. - \param s The FAX tester context. - \param user_data An opaque pointer. - \param direction True for incoming, false for outgoing. - \param msg The HDLC message. - \param len The length of the message. -*/ -typedef void (*faxtester_real_time_frame_handler_t)(faxtester_state_t *s, - void *user_data, - int direction, - const uint8_t *msg, - int len); - -typedef void (*faxtester_front_end_step_complete_handler_t)(faxtester_state_t *s, void *user_data); - /*! FAX tester descriptor. */ @@ -63,9 +46,19 @@ struct faxtester_state_s { /*! \brief The far end FAX context */ fax_state_t *far_fax; + t38_terminal_state_t *far_t38; + + int far_tag; /*! \brief The far end T.38 terminal context */ t38_terminal_state_t *far_t38_fax; + + t30_state_t *far_t30; + + t30_exchanged_info_t expected_rx_info; + + bool use_receiver_not_ready; + bool test_local_interrupt; /*! \brief Path for the FAX image test files. */ char image_path[1024]; @@ -74,27 +67,23 @@ struct faxtester_state_s xmlDocPtr doc; /*! \brief Pointer to our current step in the test. */ xmlNodePtr cur; + + int repeat_min; + int repeat_max; + int repeat_count; + xmlNodePtr repeat_start; + xmlNodePtr repeat_parent; faxtester_flush_handler_t flush_handler; void *flush_user_data; - /*! \brief A pointer to a callback routine to be called when frames are - exchanged. */ - faxtester_real_time_frame_handler_t real_time_frame_handler; - /*! \brief An opaque pointer supplied in real time frame callbacks. */ - void *real_time_frame_user_data; - - faxtester_front_end_step_complete_handler_t front_end_step_complete_handler; - void *front_end_step_complete_user_data; - - faxtester_front_end_step_complete_handler_t front_end_step_timeout_handler; - void *front_end_step_timeout_user_data; - const uint8_t *image_buffer; int image_len; int image_ptr; int image_bit_ptr; + uint8_t image[1000000]; + int ecm_frame_size; int corrupt_crc; @@ -194,26 +183,24 @@ void faxtester_set_transmit_on_idle(faxtester_state_t *s, int transmit_on_idle); */ void faxtester_set_tep_mode(faxtester_state_t *s, int use_tep); -void faxtester_set_real_time_frame_handler(faxtester_state_t *s, faxtester_real_time_frame_handler_t handler, void *user_data); - -void faxtester_set_front_end_step_complete_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t handler, void *user_data); - -void faxtester_set_front_end_step_timeout_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t handler, void *user_data); - void faxtester_set_timeout(faxtester_state_t *s, int timeout); -void faxtester_set_non_ecm_image_buffer(faxtester_state_t *s, const uint8_t *buf, int len); +SPAN_DECLARE(int) faxtester_next_step(faxtester_state_t *s); -void faxtester_set_ecm_image_buffer(faxtester_state_t *s, const uint8_t *buf, int len, int block, int frame_size, int crc_hit); - -/*! Initialise a FAX context. - \brief Initialise a FAX context. +/*! Get the logging context associated with a FAX tester context. + \brief Get the logging context associated with a FAX tester context. \param s The FAX tester context. - \param calling_party true if the context is for a calling party. FALSE if the - context is for an answering party. + \return A pointer to the logging context */ +SPAN_DECLARE(logging_state_t *) faxtester_get_logging_state(faxtester_state_t *s); + +/*! Initialise a FAX tester context. + \brief Initialise a FAX tester context. + \param s The FAX tester context. + \param test_file The name of the file of XML test scripts. + \param test The name of the XML script test. \return A pointer to the FAX context, or NULL if there was a problem. */ -faxtester_state_t *faxtester_init(faxtester_state_t *s, int calling_party); +faxtester_state_t *faxtester_init(faxtester_state_t *s, const char *test_file, const char *test); /*! Release a FAX context. \brief Release a FAX context. diff --git a/libs/spandsp/tests/fax_tests.c b/libs/spandsp/tests/fax_tests.c index f3b34b91a3..57de0da361 100644 --- a/libs/spandsp/tests/fax_tests.c +++ b/libs/spandsp/tests/fax_tests.c @@ -99,48 +99,80 @@ The T.31 and TSB85 parts are incomplete right now. #define INPUT_TIFF_FILE_NAME "../test-data/itu/fax/itutests.tif" #define OUTPUT_TIFF_FILE_NAME "fax_tests.tif" +#define INPUT_WAVE_FILE_NAME "fax_cap.wav" #define OUTPUT_WAVE_FILE_NAME "fax_tests.wav" enum { - AUDIO_FAX, - T38_TERMINAL_FAX, - T38_GATEWAY_FAX, + AUDIO_FAX = 1, + T38_FAX, T31_AUDIO_FAX, - T31_T38_TERMINAL_FAX, - T31_T38_GATEWAY_FAX, + T31_T38_FAX, TSB85_AUDIO_FAX, - TSB85_T38_TERMINAL_FAX, - TSB85_T38_GATEWAY_FAX + TSB85_T38_FAX, + REPLAY_AUDIO_FAX, + REPLAY_T38_FAX, + AUDIO_TO_T38_GATEWAY, + PASSTHROUGH, + AUDIO_CHAN, + T38_CHAN }; -int mode[2] = {AUDIO_FAX, AUDIO_FAX}; +const char *output_tiff_file_name; -t30_state_t *t30_state[2] = {NULL, NULL}; -fax_state_t *fax_state[2] = {NULL, NULL}; -t38_gateway_state_t *t38_gateway_state[2] = {NULL, NULL}; -t38_terminal_state_t *t38_state[2] = {NULL, NULL}; -t38_core_state_t *t38_core_state[2] = {NULL, NULL}; -faxtester_state_t *faxtester[2] = {NULL, NULL}; -g1050_state_t *g1050_path[2] = {NULL, NULL}; -awgn_state_t *awgn_state[2] = {NULL, NULL}; -int16_t audio_buffer[2*2][SAMPLES_PER_CHUNK]; +struct audio_buf_s +{ + int16_t amp[SAMPLES_PER_CHUNK]; + int len; +}; -int t38_subst_seq[2] = {0, 0}; +struct chain_element_s +{ + int node_type; + int left_chan_type; + int right_chan_type; + struct + { + fax_state_t *fax_state; + t38_terminal_state_t *t38_state; + faxtester_state_t *faxtester_state; + t38_gateway_state_t *t38_gateway_state; + SNDFILE *wave_handle; + } node; + struct + { + g1050_state_t *g1050_path; + both_ways_line_model_state_t *line_model; + struct audio_buf_s *audio_in_buf; + struct audio_buf_s *audio_out_buf; + } path; + t30_state_t *t30_state; + t38_core_state_t *t38_core_state; + int t38_subst_seq; + bool phase_e_reached; + bool completed; + bool succeeded; + t30_exchanged_info_t expected_rx_info; -t30_exchanged_info_t expected_rx_info[2]; + awgn_state_t *awgn_state; + struct audio_buf_s audio_buf[2]; + + int peer; + int t38_peer; + + char tag[10]; +}; + +struct chain_element_s chain[7]; +int chain_elements = 2; + +bool t38_simulate_incrementing_repeats = false; bool use_receiver_not_ready = false; bool test_local_interrupt = false; double when = 0.0; -bool phase_e_reached[2] = {false, false}; -bool completed[2] = {false, false}; -bool succeeded[2] = {false, false}; - -bool t38_simulate_incrementing_repeats = false; - static int phase_b_handler(void *user_data, int result) { int i; @@ -151,10 +183,12 @@ static int phase_b_handler(void *user_data, int result) char tag[20]; const char *u; const uint8_t *v; + t30_exchanged_info_t *info; i = (int) (intptr_t) user_data; - s = t30_state[i]; + s = chain[i].t30_state; ch = i + 'A'; + info = &chain[i].expected_rx_info; snprintf(tag, sizeof(tag), "%c: Phase B", ch); printf("%c: Phase B handler - (0x%X) %s\n", ch, result, t30_frametype(result)); fax_log_rx_parameters(s, tag); @@ -163,15 +197,15 @@ static int phase_b_handler(void *user_data, int result) if ((u = t30_get_rx_ident(s))) { printf("%c: Phase B remote ident '%s'\n", ch, u); - if (expected_rx_info[i].ident[0] && strcmp(expected_rx_info[i].ident, u)) + if (info->ident[0] && strcmp(info->ident, u)) { - printf("%c: Phase B: remote ident incorrect! - expected '%s'\n", ch, expected_rx_info[i].ident); + printf("%c: Phase B: remote ident incorrect! - expected '%s'\n", ch, info->ident); status = T30_ERR_IDENT_UNACCEPTABLE; } } else { - if (expected_rx_info[i].ident[0]) + if (info->ident[0]) { printf("%c: Phase B: remote ident missing!\n", ch); status = T30_ERR_IDENT_UNACCEPTABLE; @@ -180,15 +214,15 @@ static int phase_b_handler(void *user_data, int result) if ((u = t30_get_rx_sub_address(s))) { printf("%c: Phase B: remote sub-address '%s'\n", ch, u); - if (expected_rx_info[i].sub_address[0] && strcmp(expected_rx_info[i].sub_address, u)) + if (info->sub_address[0] && strcmp(info->sub_address, u)) { - printf("%c: Phase B: remote sub-address incorrect! - expected '%s'\n", ch, expected_rx_info[i].sub_address); + printf("%c: Phase B: remote sub-address incorrect! - expected '%s'\n", ch, info->sub_address); status = T30_ERR_SUB_UNACCEPTABLE; } } else { - if (expected_rx_info[i].sub_address[0]) + if (info->sub_address[0]) { printf("%c: Phase B: remote sub-address missing!\n", ch); status = T30_ERR_SUB_UNACCEPTABLE; @@ -197,15 +231,15 @@ static int phase_b_handler(void *user_data, int result) if ((u = t30_get_rx_polled_sub_address(s))) { printf("%c: Phase B: remote polled sub-address '%s'\n", ch, u); - if (expected_rx_info[i].polled_sub_address[0] && strcmp(expected_rx_info[i].polled_sub_address, u)) + if (info->polled_sub_address[0] && strcmp(info->polled_sub_address, u)) { - printf("%c: Phase B: remote polled sub-address incorrect! - expected '%s'\n", ch, expected_rx_info[i].polled_sub_address); + printf("%c: Phase B: remote polled sub-address incorrect! - expected '%s'\n", ch, info->polled_sub_address); status = T30_ERR_PSA_UNACCEPTABLE; } } else { - if (expected_rx_info[i].polled_sub_address[0]) + if (info->polled_sub_address[0]) { printf("%c: Phase B: remote polled sub-address missing!\n", ch); status = T30_ERR_PSA_UNACCEPTABLE; @@ -214,15 +248,15 @@ static int phase_b_handler(void *user_data, int result) if ((u = t30_get_rx_selective_polling_address(s))) { printf("%c: Phase B: remote selective polling address '%s'\n", ch, u); - if (expected_rx_info[i].selective_polling_address[0] && strcmp(expected_rx_info[i].selective_polling_address, u)) + if (info->selective_polling_address[0] && strcmp(info->selective_polling_address, u)) { - printf("%c: Phase B: remote selective polling address incorrect! - expected '%s'\n", ch, expected_rx_info[i].selective_polling_address); + printf("%c: Phase B: remote selective polling address incorrect! - expected '%s'\n", ch, info->selective_polling_address); status = T30_ERR_SEP_UNACCEPTABLE; } } else { - if (expected_rx_info[i].selective_polling_address[0]) + if (info->selective_polling_address[0]) { printf("%c: Phase B: remote selective polling address missing!\n", ch); status = T30_ERR_SEP_UNACCEPTABLE; @@ -231,15 +265,15 @@ static int phase_b_handler(void *user_data, int result) if ((u = t30_get_rx_sender_ident(s))) { printf("%c: Phase B: remote sender ident '%s'\n", ch, u); - if (expected_rx_info[i].sender_ident[0] && strcmp(expected_rx_info[i].sender_ident, u)) + if (info->sender_ident[0] && strcmp(info->sender_ident, u)) { - printf("%c: Phase B: remote sender ident incorrect! - expected '%s'\n", ch, expected_rx_info[i].sender_ident); + printf("%c: Phase B: remote sender ident incorrect! - expected '%s'\n", ch, info->sender_ident); status = T30_ERR_SID_UNACCEPTABLE; } } else { - if (expected_rx_info[i].sender_ident[0]) + if (info->sender_ident[0]) { printf("%c: Phase B: remote sender ident missing!\n", ch); status = T30_ERR_SID_UNACCEPTABLE; @@ -248,15 +282,15 @@ static int phase_b_handler(void *user_data, int result) if ((u = t30_get_rx_password(s))) { printf("%c: Phase B: remote password '%s'\n", ch, u); - if (expected_rx_info[i].password[0] && strcmp(expected_rx_info[i].password, u)) + if (info->password[0] && strcmp(info->password, u)) { - printf("%c: Phase B: remote password incorrect! - expected '%s'\n", ch, expected_rx_info[i].password); + printf("%c: Phase B: remote password incorrect! - expected '%s'\n", ch, info->password); status = T30_ERR_PWD_UNACCEPTABLE; } } else { - if (expected_rx_info[i].password[0]) + if (info->password[0]) { printf("%c: Phase B: remote password missing!\n", ch); status = T30_ERR_PWD_UNACCEPTABLE; @@ -265,46 +299,46 @@ static int phase_b_handler(void *user_data, int result) if ((len = t30_get_rx_nsf(s, &v))) { printf("%c: Phase B: NSF %d bytes\n", ch, len); - if (expected_rx_info[i].nsf_len && (expected_rx_info[i].nsf_len != len || memcmp(expected_rx_info[i].nsf, v, len))) + if (info->nsf_len && (info->nsf_len != len || memcmp(info->nsf, v, len))) { - printf("%c: Phase B: remote NSF incorrect! - expected %u bytes\n", ch, (unsigned int) expected_rx_info[i].nsf_len); + printf("%c: Phase B: remote NSF incorrect! - expected %u bytes\n", ch, (unsigned int) info->nsf_len); } } else { - if (expected_rx_info[i].nsf_len) + if (info->nsf_len) { - printf("%c: Phase B: remote NSF missing! - expected %u bytes\n", ch, (unsigned int) expected_rx_info[i].nsf_len); + printf("%c: Phase B: remote NSF missing! - expected %u bytes\n", ch, (unsigned int) info->nsf_len); } } if ((len = t30_get_rx_nsc(s, &v))) { printf("%c: Phase B: NSC %d bytes\n", ch, len); - if (expected_rx_info[i].nsc_len && (expected_rx_info[i].nsc_len != len || memcmp(expected_rx_info[i].nsc, v, len))) + if (info->nsc_len && (info->nsc_len != len || memcmp(info->nsc, v, len))) { - printf("%c: Phase B: remote NSC incorrect! - expected %u bytes\n", ch, (unsigned int) expected_rx_info[i].nsc_len); + printf("%c: Phase B: remote NSC incorrect! - expected %u bytes\n", ch, (unsigned int) info->nsc_len); } } else { - if (expected_rx_info[i].nsc_len) + if (info->nsc_len) { - printf("%c: Phase B: remote NSC missing! - expected %u bytes\n", ch, (unsigned int) expected_rx_info[i].nsc_len); + printf("%c: Phase B: remote NSC missing! - expected %u bytes\n", ch, (unsigned int) info->nsc_len); } } if ((len = t30_get_rx_nss(s, &v))) { printf("%c: Phase B: NSS %d bytes\n", ch, len); - if (expected_rx_info[i].nss_len && (expected_rx_info[i].nss_len != len || memcmp(expected_rx_info[i].nss, v, len))) + if (info->nss_len && (info->nss_len != len || memcmp(info->nss, v, len))) { - printf("%c: Phase B: remote NSS incorrect! - expected %u bytes\n", ch, (unsigned int) expected_rx_info[i].nss_len); + printf("%c: Phase B: remote NSS incorrect! - expected %u bytes\n", ch, (unsigned int) info->nss_len); } } else { - if (expected_rx_info[i].nss_len) + if (info->nss_len) { - printf("%c: Phase B: remote NSS missing! - expected %u bytes\n", ch, (unsigned int) expected_rx_info[i].nsf_len); + printf("%c: Phase B: remote NSS missing! - expected %u bytes\n", ch, (unsigned int) info->nsf_len); } } @@ -320,7 +354,7 @@ static int phase_d_handler(void *user_data, int result) char tag[20]; i = (int) (intptr_t) user_data; - s = t30_state[i]; + s = chain[i].t30_state; ch = i + 'A'; snprintf(tag, sizeof(tag), "%c: Phase D", ch); printf("%c: Phase D handler - (0x%X) %s\n", ch, result, t30_frametype(result)); @@ -367,7 +401,7 @@ static void phase_e_handler(void *user_data, int result) char tag[20]; i = (int) (intptr_t) user_data; - s = t30_state[i]; + s = chain[i].t30_state; ch = i + 'A'; snprintf(tag, sizeof(tag), "%c: Phase E", ch); printf("%c: Phase E handler - (%d) %s\n", ch, result, t30_completion_code_to_str(result)); @@ -375,15 +409,15 @@ static void phase_e_handler(void *user_data, int result) fax_log_tx_parameters(s, tag); fax_log_rx_parameters(s, tag); t30_get_transfer_statistics(s, &t); - succeeded[i] = (result == T30_ERR_OK); - phase_e_reached[i] = true; + chain[i].succeeded = (result == T30_ERR_OK); + chain[i].phase_e_reached = true; } /*- End of function --------------------------------------------------------*/ -static void real_time_frame_handler(void *user_data, - bool incoming, - const uint8_t *msg, - int len) +static void real_time_t30_frame_handler(void *user_data, + bool incoming, + const uint8_t *msg, + int len) { int i; int ch; @@ -415,7 +449,7 @@ static void set_t30_callbacks(t30_state_t *t30, int chan) t30_set_phase_b_handler(t30, phase_b_handler, (void *) (intptr_t) chan); t30_set_phase_d_handler(t30, phase_d_handler, (void *) (intptr_t) chan); t30_set_phase_e_handler(t30, phase_e_handler, (void *) (intptr_t) chan); - t30_set_real_time_frame_handler(t30, real_time_frame_handler, (void *) (intptr_t) chan); + t30_set_real_time_frame_handler(t30, real_time_t30_frame_handler, (void *) (intptr_t) chan); t30_set_document_handler(t30, document_handler, (void *) (intptr_t) chan); } /*- End of function --------------------------------------------------------*/ @@ -447,11 +481,11 @@ static int tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t { for (i = 0; i < count; i++) { - span_log(&s->logging, SPAN_LOG_FLOW, "Send seq %d, len %d\n", t38_subst_seq[chan], len); + span_log(&s->logging, SPAN_LOG_FLOW, "Send seq %d, len %d\n", chain[chan].t38_subst_seq, len); - if (g1050_put(g1050_path[chan], buf, len, t38_subst_seq[chan], when) < 0) - printf("Lost packet %d\n", t38_subst_seq[chan]); - t38_subst_seq[chan] = (t38_subst_seq[chan] + 1) & 0xFFFF; + if (g1050_put(chain[chan].path.g1050_path, buf, len, chain[chan].t38_subst_seq, when) < 0) + printf("Lost packet %d\n", chain[chan].t38_subst_seq); + chain[chan].t38_subst_seq = (chain[chan].t38_subst_seq + 1) & 0xFFFF; } } else @@ -460,7 +494,7 @@ static int tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t for (i = 0; i < count; i++) { - if (g1050_put(g1050_path[chan], buf, len, s->tx_seq_no, when) < 0) + if (g1050_put(chain[chan].path.g1050_path, buf, len, s->tx_seq_no, when) < 0) printf("Lost packet %d\n", s->tx_seq_no); } } @@ -468,36 +502,84 @@ static int tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t } /*- End of function --------------------------------------------------------*/ +static void t33_tests(void) +{ + int n; + int item_no; + int type; + uint8_t num[21]; + uint8_t new_t33[133]; + /* These patterns are from the T.33 spec */ + static const uint8_t *pkts[] = + { + (const uint8_t *) "#1234567890#1234", + (const uint8_t *) "1234#5678#8910", + (const uint8_t *) "#6174444100#1234#567", + (const uint8_t *) "1234#5678##2032223", + (const uint8_t *) "#2037445555##6446666", + (const uint8_t *) "#2037445555#1234##6446666#5678", + //(const uint8_t *) "#123456789012345678901#1234##6446666#5678", + (const uint8_t *) "" + }; + + printf("T.33 sub-address packing/unpacking tests\n"); + for (n = 0; pkts[n][0]; n++) + { + new_t33[0] = '\0'; + printf("'%s'\n", pkts[n]); + for (item_no = 0; item_no < 100; item_no++) + { + if ((type = t33_sub_address_extract_field(num, pkts[n], item_no)) <= 0) + { + if (type == T33_NONE) + break; + printf("Bad sub-address field\n"); + exit(2); + } + switch (type) + { + case T33_SST: + printf("SST '%s'\n", num); + t33_sub_address_add_field(new_t33, num, type); + break; + case T33_EXT: + printf(" EXT '%s'\n", num); + t33_sub_address_add_field(new_t33, num, type); + break; + } + } + if (strcmp((const char *) pkts[n], (const char *) new_t33)) + { + printf("Re-encode mismatch '%s' '%s'\n", pkts[n], new_t33); + exit(2); + } + } +} +/*- End of function --------------------------------------------------------*/ + int main(int argc, char *argv[]) { int16_t silence[SAMPLES_PER_CHUNK]; - int16_t t30_amp[2][SAMPLES_PER_CHUNK]; - int16_t t38_amp[2][SAMPLES_PER_CHUNK]; int16_t t38_amp_hist_a[8][SAMPLES_PER_CHUNK]; int16_t t38_amp_hist_b[8][SAMPLES_PER_CHUNK]; - int16_t out_amp[SAMPLES_PER_CHUNK*4]; - int16_t *fax_rx_buf[2]; - int16_t *fax_tx_buf[2]; - int16_t *t38_gateway_rx_buf[2]; - int16_t *t38_gateway_tx_buf[2]; - int t30_len[2]; - int t38_len[2]; + int16_t audio_log[SAMPLES_PER_CHUNK*4]; int hist_ptr; int log_audio; int msg_len; uint8_t msg[1024]; int outframes; SNDFILE *wave_handle; - SNDFILE *input_wave_handle; bool use_ecm; bool use_tep; - int feedback_audio; - int use_transmit_on_idle; + bool use_polled_mode; + bool use_transmit_on_idle; + bool feedback_audio; int t38_version; const char *input_tiff_file_name; - const char *decode_file_name; + const char *replay_file_name; int i; int j; + int k; int seq_no; int g1050_model_no; int g1050_speed_pattern_no; @@ -526,9 +608,13 @@ int main(int argc, char *argv[]) int expected_pages; char *page_header_info; char *page_header_tz; - const char *tag; const char *xml_file_name; + const char *xml_test_name[2]; + int xml_step; char buf[132 + 1]; + int line_model_no; + int channel_codec; + int rbs_pattern; #if defined(ENABLE_GUI) int use_gui; #endif @@ -540,6 +626,7 @@ int main(int argc, char *argv[]) use_ecm = false; t38_version = 1; input_tiff_file_name = INPUT_TIFF_FILE_NAME; + output_tiff_file_name = OUTPUT_TIFF_FILE_NAME; t38_simulate_incrementing_repeats = false; g1050_model_no = 0; g1050_speed_pattern_no = 1; @@ -547,6 +634,7 @@ int main(int argc, char *argv[]) use_tep = false; feedback_audio = false; use_transmit_on_idle = true; + use_polled_mode = false; supported_modems = T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17; page_header_info = NULL; page_header_tz = NULL; @@ -557,16 +645,22 @@ int main(int argc, char *argv[]) signal_level = 0; noise_level = -99; scan_line_time = 0; - decode_file_name = NULL; + replay_file_name = INPUT_WAVE_FILE_NAME; code_to_look_up = -1; allowed_bilevel_resolutions[0] = 0; allowed_bilevel_resolutions[1] = 0; allowed = 0; + line_model_no = 0; + channel_codec = MUNGE_CODEC_NONE; + rbs_pattern = 0; colour_enabled = false; t37_like_output = false; t38_transport = T38_TRANSPORT_UDPTL; xml_file_name = "../spandsp/tsb85.xml"; - while ((opt = getopt(argc, argv, "7b:c:Cd:D:efFgH:i:Ilm:M:n:p:s:S:tT:u:v:x:z:")) != -1) + xml_test_name[0] = "MRGN01"; + xml_test_name[1] = "MRGN01"; + xml_step = 0; + while ((opt = getopt(argc, argv, "7b:c:Cd:D:efFgH:i:Ilm:M:n:p:Ps:S:tT:u:v:x:X:z:")) != -1) { switch (opt) { @@ -584,7 +678,7 @@ int main(int argc, char *argv[]) colour_enabled = true; break; case 'd': - decode_file_name = optarg; + replay_file_name = optarg; break; case 'D': drop_frame_rate = @@ -629,31 +723,75 @@ int main(int argc, char *argv[]) noise_level = atoi(optarg); break; case 'p': - for (i = 0; i < 2; i++) + /* + -p FAX-audio-FAX + -p FAX-T38-FAX + -p FAX-audio-T38gateway-T38-T38gateway-audio-FAX + -p FAX-T38-T38gateway-audio-T38gateway-T38-FAX + -p FAX-T38-T38gateway-audio-FAX + -p FAX-audio-T38gateway-T38-FAX + -p tester-audio-FAX + -p tester-T38-FAX + -p tester-audio-T38gateway-T38-T38gateway-audio-FAX + -p tester-T38-T38gateway-audio-T38gateway-T38-FAX + -p tester-T38-T38gateway-audio-FAX + -p tester-audio-T38gateway-T38-FAX + */ + for (i = 0, chain_elements = 0, k = 0; chain_elements < 7; i++) { - switch (optarg[i]) + if (optarg[i] != '-' && optarg[i] != '\0') + continue; + j = optarg[i]; + optarg[i] = '\0'; + if (strcmp(&optarg[k], "FAX") == 0) { - case 'A': - mode[i] = AUDIO_FAX; - break; - case 'G': - mode[i] = T38_GATEWAY_FAX; - break; - case 'T': - mode[i] = T38_TERMINAL_FAX; - break; - default: - fprintf(stderr, "Unknown FAX path element %c\n", optarg[i]); + chain[chain_elements++].node_type = AUDIO_FAX; + } + else if (strcmp(&optarg[k], "T38") == 0) + { + chain[chain_elements++].node_type = T38_FAX; + } + else if (strcmp(&optarg[k], "T31") == 0) + { + chain[chain_elements++].node_type = T31_AUDIO_FAX; + } + else if (strcmp(&optarg[k], "tester") == 0) + { + chain[chain_elements++].node_type = TSB85_AUDIO_FAX; + } + else if (strcmp(&optarg[k], "replay") == 0) + { + chain[chain_elements++].node_type = REPLAY_AUDIO_FAX; + } + else if (strcmp(&optarg[k], "T38gateway") == 0) + { + chain[chain_elements++].node_type = AUDIO_TO_T38_GATEWAY; + } + else if (strcmp(&optarg[k], "passthrough") == 0) + { + chain[chain_elements++].node_type = PASSTHROUGH; + } + else + { + fprintf(stderr, "Unknown FAX path element %s\n", &optarg[k]); exit(2); } + k = i + 1; + if (j == '\0') + break; } - if ((mode[0] == AUDIO_FAX && mode[1] != AUDIO_FAX) +#if 0 + if ((chain[0].node_type == AUDIO_FAX && chain[chain_elements - 1].node_type != AUDIO_FAX) || - (mode[0] != AUDIO_FAX && mode[1] == AUDIO_FAX)) + (chain[0].node_type != AUDIO_FAX && chain[chain_elements - 1].node_type == AUDIO_FAX)) { - fprintf(stderr, "Invalid FAX path %s\n", optarg); + fprintf(stderr, "Invalid FAX path\n"); exit(2); } +#endif + break; + case 'P': + use_polled_mode = true; break; case 's': g1050_speed_pattern_no = atoi(optarg); @@ -692,6 +830,10 @@ int main(int argc, char *argv[]) t38_version = atoi(optarg); break; case 'x': + xml_test_name[xml_step] = optarg; + xml_step ^= 1; + break; + case 'X': xml_file_name = optarg; break; case 'z': @@ -726,336 +868,452 @@ int main(int argc, char *argv[]) memset(silence, 0, sizeof(silence)); srand48(0x1234567); + + memset(t38_amp_hist_a, 0, sizeof(t38_amp_hist_a)); + memset(t38_amp_hist_b, 0, sizeof(t38_amp_hist_b)); + /* Set up the nodes */ - input_wave_handle = NULL; - if (mode[0] == T38_TERMINAL_FAX) + chain[0].peer = chain_elements - 1; + chain[chain_elements - 1].peer = 0; + + for (i = 0; i < chain_elements; i++) { - } - else - { - if (decode_file_name) + chain[i].tag[0] = i + 'A'; + chain[i].tag[1] = '\0'; + + memset(&chain[i].audio_buf[0], 0, sizeof(chain[i].audio_buf[0])); + memset(&chain[i].audio_buf[1], 0, sizeof(chain[i].audio_buf[1])); + memset(&chain[i].expected_rx_info, 0, sizeof(chain[i].expected_rx_info)); + + switch (chain[i].node_type) { - if ((input_wave_handle = sf_open_telephony_read(decode_file_name, 1)) == NULL) - { - fprintf(stderr, " Cannot open audio file '%s'\n", decode_file_name); - exit(2); - } - } - } - - for (i = 0; i < 2; i++) - { - tag = (i == 0) ? "A" : "B"; - - memset(&expected_rx_info[i], 0, sizeof(expected_rx_info[i])); - switch (mode[i]) - { - case T38_TERMINAL_FAX: - if ((t38_state[i] = t38_terminal_init(NULL, (i == 0), tx_packet_handler, (void *) (intptr_t) i)) == NULL) - { - fprintf(stderr, "Cannot start the T.38 terminal instance\n"); - exit(2); - } - t30_state[i] = t38_terminal_get_t30_state(t38_state[i]); - t38_core_state[i] = t38_terminal_get_t38_core_state(t38_state[i]); - - logging = t38_terminal_get_logging_state(t38_state[i]); - span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); - - logging = t38_core_get_logging_state(t38_core_state[i]); - span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); - - logging = t30_get_logging_state(t30_state[i]); - span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); - break; case AUDIO_FAX: - case T38_GATEWAY_FAX: - if ((fax_state[i] = fax_init(NULL, (i == 0))) == NULL) + if ((chain[i].node.fax_state = fax_init(NULL, (i == 0))) == NULL) { - fprintf(stderr, "Cannot start FAX instance\n"); + fprintf(stderr, " Cannot start FAX instance\n"); exit(2); } - t30_state[i] = fax_get_t30_state(fax_state[i]); + chain[i].t30_state = fax_get_t30_state(chain[i].node.fax_state); - logging = fax_get_logging_state(fax_state[i]); + logging = fax_get_logging_state(chain[i].node.fax_state); span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); + span_log_set_tag(logging, chain[i].tag); - logging = fax_modems_get_logging_state(&fax_state[i]->modems); + logging = fax_modems_get_logging_state(&chain[i].node.fax_state->modems); span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); + span_log_set_tag(logging, chain[i].tag); - logging = t30_get_logging_state(t30_state[i]); + logging = t30_get_logging_state(chain[i].t30_state); span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); + span_log_set_tag(logging, chain[i].tag); - if (mode[i] == T38_GATEWAY_FAX) - { - if ((t38_gateway_state[i] = t38_gateway_init(NULL, tx_packet_handler, (void *) (intptr_t) i)) == NULL) - { - fprintf(stderr, "Cannot start the T.38 gateway instance\n"); - exit(2); - } - t38_core_state[i] = t38_gateway_get_t38_core_state(t38_gateway_state[i]); + set_t30_callbacks(chain[i].t30_state, i); - logging = t38_gateway_get_logging_state(t38_gateway_state[i]); - span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); + chain[i].path.audio_in_buf = &chain[i + ((i == 0) ? 1 : -1)].audio_buf[0]; + chain[i].path.audio_out_buf = &chain[i].audio_buf[0]; - logging = fax_modems_get_logging_state(&t38_gateway_state[i]->audio.modems); - span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); - - logging = t38_core_get_logging_state(t38_core_state[i]); - span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); - span_log_set_tag(logging, tag); - - fax_rx_buf[i] = t38_amp[i]; - fax_tx_buf[i] = t30_amp[i]; - t38_gateway_rx_buf[i] = t30_amp[i]; - t38_gateway_tx_buf[i] = t38_amp[i]; - } - else - { - fax_rx_buf[i] = t30_amp[i]; - fax_tx_buf[i] = t30_amp[i ^ 1]; - t38_gateway_rx_buf[i] = NULL; - t38_gateway_tx_buf[i] = NULL; - } - awgn_state[i] = NULL; + chain[i].awgn_state = NULL; signal_scaling = 1.0f; if (noise_level > -99) { - awgn_state[i] = awgn_init_dbm0(NULL, 1234567, noise_level); + chain[i].awgn_state = awgn_init_dbm0(NULL, 1234567, noise_level); signal_scaling = powf(10.0f, signal_level/20.0f); printf("Signal scaling %f\n", signal_scaling); } break; + case T38_FAX: + if ((chain[i].node.t38_state = t38_terminal_init(NULL, (i == 0), tx_packet_handler, (void *) (intptr_t) i)) == NULL) + { + fprintf(stderr, " Cannot start the T.38 terminal instance\n"); + exit(2); + } + chain[i].t30_state = t38_terminal_get_t30_state(chain[i].node.t38_state); + chain[i].t38_core_state = t38_terminal_get_t38_core_state(chain[i].node.t38_state); + + logging = t38_terminal_get_logging_state(chain[i].node.t38_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, chain[i].tag); + + logging = t38_core_get_logging_state(chain[i].t38_core_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, chain[i].tag); + + logging = t30_get_logging_state(chain[i].t30_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, chain[i].tag); + + set_t30_callbacks(chain[i].t30_state, i); + + if (i == 0) + { + chain[i].t38_peer = i + 1; + } + else + { + switch (chain[i - 1].node_type) + { + case T38_FAX: + case AUDIO_TO_T38_GATEWAY: + chain[i].t38_peer = i - 1; + break; + default: + chain[i].t38_peer = i + 1; + break; + } + } + break; case T31_AUDIO_FAX: break; - case T31_T38_TERMINAL_FAX: - case T31_T38_GATEWAY_FAX: + case T31_T38_FAX: break; case TSB85_AUDIO_FAX: + case TSB85_T38_FAX: + if ((chain[i].node.faxtester_state = faxtester_init(NULL, xml_file_name, xml_test_name[(i == 0) ? 0 : 1])) == NULL) + { + fprintf(stderr, " Cannot start FAX tester instance\n"); + exit(2); + } + logging = faxtester_get_logging_state(chain[i].node.faxtester_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, chain[i].tag); + + faxtester_set_transmit_on_idle(chain[i].node.faxtester_state, true); + + chain[i].path.audio_in_buf = &chain[i + ((i == 0) ? 1 : -1)].audio_buf[0]; + chain[i].path.audio_out_buf = &chain[i].audio_buf[0]; + + if (i == 0) + { + chain[i].t38_peer = i + 1; + } + else + { + switch (chain[i - 1].node_type) + { + case T38_FAX: + case AUDIO_TO_T38_GATEWAY: + chain[i].t38_peer = i - 1; + break; + default: + chain[i].t38_peer = i + 1; + break; + } + } + + chain[i].awgn_state = NULL; + signal_scaling = 1.0f; + if (noise_level > -99) + { + chain[i].awgn_state = awgn_init_dbm0(NULL, 1234567, noise_level); + signal_scaling = powf(10.0f, signal_level/20.0f); + printf("Signal scaling %f\n", signal_scaling); + } break; - case TSB85_T38_TERMINAL_FAX: - case TSB85_T38_GATEWAY_FAX: + case REPLAY_AUDIO_FAX: + if ((chain[i].node.wave_handle = sf_open_telephony_read(replay_file_name, 1)) == NULL) + { + fprintf(stderr, " Cannot open audio file '%s'\n", replay_file_name); + exit(2); + } + + chain[i].path.audio_in_buf = &chain[i + ((i == 0) ? 1 : -1)].audio_buf[0]; + chain[i].path.audio_out_buf = &chain[i].audio_buf[0]; break; + case AUDIO_TO_T38_GATEWAY: + if ((chain[i].node.t38_gateway_state = t38_gateway_init(NULL, tx_packet_handler, (void *) (intptr_t) i)) == NULL) + { + fprintf(stderr, " Cannot start T.38 gateway instance\n"); + exit(2); + } + chain[i].t38_core_state = t38_gateway_get_t38_core_state(chain[i].node.t38_gateway_state); + + logging = t38_gateway_get_logging_state(chain[i].node.t38_gateway_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, chain[i].tag); + + logging = fax_modems_get_logging_state(&chain[i].node.t38_gateway_state->audio.modems); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, chain[i].tag); + + logging = t38_core_get_logging_state(chain[i].t38_core_state); + span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); + span_log_set_tag(logging, chain[i].tag); + + t38_gateway_set_transmit_on_idle(chain[i].node.t38_gateway_state, use_transmit_on_idle); + t38_gateway_set_supported_modems(chain[i].node.t38_gateway_state, supported_modems); + //t38_gateway_set_nsx_suppression(chain[i].node.t38_state, NULL, 0, NULL, 0); + t38_gateway_set_fill_bit_removal(chain[i].node.t38_gateway_state, remove_fill_bits); + t38_gateway_set_real_time_frame_handler(chain[i].node.t38_gateway_state, real_time_gateway_frame_handler, (void *) (intptr_t) i); + t38_gateway_set_ecm_capability(chain[i].node.t38_gateway_state, use_ecm); + t38_set_t38_version(chain[i].t38_core_state, t38_version); + + if (i == 0) + { + chain[i].t38_peer = i + 1; + chain[i].path.audio_in_buf = NULL; + } + else + { + switch (chain[i - 1].node_type) + { + case T38_FAX: + case AUDIO_TO_T38_GATEWAY: + chain[i].t38_peer = i - 1; + chain[i].path.audio_in_buf = &chain[i + 1].audio_buf[0]; + break; + default: + chain[i].t38_peer = i + 1; + chain[i].path.audio_in_buf = &chain[i - 1].audio_buf[0]; + break; + } + } + + chain[i].path.audio_out_buf = &chain[i].audio_buf[0]; + + chain[i].awgn_state = NULL; + signal_scaling = 1.0f; + if (noise_level > -99) + { + chain[i].awgn_state = awgn_init_dbm0(NULL, 1234567, noise_level); + signal_scaling = powf(10.0f, signal_level/20.0f); + printf("Signal scaling %f\n", signal_scaling); + } } - set_t30_callbacks(t30_state[i], i); - } - /* Set up the channels */ - for (i = 0; i < 2; i++) - { - if ((g1050_path[i] = g1050_init(g1050_model_no, g1050_speed_pattern_no, 100, 33)) == NULL) + if ((chain[i].path.g1050_path = g1050_init(g1050_model_no, g1050_speed_pattern_no, 100, 33)) == NULL) { - fprintf(stderr, "Failed to start IP network path model\n"); + fprintf(stderr, " Failed to start IP network path model\n"); exit(2); } - memset(audio_buffer[2*i], 0, SAMPLES_PER_CHUNK*sizeof(int16_t)); - memset(audio_buffer[2*i + 1], 0, SAMPLES_PER_CHUNK*sizeof(int16_t)); - memset(t30_amp[i], 0, sizeof(t30_amp[i])); - memset(t38_amp[i], 0, sizeof(t38_amp[i])); } - memset(t38_amp_hist_a, 0, sizeof(t38_amp_hist_a)); - memset(t38_amp_hist_b, 0, sizeof(t38_amp_hist_b)); - for (i = 0; i < 2; i++) + for (i = 0; i < chain_elements; i++) { j = i + 1; - sprintf(buf, "%d%d%d%d%d%d%d%d", j, j, j, j, j, j, j, j); - t30_set_tx_ident(t30_state[i], buf); - strcpy(expected_rx_info[i ^ 1].ident, buf); - sprintf(buf, "Sub-address %d", j); - t30_set_tx_sub_address(t30_state[i], buf); - //strcpy(expected_rx_info[i ^ 1].sub_address, buf); - sprintf(buf, "Sender ID %d", j); - t30_set_tx_sender_ident(t30_state[i], buf); - //strcpy(expected_rx_info[i ^ 1].sender_ident, buf); - sprintf(buf, "Password %d", j); - t30_set_tx_password(t30_state[i], buf); - //strcpy(expected_rx_info[i ^ 1].password, buf); - sprintf(buf, "Polled sub-add %d", j); - t30_set_tx_polled_sub_address(t30_state[i], buf); - //strcpy(expected_rx_info[i ^ 1].polled_sub_address, buf); - sprintf(buf, "Select poll add %d", j); - t30_set_tx_selective_polling_address(t30_state[i], buf); - //strcpy(expected_rx_info[i ^ 1].selective_polling_address, buf); - t30_set_tx_page_header_info(t30_state[i], page_header_info); - if (page_header_tz) - t30_set_tx_page_header_tz(t30_state[i], page_header_tz); - - if ((i & 1) == 1) + if (chain[i].t30_state) { - t30_set_tx_nsf(t30_state[i], (const uint8_t *) "\x50\x00\x00\x00Spandsp\x00", 12); - expected_rx_info[i ^ 1].nsf = (uint8_t *) "\x50\x00\x00\x00Spandsp\x00"; - expected_rx_info[i ^ 1].nsf_len = 12; + sprintf(buf, "%d%d%d%d%d%d%d%d", j, j, j, j, j, j, j, j); + t30_set_tx_ident(chain[i].t30_state, buf); + strcpy(chain[chain[i].peer].expected_rx_info.ident, buf); + sprintf(buf, "Sub-address %d", j); + t30_set_tx_sub_address(chain[i].t30_state, buf); + //strcpy(chain[chain[i].peer].expected_rx_info.sub_address, buf); + sprintf(buf, "Sender ID %d", j); + t30_set_tx_sender_ident(chain[i].t30_state, buf); + //strcpy(chain[chain[i].peer].expected_rx_info.sender_ident, buf); + sprintf(buf, "Password %d", j); + t30_set_tx_password(chain[i].t30_state, buf); + //strcpy(chain[chain[i].peer].expected_rx_info.password, buf); + sprintf(buf, "Polled sub-add %d", j); + t30_set_tx_polled_sub_address(chain[i].t30_state, buf); + //strcpy(chain[chain[i].peer].expected_rx_info.polled_sub_address, buf); + sprintf(buf, "Select poll add %d", j); + t30_set_tx_selective_polling_address(chain[i].t30_state, buf); + //strcpy(chain[chain[i].peer].expected_rx_info.selective_polling_address, buf); + t30_set_tx_page_header_info(chain[i].t30_state, page_header_info); + if (page_header_tz) + t30_set_tx_page_header_tz(chain[i].t30_state, page_header_tz); + + if (i != 0) + { + t30_set_tx_nsf(chain[i].t30_state, (const uint8_t *) "\x50\x00\x00\x00Spandsp\x00", 12); + chain[chain[i].peer].expected_rx_info.nsf = (uint8_t *) "\x50\x00\x00\x00Spandsp\x00"; + chain[chain[i].peer].expected_rx_info.nsf_len = 12; + } + + t30_set_supported_modems(chain[i].t30_state, supported_modems); + t30_set_supported_t30_features(chain[i].t30_state, + T30_SUPPORT_IDENTIFICATION + | T30_SUPPORT_SELECTIVE_POLLING + | T30_SUPPORT_SUB_ADDRESSING); + t30_set_supported_image_sizes(chain[i].t30_state, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_WIDTH_255MM + | T4_SUPPORT_WIDTH_303MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED); + switch (allowed_bilevel_resolutions[(i == 0) ? 0 : 1]) + { + case 0: + /* Allow anything */ + t30_set_supported_bilevel_resolutions(chain[i].t30_state, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200); + break; + case 1: + /* Allow anything metric */ + t30_set_supported_bilevel_resolutions(chain[i].t30_state, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE); + break; + case 2: + /* Allow anything inch based */ + t30_set_supported_bilevel_resolutions(chain[i].t30_state, + T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200); + break; + case 3: + /* Allow only restricted length resolution */ + t30_set_supported_bilevel_resolutions(chain[i].t30_state, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200); + break; + case 4: + /* Allow only more restricted length resolution */ + t30_set_supported_bilevel_resolutions(chain[i].t30_state, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_200_100); + break; + } + if (colour_enabled) + { + t30_set_supported_colour_resolutions(chain[i].t30_state, + T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200); + } + else + { + t30_set_supported_colour_resolutions(chain[i].t30_state, 0); + } + if (t37_like_output) + { + t30_set_supported_output_compressions(chain[i].t30_state, + T4_COMPRESSION_T85 + | T4_COMPRESSION_T85_L0 + | T4_COMPRESSION_T6 + | T4_COMPRESSION_T42_T81); + } + else + { + t30_set_supported_output_compressions(chain[i].t30_state, + T4_COMPRESSION_T6 + | T4_COMPRESSION_JPEG); + } + + t30_set_ecm_capability(chain[i].t30_state, use_ecm); + t30_set_supported_compressions(chain[i].t30_state, + T4_COMPRESSION_T4_1D + | T4_COMPRESSION_T4_2D + | T4_COMPRESSION_T6 + | T4_COMPRESSION_T85 + | T4_COMPRESSION_T85_L0 + //| T4_COMPRESSION_T88 + | T4_COMPRESSION_T43 + | T4_COMPRESSION_T45 + | T4_COMPRESSION_T42_T81 + | T4_COMPRESSION_SYCC_T81 + | T4_COMPRESSION_GRAYSCALE + | T4_COMPRESSION_COLOUR + | T4_COMPRESSION_12BIT + | T4_COMPRESSION_COLOUR_TO_GRAY + | T4_COMPRESSION_GRAY_TO_BILEVEL + | T4_COMPRESSION_COLOUR_TO_BILEVEL + | T4_COMPRESSION_RESCALING + | 0); + t30_set_minimum_scan_line_time(chain[i].t30_state, scan_line_time); } - t30_set_supported_modems(t30_state[i], supported_modems); - t30_set_supported_t30_features(t30_state[i], - T30_SUPPORT_IDENTIFICATION - | T30_SUPPORT_SELECTIVE_POLLING - | T30_SUPPORT_SUB_ADDRESSING); - t30_set_supported_image_sizes(t30_state[i], - T4_SUPPORT_WIDTH_215MM - | T4_SUPPORT_WIDTH_255MM - | T4_SUPPORT_WIDTH_303MM - | T4_SUPPORT_LENGTH_US_LETTER - | T4_SUPPORT_LENGTH_US_LEGAL - | T4_SUPPORT_LENGTH_UNLIMITED); - switch (allowed_bilevel_resolutions[i]) + switch (chain[i].node_type) { - case 0: - /* Allow anything */ - t30_set_supported_bilevel_resolutions(t30_state[i], - T4_RESOLUTION_R8_STANDARD - | T4_RESOLUTION_R8_FINE - | T4_RESOLUTION_R8_SUPERFINE - | T4_RESOLUTION_R16_SUPERFINE - | T4_RESOLUTION_200_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_200_400 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_300_600 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_400_800 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_600_1200 - | T4_RESOLUTION_1200_1200); + case AUDIO_FAX: + fax_set_transmit_on_idle(chain[i].node.fax_state, use_transmit_on_idle); + fax_set_tep_mode(chain[i].node.fax_state, use_tep); break; - case 1: - /* Allow anything metric */ - t30_set_supported_bilevel_resolutions(t30_state[i], - T4_RESOLUTION_R8_STANDARD - | T4_RESOLUTION_R8_FINE - | T4_RESOLUTION_R8_SUPERFINE - | T4_RESOLUTION_R16_SUPERFINE); - break; - case 2: - /* Allow anything inch based */ - t30_set_supported_bilevel_resolutions(t30_state[i], - T4_RESOLUTION_200_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_200_400 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_300_600 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_400_800 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_600_1200 - | T4_RESOLUTION_1200_1200); - break; - case 3: - /* Allow only restricted length resolution */ - t30_set_supported_bilevel_resolutions(t30_state[i], - T4_RESOLUTION_R8_STANDARD - | T4_RESOLUTION_R8_FINE - | T4_RESOLUTION_200_100 - | T4_RESOLUTION_200_200); - break; - case 4: - /* Allow only more restricted length resolution */ - t30_set_supported_bilevel_resolutions(t30_state[i], - T4_RESOLUTION_R8_STANDARD - | T4_RESOLUTION_200_100); - break; - } - if (colour_enabled) - { - t30_set_supported_colour_resolutions(t30_state[i], - T4_RESOLUTION_100_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_1200_1200); - } - else - { - t30_set_supported_colour_resolutions(t30_state[i], 0); - } - if (t37_like_output) - { - t30_set_supported_output_compressions(t30_state[i], - T4_COMPRESSION_T85 - | T4_COMPRESSION_T85_L0 - | T4_COMPRESSION_T6 - | T4_COMPRESSION_T42_T81); - } - else - { - t30_set_supported_output_compressions(t30_state[i], - T4_COMPRESSION_T6 - | T4_COMPRESSION_JPEG); - } - - t30_set_ecm_capability(t30_state[i], use_ecm); - t30_set_supported_compressions(t30_state[i], - T4_COMPRESSION_T4_1D - | T4_COMPRESSION_T4_2D - | T4_COMPRESSION_T6 - | T4_COMPRESSION_T85 - | T4_COMPRESSION_T85_L0 - //| T4_COMPRESSION_T88 - | T4_COMPRESSION_T43 - | T4_COMPRESSION_T45 - | T4_COMPRESSION_T42_T81 - | T4_COMPRESSION_SYCC_T81 - | T4_COMPRESSION_GRAYSCALE - | T4_COMPRESSION_COLOUR - | T4_COMPRESSION_12BIT - | T4_COMPRESSION_COLOUR_TO_GRAY - | T4_COMPRESSION_GRAY_TO_BILEVEL - | T4_COMPRESSION_COLOUR_TO_BILEVEL - | T4_COMPRESSION_RESCALING - | 0); - t30_set_minimum_scan_line_time(t30_state[i], scan_line_time); - - if (mode[i] == T38_GATEWAY_FAX) - { - t38_gateway_set_transmit_on_idle(t38_gateway_state[i], use_transmit_on_idle); - t38_gateway_set_supported_modems(t38_gateway_state[i], supported_modems); - //t38_gateway_set_nsx_suppression(t38_state[i], NULL, 0, NULL, 0); - t38_gateway_set_fill_bit_removal(t38_gateway_state[i], remove_fill_bits); - t38_gateway_set_real_time_frame_handler(t38_gateway_state[i], real_time_gateway_frame_handler, (void *) (intptr_t) i); - t38_gateway_set_ecm_capability(t38_gateway_state[i], use_ecm); - } - if (mode[i] != AUDIO_FAX) - { - t38_set_t38_version(t38_core_state[i], t38_version); - } - - if (mode[i] == T38_TERMINAL_FAX) - { - //t30_set_iaf_mode(t30_state[i], T30_IAF_MODE_NO_FILL_BITS); + case T38_FAX: + t38_set_t38_version(chain[i].t38_core_state, t38_version); + //t30_set_iaf_mode(chain[i].t30_state, T30_IAF_MODE_NO_FILL_BITS); switch (t38_transport) { case T38_TRANSPORT_UDPTL: case T38_TRANSPORT_RTP: - t38_terminal_set_fill_bit_removal(t38_state[i], remove_fill_bits); - t38_terminal_set_tep_mode(t38_state[i], use_tep); + t38_terminal_set_fill_bit_removal(chain[i].node.t38_state, remove_fill_bits); + t38_terminal_set_tep_mode(chain[i].node.t38_state, use_tep); break; case T38_TRANSPORT_TCP: case T38_TRANSPORT_TCP_TPKT: - t38_terminal_set_fill_bit_removal(t38_state[i], true); - t38_terminal_set_config(t38_state[i], T38_TERMINAL_OPTION_NO_PACING | T38_TERMINAL_OPTION_NO_INDICATORS); - t38_terminal_set_tep_mode(t38_state[i], false); + t38_terminal_set_fill_bit_removal(chain[i].node.t38_state, true); + t38_terminal_set_config(chain[i].node.t38_state, T38_TERMINAL_OPTION_NO_PACING | T38_TERMINAL_OPTION_NO_INDICATORS); + t38_terminal_set_tep_mode(chain[i].node.t38_state, false); break; } - } - else - { - fax_set_transmit_on_idle(fax_state[i], use_transmit_on_idle); - fax_set_tep_mode(fax_state[i], use_tep); + break; } } - t30_set_tx_file(t30_state[0], input_tiff_file_name, start_page, end_page); - t30_set_rx_file(t30_state[1], OUTPUT_TIFF_FILE_NAME, -1); + for (i = 0; i < chain_elements; i++) + { + switch (chain[i].node_type) + { + case TSB85_AUDIO_FAX: + case TSB85_T38_FAX: + if (chain[chain[i].peer].node_type == AUDIO_FAX) + chain[i].node.faxtester_state->far_fax = chain[chain[i].peer].node.fax_state; + else + chain[i].node.faxtester_state->far_t38 = chain[chain[i].peer].node.t38_state; + chain[i].node.faxtester_state->far_t30 = chain[chain[i].peer].t30_state; + chain[i].node.faxtester_state->far_tag = chain[i].peer + 'A'; + + while (faxtester_next_step(chain[i].node.faxtester_state) == 0) + /*dummy loop*/; + /*endwhile*/ + break; + case REPLAY_AUDIO_FAX: + break; + case PASSTHROUGH: + if (chain[i - 1].path.audio_in_buf == &chain[i].audio_buf[0]) + chain[i - 1].path.audio_in_buf = &chain[i + 1].audio_buf[0]; + if (chain[i + 1].path.audio_in_buf == &chain[i].audio_buf[0]) + chain[i + 1].path.audio_in_buf = &chain[i - 1].audio_buf[0]; + break; + } + } + + switch (chain[chain_elements - 1].node_type) + { + case AUDIO_FAX: + case T38_FAX: + k = (use_polled_mode) ? (chain_elements - 1) : 0; + if (chain[k].t30_state) + t30_set_tx_file(chain[k].t30_state, input_tiff_file_name, start_page, end_page); + break; + } + switch (chain[0].node_type) + { + case AUDIO_FAX: + case T38_FAX: + k = (use_polled_mode) ? 0 : (chain_elements - 1); + if (chain[k].t30_state) + t30_set_rx_file(chain[k].t30_state, output_tiff_file_name, -1); + break; + } #if defined(ENABLE_GUI) if (use_gui) @@ -1064,144 +1322,225 @@ int main(int argc, char *argv[]) hist_ptr = 0; for (;;) { - memset(out_amp, 0, sizeof(out_amp)); + memset(audio_log, 0, sizeof(audio_log)); - for (i = 0; i < 2; i++) + for (i = 0; i < chain_elements; i++) { /* Update T.30 timing */ - logging = t30_get_logging_state(t30_state[i]); - span_log_bump_samples(logging, SAMPLES_PER_CHUNK); - - if (mode[i] == T38_TERMINAL_FAX) + switch (chain[i].node_type) { - /* Update T.38 termination timing */ - logging = t38_terminal_get_logging_state(t38_state[i]); + case AUDIO_FAX: + /* Update timing */ + logging = t30_get_logging_state(chain[i].t30_state); span_log_bump_samples(logging, SAMPLES_PER_CHUNK); - logging = t38_core_get_logging_state(t38_core_state[i]); + logging = fax_get_logging_state(chain[i].node.fax_state); span_log_bump_samples(logging, SAMPLES_PER_CHUNK); - - completed[i] = t38_terminal_send_timeout(t38_state[i], SAMPLES_PER_CHUNK); - } - else - { - /* Update audio FAX timing */ - logging = fax_get_logging_state(fax_state[i]); + logging = fax_modems_get_logging_state(&chain[i].node.fax_state->modems); span_log_bump_samples(logging, SAMPLES_PER_CHUNK); +#if 0 + /* Probe inside the modems to update their logs */ + span_log_bump_samples(chain[i].node.fax_state->modems.v27ter_rx.logging, len); + span_log_bump_samples(chain[i].node.fax_state->modems.v29_rx.logging, len); + span_log_bump_samples(chain[i].node.fax_state->modems.v17_rx.logging, len); +#endif #if 0 /* Mute the signal */ - vec_zeroi16(fax_rx_buf[i], SAMPLES_PER_CHUNK); + vec_zeroi16(chain[i].path.audio_in_buf->amp, SAMPLES_PER_CHUNK); + chain[i].path.audio_in_buf->len = SAMPLES_PER_CHUNK; #endif - fax_rx(fax_state[i], fax_rx_buf[i], SAMPLES_PER_CHUNK); - if (!t30_call_active(t30_state[i])) + if (log_audio) { - completed[i] = true; + k = (i == 0) ? 0 : 2; + for (j = 0; j < chain[i].path.audio_in_buf->len; j++) + audio_log[4*j + k] = chain[i].path.audio_in_buf->amp[j]; + } + fax_rx(chain[i].node.fax_state, chain[i].path.audio_in_buf->amp, chain[i].path.audio_in_buf->len); + if (!t30_call_active(chain[i].t30_state)) + { + chain[i].completed = true; continue; } - if (i == 0 && input_wave_handle) + chain[i].path.audio_out_buf->len = fax_tx(chain[i].node.fax_state, chain[i].path.audio_out_buf->amp, SAMPLES_PER_CHUNK); + if (!use_transmit_on_idle) { - t30_len[i] = sf_readf_short(input_wave_handle, fax_tx_buf[i], SAMPLES_PER_CHUNK); - if (t30_len[i] == 0) - break; + /* The receive side always expects a full block of samples, but the + transmit side may not be sending any when it doesn't need to. We + may need to pad with some silence. */ + if (chain[i].path.audio_out_buf->len < SAMPLES_PER_CHUNK) + { + vec_zeroi16(&chain[i].path.audio_out_buf->amp[chain[i].path.audio_out_buf->len], SAMPLES_PER_CHUNK - chain[i].path.audio_out_buf->len); + chain[i].path.audio_out_buf->len = SAMPLES_PER_CHUNK; + } } - else + if (chain[i].awgn_state) { - t30_len[i] = fax_tx(fax_state[i], fax_tx_buf[i], SAMPLES_PER_CHUNK); - if (!use_transmit_on_idle) - { - /* The receive side always expects a full block of samples, but the - transmit side may not be sending any when it doesn't need to. We - may need to pad with some silence. */ - if (t30_len[i] < SAMPLES_PER_CHUNK) - { - memset(t30_amp[i] + t30_len[i], 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - t30_len[i])); - t30_len[i] = SAMPLES_PER_CHUNK; - } - } - if (awgn_state[i]) - { - for (j = 0; j < t30_len[i]; j++) - fax_tx_buf[i][j] = ((int16_t) (fax_tx_buf[i][j]*signal_scaling)) + awgn(awgn_state[i]); - } + for (j = 0; j < chain[i].path.audio_out_buf->len; j++) + chain[i].path.audio_out_buf->amp[j] = ((int16_t) (chain[i].path.audio_out_buf->amp[j]*signal_scaling)) + awgn(chain[i].awgn_state); } if (log_audio) { - for (j = 0; j < t30_len[i]; j++) - out_amp[4*j + 2*i] = t30_amp[i][j]; + k = (i == 0) ? 1 : 3; + for (j = 0; j < chain[i].path.audio_out_buf->len; j++) + audio_log[4*j + k] = chain[i].path.audio_out_buf->amp[j]; } if (feedback_audio) { - for (j = 0; j < t30_len[i]; j++) - t30_amp[i][j] += t38_amp_hist_a[hist_ptr][j] >> 1; - memcpy(t38_amp_hist_a[hist_ptr], t38_amp[i], sizeof(int16_t)*SAMPLES_PER_CHUNK); + for (j = 0; j < chain[i].path.audio_out_buf->len; j++) + chain[i].path.audio_out_buf->amp[j] += t38_amp_hist_a[hist_ptr][j] >> 1; + memcpy(t38_amp_hist_a[hist_ptr], chain[i].path.audio_out_buf->amp, sizeof(int16_t)*SAMPLES_PER_CHUNK); } + break; + case T38_FAX: + /* Update timing */ + logging = t30_get_logging_state(chain[i].t30_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + logging = t38_terminal_get_logging_state(chain[i].node.t38_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + logging = t38_core_get_logging_state(chain[i].t38_core_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); - if (mode[i] == T38_GATEWAY_FAX) - { - /* Update T.38 gateway timing */ - logging = t38_gateway_get_logging_state(t38_gateway_state[i]); - span_log_bump_samples(logging, SAMPLES_PER_CHUNK); - logging = t38_core_get_logging_state(t38_core_state[i]); - span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + chain[i].completed = t38_terminal_send_timeout(chain[i].node.t38_state, SAMPLES_PER_CHUNK); - if (drop_frame_rate && --drop_frame == 0) - { - drop_frame = drop_frame_rate; - if (t38_gateway_rx_fillin(t38_gateway_state[i], SAMPLES_PER_CHUNK)) - break; - } - else - { - if (t38_gateway_rx(t38_gateway_state[i], t38_gateway_rx_buf[i], SAMPLES_PER_CHUNK)) - break; - } - - t38_len[i] = t38_gateway_tx(t38_gateway_state[i], t38_gateway_tx_buf[i], SAMPLES_PER_CHUNK); - if (!use_transmit_on_idle) - { - if (t38_len[i] < SAMPLES_PER_CHUNK) - { - memset(t38_amp[i] + t38_len[i], 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - t38_len[i])); - t38_len[i] = SAMPLES_PER_CHUNK; - } - } - if (feedback_audio) - { - for (j = 0; j < t30_len[i]; j++) - t30_amp[i][j] += t38_amp_hist_a[hist_ptr][j] >> 1; - memcpy(t38_amp_hist_a[hist_ptr], t38_amp[i], sizeof(int16_t)*SAMPLES_PER_CHUNK); - } - - if (log_audio) - { - for (j = 0; j < t38_len[i]; j++) - out_amp[4*j + 2*i + 1] = t38_amp[i][j]; - } - } - } - if (mode[i] != AUDIO_FAX) - { - while ((msg_len = g1050_get(g1050_path[i], msg, 1024, when, &seq_no, &tx_when, &rx_when)) >= 0) + while ((msg_len = g1050_get(chain[i].path.g1050_path, msg, 1024, when, &seq_no, &tx_when, &rx_when)) >= 0) { #if defined(ENABLE_GUI) if (use_gui) media_monitor_rx(seq_no, tx_when, rx_when); #endif - t38_core_rx_ifp_packet(t38_core_state[i ^ 1], msg, msg_len, seq_no); + t38_core_rx_ifp_packet(chain[chain[i].t38_peer].t38_core_state, msg, msg_len, seq_no); } + break; + case TSB85_AUDIO_FAX: + /* Update timing */ + logging = faxtester_get_logging_state(chain[i].node.faxtester_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); +#if 0 + /* Probe inside the modems to update their logs */ + span_log_bump_samples(&chain[i].node.faxtester_state->modems.v27ter_rx.logging, len); + span_log_bump_samples(&chain[i].node.faxtester_state->modems.v29_rx.logging, len); + span_log_bump_samples(&chain[i].node.faxtester_state->modems.v17_rx.logging, len); +#endif + + if (log_audio) + { + k = (i == 0) ? 0 : 2; + for (j = 0; j < chain[i].path.audio_in_buf->len; j++) + audio_log[4*j + k] = chain[i].path.audio_in_buf->amp[j]; + } + faxtester_rx(chain[i].node.faxtester_state, chain[i].path.audio_in_buf->amp, chain[i].path.audio_in_buf->len); + chain[i].path.audio_out_buf->len = faxtester_tx(chain[i].node.faxtester_state, chain[i].path.audio_out_buf->amp, SAMPLES_PER_CHUNK); + if (chain[i].path.audio_out_buf->len == 0) + break; + if (log_audio) + { + k = (i == 0) ? 1 : 3; + for (j = 0; j < chain[i].path.audio_out_buf->len; j++) + audio_log[4*j + k] = chain[i].path.audio_out_buf->amp[j]; + } + if (chain[i].node.faxtester_state->test_for_call_clear && !chain[i].node.faxtester_state->far_end_cleared_call) + { + chain[i].node.faxtester_state->call_clear_timer += chain[i].path.audio_out_buf->len; + if (!t30_call_active(chain[i].node.faxtester_state->far_t30)) + { + span_log(faxtester_get_logging_state(chain[i].node.faxtester_state), + SPAN_LOG_FLOW, + "Far end cleared after %dms (limits %dms to %dms)\n", + chain[i].node.faxtester_state->call_clear_timer/8, + chain[i].node.faxtester_state->timein_x, + chain[i].node.faxtester_state->timeout); + if (chain[i].node.faxtester_state->call_clear_timer/8 < chain[i].node.faxtester_state->timein_x || chain[i].node.faxtester_state->call_clear_timer/8 > chain[i].node.faxtester_state->timeout_x) + { + printf("Test failed\n"); + exit(2); + } + span_log(faxtester_get_logging_state(chain[i].node.faxtester_state), SPAN_LOG_FLOW, "Clear time OK\n"); + chain[i].node.faxtester_state->far_end_cleared_call = true; + chain[i].node.faxtester_state->test_for_call_clear = false; + while (faxtester_next_step(chain[i].node.faxtester_state) == 0) + /*dummy loop*/; + /*endwhile*/ + } + /*endif*/ + } + /*endif*/ + break; + case REPLAY_AUDIO_FAX: + chain[i].path.audio_out_buf->len = sf_readf_short(chain[i].node.wave_handle, chain[i].path.audio_out_buf->amp, SAMPLES_PER_CHUNK); + if (chain[i].path.audio_out_buf->len == 0) + break; + break; + case AUDIO_TO_T38_GATEWAY: + /* Update timing */ + logging = t38_gateway_get_logging_state(chain[i].node.t38_gateway_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); + logging = t38_core_get_logging_state(chain[i].t38_core_state); + span_log_bump_samples(logging, SAMPLES_PER_CHUNK); +#if 0 + /* Probe inside the modems to update their logs */ + span_log_bump_samples(&chain[i].node.t38_gateway_state->modems.v27ter_rx.logging, len); + span_log_bump_samples(&chain[i].node.t38_gateway_state->modems.v29_rx.logging, len); + span_log_bump_samples(&chain[i].node.t38_gateway_state->modems.v17_rx.logging, len); +#endif + + if (drop_frame_rate && --drop_frame == 0) + { + drop_frame = drop_frame_rate; + if (t38_gateway_rx_fillin(chain[i].node.t38_gateway_state, SAMPLES_PER_CHUNK)) + break; + } + else + { + if (t38_gateway_rx(chain[i].node.t38_gateway_state, chain[i].path.audio_in_buf->amp, chain[i].path.audio_in_buf->len)) + break; + } + + chain[i].path.audio_out_buf->len = t38_gateway_tx(chain[i].node.t38_gateway_state, chain[i].path.audio_out_buf->amp, SAMPLES_PER_CHUNK); + if (!use_transmit_on_idle) + { + if (chain[i].path.audio_out_buf->len < SAMPLES_PER_CHUNK) + { + vec_zeroi16(&chain[i].path.audio_out_buf->amp[chain[i].path.audio_out_buf->len], SAMPLES_PER_CHUNK - chain[i].path.audio_out_buf->len); + chain[i].path.audio_out_buf->len = SAMPLES_PER_CHUNK; + } + } + if (feedback_audio) + { + for (j = 0; j < chain[i].path.audio_out_buf->len; j++) + chain[i].path.audio_out_buf->amp[j] += t38_amp_hist_a[hist_ptr][j] >> 1; + vec_movei16(t38_amp_hist_a[hist_ptr], chain[i].path.audio_out_buf->amp, SAMPLES_PER_CHUNK); + } + +#if 0 + if (log_audio) + { + k = (i == 0) ? 1 : 3; + for (j = 0; j < chain[i].path.audio_out_buf->len; j++) + audio_log[4*j + k] = chain[i].path.audio_out_buf->amp[j]; + } +#endif + while ((msg_len = g1050_get(chain[i].path.g1050_path, msg, 1024, when, &seq_no, &tx_when, &rx_when)) >= 0) + { +#if defined(ENABLE_GUI) + if (use_gui) + media_monitor_rx(seq_no, tx_when, rx_when); +#endif + t38_core_rx_ifp_packet(chain[chain[i].t38_peer].t38_core_state, msg, msg_len, seq_no); + } + break; } } if (log_audio) { - outframes = sf_writef_short(wave_handle, out_amp, SAMPLES_PER_CHUNK); + outframes = sf_writef_short(wave_handle, audio_log, SAMPLES_PER_CHUNK); if (outframes != SAMPLES_PER_CHUNK) break; } when += (float) SAMPLES_PER_CHUNK/(float) SAMPLE_RATE; - if (completed[0] && completed[1]) + if (chain[0].completed && chain[chain_elements - 1].completed) break; #if defined(ENABLE_GUI) if (use_gui) @@ -1210,24 +1549,19 @@ int main(int argc, char *argv[]) if (++hist_ptr > 3) hist_ptr = 0; } - for (i = 0; i < 2; i++) + + for (i = 0; i < chain_elements; i++) { - if (mode[i] == T38_GATEWAY_FAX) + switch (chain[i].node_type) { - t38_gateway_get_transfer_statistics(t38_gateway_state[i], &t38_stats); + case AUDIO_TO_T38_GATEWAY: + t38_gateway_get_transfer_statistics(chain[i].node.t38_gateway_state, &t38_stats); printf("%c side exchanged %d pages at %dbps, in %s mode\n", i + 'A', t38_stats.pages_transferred, t38_stats.bit_rate, (t38_stats.error_correcting_mode) ? "ECM" : "non-ECM"); - } - } - if (input_wave_handle) - { - if (sf_close_telephony(input_wave_handle)) - { - fprintf(stderr, " Cannot close audio file '%s'\n", decode_file_name); - exit(2); + break; } } if (log_audio) @@ -1246,14 +1580,16 @@ int main(int argc, char *argv[]) if (start_page >= 0) expected_pages -= start_page; /* Check how many pages were transferred */ - for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) { - if (!phase_e_reached[i]) + i = (j == 0) ? 0 : (chain_elements - 1); + if (!chain[i].phase_e_reached) break; - if (!succeeded[i]) + if (!chain[i].succeeded) break; - t30_get_transfer_statistics(t30_state[i], &t30_stats); - if (i & 1) + + t30_get_transfer_statistics(chain[i].t30_state, &t30_stats); + if ((!use_polled_mode && i != 0) || (use_polled_mode && i == 0)) { if (t30_stats.pages_tx != 0 || t30_stats.pages_rx != expected_pages) break; @@ -1263,23 +1599,45 @@ int main(int argc, char *argv[]) if (t30_stats.pages_tx != expected_pages || t30_stats.pages_rx != 0) break; } - if (mode[i] == T38_TERMINAL_FAX) - t38_terminal_free(t38_state[i]); - else - fax_free(fax_state[i]); - if (mode[i] == T38_GATEWAY_FAX) - t38_gateway_free(t38_gateway_state[i]); - if (g1050_path[i]) + } + for (i = 0; i < chain_elements; i++) + { + switch (chain[i].node_type) { - g1050_free(g1050_path[i]); - g1050_path[i] = NULL; + case AUDIO_FAX: + fax_free(chain[i].node.fax_state); + break; + case T38_FAX: + t38_terminal_free(chain[i].node.t38_state); + break; + case TSB85_AUDIO_FAX: + case TSB85_T38_FAX: + faxtester_free(chain[i].node.faxtester_state); + break; + case REPLAY_AUDIO_FAX: + if (sf_close_telephony(chain[i].node.wave_handle)) + { + fprintf(stderr, " Cannot close audio file '%s'\n", replay_file_name); + exit(2); + } + chain[i].node.wave_handle = NULL; + break; + case AUDIO_TO_T38_GATEWAY: + t38_gateway_free(chain[i].node.t38_gateway_state); + break; + } + if (chain[i].path.g1050_path) + { + g1050_free(chain[i].path.g1050_path); + chain[i].path.g1050_path = NULL; } } - if (i < 2) + if (j < 2) { printf("Tests failed\n"); exit(2); } + t33_tests(); printf("Tests passed\n"); return 0; } diff --git a/libs/spandsp/tests/fax_tests.sh b/libs/spandsp/tests/fax_tests.sh index 0cacdcdd7b..140d8fddf9 100755 --- a/libs/spandsp/tests/fax_tests.sh +++ b/libs/spandsp/tests/fax_tests.sh @@ -94,7 +94,7 @@ LOCALTESTS_DIR=../test-data/local TIFFCMP=tiffcmp # Colour/gray -> bilevel by not allowing ECM -#for OPTS in "-p AA" "-p TT" "-p GG" "-p TG" "-p GT" +#for OPTS in "-p FAX-FAX" "-p T38-T38" "-p FAX-T38gateway-T38gateway-FAX" "-p T38-T38gateway-FAX" "-p FAX-T38gateway-T38" #do # IN_FILE="${LOCALTESTS_DIR}/lenna-colour.tif" # OUT_FILE="${LOCALTESTS_DIR}/lenna-colour-bilevel.tif" @@ -118,7 +118,7 @@ TIFFCMP=tiffcmp #done # Colour/gray -> colour/gray -#for OPTS in "-p AA -C -e" "-p TT -C -e" "-p GG -C -e" "-p TG -C -e" "-p GT -C -e" +#for OPTS in "-p FAX-FAX -C -e" "-p T38-T38 -C -e" "-p FAX-T38gateway-T38gateway-FAX -C -e" "-p T38-T38gateway-FAX -C -e" "-p FAX-T38gateway-T38 -C -e" #do # IN_FILE="${LOCALTESTS_DIR}/lenna-colour.tif" # OUT_FILE="${LOCALTESTS_DIR}/lenna-colour-out.tif" @@ -142,7 +142,7 @@ TIFFCMP=tiffcmp #done # Bi-level tests with image squashing -for OPTS in "-p AA" "-p AA -e" "-p TT" "-p TT -e" "-p GG" "-p GG -e" "-p TG" "-p TG -e" "-p GT" "-p GT -e" +for OPTS in "-p FAX-FAX" "-p FAX-FAX -e" "-p T38-T38" "-p T38-T38 -e" "-p FAX-T38gateway-T38gateway-FAX" "-p FAX-T38gateway-T38gateway-FAX -e" "-p T38-T38gateway-FAX" "-p T38-T38gateway-FAX -e" "-p FAX-T38gateway-T38" "-p FAX-T38gateway-T38 -e" do IN_FILE="${ITUTESTS_DIR}/bilevel_R8_77_A4.tif" OUT_FILE="${ITUTESTS_DIR}/bilevel_R8_77SQ_A4.tif" @@ -241,7 +241,7 @@ do done # Bi-level tests -for OPTS in "-p AA" "-p AA -e" "-p TT" "-p TT -e" "-p GG" "-p GG -e" "-p TG" "-p TG -e" "-p GT" "-p GT -e" +for OPTS in "-p FAX-FAX" "-p FAX-FAX -e" "-p T38-T38" "-p T38-T38 -e" "-p FAX-T38gateway-T38gateway-FAX" "-p FAX-T38gateway-T38gateway-FAX -e" "-p T38-T38gateway-FAX" "-p T38-T38gateway-FAX -e" "-p FAX-T38gateway-T38" "-p FAX-T38gateway-T38 -e" do FILE="${ITUTESTS_DIR}/itutests.tif" run_fax_test diff --git a/libs/spandsp/tests/pcap_parse.c b/libs/spandsp/tests/pcap_parse.c index 56d8d288e8..bc3805af2a 100644 --- a/libs/spandsp/tests/pcap_parse.c +++ b/libs/spandsp/tests/pcap_parse.c @@ -113,7 +113,7 @@ typedef struct _null_hdr typedef struct _ipv6_hdr { char dontcare[6]; - uint8_t nxt_header; /* we only need the next header, so we can determine, if the next header is UDP or not */ + u_int8_t nxt_header; /* we only need the next header, so we can determine, if the next header is UDP or not */ char dontcare2[33]; } ipv6_hdr; #endif diff --git a/libs/spandsp/tests/tsb85_tests.c b/libs/spandsp/tests/tsb85_tests.c index 624dcb1f6c..5b199491f1 100644 --- a/libs/spandsp/tests/tsb85_tests.c +++ b/libs/spandsp/tests/tsb85_tests.c @@ -71,1179 +71,11 @@ SNDFILE *out_handle; -bool use_receiver_not_ready = false; -bool test_local_interrupt = false; - const char *output_tiff_file_name; bool log_audio = false; -fax_state_t *fax; -faxtester_state_t state; - -uint8_t image[1000000]; - -uint8_t awaited[1000]; -int awaited_len = 0; - -char image_path[1024]; - -t30_exchanged_info_t expected_rx_info; - -char next_tx_file[1000]; - -static int timein_x = -1; -static int timeout_x = -1; - -static int next_step(faxtester_state_t *s); - -static bool test_for_call_clear = false; -static int call_clear_timer = 0; - -static bool far_end_cleared_call = false; - -struct -{ - const char *tag; - int code; -} t30_status[] = -{ - {"OK", T30_ERR_OK}, - {"CEDTONE", T30_ERR_CEDTONE}, - {"T0_EXPIRED", T30_ERR_T0_EXPIRED}, - {"T1_EXPIRED", T30_ERR_T1_EXPIRED}, - {"T3_EXPIRED", T30_ERR_T3_EXPIRED}, - {"HDLC_CARRIER", T30_ERR_HDLC_CARRIER}, - {"CANNOT_TRAIN", T30_ERR_CANNOT_TRAIN}, - {"OPER_INT_FAIL", T30_ERR_OPER_INT_FAIL}, - {"INCOMPATIBLE", T30_ERR_INCOMPATIBLE}, - {"RX_INCAPABLE", T30_ERR_RX_INCAPABLE}, - {"TX_INCAPABLE", T30_ERR_TX_INCAPABLE}, - {"NORESSUPPORT", T30_ERR_NORESSUPPORT}, - {"NOSIZESUPPORT", T30_ERR_NOSIZESUPPORT}, - {"UNEXPECTED", T30_ERR_UNEXPECTED}, - {"TX_BADDCS", T30_ERR_TX_BADDCS}, - {"TX_BADPG", T30_ERR_TX_BADPG}, - {"TX_ECMPHD", T30_ERR_TX_ECMPHD}, - {"TX_GOTDCN", T30_ERR_TX_GOTDCN}, - {"TX_INVALRSP", T30_ERR_TX_INVALRSP}, - {"TX_NODIS", T30_ERR_TX_NODIS}, - {"TX_PHBDEAD", T30_ERR_TX_PHBDEAD}, - {"TX_PHDDEAD", T30_ERR_TX_PHDDEAD}, - {"TX_T5EXP", T30_ERR_TX_T5EXP}, - {"RX_ECMPHD", T30_ERR_RX_ECMPHD}, - {"RX_GOTDCS", T30_ERR_RX_GOTDCS}, - {"RX_INVALCMD", T30_ERR_RX_INVALCMD}, - {"RX_NOCARRIER", T30_ERR_RX_NOCARRIER}, - {"RX_NOEOL", T30_ERR_RX_NOEOL}, - {"RX_NOFAX", T30_ERR_RX_NOFAX}, - {"RX_T2EXPDCN", T30_ERR_RX_T2EXPDCN}, - {"RX_T2EXPD", T30_ERR_RX_T2EXPD}, - {"RX_T2EXPFAX", T30_ERR_RX_T2EXPFAX}, - {"RX_T2EXPMPS", T30_ERR_RX_T2EXPMPS}, - {"RX_T2EXPRR", T30_ERR_RX_T2EXPRR}, - {"RX_T2EXP", T30_ERR_RX_T2EXP}, - {"RX_DCNWHY", T30_ERR_RX_DCNWHY}, - {"RX_DCNDATA", T30_ERR_RX_DCNDATA}, - {"RX_DCNFAX", T30_ERR_RX_DCNFAX}, - {"RX_DCNPHD", T30_ERR_RX_DCNPHD}, - {"RX_DCNRRD", T30_ERR_RX_DCNRRD}, - {"RX_DCNNORTN", T30_ERR_RX_DCNNORTN}, - {"FILEERROR", T30_ERR_FILEERROR}, - {"NOPAGE", T30_ERR_NOPAGE}, - {"BADTIFF", T30_ERR_BADTIFF}, - {"BADPAGE", T30_ERR_BADPAGE}, - {"BADTAG", T30_ERR_BADTAG}, - {"BADTIFFHDR", T30_ERR_BADTIFFHDR}, - {"NOMEM", T30_ERR_NOMEM}, - {"RETRYDCN", T30_ERR_RETRYDCN}, - {"CALLDROPPED", T30_ERR_CALLDROPPED}, - {"NOPOLL", T30_ERR_NOPOLL}, - {"IDENT_UNACCEPTABLE", T30_ERR_IDENT_UNACCEPTABLE}, - {"SUB_UNACCEPTABLE", T30_ERR_SUB_UNACCEPTABLE}, - {"SEP_UNACCEPTABLE", T30_ERR_SEP_UNACCEPTABLE}, - {"PSA_UNACCEPTABLE", T30_ERR_PSA_UNACCEPTABLE}, - {"SID_UNACCEPTABLE", T30_ERR_SID_UNACCEPTABLE}, - {"PWD_UNACCEPTABLE", T30_ERR_PWD_UNACCEPTABLE}, - {"TSA_UNACCEPTABLE", T30_ERR_TSA_UNACCEPTABLE}, - {"IRA_UNACCEPTABLE", T30_ERR_IRA_UNACCEPTABLE}, - {"CIA_UNACCEPTABLE", T30_ERR_CIA_UNACCEPTABLE}, - {"ISP_UNACCEPTABLE", T30_ERR_ISP_UNACCEPTABLE}, - {"CSA_UNACCEPTABLE", T30_ERR_CSA_UNACCEPTABLE}, - {NULL, -1} -}; - -static int phase_b_handler(void *user_data, int result) -{ - int ch; - int status; - t30_state_t *s; - const char *u; - - s = (t30_state_t *) user_data; - ch = 'A'; - status = T30_ERR_OK; - if ((u = t30_get_rx_ident(s))) - { - printf("%c: Phase B: remote ident '%s'\n", ch, u); - if (expected_rx_info.ident[0] && strcmp(expected_rx_info.ident, u)) - { - printf("%c: Phase B: remote ident incorrect! - expected '%s'\n", ch, expected_rx_info.ident); - status = T30_ERR_IDENT_UNACCEPTABLE; - } - } - else - { - if (expected_rx_info.ident[0]) - { - printf("%c: Phase B: remote ident missing!\n", ch); - status = T30_ERR_IDENT_UNACCEPTABLE; - } - } - if ((u = t30_get_rx_sub_address(s))) - { - printf("%c: Phase B: remote sub-address '%s'\n", ch, u); - if (expected_rx_info.sub_address[0] && strcmp(expected_rx_info.sub_address, u)) - { - printf("%c: Phase B: remote sub-address incorrect! - expected '%s'\n", ch, expected_rx_info.sub_address); - status = T30_ERR_SUB_UNACCEPTABLE; - } - } - else - { - if (expected_rx_info.sub_address[0]) - { - printf("%c: Phase B: remote sub-address missing!\n", ch); - status = T30_ERR_SUB_UNACCEPTABLE; - } - } - if ((u = t30_get_rx_polled_sub_address(s))) - { - printf("%c: Phase B: remote polled sub-address '%s'\n", ch, u); - if (expected_rx_info.polled_sub_address[0] && strcmp(expected_rx_info.polled_sub_address, u)) - { - printf("%c: Phase B: remote polled sub-address incorrect! - expected '%s'\n", ch, expected_rx_info.polled_sub_address); - status = T30_ERR_PSA_UNACCEPTABLE; - } - } - else - { - if (expected_rx_info.polled_sub_address[0]) - { - printf("%c: Phase B: remote polled sub-address missing!\n", ch); - status = T30_ERR_PSA_UNACCEPTABLE; - } - } - if ((u = t30_get_rx_selective_polling_address(s))) - { - printf("%c: Phase B: remote selective polling address '%s'\n", ch, u); - if (expected_rx_info.selective_polling_address[0] && strcmp(expected_rx_info.selective_polling_address, u)) - { - printf("%c: Phase B: remote selective polling address incorrect! - expected '%s'\n", ch, expected_rx_info.selective_polling_address); - status = T30_ERR_SEP_UNACCEPTABLE; - } - } - else - { - if (expected_rx_info.selective_polling_address[0]) - { - printf("%c: Phase B: remote selective polling address missing!\n", ch); - status = T30_ERR_SEP_UNACCEPTABLE; - } - } - if ((u = t30_get_rx_sender_ident(s))) - { - printf("%c: Phase B: remote sender ident '%s'\n", ch, u); - if (expected_rx_info.sender_ident[0] && strcmp(expected_rx_info.sender_ident, u)) - { - printf("%c: Phase B: remote sender ident incorrect! - expected '%s'\n", ch, expected_rx_info.sender_ident); - status = T30_ERR_SID_UNACCEPTABLE; - } - } - else - { - if (expected_rx_info.sender_ident[0]) - { - printf("%c: Phase B: remote sender ident missing!\n", ch); - status = T30_ERR_SID_UNACCEPTABLE; - } - } - if ((u = t30_get_rx_password(s))) - { - printf("%c: Phase B: remote password '%s'\n", ch, u); - if (expected_rx_info.password[0] && strcmp(expected_rx_info.password, u)) - { - printf("%c: Phase B: remote password incorrect! - expected '%s'\n", ch, expected_rx_info.password); - status = T30_ERR_PWD_UNACCEPTABLE; - } - } - else - { - if (expected_rx_info.password[0]) - { - printf("%c: Phase B: remote password missing!\n", ch); - status = T30_ERR_PWD_UNACCEPTABLE; - } - } - printf("%c: Phase B handler on channel %d - (0x%X) %s\n", ch, ch, result, t30_frametype(result)); - return status; -} -/*- End of function --------------------------------------------------------*/ - -static int phase_d_handler(void *user_data, int result) -{ - int i; - int ch; - t30_state_t *s; - char tag[20]; - - i = 0; - s = (t30_state_t *) user_data; - ch = i + 'A'; - snprintf(tag, sizeof(tag), "%c: Phase D", ch); - printf("%c: Phase D handler on channel %c - (0x%X) %s\n", ch, ch, result, t30_frametype(result)); - fax_log_page_transfer_statistics(s, tag); - fax_log_tx_parameters(s, tag); - fax_log_rx_parameters(s, tag); - - if (use_receiver_not_ready) - t30_set_receiver_not_ready(s, 3); - - if (test_local_interrupt) - { - if (i == 0) - { - printf("%c: Initiating interrupt request\n", ch); - t30_local_interrupt_request(s, true); - } - else - { - switch (result) - { - case T30_PIP: - case T30_PRI_MPS: - case T30_PRI_EOM: - case T30_PRI_EOP: - printf("%c: Accepting interrupt request\n", ch); - t30_local_interrupt_request(s, true); - break; - case T30_PIN: - break; - } - } - } - return T30_ERR_OK; -} -/*- End of function --------------------------------------------------------*/ - -static void phase_e_handler(void *user_data, int result) -{ - int ch; - t30_state_t *s; - char tag[20]; - - ch = 'A'; - s = (t30_state_t *) user_data; - snprintf(tag, sizeof(tag), "%c: Phase E", ch); - printf("%c: Phase E handler on channel %c - (%d) %s\n", ch, ch, result, t30_completion_code_to_str(result)); - fax_log_final_transfer_statistics(s, tag); - fax_log_tx_parameters(s, tag); - fax_log_rx_parameters(s, tag); -} -/*- End of function --------------------------------------------------------*/ - -static void t30_real_time_frame_handler(void *user_data, - bool incoming, - const uint8_t *msg, - int len) -{ - if (msg == NULL) - { - } - else - { - fprintf(stderr, - "T.30: Real time frame handler - %s, %s, length = %d\n", - (incoming) ? "line->T.30" : "T.30->line", - t30_frametype(msg[2]), - len); - } -} -/*- End of function --------------------------------------------------------*/ - -static int document_handler(void *user_data, int event) -{ - int ch; - t30_state_t *s; - - ch = 'A'; - s = (t30_state_t *) user_data; - fprintf(stderr, "%c: Document handler on channel %c - event %d\n", ch, ch, event); - if (next_tx_file[0]) - { - t30_set_tx_file(s, next_tx_file, -1, -1); - next_tx_file[0] = '\0'; - return true; - } - return false; -} -/*- End of function --------------------------------------------------------*/ - -static void faxtester_real_time_frame_handler(faxtester_state_t *s, - void *user_data, - int direction, - const uint8_t *msg, - int len) -{ - if (msg == NULL) - { - while (next_step(s) == 0) - ; - /*endwhile*/ - } - else - { - fprintf(stderr, - "TST: Real time frame handler - %s, %s, length = %d\n", - (direction) ? "line->tester" : "tester->line", - t30_frametype(msg[2]), - len); - if (direction && msg[1] == awaited[1]) - { - if ((awaited_len >= 0 && len != abs(awaited_len)) - || - (awaited_len < 0 && len < abs(awaited_len)) - || - memcmp(msg, awaited, abs(awaited_len)) != 0) - { - span_log_buf(&s->logging, SPAN_LOG_FLOW, "Expected", awaited, abs(awaited_len)); - span_log_buf(&s->logging, SPAN_LOG_FLOW, "Received", msg, len); - printf("Test failed\n"); - exit(2); - } - } - if (msg[1] == awaited[1]) - { - while (next_step(s) == 0) - ; - /*endwhile*/ - } - } -} -/*- End of function --------------------------------------------------------*/ - -static void faxtester_front_end_step_complete_handler(faxtester_state_t *s, void *user_data) -{ - while (next_step(s) == 0) - ; - /*endwhile*/ -} -/*- End of function --------------------------------------------------------*/ - -static void faxtester_front_end_step_timeout_handler(faxtester_state_t *s, void *user_data) -{ - span_log(&s->logging, SPAN_LOG_FLOW, "FAX tester step timed out\n"); - printf("Test failed\n"); - exit(2); -} -/*- End of function --------------------------------------------------------*/ - -static void fax_prepare(void) -{ - t30_state_t *t30; - logging_state_t *logging; - - t30 = fax_get_t30_state(fax); - fax_set_transmit_on_idle(fax, true); - fax_set_tep_mode(fax, true); -#if 0 - t30_set_tx_ident(t30, "1234567890"); - t30_set_tx_sub_address(t30, "Sub-address"); - t30_set_tx_sender_ident(t30, "Sender ID"); - t30_set_tx_password(t30, "Password"); - t30_set_tx_polled_sub_address(t30, "Polled sub-address"); - t30_set_tx_selective_polling_address(t30, "Sel polling address"); -#endif - t30_set_tx_nsf(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSF\x00", 16); - //t30_set_tx_nss(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSS\x00", 16); - t30_set_tx_nsc(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp NSC\x00", 16); - t30_set_ecm_capability(t30, true); - t30_set_supported_t30_features(t30, - T30_SUPPORT_IDENTIFICATION - | T30_SUPPORT_SELECTIVE_POLLING - | T30_SUPPORT_SUB_ADDRESSING); - t30_set_supported_image_sizes(t30, - T4_SUPPORT_WIDTH_215MM - | T4_SUPPORT_WIDTH_255MM - | T4_SUPPORT_WIDTH_303MM - | T4_SUPPORT_LENGTH_US_LETTER - | T4_SUPPORT_LENGTH_US_LEGAL - | T4_SUPPORT_LENGTH_UNLIMITED); - t30_set_supported_bilevel_resolutions(t30, - T4_RESOLUTION_R8_STANDARD - | T4_RESOLUTION_R8_FINE - | T4_RESOLUTION_R8_SUPERFINE - | T4_RESOLUTION_R16_SUPERFINE - | T4_RESOLUTION_100_100 - | T4_RESOLUTION_200_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_200_400 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_300_600 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_400_800 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_600_1200 - | T4_RESOLUTION_1200_1200); - t30_set_supported_colour_resolutions(t30, 0); - t30_set_supported_modems(t30, T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17); - t30_set_supported_compressions(t30, T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6); - t30_set_phase_b_handler(t30, phase_b_handler, (void *) t30); - t30_set_phase_d_handler(t30, phase_d_handler, (void *) t30); - t30_set_phase_e_handler(t30, phase_e_handler, (void *) t30); - t30_set_real_time_frame_handler(t30, t30_real_time_frame_handler, (void *) t30); - t30_set_document_handler(t30, document_handler, (void *) t30); - - logging = fax_get_logging_state(fax); - span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); - span_log_set_tag(logging, "A"); - - logging = t30_get_logging_state(t30); - span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); - span_log_set_tag(logging, "A"); - -#if 0 - span_log_set_level(&fax.modems.v27ter_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); - span_log_set_tag(&fax.modems.v27ter_rx.logging, "A"); - span_log_set_level(&fax.modems.v29_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); - span_log_set_tag(&fax.modems.v29_rx.logging, "A"); - span_log_set_level(&fax.modems.v17_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); - span_log_set_tag(&fax.modems.v17_rx.logging, "A"); -#endif -} -/*- End of function --------------------------------------------------------*/ - -static int string_to_msg(uint8_t msg[], uint8_t mask[], const char buf[]) -{ - int i; - int x; - const char *t; - - msg[0] = 0; - mask[0] = 0xFF; - i = 0; - t = (char *) buf; - while (*t) - { - /* Skip white space */ - while (isspace((int) *t)) - t++; - /* If we find ... we allow arbitrary addition info beyond this point in the message */ - if (t[0] == '.' && t[1] == '.' && t[2] == '.') - { - return -i; - } - else if (isxdigit((int) *t)) - { - for ( ; isxdigit((int) *t); t++) - { - x = *t; - if (x >= 'a') - x -= 0x20; - if (x >= 'A') - x -= ('A' - 10); - else - x -= '0'; - msg[i] = (msg[i] << 4) | x; - } - mask[i] = 0xFF; - if (*t == '/') - { - /* There is a mask following the byte */ - mask[i] = 0; - for (t++; isxdigit((int) *t); t++) - { - x = *t; - if (x >= 'a') - x -= 0x20; - if (x >= 'A') - x -= ('A' - 10); - else - x -= '0'; - mask[i] = (mask[i] << 4) | x; - } - } - if (*t && !isspace((int) *t)) - { - /* Bad string */ - return 0; - } - i++; - } - } - return i; -} -/*- End of function --------------------------------------------------------*/ - -#if 0 -static void string_test2(const uint8_t msg[], int len) -{ - int i; - - if (len > 0) - { - for (i = 0; i < len - 1; i++) - printf("%02X ", msg[i]); - printf("%02X", msg[i]); - } -} -/*- End of function --------------------------------------------------------*/ - -static void string_test3(const char buf[]) -{ - uint8_t msg[1000]; - uint8_t mask[1000]; - int len; - int i; - - len = string_to_msg(msg, mask, buf); - printf("Len = %d: ", len); - string_test2(msg, abs(len)); - printf("/"); - string_test2(mask, abs(len)); - printf("\n"); -} -/*- End of function --------------------------------------------------------*/ - -static int string_test(void) -{ - string_test3("FF C8 12 34 56 78"); - string_test3("FF C8 12/55 34 56/aA 78 "); - string_test3("FF C8 12/55 34 56/aA 78 ..."); - string_test3("FF C8 12/55 34 56/aA 78..."); - string_test3("FF C8 12/55 34 56/aA 78 ... 99 88 77"); - exit(0); -} -/*- End of function --------------------------------------------------------*/ -#endif - -static void corrupt_image(faxtester_state_t *s, uint8_t image[], int len, const char *bad_rows) -{ - int i; - int j; - int k; - uint32_t bits; - uint32_t bitsx; - int list[1000]; - int x; - int row; - const char *t; - - /* Form the list of rows to be hit */ - x = 0; - t = bad_rows; - while (*t) - { - while (isspace((int) *t)) - t++; - if (sscanf(t, "%d", &list[x]) < 1) - break; - x++; - while (isdigit((int) *t)) - t++; - if (*t == ',') - t++; - } - - /* Go through the image, and corrupt the first bit of every listed row */ - bits = 0x7FF; - bitsx = 0x7FF; - row = 0; - for (i = 0; i < len; i++) - { - bits ^= (image[i] << 11); - bitsx ^= (image[i] << 11); - for (j = 0; j < 8; j++) - { - if ((bits & 0xFFF) == 0x800) - { - /* We are at an EOL. Is this row in the list of rows to be corrupted? */ - row++; - for (k = 0; k < x; k++) - { - if (list[k] == row) - { - /* Corrupt this row. TSB85 says to hit the first bit after the EOL */ - bitsx ^= 0x1000; - } - } - } - bits >>= 1; - bitsx >>= 1; - } - image[i] = (bitsx >> 3) & 0xFF; - } - span_log(&s->logging, SPAN_LOG_FLOW, "%d rows found. %d corrupted\n", row, x); -} -/*- End of function --------------------------------------------------------*/ - -static int next_step(faxtester_state_t *s) -{ - int delay; - int flags; - xmlChar *dir; - xmlChar *type; - xmlChar *modem; - xmlChar *value; - xmlChar *tag; - xmlChar *bad_rows; - xmlChar *crc_error; - xmlChar *pattern; - xmlChar *timein; - xmlChar *timeout; - xmlChar *min_bits; - xmlChar *frame_size; - xmlChar *block; - xmlChar *compression; - uint8_t buf[1000]; - uint8_t mask[1000]; - char path[1024]; - int i; - int j; - int hdlc; - int short_train; - int min_row_bits; - int ecm_frame_size; - int ecm_block; - int compression_type; - int len; - t4_tx_state_t t4_tx_state; - t30_state_t *t30; - t30_stats_t t30_stats; - - test_for_call_clear = false; - if (s->cur == NULL) - { - if (!s->final_delayed) - { - /* Add a bit of waiting at the end, to ensure everything gets flushed through, - any timers can expire, etc. */ - faxtester_set_timeout(s, -1); - faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); - faxtester_set_tx_type(s, T30_MODEM_PAUSE, 0, 120000, false); - s->final_delayed = true; - return 1; - } - /* Finished */ - printf("Test passed\n"); - exit(0); - } - while (s->cur && xmlStrcmp(s->cur->name, (const xmlChar *) "step") != 0) - s->cur = s->cur->next; - if (s->cur == NULL) - { - /* Finished */ - printf("Test passed\n"); - exit(0); - } - - dir = xmlGetProp(s->cur, (const xmlChar *) "dir"); - type = xmlGetProp(s->cur, (const xmlChar *) "type"); - modem = xmlGetProp(s->cur, (const xmlChar *) "modem"); - value = xmlGetProp(s->cur, (const xmlChar *) "value"); - tag = xmlGetProp(s->cur, (const xmlChar *) "tag"); - bad_rows = xmlGetProp(s->cur, (const xmlChar *) "bad_rows"); - crc_error = xmlGetProp(s->cur, (const xmlChar *) "crc_error"); - pattern = xmlGetProp(s->cur, (const xmlChar *) "pattern"); - timein = xmlGetProp(s->cur, (const xmlChar *) "timein"); - timeout = xmlGetProp(s->cur, (const xmlChar *) "timeout"); - min_bits = xmlGetProp(s->cur, (const xmlChar *) "min_bits"); - frame_size = xmlGetProp(s->cur, (const xmlChar *) "frame_size"); - block = xmlGetProp(s->cur, (const xmlChar *) "block"); - compression = xmlGetProp(s->cur, (const xmlChar *) "compression"); - - s->cur = s->cur->next; - - span_log(&s->logging, - SPAN_LOG_FLOW, - "Dir - %s, type - %s, modem - %s, value - %s, timein - %s, timeout - %s, tag - %s\n", - (dir) ? (const char *) dir : "", - (type) ? (const char *) type : "", - (modem) ? (const char *) modem : "", - (value) ? (const char *) value : "", - (timein) ? (const char *) timein : "", - (timeout) ? (const char *) timeout : "", - (tag) ? (const char *) tag : ""); - if (type == NULL) - return 1; - if (timein) - timein_x = atoi((const char *) timein); - else - timein_x = -1; - if (timeout) - timeout_x = atoi((const char *) timeout); - else - timeout_x = -1; - - if (dir && strcasecmp((const char *) dir, "R") == 0) - { - /* Receive always has a timeout applied. */ - if (timeout_x < 0) - timeout_x = 7000; - faxtester_set_timeout(s, timeout_x); - if (modem) - { - hdlc = (strcasecmp((const char *) type, "PREAMBLE") == 0); - short_train = (strcasecmp((const char *) type, "TCF") != 0); - faxtester_set_tx_type(s, T30_MODEM_NONE, 0, false, false); - if (strcasecmp((const char *) modem, "V.21") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V21, 300, false, true); - } - else if (strcasecmp((const char *) modem, "V.17/14400") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V17, 14400, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.17/12000") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V17, 12000, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.17/9600") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V17, 9600, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.17/7200") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V17, 7200, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.29/9600") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V29, 9600, false, hdlc); - } - else if (strcasecmp((const char *) modem, "V.29/7200") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V29, 7200, false, hdlc); - } - else if (strcasecmp((const char *) modem, "V.27ter/4800") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V27TER, 4800, false, hdlc); - } - else if (strcasecmp((const char *) modem, "V.27ter/2400") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_V27TER, 2400, false, hdlc); - } - else - { - span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised modem\n"); - } - } - - if (strcasecmp((const char *) type, "SET") == 0) - { - if (strcasecmp((const char *) tag, "IDENT") == 0) - strcpy(expected_rx_info.ident, (const char *) value); - else if (strcasecmp((const char *) tag, "SUB") == 0) - strcpy(expected_rx_info.sub_address, (const char *) value); - else if (strcasecmp((const char *) tag, "SEP") == 0) - strcpy(expected_rx_info.selective_polling_address, (const char *) value); - else if (strcasecmp((const char *) tag, "PSA") == 0) - strcpy(expected_rx_info.polled_sub_address, (const char *) value); - else if (strcasecmp((const char *) tag, "SID") == 0) - strcpy(expected_rx_info.sender_ident, (const char *) value); - else if (strcasecmp((const char *) tag, "PWD") == 0) - strcpy(expected_rx_info.password, (const char *) value); - return 0; - } - else if (strcasecmp((const char *) type, "CNG") == 0) - { - /* Look for CNG */ - faxtester_set_rx_type(s, T30_MODEM_CNG, 0, false, false); - faxtester_set_tx_type(s, T30_MODEM_NONE, 0, false, false); - } - else if (strcasecmp((const char *) type, "CED") == 0) - { - /* Look for CED */ - faxtester_set_rx_type(s, T30_MODEM_CED, 0, false, false); - faxtester_set_tx_type(s, T30_MODEM_NONE, 0, false, false); - } - else if (strcasecmp((const char *) type, "HDLC") == 0) - { - i = string_to_msg(buf, mask, (const char *) value); - bit_reverse(awaited, buf, abs(i)); - awaited_len = i; - } - else if (strcasecmp((const char *) type, "TCF") == 0) - { - } - else if (strcasecmp((const char *) type, "MSG") == 0) - { - } - else if (strcasecmp((const char *) type, "PP") == 0) - { - } - else if (strcasecmp((const char *) type, "SILENCE") == 0) - { - faxtest_set_rx_silence(s); - } - else if (strcasecmp((const char *) type, "CLEAR") == 0) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Far end should drop the call\n"); - test_for_call_clear = true; - call_clear_timer = 0; - } - else - { - span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised type '%s'\n", (const char *) type); - return 0; - } - } - else - { - faxtester_set_timeout(s, timeout_x); - if (modem) - { - hdlc = (strcasecmp((const char *) type, "PREAMBLE") == 0); - short_train = (strcasecmp((const char *) type, "TCF") != 0); - faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); - if (strcasecmp((const char *) modem, "V.21") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V21, 300, false, true); - } - else if (strcasecmp((const char *) modem, "V.17/14400") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V17, 14400, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.17/12000") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V17, 12000, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.17/9600") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V17, 9600, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.17/7200") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V17, 7200, short_train, hdlc); - } - else if (strcasecmp((const char *) modem, "V.29/9600") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V29, 9600, false, hdlc); - } - else if (strcasecmp((const char *) modem, "V.29/7200") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V29, 7200, false, hdlc); - } - else if (strcasecmp((const char *) modem, "V.27ter/4800") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V27TER, 4800, false, hdlc); - } - else if (strcasecmp((const char *) modem, "V.27ter/2400") == 0) - { - faxtester_set_tx_type(s, T30_MODEM_V27TER, 2400, false, hdlc); - } - else - { - span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised modem\n"); - } - } - - if (strcasecmp((const char *) type, "SET") == 0) - { - t30 = fax_get_t30_state(fax); - if (strcasecmp((const char *) tag, "IDENT") == 0) - t30_set_tx_ident(t30, (const char *) value); - else if (strcasecmp((const char *) tag, "SUB") == 0) - t30_set_tx_sub_address(t30, (const char *) value); - else if (strcasecmp((const char *) tag, "SEP") == 0) - t30_set_tx_selective_polling_address(t30, (const char *) value); - else if (strcasecmp((const char *) tag, "PSA") == 0) - t30_set_tx_polled_sub_address(t30, (const char *) value); - else if (strcasecmp((const char *) tag, "SID") == 0) - t30_set_tx_sender_ident(t30, (const char *) value); - else if (strcasecmp((const char *) tag, "PWD") == 0) - t30_set_tx_password(t30, (const char *) value); - else if (strcasecmp((const char *) tag, "RXFILE") == 0) - { - if (value) - t30_set_rx_file(t30, (const char *) value, -1); - else - t30_set_rx_file(t30, output_tiff_file_name, -1); - } - else if (strcasecmp((const char *) tag, "TXFILE") == 0) - { - sprintf(next_tx_file, "%s/%s", image_path, (const char *) value); - printf("Push '%s'\n", next_tx_file); - } - return 0; - } - else if (strcasecmp((const char *) type, "CALL") == 0) - { - fax = fax_init(NULL, false); - fax_prepare(); - next_tx_file[0] = '\0'; - t30 = fax_get_t30_state(fax); - t30_set_rx_file(t30, output_tiff_file_name, -1); - /* Avoid libtiff 3.8.2 and earlier bug on complex 2D lines. */ - t30_set_supported_output_compressions(t30, T4_COMPRESSION_T4_1D); - if (value) - { - sprintf(path, "%s/%s", image_path, (const char *) value); - t30_set_tx_file(t30, path, -1, -1); - } - return 0; - } - else if (strcasecmp((const char *) type, "ANSWER") == 0) - { - fax = fax_init(NULL, true); - fax_prepare(); - next_tx_file[0] = '\0'; - t30 = fax_get_t30_state(fax); - /* Avoid libtiff 3.8.2 and earlier bug on complex 2D lines. */ - t30_set_supported_output_compressions(t30, T4_COMPRESSION_T4_1D); - if (value) - { - sprintf(path, "%s/%s", image_path, (const char *) value); - t30_set_tx_file(t30, path, -1, -1); - } - return 0; - } - else if (strcasecmp((const char *) type, "CNG") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); - faxtester_set_tx_type(s, T30_MODEM_CNG, 0, false, false); - } - else if (strcasecmp((const char *) type, "CED") == 0) - { - faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); - faxtester_set_tx_type(s, T30_MODEM_CED, 0, false, false); - } - else if (strcasecmp((const char *) type, "WAIT") == 0) - { - delay = (value) ? atoi((const char *) value) : 1; - faxtester_set_rx_type(s, T30_MODEM_NONE, 0, false, false); - faxtester_set_tx_type(s, T30_MODEM_PAUSE, 0, delay, false); - } - else if (strcasecmp((const char *) type, "PREAMBLE") == 0) - { - flags = (value) ? atoi((const char *) value) : 37; - faxtester_send_hdlc_flags(s, flags); - } - else if (strcasecmp((const char *) type, "POSTAMBLE") == 0) - { - flags = (value) ? atoi((const char *) value) : 5; - faxtester_send_hdlc_flags(s, flags); - } - else if (strcasecmp((const char *) type, "HDLC") == 0) - { - i = string_to_msg(buf, mask, (const char *) value); - bit_reverse(buf, buf, abs(i)); - if (crc_error && strcasecmp((const char *) crc_error, "0") == 0) - faxtester_send_hdlc_msg(s, buf, abs(i), false); - else - faxtester_send_hdlc_msg(s, buf, abs(i), true); - } - else if (strcasecmp((const char *) type, "TCF") == 0) - { - if (value) - i = atoi((const char *) value); - else - i = 450; - if (pattern) - { - /* TODO: implement proper patterns */ - j = atoi((const char *) pattern); - memset(image, 0x55, j); - if (i > j) - memset(image + j, 0, i - j); - } - else - { - memset(image, 0, i); - } - faxtester_set_non_ecm_image_buffer(s, image, i); - } - else if (strcasecmp((const char *) type, "MSG") == 0) - { - /* A non-ECM page */ - min_row_bits = (min_bits) ? atoi((const char *) min_bits) : 0; - sprintf(path, "%s/%s", image_path, (const char *) value); - if (t4_tx_init(&t4_tx_state, path, -1, -1) == NULL) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Failed to init T.4 send\n"); - printf("Test failed\n"); - exit(2); - } - t4_tx_set_header_info(&t4_tx_state, NULL); - compression_type = T4_COMPRESSION_T4_1D; - if (compression) - { - if (strcasecmp((const char *) compression, "T.4 2D") == 0) - compression_type = T4_COMPRESSION_T4_2D; - else if (strcasecmp((const char *) compression, "T.6") == 0) - compression_type = T4_COMPRESSION_T6; - } - if (t4_tx_set_tx_image_format(&t4_tx_state, - compression_type, - T4_SUPPORT_WIDTH_215MM - | T4_SUPPORT_LENGTH_US_LETTER - | T4_SUPPORT_LENGTH_US_LEGAL - | T4_SUPPORT_LENGTH_UNLIMITED, - T4_RESOLUTION_R8_STANDARD - | T4_RESOLUTION_R8_FINE - | T4_RESOLUTION_R8_SUPERFINE - | T4_RESOLUTION_R16_SUPERFINE - | T4_RESOLUTION_200_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_200_400 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_300_600 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_400_800 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_600_1200 - | T4_RESOLUTION_1200_1200, - T4_RESOLUTION_100_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_1200_1200) < 0) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); - printf("Test failed\n"); - exit(2); - } - t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); - if (t4_tx_start_page(&t4_tx_state)) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Failed to start T.4 send\n"); - printf("Test failed\n"); - exit(2); - } - len = t4_tx_get(&t4_tx_state, image, sizeof(image)); - if (bad_rows) - { - span_log(&s->logging, SPAN_LOG_FLOW, "We need to corrupt the image\n"); - corrupt_image(s, image, len, (const char *) bad_rows); - } - t4_tx_release(&t4_tx_state); - span_log(&s->logging, SPAN_LOG_FLOW, "Non-ECM image is %d bytes (min row bits %d)\n", len, min_row_bits); - faxtester_set_non_ecm_image_buffer(s, image, len); - } - else if (strcasecmp((const char *) type, "PP") == 0) - { - min_row_bits = (min_bits) ? atoi((const char *) min_bits) : 0; - ecm_block = (block) ? atoi((const char *) block) : 0; - ecm_frame_size = (frame_size) ? atoi((const char *) frame_size) : 64; - i = (crc_error) ? atoi((const char *) crc_error) : -1; - sprintf(path, "%s/%s", image_path, (const char *) value); - if (t4_tx_init(&t4_tx_state, path, -1, -1) == NULL) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Failed to init T.4 send\n"); - printf("Test failed\n"); - exit(2); - } - t4_tx_set_header_info(&t4_tx_state, NULL); - compression_type = T4_COMPRESSION_T4_1D; - if (compression) - { - if (strcasecmp((const char *) compression, "T.4 2D") == 0) - compression_type = T4_COMPRESSION_T4_2D; - else if (strcasecmp((const char *) compression, "T.6") == 0) - compression_type = T4_COMPRESSION_T6; - } - if (t4_tx_set_tx_image_format(&t4_tx_state, - compression_type, - T4_SUPPORT_WIDTH_215MM - | T4_SUPPORT_LENGTH_US_LETTER - | T4_SUPPORT_LENGTH_US_LEGAL - | T4_SUPPORT_LENGTH_UNLIMITED, - T4_RESOLUTION_R8_STANDARD - | T4_RESOLUTION_R8_FINE - | T4_RESOLUTION_R8_SUPERFINE - | T4_RESOLUTION_R16_SUPERFINE - | T4_RESOLUTION_200_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_200_400 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_300_600 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_400_800 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_600_1200 - | T4_RESOLUTION_1200_1200, - T4_RESOLUTION_100_100 - | T4_RESOLUTION_200_200 - | T4_RESOLUTION_300_300 - | T4_RESOLUTION_400_400 - | T4_RESOLUTION_600_600 - | T4_RESOLUTION_1200_1200) < 0) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); - printf("Test failed\n"); - exit(2); - } - t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); - if (t4_tx_start_page(&t4_tx_state)) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Failed to start T.4 send\n"); - printf("Test failed\n"); - exit(2); - } - /*endif*/ - len = t4_tx_get(&t4_tx_state, image, sizeof(image)); - if (bad_rows) - { - span_log(&s->logging, SPAN_LOG_FLOW, "We need to corrupt the image\n"); - corrupt_image(s, image, len, (const char *) bad_rows); - } - /*endif*/ - t4_tx_release(&t4_tx_state); - span_log(&s->logging, SPAN_LOG_FLOW, "ECM image is %d bytes (min row bits %d)\n", len, min_row_bits); - faxtester_set_ecm_image_buffer(s, image, len, ecm_block, ecm_frame_size, i); - } - else if (strcasecmp((const char *) type, "CLEAR") == 0) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Time to drop the call\n"); - t30 = fax_get_t30_state(fax); - t30_terminate(t30); - return 0; - } - else if (strcasecmp((const char *) type, "STATUS") == 0) - { - if (value) - { - for (i = 0; t30_status[i].code >= 0; i++) - { - if (strcmp(t30_status[i].tag, (const char *) value) == 0) - break; - } - if (t30_status[i].code >= 0) - delay = t30_status[i].code; - else - delay = atoi((const char *) value); - t30 = fax_get_t30_state(fax); - t30_get_transfer_statistics(t30, &t30_stats); - span_log(&s->logging, SPAN_LOG_FLOW, "Expect status %d. Got %d\n", delay, t30_stats.current_status); - if (delay != t30_stats.current_status) - { - printf("Test failed\n"); - exit(2); - } - } - return 0; - } - else - { - span_log(&s->logging, SPAN_LOG_FLOW, "Unrecognised type '%s'\n", (const char *) type); - return 0; - } - /*endif*/ - } - /*endif*/ - return 1; -} -/*- End of function --------------------------------------------------------*/ +faxtester_state_t *state; static void exchange(faxtester_state_t *s) { @@ -1270,21 +102,37 @@ static void exchange(faxtester_state_t *s) total_audio_time = 0; - faxtester_set_transmit_on_idle(&state, true); - faxtester_set_real_time_frame_handler(&state, faxtester_real_time_frame_handler, NULL); - faxtester_set_front_end_step_complete_handler(&state, faxtester_front_end_step_complete_handler, NULL); - faxtester_set_front_end_step_timeout_handler(&state, faxtester_front_end_step_timeout_handler, NULL); + faxtester_set_transmit_on_idle(s, true); - fax = fax_init(NULL, false); - fax_prepare(); - next_tx_file[0] = '\0'; + s->far_fax = fax_init(NULL, false); + s->far_t30 = fax_get_t30_state(s->far_fax); + s->far_tag = 'A'; - while (next_step(s) == 0) + if (s->far_fax) + logging = fax_get_logging_state(s->far_fax); + else + logging = t38_terminal_get_logging_state(s->far_t38); + span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); + span_log_set_tag(logging, "A"); + + logging = t30_get_logging_state(s->far_t30); + span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); + span_log_set_tag(logging, "A"); + +#if 0 + span_log_set_level(&fax.modems.v27ter_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); + span_log_set_tag(&fax.modems.v27ter_rx.logging, "A"); + span_log_set_level(&fax.modems.v29_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); + span_log_set_tag(&fax.modems.v29_rx.logging, "A"); + span_log_set_level(&fax.modems.v17_rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); + span_log_set_tag(&fax.modems.v17_rx.logging, "A"); +#endif + while (faxtester_next_step(s) == 0) ; /*endwhile*/ for (;;) { - len = fax_tx(fax, amp, SAMPLES_PER_CHUNK); + len = fax_tx(s->far_fax, amp, SAMPLES_PER_CHUNK); faxtester_rx(s, amp, len); if (log_audio) { @@ -1296,20 +144,21 @@ static void exchange(faxtester_state_t *s) total_audio_time += SAMPLES_PER_CHUNK; - logging = t30_get_logging_state(fax_get_t30_state(fax)); + logging = t30_get_logging_state(s->far_t30); span_log_bump_samples(logging, len); #if 0 span_log_bump_samples(&fax.modems.v27ter_rx.logging, len); span_log_bump_samples(&fax.modems.v29_rx.logging, len); span_log_bump_samples(&fax.modems.v17_rx.logging, len); #endif - logging = fax_get_logging_state(fax); + logging = fax_get_logging_state(s->far_fax); span_log_bump_samples(logging, len); - span_log_bump_samples(&s->logging, len); + logging = faxtester_get_logging_state(s); + span_log_bump_samples(logging, len); len = faxtester_tx(s, amp, SAMPLES_PER_CHUNK); - if (fax_rx(fax, amp, len)) + if (fax_rx(s->far_fax, amp, len)) break; /*endif*/ if (log_audio) @@ -1322,21 +171,21 @@ static void exchange(faxtester_state_t *s) /*endif*/ } /*endif*/ - if (test_for_call_clear && !far_end_cleared_call) + if (s->test_for_call_clear && !s->far_end_cleared_call) { - call_clear_timer += len; - if (!t30_call_active(fax_get_t30_state(fax))) + s->call_clear_timer += len; + if (!t30_call_active(s->far_t30)) { - span_log(&s->logging, SPAN_LOG_FLOW, "Far end cleared after %dms (limits %dms to %dms)\n", call_clear_timer/8, timein_x, timeout_x); - if (call_clear_timer/8 < timein_x || call_clear_timer/8 > timeout_x) + span_log(faxtester_get_logging_state(s), SPAN_LOG_FLOW, "Far end cleared after %dms (limits %dms to %dms)\n", s->call_clear_timer/8, s->timein_x, s->timeout); + if (s->call_clear_timer/8 < s->timein_x || s->call_clear_timer/8 > s->timeout_x) { printf("Test failed\n"); exit(2); } - span_log(&s->logging, SPAN_LOG_FLOW, "Clear time OK\n"); - far_end_cleared_call = true; - test_for_call_clear = false; - while (next_step(s) == 0) + span_log(faxtester_get_logging_state(s), SPAN_LOG_FLOW, "Clear time OK\n"); + s->far_end_cleared_call = true; + s->test_for_call_clear = false; + while (faxtester_next_step(s) == 0) ; /*endwhile*/ } @@ -1359,154 +208,11 @@ static void exchange(faxtester_state_t *s) } /*- End of function --------------------------------------------------------*/ -static int parse_config(faxtester_state_t *s, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur) -{ - xmlChar *x; - xmlChar *y; - - while (cur) - { - if (xmlStrcmp(cur->name, (const xmlChar *) "path") == 0) - { - if ((x = xmlGetProp(cur, (const xmlChar *) "type")) - && - (y = xmlGetProp(cur, (const xmlChar *) "value"))) - { - if (strcasecmp((const char *) x, "IMAGE") == 0) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Found '%s' '%s'\n", (char *) x, (char *) y); - strcpy(image_path, (const char *) y); - } - /*endif*/ - } - /*endif*/ - } - /*endif*/ - cur = cur->next; - } - /*endwhile*/ - return -1; -} -/*- End of function --------------------------------------------------------*/ - -static int parse_test_group(faxtester_state_t *s, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, const char *test) -{ - xmlChar *x; - - while (cur) - { - if (xmlStrcmp(cur->name, (const xmlChar *) "test") == 0) - { - if ((x = xmlGetProp(cur, (const xmlChar *) "name"))) - { - if (xmlStrcmp(x, (const xmlChar *) test) == 0) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Found '%s'\n", (char *) x); - s->cur = cur->xmlChildrenNode; - return 0; - } - /*endif*/ - } - /*endif*/ - } - /*endif*/ - cur = cur->next; - } - /*endwhile*/ - return -1; -} -/*- End of function --------------------------------------------------------*/ - -static int get_test_set(faxtester_state_t *s, const char *test_file, const char *test) -{ - xmlParserCtxtPtr ctxt; - xmlNsPtr ns; - xmlNodePtr cur; - - ns = NULL; - xmlKeepBlanksDefault(0); - xmlCleanupParser(); - - if ((ctxt = xmlNewParserCtxt()) == NULL) - { - fprintf(stderr, "Failed to allocate parser context\n"); - printf("Test failed\n"); - exit(2); - } - /* parse the file, activating the DTD validation option */ - if ((s->doc = xmlCtxtReadFile(ctxt, test_file, NULL, XML_PARSE_XINCLUDE | XML_PARSE_DTDVALID)) == NULL) - { - fprintf(stderr, "Failed to read the XML document\n"); - printf("Test failed\n"); - exit(2); - } - if (ctxt->valid == 0) - { - fprintf(stderr, "Failed to validate the XML document\n"); - xmlFreeDoc(s->doc); - xmlFreeParserCtxt(ctxt); - printf("Test failed\n"); - exit(2); - } - xmlFreeParserCtxt(ctxt); - - /* Check the document is of the right kind */ - if ((cur = xmlDocGetRootElement(s->doc)) == NULL) - { - xmlFreeDoc(s->doc); - fprintf(stderr, "Empty document\n"); - printf("Test failed\n"); - exit(2); - } - /*endif*/ - if (xmlStrcmp(cur->name, (const xmlChar *) "fax-tests")) - { - xmlFreeDoc(s->doc); - fprintf(stderr, "Document of the wrong type, root node != fax-tests"); - printf("Test failed\n"); - exit(2); - } - /*endif*/ - cur = cur->xmlChildrenNode; - while (cur && xmlIsBlankNode(cur)) - cur = cur->next; - /*endwhile*/ - if (cur == NULL) - { - printf("Test failed\n"); - exit(2); - } - /*endif*/ - while (cur) - { - if (xmlStrcmp(cur->name, (const xmlChar *) "config") == 0) - { - parse_config(s, s->doc, ns, cur->xmlChildrenNode); - } - /*endif*/ - if (xmlStrcmp(cur->name, (const xmlChar *) "test-group") == 0) - { - if (parse_test_group(s, s->doc, ns, cur->xmlChildrenNode, test) == 0) - { - /* We found the test we want, so run it. */ - exchange(s); - break; - } - /*endif*/ - } - /*endif*/ - cur = cur->next; - } - /*endwhile*/ - xmlFreeDoc(s->doc); - return 0; -} -/*- End of function --------------------------------------------------------*/ - int main(int argc, char *argv[]) { const char *xml_file_name; const char *test_name; + logging_state_t *logging; int opt; #if 0 @@ -1537,13 +243,18 @@ int main(int argc, char *argv[]) if (argc > 0) test_name = argv[0]; - strcpy(image_path, "."); - faxtester_init(&state, true); - memset(&expected_rx_info, 0, sizeof(expected_rx_info)); - span_log_set_level(&state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); - span_log_set_tag(&state.logging, "B"); - get_test_set(&state, xml_file_name, test_name); - faxtester_release(&state); + if ((state = faxtester_init(NULL, xml_file_name, test_name)) == NULL) + { + fprintf(stderr, "Cannot start FAX tester instance\n"); + printf("Test failed\n"); + exit(2); + } + logging = faxtester_get_logging_state(state); + span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW); + span_log_set_tag(logging, "B"); + /* We found the test we want, so run it. */ + exchange(state); + faxtester_free(state); printf("Done\n"); return 0; } diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 75172cff6e..f6472be2f3 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -2243,6 +2243,12 @@ SWITCH_DECLARE(switch_core_flag_t) switch_core_flags(void); */ SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid, switch_management_action_t action, char *data, switch_size_t datalen); +/*! + \brief Switch on the privilege awareness for the process and request required privileges + \return 0 on success +*/ + +SWITCH_DECLARE(int32_t) switch_core_set_process_privileges(void); /*! \brief Set the maximum priority the process can obtain diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 412cb63757..ca0c710273 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -122,6 +122,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_events(switch_core_session_ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_next_event(switch_core_session_t *session); SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_messages(switch_core_session_t *session); SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_signal_data(switch_core_session_t *session); +SWITCH_DECLARE(switch_status_t) switch_ivr_parse_signal_data(switch_core_session_t *session, switch_bool_t all, switch_bool_t only_session_thread); SWITCH_DECLARE(switch_status_t) switch_ivr_parse_next_signal_data(switch_core_session_t *session); SWITCH_DECLARE(switch_status_t) switch_ivr_process_indications(switch_core_session_t *session, switch_core_session_message_t *message); diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index feb9337d54..1d31bc8b88 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -305,6 +305,12 @@ typedef enum { SWITCH_VIDEO_ENCODE_SPEED_FAST } switch_video_encode_speed_t; +typedef enum { + SWITCH_VIDEO_PROFILE_BASELINE, + SWITCH_VIDEO_PROFILE_MAIN, + SWITCH_VIDEO_PROFILE_HIGH +} switch_video_profile_t; + typedef struct switch_mm_s { int samplerate; int channels; @@ -314,7 +320,9 @@ typedef struct switch_mm_s { int vw; int vh; float fps; + float source_fps; int vbuf; + switch_video_profile_t vprofile; switch_video_encode_speed_t vencspd; } switch_mm_t; diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 126f722ce2..ef4b9eb077 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1170,6 +1170,7 @@ typedef enum { SWITCH_STATUS_CONTINUE, SWITCH_STATUS_TERM, SWITCH_STATUS_NOT_INITALIZED, + SWITCH_STATUS_TOO_LATE, SWITCH_STATUS_XBREAK = 35, SWITCH_STATUS_WINBREAK = 730035 } switch_status_t; diff --git a/src/mod/applications/mod_av/avcodec.c b/src/mod/applications/mod_av/avcodec.c index 5d4a0e1a16..3589eb3389 100644 --- a/src/mod/applications/mod_av/avcodec.c +++ b/src/mod/applications/mod_av/avcodec.c @@ -866,7 +866,7 @@ static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t widt av_opt_set_int(context->encoder_ctx->priv_data, "mb_info", SLICE_SIZE - 8, 0); } else if (context->av_codec_id == AV_CODEC_ID_H264) { context->encoder_ctx->profile = FF_PROFILE_H264_BASELINE; - context->encoder_ctx->level = 30; + context->encoder_ctx->level = 41; av_opt_set(context->encoder_ctx->priv_data, "preset", "veryfast", 0); av_opt_set(context->encoder_ctx->priv_data, "tune", "zerolatency", 0); av_opt_set(context->encoder_ctx->priv_data, "profile", "baseline", 0); diff --git a/src/mod/applications/mod_av/avformat.c b/src/mod/applications/mod_av/avformat.c index e3f4ed6cd2..3e60049915 100644 --- a/src/mod/applications/mod_av/avformat.c +++ b/src/mod/applications/mod_av/avformat.c @@ -41,7 +41,7 @@ #include #define SCALE_FLAGS SWS_BICUBIC -#define DFT_RECORD_OFFSET 350 +#define DFT_RECORD_OFFSET 0 static switch_status_t av_file_close(switch_file_handle_t *handle); SWITCH_MODULE_LOAD_FUNCTION(mod_avformat_load); @@ -139,7 +139,6 @@ typedef struct record_helper_s { AVFormatContext *fc; MediaStream *video_st; switch_timer_t *timer; - switch_timer_t *other_timer; int in_callback; switch_queue_t *video_queue; switch_thread_t *video_thread; @@ -259,11 +258,6 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec c->channels = mst->channels; c->channel_layout = av_get_default_channel_layout(c->channels); - mst->st->time_base.den = 1000; - mst->st->time_base.num = 1; - c->time_base.den = 1000; - c->time_base.num = 1; - if (mm) { if (mm->ab) { c->bit_rate = mm->ab * 1024; @@ -312,18 +306,34 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec if (codec_id == AV_CODEC_ID_H264) { c->ticks_per_frame = 2; + switch (mm->vprofile) { + case SWITCH_VIDEO_PROFILE_BASELINE: + av_opt_set(c->priv_data, "profile", "baseline", 0); + c->level = 41; + break; + case SWITCH_VIDEO_PROFILE_MAIN: + av_opt_set(c->priv_data, "profile", "main", 0); + av_opt_set(c->priv_data, "level", "5", 0); + break; + case SWITCH_VIDEO_PROFILE_HIGH: + av_opt_set(c->priv_data, "profile", "high", 0); + av_opt_set(c->priv_data, "level", "52", 0); + break; + } + switch (mm->vencspd) { - case SWITCH_VIDEO_ENCODE_SPEED_SLOW: - av_opt_set(c->priv_data, "preset", "veryslow", 0); - break; - case SWITCH_VIDEO_ENCODE_SPEED_MEDIUM: - av_opt_set(c->priv_data, "preset", "medium", 0); - break; - case SWITCH_VIDEO_ENCODE_SPEED_FAST: - av_opt_set(c->priv_data, "preset", "ultrafast", 0); - break; - default: - break; + case SWITCH_VIDEO_ENCODE_SPEED_SLOW: + av_opt_set(c->priv_data, "preset", "veryslow", 0); + break; + case SWITCH_VIDEO_ENCODE_SPEED_MEDIUM: + av_opt_set(c->priv_data, "preset", "medium", 0); + break; + case SWITCH_VIDEO_ENCODE_SPEED_FAST: + av_opt_set(c->priv_data, "preset", "veryfast", 0); + av_opt_set(c->priv_data, "tune", "zerolatency", 0); + break; + default: + break; } } @@ -481,62 +491,86 @@ static switch_status_t open_audio(AVFormatContext *fc, AVCodec *codec, MediaStre return SWITCH_STATUS_SUCCESS; } +static int flush_video_queue(switch_queue_t *q, int min) +{ + void *pop; + + if (switch_queue_size(q) > min) { + while (switch_queue_trypop(q, &pop) == SWITCH_STATUS_SUCCESS) { + switch_image_t *img = (switch_image_t *) pop; + switch_img_free(&img); + if (min && switch_queue_size(q) <= min) { + break; + } + } + } + + return switch_queue_size(q); +} + static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *obj) { record_helper_t *eh = (record_helper_t *) obj; - void *pop; - switch_image_t *img, *last_img = NULL, *tmp_img = NULL; - switch_size_t size; + void *pop = NULL; + switch_image_t *img = NULL, *tmp_img = NULL; + int d_w = 0, d_h = 0; + int size = 0, skip = 0, skip_freq = 0, skip_count = 0, skip_total = 0, skip_total_count = 0; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "video thread start\n"); for(;;) { AVPacket pkt = { 0 }; int got_packet; - int ret = -1, popped = 0; + int ret = -1; - do { - switch_status_t status; - img = NULL; - - if (!popped) { - status = switch_queue_pop(eh->video_queue, &pop); - popped++; - } else { - status = switch_queue_trypop(eh->video_queue, &pop); + top: + + if (switch_queue_pop(eh->video_queue, &pop) == SWITCH_STATUS_SUCCESS) { + switch_img_free(&img); + + if (!pop) { + goto endfor; } - - if (status == SWITCH_STATUS_SUCCESS) { + img = (switch_image_t *) pop; + + if (!d_w) d_w = img->d_w; + if (!d_h) d_h = img->d_h; + + if (d_w && d_h && (d_w != img->d_w || d_h != img->d_h)) { + /* scale to match established stream */ + switch_img_scale(img, &tmp_img, d_w, d_h); switch_img_free(&img); - - if (!pop) { - goto endfor; - } - img = (switch_image_t *)pop; - } else { - if (img) { - break; + img = tmp_img; + tmp_img = NULL; + } + } else { + continue; + } + + if (skip) { + if ((skip_total_count > 0 && !--skip_total_count) || ++skip_count >= skip_freq) { + skip_total_count = skip_total; + skip_count = 0; + skip--; + goto top; + } + } else { + + size = switch_queue_size(eh->video_queue); + + if (size > 5) { + skip = size; + + if (size > 10) { + skip_freq = 3; + skip_total = 1; } else { - popped = 0; - continue; + skip_freq = 2; + skip_total = 1; } } - - size = switch_queue_size(eh->video_queue); - } while(img && size > 1); - - - if (last_img && (last_img->d_w != img->d_w || last_img->d_h != img->d_h)) { - /* scale to match established stream */ - switch_img_scale(img, &tmp_img, last_img->d_w, last_img->d_h); - switch_img_free(&img); - img = tmp_img; - tmp_img = NULL; } - switch_img_free(&last_img); - last_img = img; - //switch_mutex_lock(eh->mutex); eh->in_callback = 1; @@ -552,14 +586,6 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void * fill_avframe(eh->video_st->frame, img); switch_core_timer_sync(eh->timer); - if (eh->other_timer) { - if (eh->timer->samplecount > eh->other_timer->samplecount) { - int sleepfor = (eh->timer->samplecount - eh->other_timer->samplecount) * 1000; - switch_yield(sleepfor); - switch_core_timer_sync(eh->timer); - } - } - if (eh->video_st->frame->pts == eh->timer->samplecount) { // never use the same pts, or the encoder coughs eh->video_st->frame->pts++; @@ -590,8 +616,6 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void * endfor: - switch_img_free(&last_img); - while(switch_queue_trypop(eh->video_queue, &pop) == SWITCH_STATUS_SUCCESS) { if (!pop) break; img = (switch_image_t *) pop; @@ -614,7 +638,7 @@ static switch_status_t video_read_callback(switch_core_session_t *session, switc switch_queue_push(eh->video_queue, img); } - return SWITCH_STATUS_SUCCESS;; + return SWITCH_STATUS_SUCCESS; } static void close_stream(AVFormatContext *fc, MediaStream *mst) @@ -1077,7 +1101,6 @@ struct av_file_context { switch_buffer_t *buf; switch_buffer_t *audio_buffer; switch_timer_t video_timer; - switch_timer_t audio_timer; int offset; int audio_start; int vid_ready; @@ -1097,6 +1120,7 @@ struct av_file_context { int file_read_thread_running; switch_time_t video_start_time; switch_image_t *last_img; + int read_fps; }; typedef struct av_file_context av_file_context_t; @@ -1117,7 +1141,7 @@ static void mod_avformat_destroy_output_context(av_file_context_t *context) static switch_status_t open_input_file(av_file_context_t *context, switch_file_handle_t *handle, const char *filename) { AVCodec *audio_codec = NULL; - AVCodec *video_codec; + AVCodec *video_codec = NULL; int error; int i; switch_status_t status = SWITCH_STATUS_SUCCESS; @@ -1146,6 +1170,8 @@ static switch_status_t open_input_file(av_file_context_t *context, switch_file_h if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { context->has_video = 1; } + handle->mm.source_fps = ceil(av_q2d(context->video_st.st->avg_frame_rate)); + context->read_fps = (int)handle->mm.source_fps; } } @@ -1231,6 +1257,7 @@ static void *SWITCH_THREAD_FUNC file_read_thread_run(switch_thread_t *thread, vo AVPacket pkt = { 0 }; int got_data = 0; int error; + int sync = 0; context->file_read_thread_running = 1; @@ -1238,10 +1265,10 @@ static void *SWITCH_THREAD_FUNC file_read_thread_run(switch_thread_t *thread, vo while (context->file_read_thread_running) { if (switch_buffer_inuse(context->audio_buffer) > AUDIO_BUF_SEC * context->audio_st.sample_rate * context->audio_st.channels * 2) { - switch_yield(100000); + switch_yield(10000); continue; } - + av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; @@ -1257,7 +1284,10 @@ static void *SWITCH_THREAD_FUNC file_read_thread_run(switch_thread_t *thread, vo if (context->has_video && pkt.stream_index == context->video_st.st->index) { AVFrame *vframe = av_frame_alloc(); switch_image_t *img; - + if (!sync) { + switch_buffer_zero(context->audio_buffer); + sync = 1; + } switch_assert(vframe); if ((error = avcodec_decode_video2(context->video_st.st->codec, vframe, &got_data, &pkt)) < 0) { @@ -1270,11 +1300,11 @@ static void *SWITCH_THREAD_FUNC file_read_thread_run(switch_thread_t *thread, vo // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pkt: %d, pts: %lld dts: %lld\n", pkt.size, pkt.pts, pkt.dts); av_free_packet(&pkt); - if (switch_queue_size(context->eh.video_queue) > 300) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Dropping frames\n"); - av_frame_free(&vframe); - continue; - } + //if (switch_queue_size(context->eh.video_queue) > 300) { + // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Dropping frames\n"); + // av_frame_free(&vframe); + // continue; + //} if (got_data && error > 0) { // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got picture %dx%d fmt: %d pktpts:%lld pktdts:%lld\n", vframe->width, vframe->height, vframe->format, vframe->pkt_pts, vframe->pkt_dts); @@ -1508,6 +1538,8 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa handle->samplerate = 44100; handle->mm.samplerate = 44100; handle->mm.ab = 128; + //handle->mm.vencspd = SWITCH_VIDEO_ENCODE_SPEED_FAST; + handle->mm.vprofile = SWITCH_VIDEO_PROFILE_BASELINE; if (!handle->mm.vb && handle->mm.vw && handle->mm.vh) { switch(handle->mm.vh) { @@ -1563,9 +1595,6 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa handle->pos = 0; - switch_core_timer_init(&context->audio_timer, "soft", 1, 1, /*handle->samplerate / 1000,*/ context->pool); - switch_core_timer_init(&context->video_timer, "soft", 1, 1, context->pool); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening File [%s] %dhz %s\n", file, handle->samplerate, switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO) ? " with VIDEO" : ""); @@ -1581,10 +1610,6 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa switch_core_timer_destroy(&context->video_timer); } - if (context->audio_timer.interval) { - switch_core_timer_destroy(&context->audio_timer); - } - if (context->audio_buffer) { switch_buffer_destroy(&context->audio_buffer); } @@ -1597,19 +1622,104 @@ static switch_status_t av_file_truncate(switch_file_handle_t *handle, int64_t of return SWITCH_STATUS_FALSE; } -static void flush_video_queue(switch_queue_t *q) + +static switch_status_t av_file_write(switch_file_handle_t *handle, void *data, size_t *len) { - void *pop; - if (switch_queue_size(q) == 0) { - return; + uint32_t datalen = 0; + switch_status_t status = SWITCH_STATUS_SUCCESS; + // uint8_t buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }, *bp = buf; + // uint32_t encoded_rate; + av_file_context_t *context = (av_file_context_t *)handle->private_info; + // uint32_t size = 0; + uint32_t bytes; + int inuse; + + if (!context->vid_ready) { + return status; } - while (switch_queue_trypop(q, &pop) == SWITCH_STATUS_SUCCESS) { - switch_image_t *img = (switch_image_t *) pop; - switch_img_free(&img); + if (data && len) { + datalen = *len * 2 * handle->channels; + + if (context->offset) { + char buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; + switch_size_t samples = *len; + int fps = handle->samplerate / samples; + int lead_frames = (context->offset * fps) / 1000; + + for (int x = 0; x < lead_frames; x++) { + switch_buffer_write(context->audio_buffer, buf, datalen); + } + context->offset = 0; + } + + switch_buffer_write(context->audio_buffer, data, datalen); + } + + bytes = context->audio_st.frame->nb_samples * 2 * context->audio_st.st->codec->channels; + + + //inuse = switch_buffer_inuse(context->audio_buffer); + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "inuse: %d samples: %d bytes: %d\n", inuse, context->audio_st.frame->nb_samples, bytes); + + + while ((inuse = switch_buffer_inuse(context->audio_buffer)) >= bytes) { + AVPacket pkt = { 0 }; + int got_packet = 0; + int ret; + + av_init_packet(&pkt); + + if (context->audio_st.resample_ctx) { // need resample + int out_samples = avresample_get_out_samples(context->audio_st.resample_ctx, context->audio_st.frame->nb_samples); + + av_frame_make_writable(context->audio_st.frame); + av_frame_make_writable(context->audio_st.tmp_frame); + switch_buffer_read(context->audio_buffer, context->audio_st.frame->data[0], bytes); + /* convert to destination format */ + ret = avresample_convert(context->audio_st.resample_ctx, + (uint8_t **)context->audio_st.frame->data, 0, out_samples, + context->audio_st.tmp_frame->data, 0, context->audio_st.frame->nb_samples); + + if (ret < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while converting %d samples, error text: %s\n", + context->audio_st.frame->nb_samples, get_error_text(ret)); + continue; + } + + context->audio_st.tmp_frame->pts = context->audio_st.next_pts; + context->audio_st.next_pts += context->audio_st.frame->nb_samples; + ret = avcodec_encode_audio2(context->audio_st.st->codec, &pkt, context->audio_st.tmp_frame, &got_packet); + } else { + av_frame_make_writable(context->audio_st.frame); + switch_buffer_read(context->audio_buffer, context->audio_st.frame->data[0], bytes); + context->audio_st.frame->pts = context->audio_st.next_pts; + context->audio_st.next_pts += context->audio_st.frame->nb_samples; + + ret = avcodec_encode_audio2(context->audio_st.st->codec, &pkt, context->audio_st.frame, &got_packet); + } + + if (ret < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Error encoding audio frame: %d\n", ret); + continue; + } + + if (got_packet) { + if (context->mutex) switch_mutex_lock(context->mutex); + ret = write_frame(context->fc, &context->audio_st.st->codec->time_base, context->audio_st.st, &pkt); + if (context->mutex) switch_mutex_unlock(context->mutex); + if (ret < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while writing audio frame: %s\n", get_error_text(ret)); + //switch_goto_status(SWITCH_STATUS_FALSE, end); + } + } + if (data) { + break; + } } + return status; } static switch_status_t av_file_close(switch_file_handle_t *handle) @@ -1624,6 +1734,8 @@ static switch_status_t av_file_close(switch_file_handle_t *handle) if (context->eh.video_thread) { switch_thread_join(&status, context->eh.video_thread); } + + av_file_write(handle, NULL, NULL); if (context->file_read_thread_running && context->file_read_thread) { context->file_read_thread_running = 0; @@ -1631,7 +1743,7 @@ static switch_status_t av_file_close(switch_file_handle_t *handle) } if (context->eh.video_queue) { - flush_video_queue(context->eh.video_queue); + flush_video_queue(context->eh.video_queue, 0); } if (context->fc) { @@ -1644,10 +1756,6 @@ static switch_status_t av_file_close(switch_file_handle_t *handle) switch_core_timer_destroy(&context->video_timer); } - if (context->audio_timer.interval) { - switch_core_timer_destroy(&context->audio_timer); - } - switch_img_free(&context->last_img); switch_buffer_destroy(&context->audio_buffer); @@ -1701,103 +1809,6 @@ static switch_status_t av_file_read(switch_file_handle_t *handle, void *data, si return *len == 0 ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS; } -static switch_status_t av_file_write(switch_file_handle_t *handle, void *data, size_t *len) -{ - - uint32_t datalen = *len * 2 * handle->channels; - switch_status_t status = SWITCH_STATUS_SUCCESS; - // uint8_t buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }, *bp = buf; - // uint32_t encoded_rate; - av_file_context_t *context = (av_file_context_t *)handle->private_info; - // uint32_t size = 0; - uint32_t bytes; - int inuse; - - if (!context->vid_ready) { - return status; - } - - if (context->offset) { - char buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; - switch_size_t samples = *len; - int fps = handle->samplerate / samples; - int lead_frames = (context->offset * fps) / 1000; - - for (int x = 0; x < lead_frames; x++) { - switch_buffer_write(context->audio_buffer, buf, datalen); - } - context->offset = 0; - } - - switch_buffer_write(context->audio_buffer, data, datalen); - bytes = context->audio_st.frame->nb_samples * 2 * context->audio_st.st->codec->channels; - - //inuse = switch_buffer_inuse(context->audio_buffer); - //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "inuse: %d samples: %d bytes: %d\n", inuse, context->audio_st.frame->nb_samples, bytes); - - while ((inuse = switch_buffer_inuse(context->audio_buffer)) >= bytes) { - AVPacket pkt = { 0 }; - int got_packet = 0; - int ret; - - av_init_packet(&pkt); - - if (context->audio_st.resample_ctx) { // need resample - int out_samples = avresample_get_out_samples(context->audio_st.resample_ctx, context->audio_st.frame->nb_samples); - - av_frame_make_writable(context->audio_st.frame); - av_frame_make_writable(context->audio_st.tmp_frame); - switch_buffer_read(context->audio_buffer, context->audio_st.frame->data[0], bytes); - /* convert to destination format */ - ret = avresample_convert(context->audio_st.resample_ctx, - (uint8_t **)context->audio_st.frame->data, 0, out_samples, - context->audio_st.tmp_frame->data, 0, context->audio_st.frame->nb_samples); - - if (ret < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while converting %d samples, error text: %s\n", - context->audio_st.frame->nb_samples, get_error_text(ret)); - continue; - } - - context->audio_st.tmp_frame->pts = context->audio_st.next_pts; - context->audio_st.next_pts += context->audio_st.frame->nb_samples; - ret = avcodec_encode_audio2(context->audio_st.st->codec, &pkt, context->audio_st.tmp_frame, &got_packet); - } else { - av_frame_make_writable(context->audio_st.frame); - switch_buffer_read(context->audio_buffer, context->audio_st.frame->data[0], bytes); - - switch_core_timer_sync(&context->audio_timer); - context->audio_st.frame->pts = context->audio_timer.samplecount; - //context->audio_st.frame->pts = context->audio_st.next_pts; - //context->audio_st.next_pts += context->audio_st.frame->nb_samples; - - ret = avcodec_encode_audio2(context->audio_st.st->codec, &pkt, context->audio_st.frame, &got_packet); - } - - if (ret < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Error encoding audio frame: %d\n", ret); - continue; - } - - if (got_packet) { - if (context->mutex) switch_mutex_lock(context->mutex); - ret = write_frame(context->fc, &context->audio_st.st->codec->time_base, context->audio_st.st, &pkt); - if (context->mutex) switch_mutex_unlock(context->mutex); - if (ret < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while writing audio frame: %s\n", get_error_text(ret)); - switch_goto_status(SWITCH_STATUS_FALSE, end); - } - } - - break; - } - - - -end: - return status; -} - static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_frame_t *frame, switch_video_read_flag_t flags) { av_file_context_t *context = (av_file_context_t *)handle->private_info; @@ -1807,14 +1818,21 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f int ticks = 0; int max_delta = 1 * AV_TIME_BASE; // 1 second switch_status_t status = SWITCH_STATUS_SUCCESS; - + double fl_to = 0.02; + int do_fl = 0; + if (!context->has_video) return SWITCH_STATUS_FALSE; if ((flags & SVR_CHECK)) { return SWITCH_STATUS_BREAK; } - if (flags & SVR_FLUSH) max_delta = 0.02 * AV_TIME_BASE; + fl_to = (1000 / context->read_fps) * 1000; + //printf("WTF %d (%f)\n",switch_queue_size(context->eh.video_queue), fl_to); + if (flags & SVR_FLUSH) { + max_delta = fl_to * AV_TIME_BASE; + do_fl = 1; + } if (context->last_img) { if (mst->next_pts && (switch_time_now() - mst->next_pts > max_delta)) { @@ -1825,7 +1843,7 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f return SWITCH_STATUS_SUCCESS; } - if (!(flags & SVR_BLOCK)) return SWITCH_STATUS_BREAK; + if (!(flags & SVR_BLOCK) && !do_fl) return SWITCH_STATUS_BREAK; } if (!context->file_read_thread_running && switch_queue_size(context->eh.video_queue) == 0) { @@ -1845,7 +1863,7 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f again: - if (flags & SVR_BLOCK) { + if ((flags & SVR_BLOCK)) { status = switch_queue_pop(context->eh.video_queue, &pop); } else { status = switch_queue_trypop(context->eh.video_queue, &pop); @@ -1853,14 +1871,6 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f if (pop && status == SWITCH_STATUS_SUCCESS) { switch_image_t *img = (switch_image_t *)pop; - - // #define YIELD 40000 // use a constant FPS -#ifdef YIELD - switch_yield(YIELD); - frame->img = img; - if (0) goto again; -#else - uint64_t pts; uint64_t now = switch_time_now(); @@ -1884,35 +1894,33 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f if ((mst->next_pts && switch_time_now() - mst->next_pts > max_delta)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG3, "picture is too late, off: %" SWITCH_INT64_T_FMT " queue size:%u\n", (int64_t)(switch_time_now() - mst->next_pts), switch_queue_size(context->eh.video_queue)); switch_img_free(&img); - + max_delta = AV_TIME_BASE; if (switch_queue_size(context->eh.video_queue) > 0) { goto again; - } else { + } else if (!(flags & SVR_BLOCK) && !do_fl) { mst->next_pts = 0; return SWITCH_STATUS_BREAK; } } - if (flags & SVR_BLOCK) { - while (switch_time_now() - mst->next_pts < -10000 / 2) { - switch_cond_next(); + if ((flags & SVR_BLOCK) || do_fl) { + while (switch_micro_time_now() - mst->next_pts < -10000 / 2) { + // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "yield\n"); + switch_yield(10000); } frame->img = img; + do_fl = 0; } else { - if (switch_time_now() - mst->next_pts > -10000 / 2) { + if (switch_micro_time_now() - mst->next_pts > -10000 / 2) { frame->img = img; } else { context->last_img = img; return SWITCH_STATUS_BREAK; } } -#endif - + } else { - if ((flags & SVR_BLOCK)) { - switch_yield(10000); - } return SWITCH_STATUS_BREAK; } @@ -1965,13 +1973,16 @@ static switch_status_t av_file_write_video(switch_file_handle_t *handle, switch_ context->eh.fc = context->fc; context->eh.mm = &handle->mm; context->eh.timer = &context->video_timer; - context->eh.other_timer = &context->audio_timer; switch_queue_create(&context->eh.video_queue, SWITCH_CORE_QUEUE_LEN, handle->memory_pool); switch_threadattr_create(&thd_attr, handle->memory_pool); //switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_thread_create(&context->eh.video_thread, thd_attr, video_thread_run, &context->eh, handle->memory_pool); + switch_core_timer_init(&context->video_timer, "soft", 1, 1, context->pool); + switch_buffer_zero(context->audio_buffer); + context->audio_st.frame->pts = 0; + context->audio_st.next_pts = 0; } if (context->has_video) { @@ -1987,6 +1998,7 @@ end: return status; } + static switch_status_t av_file_set_string(switch_file_handle_t *handle, switch_audio_col_t col, const char *string) { av_file_context_t *context = (av_file_context_t *)handle->private_info; diff --git a/src/mod/applications/mod_conference/conference_file.c b/src/mod/applications/mod_conference/conference_file.c index e720d46d8e..45a7913d3f 100644 --- a/src/mod/applications/mod_conference/conference_file.c +++ b/src/mod/applications/mod_conference/conference_file.c @@ -56,6 +56,11 @@ switch_status_t conference_file_close(conference_obj_t *conference, conference_f switch_event_add_header(event, SWITCH_STACK_BOTTOM, "milliseconds", "%ld", (long) node->fh.samples_in / (node->fh.native_rate / 1000)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "samples", "%ld", (long) node->fh.samples_in); + if (node->layer_id && node->layer_id > -1) { + if (node->canvas_id < 0) node->canvas_id = 0; + conference_video_canvas_del_fnode_layer(conference, node); + } + if (node->fh.params) { switch_event_merge(event, node->fh.params); } @@ -86,11 +91,13 @@ switch_status_t conference_file_close(conference_obj_t *conference, conference_f conference_al_close(node->al); } #endif - if (switch_core_file_has_video(&node->fh) && conference->canvases[0] && node->canvas_id > -1) { - conference->canvases[node->canvas_id]->timer.interval = conference->video_fps.ms; - conference->canvases[node->canvas_id]->timer.samples = conference->video_fps.samples; - switch_core_timer_sync(&conference->canvases[node->canvas_id]->timer); - conference->canvases[node->canvas_id]->send_keyframe = 1; + if (conference->playing_video_file && switch_core_file_has_video(&node->fh) && conference->canvases[0] && node->canvas_id > -1) { + if (conference->canvases[node->canvas_id]->timer.timer_interface) { + conference->canvases[node->canvas_id]->timer.interval = conference->video_fps.ms; + conference->canvases[node->canvas_id]->timer.samples = conference->video_fps.samples; + switch_core_timer_sync(&conference->canvases[node->canvas_id]->timer); + conference->canvases[node->canvas_id]->send_keyframe = 1; + } conference->playing_video_file = 0; } return switch_core_file_close(&node->fh); @@ -239,6 +246,7 @@ switch_status_t conference_file_play(conference_obj_t *conference, char *file, u /* Open the file */ fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN; + fnode->fh.mm.fps = conference->video_fps.fps; if (switch_core_file_open(&fnode->fh, file, channels, conference->rate, flags, pool) != SWITCH_STATUS_SUCCESS) { switch_event_t *event; @@ -299,7 +307,7 @@ switch_status_t conference_file_play(conference_obj_t *conference, char *file, u fnode->file = switch_core_strdup(fnode->pool, file); if (!conference->fnode || (async && !conference->async_fnode)) { - conference_video_fnode_check(fnode); + conference_video_fnode_check(fnode, -1); } /* Queue the node */ diff --git a/src/mod/applications/mod_conference/conference_member.c b/src/mod/applications/mod_conference/conference_member.c index 8d5af7fa62..5d60dcd16c 100644 --- a/src/mod/applications/mod_conference/conference_member.c +++ b/src/mod/applications/mod_conference/conference_member.c @@ -1422,7 +1422,7 @@ switch_status_t conference_member_say(conference_member_t *member, char *text, u fp = switch_core_strdup(pool, text); switch_assert(fp); - if (!switch_event_create_brackets(fp, '{', '}', ',', ¶ms, &new_fp, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { + if (switch_event_create_brackets(fp, '{', '}', ',', ¶ms, &new_fp, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { new_fp = fp; } diff --git a/src/mod/applications/mod_conference/conference_video.c b/src/mod/applications/mod_conference/conference_video.c index e78aa25700..bf469577ca 100644 --- a/src/mod/applications/mod_conference/conference_video.c +++ b/src/mod/applications/mod_conference/conference_video.c @@ -789,7 +789,7 @@ void conference_video_layer_set_banner(conference_member_t *member, mcu_layer_t switch_img_fill(layer->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, &layer->canvas->letterbox_bgcolor); - if (!strcasecmp(text, "allclear")) { + if (zstr(text) || !strcasecmp(text, "allclear")) { switch_channel_set_variable(member->channel, "video_banner_text", NULL); } @@ -1380,8 +1380,8 @@ void conference_video_canvas_set_fnode_layer(mcu_canvas_t *canvas, conference_fi if (canvas->layout_floor_id > -1) { idx = canvas->layout_floor_id; xlayer = &canvas->layers[idx]; - - if (xlayer->fnode) { + + if (xlayer->fnode && xlayer->fnode != fnode) { idx = -1; } } @@ -1492,8 +1492,8 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_write_thread_run(switch_thread_ if (last) { int delta = now - last; - if (delta > member->conference->video_fps.ms * 2) { - switch_core_session_request_video_refresh(member->session); + if (delta > member->conference->video_fps.ms * 5000) { + switch_core_session_request_video_refresh(member->session); } } @@ -1659,18 +1659,27 @@ void conference_video_patch_fnode(mcu_canvas_t *canvas, conference_file_node_t * } else if (status == SWITCH_STATUS_IGNORE) { if (canvas && fnode->layer_id > -1 ) { conference_video_canvas_del_fnode_layer(canvas->conference, fnode); + fnode->canvas_id = canvas->canvas_id; } } } } -void conference_video_fnode_check(conference_file_node_t *fnode) { - mcu_canvas_t *canvas = fnode->conference->canvases[fnode->canvas_id]; - +void conference_video_fnode_check(conference_file_node_t *fnode, int canvas_id) { + mcu_canvas_t *canvas = NULL; + if (switch_core_file_has_video(&fnode->fh) && switch_core_file_read_video(&fnode->fh, NULL, SVR_CHECK) == SWITCH_STATUS_BREAK) { int full_screen = 0; char *res_id = NULL; + if (fnode->canvas_id == -1) { + if (canvas_id == -1) { + return; + } + fnode->canvas_id = canvas_id; + } + + canvas = fnode->conference->canvases[fnode->canvas_id]; if (fnode->fh.params && fnode->conference->canvas_count == 1) { full_screen = switch_true(switch_event_get_header(fnode->fh.params, "full-screen")); } @@ -2296,7 +2305,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr } if (check_async_file) { - if (switch_core_file_read_video(&conference->async_fnode->fh, &file_frame, SVR_BLOCK | SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { + if (switch_core_file_read_video(&conference->async_fnode->fh, &file_frame, SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { if ((async_file_img = file_frame.img)) { file_imgs[j++] = async_file_img; } @@ -2304,7 +2313,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr } if (check_file) { - if (switch_core_file_read_video(&conference->fnode->fh, &file_frame, SVR_BLOCK | SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { + if (switch_core_file_read_video(&conference->fnode->fh, &file_frame, SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { if ((normal_file_img = file_frame.img)) { file_imgs[j++] = normal_file_img; } @@ -2456,19 +2465,19 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr switch_mutex_unlock(conference->member_mutex); } else { - if (conference->async_fnode && conference->async_fnode->canvas_id == canvas->canvas_id) { + if (conference->async_fnode && (conference->async_fnode->canvas_id == canvas->canvas_id || conference->async_fnode->canvas_id == -1)) { if (conference->async_fnode->layer_id > -1) { conference_video_patch_fnode(canvas, conference->async_fnode); } else { - conference_video_fnode_check(conference->async_fnode); + conference_video_fnode_check(conference->async_fnode, canvas->canvas_id); } } - if (conference->fnode && conference->fnode->canvas_id == canvas->canvas_id) { + if (conference->fnode && (conference->fnode->canvas_id == canvas->canvas_id || conference->fnode->canvas_id == -1)) { if (conference->fnode->layer_id > -1) { conference_video_patch_fnode(canvas, conference->fnode); } else { - conference_video_fnode_check(conference->fnode); + conference_video_fnode_check(conference->fnode, canvas->canvas_id); } } @@ -2540,7 +2549,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr timestamp = canvas->timer.samplecount; if (conference->playing_video_file) { - if (switch_core_file_read_video(&conference->fnode->fh, &write_frame, SVR_BLOCK | SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { + if (switch_core_file_read_video(&conference->fnode->fh, &write_frame, SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { switch_img_free(&file_img); if (canvas->play_file) { diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 0802b95987..963ae9aa15 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -667,7 +667,7 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob conference->fnode = conference->fnode->next; if (conference->fnode) { - conference_video_fnode_check(conference->fnode); + conference_video_fnode_check(conference->fnode, -1); } @@ -899,7 +899,7 @@ switch_status_t conference_say(conference_obj_t *conference, const char *text, u fp = switch_core_strdup(pool, text); switch_assert(fp); - if (!switch_event_create_brackets(fp, '{', '}', ',', ¶ms, &new_fp, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { + if (switch_event_create_brackets(fp, '{', '}', ',', ¶ms, &new_fp, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { new_fp = fp; } diff --git a/src/mod/applications/mod_conference/mod_conference.h b/src/mod/applications/mod_conference/mod_conference.h index ff19ff44f5..3adb7f49a2 100644 --- a/src/mod/applications/mod_conference/mod_conference.h +++ b/src/mod/applications/mod_conference/mod_conference.h @@ -930,7 +930,7 @@ video_layout_t *conference_video_get_layout(conference_obj_t *conference, const void conference_video_check_avatar(conference_member_t *member, switch_bool_t force); void conference_video_find_floor(conference_member_t *member, switch_bool_t entering); void conference_video_destroy_canvas(mcu_canvas_t **canvasP); -void conference_video_fnode_check(conference_file_node_t *fnode); +void conference_video_fnode_check(conference_file_node_t *fnode, int canvas_id); switch_status_t conference_video_set_canvas_bgimg(mcu_canvas_t *canvas, const char *img_path); switch_status_t conference_al_parse_position(al_handle_t *al, const char *data); switch_status_t conference_video_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data); diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 310aa302e4..cf31deea75 100644 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -2424,15 +2424,30 @@ static void att_xfer_set_result(switch_channel_t *channel, switch_status_t statu switch_channel_set_variable(channel, SWITCH_ATT_XFER_RESULT_VARIABLE, status == SWITCH_STATUS_SUCCESS ? "success" : "failure"); } -SWITCH_STANDARD_APP(att_xfer_function) +struct att_obj { + switch_core_session_t *session; + const char *data; + int running; +}; + +void *SWITCH_THREAD_FUNC att_thread_run(switch_thread_t *thread, void *obj) { + struct att_obj *att = (struct att_obj *) obj; + switch_core_session_t *session = att->session; switch_core_session_t *peer_session = NULL; + const char *data = att->data; switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; switch_channel_t *channel = switch_core_session_get_channel(session), *peer_channel = NULL; const char *bond = NULL; switch_core_session_t *b_session = NULL; switch_bool_t follow_recording = switch_true(switch_channel_get_variable(channel, "recording_follow_attxfer")); - + + att->running = 1; + + if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) { + return NULL; + } + bond = switch_channel_get_partner_uuid(channel); switch_channel_set_variable(channel, SWITCH_SOFT_HOLDING_UUID_VARIABLE, bond); switch_core_event_hook_add_state_change(session, tmp_hanguphook); @@ -2505,6 +2520,35 @@ SWITCH_STANDARD_APP(att_xfer_function) switch_channel_set_variable(channel, SWITCH_SOFT_HOLDING_UUID_VARIABLE, NULL); switch_channel_clear_flag(channel, CF_XFER_ZOMBIE); + + switch_core_session_rwunlock(session); + att->running = 0; + + return NULL; +} + +SWITCH_STANDARD_APP(att_xfer_function) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + switch_memory_pool_t *pool = switch_core_session_get_pool(session); + struct att_obj *att; + switch_channel_t *channel = switch_core_session_get_channel(session); + + switch_threadattr_create(&thd_attr, pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_threadattr_detach_set(thd_attr, 1); + + att = switch_core_session_alloc(session, sizeof(*att)); + att->running = -1; + att->session = session; + att->data = switch_core_session_strdup(session, data); + switch_thread_create(&thread, thd_attr, att_thread_run, att, pool); + + while(att->running && switch_channel_up(channel)) { + switch_yield(100000); + } } SWITCH_STANDARD_APP(read_function) @@ -3146,6 +3190,8 @@ static void *SWITCH_THREAD_FUNC camp_music_thread(switch_thread_t *thread, void } while (stake->running && switch_channel_ready(channel)) { + switch_ivr_parse_signal_data(session, SWITCH_TRUE, SWITCH_FALSE); + if (status != SWITCH_STATUS_BREAK) { if (!strcasecmp(moh, "silence")) { status = switch_ivr_collect_digits_callback(session, &args, 0, 0); diff --git a/src/mod/applications/mod_easyroute/mod_easyroute.c b/src/mod/applications/mod_easyroute/mod_easyroute.c index 5fdbf3abb2..e039c86e38 100644 --- a/src/mod/applications/mod_easyroute/mod_easyroute.c +++ b/src/mod/applications/mod_easyroute/mod_easyroute.c @@ -357,7 +357,7 @@ SWITCH_STANDARD_API(easyroute_function) } } - if (!route_lookup(destnum, &results, noat, separator) == SWITCH_STATUS_SUCCESS) { + if (route_lookup(destnum, &results, noat, separator) != SWITCH_STATUS_SUCCESS) { stream->write_function(stream, "No Match!\n"); status = SWITCH_STATUS_SUCCESS; goto done; diff --git a/src/mod/applications/mod_enum/mod_enum.c b/src/mod/applications/mod_enum/mod_enum.c index 42126f3009..efa3fe3125 100644 --- a/src/mod/applications/mod_enum/mod_enum.c +++ b/src/mod/applications/mod_enum/mod_enum.c @@ -838,7 +838,7 @@ SWITCH_STANDARD_API(enum_function) } - if (!enum_lookup(root, dest, &results, NULL, session) == SWITCH_STATUS_SUCCESS) { + if (enum_lookup(root, dest, &results, NULL, session) != SWITCH_STATUS_SUCCESS) { stream->write_function(stream, "No Match!\n"); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index 994ec33cce..18583bf181 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -3290,7 +3290,7 @@ static switch_status_t voicemail_inject(const char *data, switch_core_session_t switch_event_create(&my_params, SWITCH_EVENT_REQUEST_PARAMS); status = deliver_vm(profile, ux, domain, path, 0, read_flags, my_params, pool, cid_name, cid_num, forwarded_by, - SWITCH_TRUE, session ? switch_core_session_get_uuid(session) : NULL, NULL); + SWITCH_TRUE, session ? switch_core_session_get_uuid(session) : NULL, session); switch_event_destroy(&my_params); } continue; @@ -3300,7 +3300,7 @@ static switch_status_t voicemail_inject(const char *data, switch_core_session_t switch_event_create(&my_params, SWITCH_EVENT_REQUEST_PARAMS); status = deliver_vm(profile, ut, domain, path, 0, read_flags, my_params, pool, cid_name, cid_num, forwarded_by, SWITCH_TRUE, - session ? switch_core_session_get_uuid(session) : NULL, NULL); + session ? switch_core_session_get_uuid(session) : NULL, session); switch_event_destroy(&my_params); } } @@ -3324,7 +3324,7 @@ static switch_status_t voicemail_inject(const char *data, switch_core_session_t switch_event_create(&my_params, SWITCH_EVENT_REQUEST_PARAMS); status = deliver_vm(profile, ut, domain, path, 0, read_flags, my_params, pool, cid_name, cid_num, forwarded_by, SWITCH_TRUE, - session ? switch_core_session_get_uuid(session) : NULL, NULL); + session ? switch_core_session_get_uuid(session) : NULL, session); switch_event_destroy(&my_params); } } @@ -3339,7 +3339,7 @@ static switch_status_t voicemail_inject(const char *data, switch_core_session_t switch_event_create(&my_params, SWITCH_EVENT_REQUEST_PARAMS); status = deliver_vm(profile, ut, domain, path, 0, read_flags, my_params, pool, cid_name, cid_num, forwarded_by, SWITCH_TRUE, - session ? switch_core_session_get_uuid(session) : NULL, NULL); + session ? switch_core_session_get_uuid(session) : NULL, session); switch_event_destroy(&my_params); } else { status = SWITCH_STATUS_FALSE; diff --git a/src/mod/codecs/mod_opus/mod_opus.c b/src/mod/codecs/mod_opus/mod_opus.c index 159a50c22b..9b9ec670e0 100644 --- a/src/mod/codecs/mod_opus/mod_opus.c +++ b/src/mod/codecs/mod_opus/mod_opus.c @@ -63,8 +63,23 @@ static opus_codec_settings_t default_codec_settings = { /*.cbr*/ 0, /*.sprop_maxcapturerate*/ 0, /*.sprop_stereo*/ 0, - /*.maxptime*/ 0, - /*.minptime*/ 0, + /*.maxptime*/ 40, + /*.minptime*/ 10, + /*.ptime*/ 0, + /*.samplerate*/ 0 +}; + +static opus_codec_settings_t default_codec_settings_8k = { + /*.useinbandfec */ 1, + /*.usedtx */ 1, + /*.maxaveragebitrate */ 14000, + /*.maxplaybackrate */ 8000, + /*.stereo*/ 0, + /*.cbr*/ 0, + /*.sprop_maxcapturerate*/ 8000, + /*.sprop_stereo*/ 0, + /*.maxptime*/ 120, + /*.minptime*/ 10, /*.ptime*/ 0, /*.samplerate*/ 0 }; @@ -78,6 +93,8 @@ struct opus_context { uint32_t debug; uint32_t use_jb_lookahead; opus_codec_settings_t codec_settings; + int look_check; + int look_ts; }; struct { @@ -90,6 +107,7 @@ struct { int plpct; int asymmetric_samplerates; int keep_fec; + int fec_decode; int debuginfo; uint32_t use_jb_lookahead; switch_mutex_t *mutex; @@ -107,6 +125,57 @@ static switch_bool_t switch_opus_acceptable_rate(int rate) return SWITCH_TRUE; } +static uint32_t switch_opus_encoder_set_audio_bandwidth(OpusEncoder *encoder_object,int enc_samplerate) +{ + if (enc_samplerate == 8000) { /* Audio Bandwidth: 0-4000Hz Sampling Rate: 8000Hz */ + opus_encoder_ctl(encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + return OPUS_BANDWIDTH_NARROWBAND; + } else if (enc_samplerate == 12000) { /* Audio Bandwidth: 0-6000Hz Sampling Rate: 12000Hz */ + opus_encoder_ctl(encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + return OPUS_BANDWIDTH_MEDIUMBAND; + } else if (enc_samplerate == 16000) { /* Audio Bandwidth: 0-8000Hz Sampling Rate: 16000Hz */ + opus_encoder_ctl(encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + return OPUS_BANDWIDTH_WIDEBAND; + } else if (enc_samplerate == 24000) { /* Audio Bandwidth: 0-12000Hz Sampling Rate: 24000Hz */ + opus_encoder_ctl(encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_SIGNAL(OPUS_AUTO)); + return OPUS_BANDWIDTH_SUPERWIDEBAND; + } + /* Audio Bandwidth: 0-20000Hz Sampling Rate: 48000Hz */ + opus_encoder_ctl(encoder_object, OPUS_SET_BANDWIDTH(OPUS_AUTO)); + opus_encoder_ctl(encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); + opus_encoder_ctl(encoder_object, OPUS_SET_SIGNAL(OPUS_AUTO)); + return OPUS_BANDWIDTH_FULLBAND; +} + +static switch_bool_t switch_opus_show_audio_bandwidth(int audiobandwidth,char *audiobandwidth_str) +{ + if (audiobandwidth == OPUS_BANDWIDTH_NARROWBAND) { + strncpy(audiobandwidth_str, "NARROWBAND",10); + return SWITCH_TRUE; + } else if (audiobandwidth == OPUS_BANDWIDTH_MEDIUMBAND) { + strncpy(audiobandwidth_str, "MEDIUMBAND",10); + return SWITCH_TRUE; + } else if (audiobandwidth == OPUS_BANDWIDTH_WIDEBAND) { + strncpy(audiobandwidth_str,"WIDEBAND",8); + return SWITCH_TRUE; + } else if (audiobandwidth == OPUS_BANDWIDTH_SUPERWIDEBAND) { + strncpy(audiobandwidth_str, "SUPERWIDEBAND",13); + return SWITCH_TRUE; + } else if (audiobandwidth == OPUS_BANDWIDTH_FULLBAND) { + strncpy(audiobandwidth_str, "FULLBAND",8); + return SWITCH_TRUE; + } + return SWITCH_FALSE; +} + static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmtp_t *codec_fmtp) { if (codec_fmtp) { @@ -165,11 +234,6 @@ static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmt codec_fmtp->microseconds_per_packet = codec_settings->ptime * 1000; } - if (!strcasecmp(data, "samplerate")) { - codec_settings->samplerate = atoi(arg); - codec_fmtp->actual_samples_per_second = codec_settings->samplerate; - } - if (!strcasecmp(data, "stereo")) { codec_settings->stereo = atoi(arg); codec_fmtp->stereo = codec_settings->stereo; @@ -212,9 +276,7 @@ static char *gen_fmtp(opus_codec_settings_t *settings, switch_memory_pool_t *poo { char buf[256] = { 0 }; - if (settings->useinbandfec) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "useinbandfec=1; "); - } + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "useinbandfec=%d; ", settings->useinbandfec); if (settings->usedtx) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "usedtx=1; "); @@ -248,12 +310,7 @@ static char *gen_fmtp(opus_codec_settings_t *settings, switch_memory_pool_t *poo snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "maxptime=%d; ", settings->maxptime); } - if (settings->samplerate) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "samplerate=%d; ", settings->samplerate); - } - if (settings->stereo) { - settings->sprop_stereo = settings->stereo; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "stereo=%d; ", settings->stereo); } @@ -271,26 +328,50 @@ static char *gen_fmtp(opus_codec_settings_t *settings, switch_memory_pool_t *poo static switch_bool_t switch_opus_has_fec(const uint8_t* payload,int payload_length_bytes) { - int frames, payload_length_ms; - int n; + int nb_silk_frames, nb_opus_frames, n, i; opus_int16 frame_sizes[48]; const unsigned char *frame_data[48]; + int frames; - if (payload == NULL || payload_length_bytes <= 0){ - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted packet\n"); + if (payload == NULL || payload_length_bytes <= 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted packet (invalid size)\n"); + return SWITCH_FALSE; + } + if (payload[0] & 0x80) { + /* this scares users and its harmless so commenting it */ + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "FEC in CELT_ONLY mode ?!\n"); + return SWITCH_FALSE; + } + + if ((nb_opus_frames = opus_packet_parse(payload, payload_length_bytes, NULL, frame_data, frame_sizes, NULL)) < 0 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OPUS_INVALID_PACKET ! nb_opus_frames: %d\n", nb_opus_frames); return SWITCH_FALSE; } - if (payload[0] & 0x80){ - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "FEC in CELT_ONLY mode ?!\n"); - return SWITCH_FALSE; + nb_silk_frames = 0; + + if ((payload[0] >> 3 ) < 12) { /* config in silk-only : NB,MB,WB */ + nb_silk_frames = (payload[0] >> 3) & 0x3; + if(nb_silk_frames == 0) { + nb_silk_frames = 1; + } + if (nb_silk_frames == 1) { + for (n = 0; n <= (payload[0]&0x4) ; n++) { /* mono or stereo */ + if (frame_data[0][0] & (0x80 >> ((n + 1) * (nb_silk_frames + 1) - 1))) { + return SWITCH_TRUE; /* 1st 20ms (or 10 ms) frame has FEC */ + } + } + } else { + opus_int16 LBRR_flag = 0 ; + for (i=0 ; i < nb_opus_frames; i++ ) { /* only mono */ + LBRR_flag = (frame_data[i][0] >> (7 - nb_silk_frames)) & 0x1; + if (LBRR_flag) { + return SWITCH_TRUE; /* one of the silk frames has FEC */ + } + } + } } - - payload_length_ms = opus_packet_get_samples_per_frame(payload, 48000) / 48; - if (10 > payload_length_ms) { - payload_length_ms = 10; - } - + frames = opus_packet_parse(payload, payload_length_bytes, NULL, frame_data, frame_sizes, NULL); if (frames < 0) { @@ -310,50 +391,96 @@ static switch_bool_t switch_opus_has_fec(const uint8_t* payload,int payload_leng return SWITCH_FALSE; } +/* this is only useful for fs = 8000 hz, the map is only used + * at the beginning of the call. */ +static int switch_opus_get_fec_bitrate(int fs, int loss) +{ + int threshold_bitrates[25] = { + 15600,15200,15200,15200,14800, + 14800,14800,14800,14400,14400, + 14400,14000,14000,14000,13600, + 13600,13600,13600,13200,13200, + 13200,12800,12800,12800,12400 + }; + + if (loss <= 0){ + return SWITCH_STATUS_FALSE; + } + + if (fs == 8000) { + if (loss >=25) { + return threshold_bitrates[24]; + } else { + return threshold_bitrates[loss-1]; + } + } + + return SWITCH_STATUS_FALSE ; +} + static switch_status_t switch_opus_info(void * encoded_data, uint32_t len, uint32_t samples_per_second, char *print_text) { - int nb_samples; - int nb_frames; + /* nb_silk_frames: number of silk-frames (10 or 20 ms) in an opus frame: 0, 1, 2 or 3 */ + /* computed from the 5 MSB (configuration) of the TOC byte (encoded_data[0]) */ + /* nb_opus_frames: number of opus frames in the packet */ + /* computed from the 2 LSB (p0p1) of the TOC byte */ + /* p0p1 = 0 => nb_opus_frames= 1 */ + /* p0p1 = 1 or 2 => nb_opus_frames= 2 */ + /* p0p1 = 3 => given by the 6 LSB of encoded_data[1] */ + + int nb_samples, nb_silk_frames, nb_opus_frames, n, i; int audiobandwidth; - const char *audiobandwidth_str = "UNKNOWN"; + char audiobandwidth_str[32] = {0}; opus_int16 frame_sizes[48]; const unsigned char *frame_data[48]; char has_fec = 0; + uint8_t * payload = encoded_data ; if (!encoded_data) { return SWITCH_STATUS_FALSE; } - nb_frames = opus_packet_get_nb_frames(encoded_data, len); - nb_samples = opus_packet_get_samples_per_frame(encoded_data, samples_per_second) * nb_frames; audiobandwidth = opus_packet_get_bandwidth(encoded_data); - if (audiobandwidth == OPUS_BANDWIDTH_NARROWBAND) { - audiobandwidth_str = "NARROWBAND"; - } else if (audiobandwidth == OPUS_BANDWIDTH_MEDIUMBAND) { - audiobandwidth_str = "MEDIUMBAND"; - } else if (audiobandwidth == OPUS_BANDWIDTH_WIDEBAND) { - audiobandwidth_str = "WIDEBAND"; - } else if (audiobandwidth == OPUS_BANDWIDTH_SUPERWIDEBAND) { - audiobandwidth_str = "SUPERWIDEBAND"; - } else if (audiobandwidth == OPUS_BANDWIDTH_FULLBAND) { - audiobandwidth_str = "FULLBAND"; - } else if (audiobandwidth == OPUS_INVALID_PACKET) { + if (!switch_opus_show_audio_bandwidth(audiobandwidth,audiobandwidth_str)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: OPUS_INVALID_PACKET !\n", print_text); } - if (opus_packet_parse(encoded_data, len, NULL, frame_data, frame_sizes, NULL)) { - if (frame_data[0]) { - /*check only 1st frame*/ - has_fec = frame_data[0][0] & (0x80 >> 1); + if ((nb_opus_frames = opus_packet_parse(encoded_data, len, NULL, frame_data, frame_sizes, NULL)) < 0 ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: OPUS_INVALID_PACKET ! frames: %d\n", print_text, nb_opus_frames); + return SWITCH_STATUS_FALSE; + } + + nb_samples = opus_packet_get_samples_per_frame(encoded_data, samples_per_second) * nb_opus_frames; + nb_silk_frames = 0; + + if ((payload[0] >> 3 ) < 12) { /* config in silk-only : NB,MB,WB */ + nb_silk_frames = (payload[0] >> 3) & 0x3; + if(nb_silk_frames == 0) { + nb_silk_frames = 1; + } + if (nb_silk_frames == 1) { + for (n = 0; n <= (payload[0]&0x4) ; n++) { /* mono or stereo */ + if (frame_data[0][0] & (0x80 >> ((n + 1) * (nb_silk_frames + 1) - 1))){ + has_fec = 1 ; /* 1st 20ms (or 10 ms) frame has fec */ + } + } + } else { + opus_int16 LBRR_flag = 0 ; + for (i=0 ; i < nb_opus_frames; i++ ) { /* only mono */ + LBRR_flag = (frame_data[i][0] >> (7 - nb_silk_frames)) & 0x1; + if (LBRR_flag) { + has_fec = 1; + } + } } } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: frames [%d] samples [%d] audio bandwidth [%s] bytes [%d] FEC[%s]\n", - print_text, nb_frames, nb_samples, audiobandwidth_str, len, has_fec ? "yes" : "no"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: nb_opus_frames [%d] nb_silk_frames [%d] samples [%d] audio bandwidth [%s] bytes [%d] FEC[%s]\n", + print_text, nb_opus_frames, nb_silk_frames, nb_samples, audiobandwidth_str, len, has_fec ? "yes" : "no" ); return SWITCH_STATUS_SUCCESS; -} +} static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) { @@ -402,6 +529,8 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag opus_codec_settings.sprop_maxcapturerate = opus_codec_settings_remote.sprop_maxcapturerate; } + opus_codec_settings.useinbandfec = opus_prefs.fec_decode; + opus_codec_settings.cbr = !opus_prefs.use_vbr; opus_codec_settings.usedtx = opus_prefs.use_dtx; @@ -445,36 +574,27 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag return SWITCH_STATUS_GENERR; } - /* Setting documented in "RTP Payload Format for Opus Speech and Audio Codec" draft-spittka-payload-rtp-opus-03 */ - if (opus_codec_settings.maxaveragebitrate) { /* Remote codec settings found in SDP "fmtp", we accept to tune the Encoder */ + /* https://tools.ietf.org/html/rfc7587 */ + if (opus_codec_settings.maxaveragebitrate) { opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(opus_codec_settings.maxaveragebitrate)); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder set bitrate based on maxaveragebitrate found in SDP [%dbps]\n", opus_codec_settings.maxaveragebitrate); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: set bitrate based on maxaveragebitrate value found in SDP or local config [%dbps]\n", opus_codec_settings.maxaveragebitrate); } else { - /* Default codec settings used, may have been modified by SDP "samplerate" */ - opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(bitrate_bps)); - if (codec->implementation->actual_samples_per_second == 8000) { - opus_encoder_ctl(context->encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); - opus_encoder_ctl(context->encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); - } else { - opus_encoder_ctl(context->encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); - } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder set bitrate to local settings [%dbps]\n", bitrate_bps); + opus_encoder_ctl(context->encoder_object, OPUS_SET_BANDWIDTH(OPUS_AUTO)); + opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(bitrate_bps)); /* OPUS_AUTO */ + opus_encoder_ctl(context->encoder_object, OPUS_GET_BITRATE(&bitrate_bps)); /* return average bps for this audio bandwidth */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: set bitrate to local settings [%dbps]\n", bitrate_bps); } - - /* Another setting from "RTP Payload Format for Opus Speech and Audio Codec" */ + /* Another fmtp setting from https://tools.ietf.org/html/rfc7587 - "RTP Payload Format for the Opus Speech and Audio Codec" */ if (opus_codec_settings.maxplaybackrate) { - if (opus_codec_settings.maxplaybackrate == 8000) { /* Audio Bandwidth: 0-4000Hz Sampling Rate: 8000Hz */ - opus_encoder_ctl(context->encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); - } else if (opus_codec_settings.maxplaybackrate == 12000) { /* Audio Bandwidth: 0-6000Hz Sampling Rate: 12000Hz */ - opus_encoder_ctl(context->encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); - } else if (opus_codec_settings.maxplaybackrate == 16000) { /* Audio Bandwidth: 0-8000Hz Sampling Rate: 16000Hz */ - opus_encoder_ctl(context->encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); - } else if (opus_codec_settings.maxplaybackrate == 24000) { /* Audio Bandwidth: 0-12000Hz Sampling Rate: 24000Hz */ - opus_encoder_ctl(context->encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); - } else if (opus_codec_settings.maxplaybackrate == 48000) { /* Audio Bandwidth: 0-20000Hz Sampling Rate: 48000Hz */ - opus_encoder_ctl(context->encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); + opus_int32 audiobandwidth; + char audiobandwidth_str[32] = {0}; + + audiobandwidth = switch_opus_encoder_set_audio_bandwidth(context->encoder_object,opus_codec_settings.maxplaybackrate); + if (!switch_opus_show_audio_bandwidth(audiobandwidth,audiobandwidth_str)) { + memset(audiobandwidth_str,0,sizeof(audiobandwidth_str)); + strncpy(audiobandwidth_str, "OPUS_AUTO",sizeof(audiobandwidth_str)-1); } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder set bandwidth based on maxplaybackrate found in SDP [%dHz]\n", opus_codec_settings.maxplaybackrate); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: set audio bandwidth to [%s] based on maxplaybackrate value found in SDP or local config [%dHz]\n",audiobandwidth_str,opus_codec_settings.maxplaybackrate); } if (use_vbr) { @@ -494,7 +614,20 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag } if (opus_codec_settings.useinbandfec) { + /* FEC on the encoder: start the call with a preconfigured packet loss percentage */ + int fec_bitrate = opus_codec_settings.maxaveragebitrate; + int loss_percent = opus_prefs.plpct ; opus_encoder_ctl(context->encoder_object, OPUS_SET_INBAND_FEC(opus_codec_settings.useinbandfec)); + opus_encoder_ctl(context->encoder_object, OPUS_SET_PACKET_LOSS_PERC(loss_percent)); + if (opus_prefs.keep_fec){ + fec_bitrate = switch_opus_get_fec_bitrate(enc_samplerate,loss_percent); + /* keep a bitrate for which the encoder will always add FEC */ + if (fec_bitrate != SWITCH_STATUS_FALSE) { + opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(fec_bitrate)); + /* will override the maxaveragebitrate set in opus.conf.xml */ + opus_codec_settings.maxaveragebitrate = fec_bitrate; + } + } } if (opus_codec_settings.usedtx) { @@ -608,7 +741,7 @@ static switch_status_t switch_opus_decode(switch_codec_t *codec, struct opus_context *context = codec->private_info; int samples = 0; int fec = 0, plc = 0; - int32_t frame_size; + int32_t frame_size = 0, last_frame_size = 0; uint32_t frame_samples; if (!context) { @@ -620,47 +753,66 @@ static switch_status_t switch_opus_decode(switch_codec_t *codec, if (*flag & SFF_PLC) { switch_core_session_t *session = codec->session; + switch_channel_t *channel = switch_core_session_get_channel(session); switch_jb_t *jb = NULL; - int got_frame = 0; plc = 1; encoded_data = NULL; if ((opus_prefs.use_jb_lookahead || context->use_jb_lookahead) && context->codec_settings.useinbandfec && session) { - if (opus_packet_get_bandwidth(codec->cur_frame->data) != OPUS_BANDWIDTH_FULLBAND && - codec->cur_frame && (jb = switch_core_session_get_jb(session, SWITCH_MEDIA_TYPE_AUDIO))) { + if (!context->look_check) { + context->look_ts = switch_true(switch_channel_get_variable_dup(channel, "jb_use_timestamps", SWITCH_FALSE, -1)); + context->look_check = 1; + } + if (codec->cur_frame && (jb = switch_core_session_get_jb(session, SWITCH_MEDIA_TYPE_AUDIO))) { switch_frame_t frame = { 0 }; uint8_t buf[SWITCH_RTP_MAX_BUF_LEN]; + uint32_t ts = 0; + uint16_t seq = 0; + + if (context->look_ts) { + ts = codec->cur_frame->timestamp; + } else { + seq = codec->cur_frame->seq; + } + frame.data = buf; frame.buflen = sizeof(buf); - - if (switch_jb_peek_frame(jb, codec->cur_frame->timestamp, 0, 1, &frame)) { - got_frame = 1; - fec = 1; - encoded_data = frame.data; - encoded_data_len = frame.datalen; + + if (globals.debug || context->debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Missing %s %u Checking JB\n", seq ? "SEQ" : "TS", seq ? seq : ts); + } + + if (switch_jb_peek_frame(jb, ts, seq, 1, &frame) == SWITCH_STATUS_SUCCESS) { if (globals.debug || context->debug) { - if (switch_opus_has_fec(frame.data, frame.datalen)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FEC info available in packet with SEQ[%d] encoded_data_len: %d\n", - codec->cur_frame->seq, encoded_data_len); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Lookahead frame found: %u:%u\n", + frame.timestamp, frame.seq); + } + + + if ((fec = switch_opus_has_fec(frame.data, frame.datalen))) { + encoded_data = frame.data; + encoded_data_len = frame.datalen; + } + + if (globals.debug || context->debug) { + if (fec) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FEC info available in packet with SEQ: %d LEN: %d\n", + frame.seq, frame.datalen); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NO FEC info in this packet with SEQ[%d] encoded_data_len: %d\n", - codec->cur_frame->seq, encoded_data_len ); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NO FEC info in this packet with SEQ: %d LEN: %d\n", + frame.seq, frame.datalen); } } - } + } } } - if (!got_frame) { - opus_decoder_ctl(context->decoder_object, OPUS_GET_LAST_PACKET_DURATION(&frame_size)); - - if (!frame_size) { - frame_size = frame_samples - (frame_samples % (codec->implementation->actual_samples_per_second / 400)); - } - } + opus_decoder_ctl(context->decoder_object, OPUS_GET_LAST_PACKET_DURATION(&last_frame_size)); + if (last_frame_size) frame_size = last_frame_size; + if (globals.debug || context->debug) { if (opus_prefs.use_jb_lookahead || context->use_jb_lookahead) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "MISSING FRAME: %s\n", fec ? "Look-ahead FEC" : "PLC"); @@ -675,7 +827,8 @@ static switch_status_t switch_opus_decode(switch_codec_t *codec, if (globals.debug || context->debug > 1) { int samplerate = context->dec_frame_size * 1000 / (codec->implementation->microseconds_per_packet / 1000); switch_opus_info(encoded_data, encoded_data_len, - samplerate ? samplerate : codec->implementation->actual_samples_per_second, "decode"); + samplerate ? samplerate : codec->implementation->actual_samples_per_second, + !encoded_data ? "PLC correction" : fec ? "FEC correction" : "decode"); } samples = opus_decode(context->decoder_object, encoded_data, encoded_data_len, decoded_data, frame_size, fec); @@ -794,6 +947,14 @@ static switch_status_t opus_load_config(switch_bool_t reload) return status; } + memset(&opus_prefs, 0, sizeof(opus_prefs)); + opus_prefs.use_jb_lookahead = 1; + opus_prefs.keep_fec = 1; + opus_prefs.use_dtx = 0; + opus_prefs.plpct = 20; + opus_prefs.use_vbr = 0; + //opus_prefs.fec_decode = 1; + if ((settings = switch_xml_child(cfg, "settings"))) { for (param = switch_xml_child(settings, "param"); param; param = param->next) { char *key = (char *) switch_xml_attr_soft(param, "name"); @@ -811,8 +972,10 @@ static switch_status_t opus_load_config(switch_bool_t reload) opus_prefs.asymmetric_samplerates = atoi(val); } else if (!strcasecmp(key, "use-jb-lookahead")) { opus_prefs.use_jb_lookahead = switch_true(val); - } else if (!strcasecmp(key, "keep-fec-enabled")) { + } else if (!strcasecmp(key, "keep-fec-enabled")) { /* encoder */ opus_prefs.keep_fec = atoi(val); + } else if (!strcasecmp(key, "advertise_useinbandfec")) { /*decoder, has meaning only for FMTP: useinbandfec=1 by default */ + opus_prefs.fec_decode = atoi(val); } else if (!strcasecmp(key, "maxaveragebitrate")) { opus_prefs.maxaveragebitrate = atoi(val); if (opus_prefs.maxaveragebitrate < 6000 || opus_prefs.maxaveragebitrate > 510000) { @@ -955,18 +1118,21 @@ static switch_status_t switch_opus_control(switch_codec_t *codec, plpct = 100; } - calc = plpct % 10; - plpct = plpct - calc + ( calc ? 10 : 0); - - if (opus_prefs.plpct > 0 && plpct < opus_prefs.plpct) { - plpct = opus_prefs.plpct; - } - - if (plpct != context->old_plpct) { + if (opus_prefs.keep_fec) { opus_encoder_ctl(context->encoder_object, OPUS_SET_PACKET_LOSS_PERC(plpct)); - + } else { + calc = plpct % 10; + plpct = plpct - calc + ( calc ? 10 : 0); + } + if (plpct != context->old_plpct) { if (opus_prefs.keep_fec) { - switch_opus_keep_fec_enabled(codec); + if (plpct > 10) { + /* this will increase bitrate a little bit, just to keep FEC enabled */ + switch_opus_keep_fec_enabled(codec); + } + } else { + /* this can have no effect because FEC is F(bitrate,packetloss), let the codec decide if FEC is to be used or not */ + opus_encoder_ctl(context->encoder_object, OPUS_SET_PACKET_LOSS_PERC(plpct)); } if (globals.debug || context->debug) { @@ -992,6 +1158,7 @@ SWITCH_STANDARD_API(mod_opus_debug) if (!strcasecmp(cmd, "on")) { globals.debug = 1; stream->write_function(stream, "OPUS Debug: on\n"); + stream->write_function(stream, "Library version: %s\n",opus_get_version_string()); } else if (!strcasecmp(cmd, "off")) { globals.debug = 0; stream->write_function(stream, "OPUS Debug: off\n"); @@ -1035,6 +1202,12 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) settings = default_codec_settings; + settings.useinbandfec = opus_prefs.fec_decode; + + settings.cbr = !opus_prefs.use_vbr; + + settings.usedtx = opus_prefs.use_dtx; + if (opus_prefs.maxaveragebitrate) { settings.maxaveragebitrate = opus_prefs.maxaveragebitrate; } @@ -1050,8 +1223,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) for (x = 0; x < 3; x++) { settings.ptime = mss / 1000; - settings.maxptime = settings.ptime; - settings.minptime = settings.ptime; settings.samplerate = rate; settings.stereo = 0; dft_fmtp = gen_fmtp(&settings, pool); @@ -1109,11 +1280,27 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) mss = 10000; rate = 8000; + settings = default_codec_settings_8k; + + settings.useinbandfec = opus_prefs.fec_decode; + + settings.cbr = !opus_prefs.use_vbr; + + settings.usedtx = opus_prefs.use_dtx; + + if (opus_prefs.maxaveragebitrate) { + settings.maxaveragebitrate = opus_prefs.maxaveragebitrate; + } + if (opus_prefs.maxplaybackrate) { + settings.maxplaybackrate = opus_prefs.maxplaybackrate; + } + if (opus_prefs.sprop_maxcapturerate) { + settings.sprop_maxcapturerate = opus_prefs.sprop_maxcapturerate; + } + for (x = 0; x < 3; x++) { settings.stereo = 0; settings.ptime = mss / 1000; - settings.maxptime = settings.ptime; - settings.minptime = settings.ptime; settings.samplerate = rate; dft_fmtp = gen_fmtp(&settings, pool); @@ -1158,6 +1345,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) if (x == 1) { /*20 ms * 3 = 60 ms */ int nb_frames; settings.stereo = 0; + settings.ptime = mss * 3 / 1000; dft_fmtp = gen_fmtp(&settings, pool); switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ 116, /* the IANA code number */ @@ -1181,6 +1369,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) for (nb_frames = 4; nb_frames <= 6; nb_frames++) { /*20 ms * nb_frames = 80 ms , 100 ms , 120 ms */ settings.stereo = 0; + settings.ptime = mss * nb_frames / 1000; dft_fmtp = gen_fmtp(&settings, pool); switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ 116, /* the IANA code number */ diff --git a/src/mod/endpoints/mod_gsmopen/mobigater b/src/mod/endpoints/mod_gsmopen/mobigater deleted file mode 120000 index cfea4c01b2..0000000000 --- a/src/mod/endpoints/mod_gsmopen/mobigater +++ /dev/null @@ -1 +0,0 @@ -alsa_nogsmlib_nocplusplus \ No newline at end of file diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 28b68ab8fd..85673df525 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -150,12 +150,12 @@ switch_status_t skinny_create_incoming_session(listener_t *listener, uint32_t *l } /* First create the caller profile in the patterns Dialplan */ if (!(tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(nsession), - NULL, listener->profile->patterns_dialplan, - button->displayname, button->shortname, + NULL, listener->profile->patterns_dialplan, + button->displayname, button->shortname, listener->remote_ip, NULL, NULL, NULL, "skinny" /* modname */, - listener->profile->patterns_context, - "")) != 0) { + listener->profile->patterns_context, + ""))) { skinny_log_ls_msg(listener, nsession, SWITCH_LOG_CRIT, "Error Creating Session caller profile\n"); goto error; } diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index e979618a8a..d69a5bd291 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -6969,6 +6969,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, NUTAG_MEDIA_ENABLE(0), TAG_IF(!zstr(other_tech_pvt->user_via), SIPTAG_VIA_STR(other_tech_pvt->user_via)), SIPTAG_CONTACT_STR(other_tech_pvt->reply_contact), + SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(r_sdp), TAG_END()); diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c index 69ef7ac767..86afb899d8 100644 --- a/src/mod/endpoints/mod_verto/mod_verto.c +++ b/src/mod/endpoints/mod_verto/mod_verto.c @@ -3125,6 +3125,7 @@ static switch_bool_t verto__attach_func(const char *method, cJSON *params, jsock if (tech_pvt) { switch_channel_clear_flag(tech_pvt->channel, CF_REINVITE); + switch_channel_clear_flag(tech_pvt->channel, CF_RECOVERING); switch_clear_flag(tech_pvt, TFLAG_ATTACH_REQ); if (switch_channel_test_flag(tech_pvt->channel, CF_CONFERENCE)) { switch_channel_set_flag(tech_pvt->channel, CF_CONFERENCE_ADV); diff --git a/src/mod/event_handlers/mod_json_cdr/conf/autoload_configs/json_cdr.conf.xml b/src/mod/event_handlers/mod_json_cdr/conf/autoload_configs/json_cdr.conf.xml index 374ec8d2e2..a51a6518cf 100644 --- a/src/mod/event_handlers/mod_json_cdr/conf/autoload_configs/json_cdr.conf.xml +++ b/src/mod/event_handlers/mod_json_cdr/conf/autoload_configs/json_cdr.conf.xml @@ -31,8 +31,8 @@ - - + + diff --git a/src/mod/formats/mod_local_stream/mod_local_stream.c b/src/mod/formats/mod_local_stream/mod_local_stream.c index 9c9eedd8dd..a8a530bd46 100644 --- a/src/mod/formats/mod_local_stream/mod_local_stream.c +++ b/src/mod/formats/mod_local_stream/mod_local_stream.c @@ -89,6 +89,7 @@ struct local_stream_source { char *timer_name; local_stream_context_t *context_list; int total; + int first; switch_dir_t *dir_handle; switch_mutex_t *mutex; switch_memory_pool_t *pool; @@ -117,6 +118,41 @@ struct local_stream_source { typedef struct local_stream_source local_stream_source_t; +switch_status_t list_streams_full(const char *line, const char *cursor, switch_console_callback_match_t **matches, switch_bool_t show_aliases) +{ + local_stream_source_t *source; + switch_hash_index_t *hi; + void *val; + const void *vvar; + switch_console_callback_match_t *my_matches = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(globals.mutex); + for (hi = switch_core_hash_first(globals.source_hash); hi; hi = switch_core_hash_next(&hi)) { + switch_core_hash_this(hi, &vvar, NULL, &val); + + source = (local_stream_source_t *) val; + if (!show_aliases && strcmp((char *)vvar, source->name)) { + continue; + } + + switch_console_push_match(&my_matches, (const char *) vvar); + } + switch_mutex_unlock(globals.mutex); + + if (my_matches) { + *matches = my_matches; + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} + +switch_status_t list_streams(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return list_streams_full(line, cursor, matches, SWITCH_TRUE); +} + static int do_rand(uint32_t count) { double r; @@ -279,6 +315,8 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void flush_video_queue(source->video_q); } + switch_buffer_zero(audio_buffer); + if (switch_core_timer_init(&timer, source->timer_name, source->interval, (int)source->samples, temp_pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Can't start timer.\n"); switch_dir_close(source->dir_handle); @@ -325,7 +363,7 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void if (source->chime_counter > 0) { source->chime_counter -= (int32_t)source->samples; } - + if (!switch_test_flag((&source->chime_fh), SWITCH_FILE_OPEN) && source->chime_counter <= 0) { char *val; @@ -376,22 +414,26 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void } } - if (is_open) { + if (!is_open) { + switch_buffer_zero(audio_buffer); + break; + } else { + int svr = 0; + if (switch_core_has_video() && switch_core_file_has_video(use_fh)) { switch_frame_t vid_frame = { 0 }; if (use_fh == &source->chime_fh && switch_core_file_has_video(&fh)) { - if (switch_core_file_read_video(&fh, &vid_frame, SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { + if (switch_core_file_read_video(&fh, &vid_frame, svr) == SWITCH_STATUS_SUCCESS) { switch_img_free(&vid_frame.img); } } - if (switch_core_file_read_video(use_fh, &vid_frame, SVR_FLUSH) == SWITCH_STATUS_SUCCESS) { + while (switch_core_file_read_video(use_fh, &vid_frame, svr) == SWITCH_STATUS_SUCCESS) { if (vid_frame.img) { int flush = 1; source->has_video = 1; - if (source->total) { if (switch_queue_trypush(source->video_q, vid_frame.img) == SWITCH_STATUS_SUCCESS) { flush = 0; @@ -413,7 +455,7 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void switch_core_file_read(&fh, abuf, &olen); olen = source->samples; } - + if (switch_core_file_read(use_fh, abuf, &olen) != SWITCH_STATUS_SUCCESS || !olen) { switch_core_file_close(use_fh); flush_video_queue(source->video_q); @@ -451,67 +493,70 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void break; } - if (!is_open || used >= source->prebuf || (source->total && used > source->samples * 2 * source->channels)) { - void *pop; + source->prebuf = source->samples * 2 * source->channels; + if (!source->total) { + flush_video_queue(source->video_q); + switch_buffer_zero(audio_buffer); + } else if (used > source->samples * 2 * source->channels) { + //if (!is_open || used >= source->prebuf || (source->total && used > source->samples * 2 * source->channels)) { + void *pop; + uint32_t bused; + used = switch_buffer_read(audio_buffer, dist_buf, source->samples * 2 * source->channels); - if (!source->total) { - flush_video_queue(source->video_q); - } else { - uint32_t bused = 0; + bused = 0; - switch_mutex_lock(source->mutex); - for (cp = source->context_list; cp && RUNNING; cp = cp->next) { + switch_mutex_lock(source->mutex); + for (cp = source->context_list; cp && RUNNING; cp = cp->next) { - if (source->has_video) { - switch_set_flag(cp->handle, SWITCH_FILE_FLAG_VIDEO); - } else { - switch_clear_flag(cp->handle, SWITCH_FILE_FLAG_VIDEO); - } - - if (switch_test_flag(cp->handle, SWITCH_FILE_CALLBACK)) { - continue; - } - - switch_mutex_lock(cp->audio_mutex); - bused = (uint32_t)switch_buffer_inuse(cp->audio_buffer); - if (bused > source->samples * 768) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Flushing Stream Handle Buffer [%s() %s:%d] size: %u samples: %ld\n", - cp->func, cp->file, cp->line, bused, (long)source->samples); - switch_buffer_zero(cp->audio_buffer); - } else { - switch_buffer_write(cp->audio_buffer, dist_buf, used); - } - switch_mutex_unlock(cp->audio_mutex); + if (source->has_video) { + switch_set_flag(cp->handle, SWITCH_FILE_FLAG_VIDEO); + } else { + switch_clear_flag(cp->handle, SWITCH_FILE_FLAG_VIDEO); } - switch_mutex_unlock(source->mutex); + + if (switch_test_flag(cp->handle, SWITCH_FILE_CALLBACK)) { + continue; + } + + switch_mutex_lock(cp->audio_mutex); + bused = (uint32_t)switch_buffer_inuse(cp->audio_buffer); + if (bused > source->samples * 768) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Flushing Stream Handle Buffer [%s() %s:%d] size: %u samples: %ld\n", + cp->func, cp->file, cp->line, bused, (long)source->samples); + switch_buffer_zero(cp->audio_buffer); + } else { + switch_buffer_write(cp->audio_buffer, dist_buf, used); + } + switch_mutex_unlock(cp->audio_mutex); + } + switch_mutex_unlock(source->mutex); - while (switch_queue_trypop(source->video_q, &pop) == SWITCH_STATUS_SUCCESS) { - switch_image_t *img = (switch_image_t *) pop; - switch_image_t *imgcp = NULL; + while (switch_queue_trypop(source->video_q, &pop) == SWITCH_STATUS_SUCCESS) { + switch_image_t *img = (switch_image_t *) pop; + switch_image_t *imgcp = NULL; - if (source->total == 1) { - switch_queue_push(source->context_list->video_q, img); - } else { - if (source->context_list) { - switch_mutex_lock(source->mutex); - for (cp = source->context_list; cp && RUNNING; cp = cp->next) { - if (cp->video_q) { - imgcp = NULL; - switch_img_copy(img, &imgcp); - if (imgcp) { - if (switch_queue_trypush(cp->video_q, imgcp) != SWITCH_STATUS_SUCCESS) { - flush_video_queue(cp->video_q); - } + if (source->total == 1) { + switch_queue_push(source->context_list->video_q, img); + } else { + if (source->context_list) { + switch_mutex_lock(source->mutex); + for (cp = source->context_list; cp && RUNNING; cp = cp->next) { + if (cp->video_q) { + imgcp = NULL; + switch_img_copy(img, &imgcp); + if (imgcp) { + if (switch_queue_trypush(cp->video_q, imgcp) != SWITCH_STATUS_SUCCESS) { + flush_video_queue(cp->video_q); } } } - switch_mutex_unlock(source->mutex); } - switch_img_free(&img); + switch_mutex_unlock(source->mutex); } + switch_img_free(&img); } } } @@ -709,6 +754,9 @@ static switch_status_t local_stream_file_open(switch_file_handle_t *handle, cons context->next = source->context_list; source->context_list = context; source->total++; + if (source->total == 1) { + source->first = 1; + } switch_mutex_unlock(source->mutex); end: @@ -758,7 +806,9 @@ static switch_status_t local_stream_file_read_video(switch_file_handle_t *handle local_stream_context_t *context = handle->private_info; switch_status_t status; switch_time_t now; - + int fps = (int)ceil(handle->mm.fps); + int min_qsize = fps; + if (!(context->ready && context->source->ready)) { return SWITCH_STATUS_FALSE; } @@ -796,7 +846,7 @@ static switch_status_t local_stream_file_read_video(switch_file_handle_t *handle return SWITCH_STATUS_BREAK; } - while(context->ready && context->source->ready && (flags & SVR_FLUSH) && switch_queue_size(context->video_q) > 1) { + while(context->ready && context->source->ready && (flags & SVR_FLUSH) && switch_queue_size(context->video_q) > min_qsize / 2) { if (switch_queue_trypop(context->video_q, &pop) == SWITCH_STATUS_SUCCESS) { switch_image_t *img = (switch_image_t *) pop; switch_img_free(&img); @@ -807,6 +857,10 @@ static switch_status_t local_stream_file_read_video(switch_file_handle_t *handle return SWITCH_STATUS_FALSE; } + while (switch_queue_size(context->video_q) < 5) { + return SWITCH_STATUS_BREAK; + } + if ((flags & SVR_BLOCK)) { status = switch_queue_pop(context->video_q, &pop); } else { @@ -869,7 +923,7 @@ static switch_status_t local_stream_file_read_video(switch_file_handle_t *handle //switch_img_overlay(frame->img, context->banner_img, 0, frame->img->d_h - context->banner_img->d_h, 100); switch_img_patch(frame->img, context->banner_img, 0, frame->img->d_h - context->banner_img->d_h); } - + return SWITCH_STATUS_SUCCESS; } @@ -1031,197 +1085,11 @@ static void event_handler(switch_event_t *event) RUNNING = 0; } -#define RELOAD_LOCAL_STREAM_SYNTAX "" -SWITCH_STANDARD_API(reload_local_stream_function) +#define LOCAL_STREAM_SYNTAX " " +SWITCH_STANDARD_API(local_stream_function) { local_stream_source_t *source = NULL; - char *mycmd = NULL, *argv[1] = { 0 }; - char *local_stream_name = NULL; - int argc = 0; - - - if (zstr(cmd)) { - goto usage; - } - - if (!(mycmd = strdup(cmd))) { - goto usage; - } - - if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) { - goto usage; - } - - local_stream_name = argv[0]; - if (zstr(local_stream_name)) { - goto usage; - } - - switch_mutex_lock(globals.mutex); - source = switch_core_hash_find(globals.source_hash, local_stream_name); - switch_mutex_unlock(globals.mutex); - - if (!source) { - stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); - goto done; - } - - source->full_reload = 1; - source->part_reload = 1; - stream->write_function(stream, "+OK"); - goto done; - - usage: - stream->write_function(stream, "-USAGE: %s\n", RELOAD_LOCAL_STREAM_SYNTAX); - switch_safe_free(mycmd); - - done: - - switch_safe_free(mycmd); - return SWITCH_STATUS_SUCCESS; -} - -#define STOP_LOCAL_STREAM_SYNTAX "" -SWITCH_STANDARD_API(stop_local_stream_function) -{ - local_stream_source_t *source = NULL; - char *mycmd = NULL, *argv[1] = { 0 }; - char *local_stream_name = NULL; - int argc = 0; - - - if (zstr(cmd)) { - goto usage; - } - - if (!(mycmd = strdup(cmd))) { - goto usage; - } - - if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) { - goto usage; - } - - local_stream_name = argv[0]; - if (zstr(local_stream_name)) { - goto usage; - } - - switch_mutex_lock(globals.mutex); - source = switch_core_hash_find(globals.source_hash, local_stream_name); - switch_mutex_unlock(globals.mutex); - - if (!source) { - stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); - goto done; - } - - source->stopped = 1; - stream->write_function(stream, "+OK"); - goto done; - - usage: - stream->write_function(stream, "-USAGE: %s\n", STOP_LOCAL_STREAM_SYNTAX); - switch_safe_free(mycmd); - - done: - - switch_safe_free(mycmd); - return SWITCH_STATUS_SUCCESS; -} - -#define SHOW_LOCAL_STREAM_SYNTAX "[local_stream_name [xml]]" -SWITCH_STANDARD_API(show_local_stream_function) -{ - local_stream_source_t *source = NULL; - char *mycmd = NULL, *argv[2] = { 0 }; - char *local_stream_name = NULL; - int argc = 0; - switch_hash_index_t *hi; - const void *var; - void *val; - switch_bool_t xml = SWITCH_FALSE; - - switch_mutex_lock(globals.mutex); - - if (zstr(cmd)) { - for (hi = switch_core_hash_first(globals.source_hash); hi; hi = switch_core_hash_next(&hi)) { - switch_core_hash_this(hi, &var, NULL, &val); - if ((source = (local_stream_source_t *) val)) { - stream->write_function(stream, "%s,%s\n", source->name, source->location); - } - } - } else { - if (!(mycmd = strdup(cmd))) { - goto usage; - } - - if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { - local_stream_name = argv[0]; - if (argc > 1 && !strcasecmp("xml", argv[1])) { - xml = SWITCH_TRUE; - } - } - - if (!local_stream_name) { - goto usage; - } - - source = switch_core_hash_find(globals.source_hash, local_stream_name); - if (source) { - if (xml) { - stream->write_function(stream, "\n\n", source->name); - stream->write_function(stream, " %s\n", source->location); - stream->write_function(stream, " %d\n", source->channels); - stream->write_function(stream, " %d\n", source->rate); - stream->write_function(stream, " %d\n", source->interval); - stream->write_function(stream, " %d\n", source->samples); - stream->write_function(stream, " %d\n", source->prebuf); - stream->write_function(stream, " %s\n", source->timer_name); - stream->write_function(stream, " %d\n", source->total); - stream->write_function(stream, " %s\n", (source->shuffle) ? "true" : "false"); - stream->write_function(stream, " %s\n", (source->ready) ? "true" : "false"); - stream->write_function(stream, " %s\n", (source->stopped) ? "true" : "false"); - stream->write_function(stream, "\n"); - } else { - stream->write_function(stream, "%s\n", source->name); - stream->write_function(stream, " location: %s\n", source->location); - stream->write_function(stream, " channels: %d\n", source->channels); - stream->write_function(stream, " rate: %d\n", source->rate); - stream->write_function(stream, " interval: %d\n", source->interval); - stream->write_function(stream, " samples: %d\n", source->samples); - stream->write_function(stream, " prebuf: %d\n", source->prebuf); - stream->write_function(stream, " timer: %s\n", source->timer_name); - stream->write_function(stream, " total: %d\n", source->total); - stream->write_function(stream, " shuffle: %s\n", (source->shuffle) ? "true" : "false"); - stream->write_function(stream, " ready: %s\n", (source->ready) ? "true" : "false"); - stream->write_function(stream, " stopped: %s\n", (source->stopped) ? "true" : "false"); - stream->write_function(stream, " reloading: %s\n", (source->full_reload) ? "true" : "false"); - } - } else { - stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); - goto done; - } - } - - stream->write_function(stream, "+OK"); - goto done; - - usage: - stream->write_function(stream, "-USAGE: %s\n", SHOW_LOCAL_STREAM_SYNTAX); - - done: - - switch_mutex_unlock(globals.mutex); - switch_safe_free(mycmd); - return SWITCH_STATUS_SUCCESS; -} - -#define START_LOCAL_STREAM_SYNTAX "" -SWITCH_STANDARD_API(start_local_stream_function) -{ - local_stream_source_t *source = NULL; - char *mycmd = NULL, *argv[8] = { 0 }; + char *mycmd = NULL, *argv[5] = { 0 }; char *local_stream_name = NULL; int argc = 0; int ok = 0; @@ -1238,68 +1106,124 @@ SWITCH_STANDARD_API(start_local_stream_function) goto usage; } - local_stream_name = argv[0]; + local_stream_name = argv[1]; + + if (!strcasecmp(argv[0], "hup")) { + switch_mutex_lock(globals.mutex); + source = switch_core_hash_find(globals.source_hash, local_stream_name); + switch_mutex_unlock(globals.mutex); + + if (source) { + source->hup = 1; + stream->write_function(stream, "+OK hup stream: %s", source->name); + goto done; + } + } else if (!strcasecmp(argv[0], "stop")) { + switch_mutex_lock(globals.mutex); + source = switch_core_hash_find(globals.source_hash, local_stream_name); + switch_mutex_unlock(globals.mutex); + + if (!source) { + stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); + goto done; + } + + source->stopped = 1; + stream->write_function(stream, "+OK"); + } else if (!strcasecmp(argv[0], "reload")) { + switch_mutex_lock(globals.mutex); + source = switch_core_hash_find(globals.source_hash, local_stream_name); + switch_mutex_unlock(globals.mutex); + + if (!source) { + stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); + goto done; + } + + source->full_reload = 1; + source->part_reload = 1; + stream->write_function(stream, "+OK"); + } else if (!strcasecmp(argv[0], "start")) { + switch_mutex_lock(globals.mutex); + source = switch_core_hash_find(globals.source_hash, local_stream_name); + switch_mutex_unlock(globals.mutex); + + if (source) { + source->stopped = 0; + stream->write_function(stream, "+OK stream: %s", source->name); + goto done; + } + + if ((ok = launch_streams(local_stream_name))) { + stream->write_function(stream, "+OK stream: %s", local_stream_name); + goto done; + } + + } else if (!strcasecmp(argv[0], "show")) { + switch_hash_index_t *hi; + const void *var; + void *val; + switch_bool_t xml = SWITCH_FALSE; + + switch_mutex_lock(globals.mutex); + if (argc == 1) { + for (hi = switch_core_hash_first(globals.source_hash); hi; hi = switch_core_hash_next(&hi)) { + switch_core_hash_this(hi, &var, NULL, &val); + if ((source = (local_stream_source_t *) val)) { + stream->write_function(stream, "%s,%s\n", source->name, source->location); + } + } + } else { + if (argc == 4 && !strcasecmp("xml", argv[3])) { + xml = SWITCH_TRUE; + } + + source = switch_core_hash_find(globals.source_hash, local_stream_name); + + if (source) { + if (xml) { + stream->write_function(stream, "\n\n", source->name); + stream->write_function(stream, " %s\n", source->location); + stream->write_function(stream, " %d\n", source->channels); + stream->write_function(stream, " %d\n", source->rate); + stream->write_function(stream, " %d\n", source->interval); + stream->write_function(stream, " %d\n", source->samples); + stream->write_function(stream, " %d\n", source->prebuf); + stream->write_function(stream, " %s\n", source->timer_name); + stream->write_function(stream, " %d\n", source->total); + stream->write_function(stream, " %s\n", (source->shuffle) ? "true" : "false"); + stream->write_function(stream, " %s\n", (source->ready) ? "true" : "false"); + stream->write_function(stream, " %s\n", (source->stopped) ? "true" : "false"); + stream->write_function(stream, "\n"); + } else { + stream->write_function(stream, "%s\n", source->name); + stream->write_function(stream, " location: %s\n", source->location); + stream->write_function(stream, " channels: %d\n", source->channels); + stream->write_function(stream, " rate: %d\n", source->rate); + stream->write_function(stream, " interval: %d\n", source->interval); + stream->write_function(stream, " samples: %d\n", source->samples); + stream->write_function(stream, " prebuf: %d\n", source->prebuf); + stream->write_function(stream, " timer: %s\n", source->timer_name); + stream->write_function(stream, " total: %d\n", source->total); + stream->write_function(stream, " shuffle: %s\n", (source->shuffle) ? "true" : "false"); + stream->write_function(stream, " ready: %s\n", (source->ready) ? "true" : "false"); + stream->write_function(stream, " stopped: %s\n", (source->stopped) ? "true" : "false"); + stream->write_function(stream, " reloading: %s\n", (source->full_reload) ? "true" : "false"); + } + } else { + stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); + } + } + switch_mutex_unlock(globals.mutex); - switch_mutex_lock(globals.mutex); - source = switch_core_hash_find(globals.source_hash, local_stream_name); - switch_mutex_unlock(globals.mutex); - if (source) { - source->stopped = 0; - stream->write_function(stream, "+OK stream: %s", source->name); goto done; } - - if ((ok = launch_streams(local_stream_name))) { - stream->write_function(stream, "+OK stream: %s", local_stream_name); - goto done; - } - - usage: - stream->write_function(stream, "-USAGE: %s\n", START_LOCAL_STREAM_SYNTAX); - - done: - - switch_safe_free(mycmd); - return SWITCH_STATUS_SUCCESS; -} - -#define HUP_LOCAL_STREAM_SYNTAX "" -SWITCH_STANDARD_API(hup_local_stream_function) -{ - local_stream_source_t *source = NULL; - char *mycmd = NULL, *argv[8] = { 0 }; - char *local_stream_name = NULL; - int argc = 0; - - if (zstr(cmd)) { - goto usage; - } - - if (!(mycmd = strdup(cmd))) { - goto usage; - } - - if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) { - goto usage; - } - - local_stream_name = argv[0]; - - switch_mutex_lock(globals.mutex); - source = switch_core_hash_find(globals.source_hash, local_stream_name); - switch_mutex_unlock(globals.mutex); - - if (source) { - source->hup = 1; - stream->write_function(stream, "+OK hup stream: %s", source->name); - goto done; - } - + goto done; - - usage: - stream->write_function(stream, "-USAGE: %s\n", START_LOCAL_STREAM_SYNTAX); - + + usage: + stream->write_function(stream, "-USAGE: %s\n", LOCAL_STREAM_SYNTAX); + done: switch_safe_free(mycmd); @@ -1337,15 +1261,16 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind event handler!\n"); } - - - SWITCH_ADD_API(commands_api_interface, "hup_local_stream", "Skip to next file in local_stream", hup_local_stream_function, RELOAD_LOCAL_STREAM_SYNTAX); - SWITCH_ADD_API(commands_api_interface, "reload_local_stream", "Reloads a local_stream", reload_local_stream_function, RELOAD_LOCAL_STREAM_SYNTAX); - SWITCH_ADD_API(commands_api_interface, "stop_local_stream", "Stops and unloads a local_stream", stop_local_stream_function, STOP_LOCAL_STREAM_SYNTAX); - SWITCH_ADD_API(commands_api_interface, "start_local_stream", "Starts a new local_stream", start_local_stream_function, START_LOCAL_STREAM_SYNTAX); - SWITCH_ADD_API(commands_api_interface, "show_local_stream", "Shows a local stream", show_local_stream_function, SHOW_LOCAL_STREAM_SYNTAX); - + SWITCH_ADD_API(commands_api_interface, "local_stream", "manage local streams", local_stream_function, LOCAL_STREAM_SYNTAX); + // switch_console_set_complete("add sofia profile ::sofia::list_profiles start"); + switch_console_set_complete("add local_stream show ::console::list_streams as xml"); + switch_console_set_complete("add local_stream start"); + switch_console_set_complete("add local_stream reload ::console::list_streams"); + switch_console_set_complete("add local_stream stop ::console::list_streams"); + switch_console_set_complete("add local_stream hup ::console::list_streams"); + switch_console_add_complete_func("::console::list_streams", list_streams); /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; } diff --git a/src/switch.c b/src/switch.c index bd8c73ae91..942b5cd763 100644 --- a/src/switch.c +++ b/src/switch.c @@ -1089,6 +1089,10 @@ int main(int argc, char *argv[]) reincarnate_protect(reincarnate_reexec ? argv : NULL); #endif + if (switch_core_set_process_privileges() < 0) { + return 255; + } + switch (priority) { case 2: set_realtime_priority(); diff --git a/src/switch_core.c b/src/switch_core.c index e0c800e2bf..7e79a92f42 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -53,7 +53,9 @@ #ifdef HAVE_SYS_PRCTL_H #include #endif - +#ifdef SOLARIS_PRIVILEGES +#include +#endif SWITCH_DECLARE_DATA switch_directories SWITCH_GLOBAL_dirs = { 0 }; SWITCH_DECLARE_DATA switch_filenames SWITCH_GLOBAL_filenames = { 0 }; @@ -888,20 +890,54 @@ SWITCH_DECLARE(void) switch_core_set_globals(void) } +SWITCH_DECLARE(int32_t) switch_core_set_process_privileges(void) +{ +#ifdef SOLARIS_PRIVILEGES + priv_set_t *basicset; + + /* make the process privilege-aware */ + setpflags(PRIV_AWARE, 1); + + /* reset the privileges to basic */ + basicset = priv_str_to_set("basic", ",", NULL); + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, basicset) != 0) { + fprintf(stderr, "ERROR: Failed to acquire basic privileges (%s)\n", strerror(errno)); + } + + /* we need high-resolution clock, and this requires a non-basic privilege */ + if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_CLOCK_HIGHRES, NULL) < 0) { + fprintf(stderr, "ERROR: Failed to acquire proc_clock_highres privilege (%s)\n", strerror(errno)); + return -1; + } + + /* need this for setrlimit */ + if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_RESOURCE, NULL) < 0) { + fprintf(stderr, "ERROR: Failed to acquire sys_resource privilege (%s)\n", strerror(errno)); + return -1; + } + + /* we need to read directories belonging to other uid */ + if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_SEARCH, NULL) < 0) { + fprintf(stderr, "ERROR: Failed to acquire file_dac_search privilege (%s)\n", strerror(errno)); + return -1; + } +#endif + return 0; +} + SWITCH_DECLARE(int32_t) set_low_priority(void) { - - #ifdef WIN32 SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); #else -#ifdef USE_SCHED_SETSCHEDULER +#if defined(USE_SCHED_SETSCHEDULER) && ! defined(SOLARIS_PRIVILEGES) /* * Try to use a normal scheduler */ struct sched_param sched = { 0 }; sched.sched_priority = 0; - if (sched_setscheduler(0, SCHED_OTHER, &sched)) { + if (sched_setscheduler(0, SCHED_OTHER, &sched) < 0) { + fprintf(stderr, "ERROR: Failed to set SCHED_OTHER scheduler (%s)\n", strerror(errno)); return -1; } #endif @@ -911,12 +947,12 @@ SWITCH_DECLARE(int32_t) set_low_priority(void) * setpriority() works on FreeBSD (6.2), nice() doesn't */ if (setpriority(PRIO_PROCESS, getpid(), 19) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n"); + fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #else if (nice(19) != 19) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n"); + fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #endif @@ -937,32 +973,60 @@ SWITCH_DECLARE(int32_t) set_realtime_priority(void) */ struct sched_param sched = { 0 }; sched.sched_priority = SWITCH_PRI_LOW; - if (sched_setscheduler(0, SCHED_FIFO, &sched)) { +#endif + +#ifdef SOLARIS_PRIVILEGES + /* request the privileges to elevate the priority */ + if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_PRIOCNTL, NULL) < 0) { + fprintf(stderr, "WARN: Failed to acquire proc_priocntl privilege (%s)\n", strerror(errno)); + } else { + if (sched_setscheduler(0, SCHED_FIFO, &sched) < 0) { + fprintf(stderr, "ERROR: Failed to set SCHED_FIFO scheduler (%s)\n", strerror(errno)); + } else { + return 0; + } + } + + if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_PRIOUP, NULL) < 0) { + fprintf(stderr, "ERROR: Failed to acquire proc_prioup privilege (%s)\n", strerror(errno)); + return -1; + } else { + if (setpriority(PRIO_PROCESS, 0, -10) < 0) { + fprintf(stderr, "ERROR: Could not set nice level\n"); + return -1; + } + } + return 0; +#else + +#ifdef USE_SCHED_SETSCHEDULER + if (sched_setscheduler(0, SCHED_FIFO, &sched) < 0) { + fprintf(stderr, "ERROR: Failed to set SCHED_FIFO scheduler (%s)\n", strerror(errno)); sched.sched_priority = 0; - if (sched_setscheduler(0, SCHED_OTHER, &sched)) { + if (sched_setscheduler(0, SCHED_OTHER, &sched) < 0 ) { + fprintf(stderr, "ERROR: Failed to set SCHED_OTHER scheduler (%s)\n", strerror(errno)); return -1; } } #endif - - #ifdef HAVE_SETPRIORITY /* * setpriority() works on FreeBSD (6.2), nice() doesn't */ if (setpriority(PRIO_PROCESS, getpid(), -10) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n"); + fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #else if (nice(-10) != -10) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n"); + fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #endif #endif return 0; +#endif } SWITCH_DECLARE(uint32_t) switch_core_cpu_count(void) @@ -1006,7 +1070,7 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) */ runas_pw = getpwnam(user); if (!runas_pw) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unknown user \"%s\"\n", user); + fprintf(stderr, "ERROR: Unknown user \"%s\"\n", user); return -1; } runas_uid = runas_pw->pw_uid; @@ -1020,7 +1084,7 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) */ gr = getgrnam(group); if (!gr) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unknown group \"%s\"\n", group); + fprintf(stderr, "ERROR: Unknown group \"%s\"\n", group); return -1; } runas_gid = gr->gr_gid; @@ -1032,6 +1096,13 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) } if (runas_uid) { +#ifdef SOLARIS_PRIVILEGES + /* request the privilege to set the UID */ + if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_SETID, NULL) < 0) { + fprintf(stderr, "ERROR: Failed to acquire proc_setid privilege (%s)\n", strerror(errno)); + return -1; + } +#endif #ifdef HAVE_SETGROUPS /* * Drop all group memberships prior to changing anything @@ -1039,7 +1110,7 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) * (which is not what we want...) */ if (setgroups(0, NULL) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to drop group access list\n"); + fprintf(stderr, "ERROR: Failed to drop group access list\n"); return -1; } #endif @@ -1049,7 +1120,7 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) * (without loading the user's other groups) */ if (setgid(runas_gid) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change gid!\n"); + fprintf(stderr, "ERROR: Failed to change gid!\n"); return -1; } } else { @@ -1057,7 +1128,7 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) * No group has been passed, use the user's primary group in this case */ if (setgid(runas_pw->pw_gid) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change gid!\n"); + fprintf(stderr, "ERROR: Failed to change gid!\n"); return -1; } #ifdef HAVE_INITGROUPS @@ -1066,7 +1137,7 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) * (This can be really useful for fine-grained access control) */ if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to set group access list for user\n"); + fprintf(stderr, "ERROR: Failed to set group access list for user\n"); return -1; } #endif @@ -1076,12 +1147,12 @@ SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) * Finally drop all privileges by switching to the new userid */ if (setuid(runas_uid) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change uid!\n"); + fprintf(stderr, "ERROR: Failed to change uid!\n"); return -1; } #ifdef HAVE_SYS_PRCTL_H if (prctl(PR_SET_DUMPABLE, 1) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to enable core dumps!\n"); + fprintf(stderr, "ERROR: Failed to enable core dumps!\n"); return -1; } #endif diff --git a/src/switch_core_file.c b/src/switch_core_file.c index 69433dbdb2..c6d92ded06 100644 --- a/src/switch_core_file.c +++ b/src/switch_core_file.c @@ -85,6 +85,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file, fh->mm.keyint = 60; fh->mm.ab = 128; fh->mm.vencspd = SWITCH_VIDEO_ENCODE_SPEED_DEFAULT; + fh->mm.vprofile = SWITCH_VIDEO_PROFILE_BASELINE; if (*file_path == '{') { char *timeout; @@ -199,6 +200,18 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file, switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid video encode speed: %s\n", val); } } + + if ((val = switch_event_get_header(fh->params, "vprofile"))) { + if (!strcasecmp(val, "baseline")) { + fh->mm.vprofile = SWITCH_VIDEO_PROFILE_BASELINE; + } else if (!strcasecmp(val, "main")) { + fh->mm.vprofile = SWITCH_VIDEO_PROFILE_MAIN; + } else if (!strcasecmp(val, "high")) { + fh->mm.vprofile = SWITCH_VIDEO_PROFILE_HIGH; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid video profile: %s\n", val); + } + } } if (switch_directory_exists(file_path, fh->memory_pool) == SWITCH_STATUS_SUCCESS) { diff --git a/src/switch_core_media.c b/src/switch_core_media.c index c0163f07e6..d2846fa637 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -1429,7 +1429,7 @@ SWITCH_DECLARE(void) switch_core_session_check_outgoing_crypto(switch_core_sessi switch_media_handle_t *smh; int i; - if (!switch_core_session_media_handle_ready(session) == SWITCH_STATUS_SUCCESS) { + if (switch_core_session_media_handle_ready(session) != SWITCH_STATUS_SUCCESS) { return; } @@ -1748,7 +1748,7 @@ SWITCH_DECLARE(switch_core_media_params_t *) switch_core_media_get_mparams(switc SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *session, switch_bool_t force) { const char *abs, *codec_string = NULL; - const char *ocodec = NULL; + const char *ocodec = NULL, *val; switch_media_handle_t *smh; switch_assert(session); @@ -1778,6 +1778,14 @@ SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *ses goto ready; } + val = switch_channel_get_variable_dup(session->channel, "media_mix_inbound_outbound_codecs", SWITCH_FALSE, -1); + if (!val || !switch_true(val)) { + if ((ocodec = switch_channel_get_variable(session->channel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) { + codec_string = ocodec; + goto ready; + } + } + if (!(codec_string = switch_channel_get_variable(session->channel, "codec_string"))) { codec_string = switch_core_media_get_codec_string(smh->session); } @@ -1787,7 +1795,7 @@ SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *ses goto ready; } - if ((ocodec = switch_channel_get_variable(session->channel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) { + if (ocodec) { if (!codec_string || (smh->media_flags[SCMF_DISABLE_TRANSCODING])) { codec_string = ocodec; } else { @@ -4053,6 +4061,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s if (!best_te || map->rm_rate == a_engine->cur_payload_map->rm_rate) { best_te = (switch_payload_t) map->rm_pt; best_te_rate = map->rm_rate; + smh->mparams->recv_te = best_te; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set telephone-event payload to %u@%ld\n", best_te, best_te_rate); } continue; @@ -4396,6 +4405,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s if (!best_te || map->rm_rate == a_engine->cur_payload_map->adv_rm_rate) { best_te = (switch_payload_t) map->rm_pt; best_te_rate = map->rm_rate; + smh->mparams->recv_te = best_te; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set telephone-event payload to %u@%lu\n", best_te, best_te_rate); } continue; @@ -4432,9 +4442,10 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s } if (best_te) { - if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { + if (sdp_type == SDP_TYPE_RESPONSE) { te = smh->mparams->te = (switch_payload_t) best_te; - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send payload to %u\n", best_te); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Set 2833 dtmf send payload to %u\n", + switch_channel_get_name(session->channel), best_te); switch_channel_set_variable(session->channel, "dtmf_type", "rfc2833"); smh->mparams->dtmf_type = DTMF_2833; if (a_engine->rtp_session) { @@ -4444,7 +4455,8 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s } else { te = smh->mparams->recv_te = smh->mparams->te = (switch_payload_t) best_te; smh->mparams->te_rate = best_te_rate; - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send/recv payload to %u\n", te); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Set 2833 dtmf send/recv payload to %u\n", + switch_channel_get_name(session->channel), te); switch_channel_set_variable(session->channel, "dtmf_type", "rfc2833"); smh->mparams->dtmf_type = DTMF_2833; if (a_engine->rtp_session) { @@ -5116,9 +5128,9 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi if (switch_channel_test_flag(channel, CF_VIDEO_READY)) { switch_mutex_lock(mh->file_mutex); if (smh->video_write_fh && switch_channel_ready(session->channel) && switch_test_flag(smh->video_write_fh, SWITCH_FILE_OPEN)) { - switch_status_t wstatus = switch_core_file_read_video(smh->video_write_fh, &fr, SVR_FLUSH); + switch_status_t wstatus = switch_core_file_read_video(smh->video_write_fh, &fr, 0); if (wstatus == SWITCH_STATUS_SUCCESS) { - switch_core_session_write_video_frame(session, &fr, SWITCH_IO_FLAG_NONE, 0); + switch_core_session_write_video_frame(session, &fr, SWITCH_IO_FLAG_NONE, SVR_FLUSH); switch_img_free(&fr.img); } else if (wstatus != SWITCH_STATUS_BREAK && wstatus != SWITCH_STATUS_IGNORE) { smh->video_write_fh = NULL; @@ -6303,13 +6315,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi } if (smh->mparams->te) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send payload to %u\n", smh->mparams->te); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Set 2833 dtmf send payload to %u\n", + switch_channel_get_name(session->channel), smh->mparams->te); switch_rtp_set_telephony_event(a_engine->rtp_session, smh->mparams->te); switch_channel_set_variable_printf(session->channel, "rtp_2833_send_payload", "%d", smh->mparams->te); } if (smh->mparams->recv_te) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf receive payload to %u\n", smh->mparams->recv_te); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Set 2833 dtmf receive payload to %u\n", + switch_channel_get_name(session->channel), smh->mparams->recv_te); switch_rtp_set_telephony_recv_event(a_engine->rtp_session, smh->mparams->recv_te); switch_channel_set_variable_printf(session->channel, "rtp_2833_recv_payload", "%d", smh->mparams->recv_te); } @@ -7925,8 +7939,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess if (ov_fmtp) { pass_fmtp = ov_fmtp; - } else { //if (switch_true(switch_channel_get_variable_dup(session->channel, "rtp_mirror_fmtp", SWITCH_FALSE, -1))) { - // seems to break eyebeam at least... + } else if (switch_true(switch_channel_get_variable_dup(session->channel, "rtp_mirror_fmtp", SWITCH_FALSE, -1))) { pass_fmtp = switch_channel_get_variable(session->channel, "rtp_video_fmtp"); } } @@ -8306,13 +8319,13 @@ SWITCH_DECLARE(void) switch_core_media_set_udptl_image_sdp(switch_core_session_t uint32_t port; const char *family = "IP4"; const char *username; - const char *bit_removal_on = "a=T38FaxFillBitRemoval\n"; + const char *bit_removal_on = "a=T38FaxFillBitRemoval\r\n"; const char *bit_removal_off = ""; - const char *mmr_on = "a=T38FaxTranscodingMMR\n"; + const char *mmr_on = "a=T38FaxTranscodingMMR\r\n"; const char *mmr_off = ""; - const char *jbig_on = "a=T38FaxTranscodingJBIG\n"; + const char *jbig_on = "a=T38FaxTranscodingJBIG\r\n"; const char *jbig_off = ""; const char *var; int broken_boolean; @@ -8375,46 +8388,46 @@ SWITCH_DECLARE(void) switch_core_media_set_udptl_image_sdp(switch_core_session_t switch_snprintf(buf, sizeof(buf), - "v=0\n" - "o=%s %010u %010u IN %s %s\n" - "s=%s\n" "c=IN %s %s\n" "t=0 0\n", username, smh->owner_id, smh->session_id, family, ip, username, family, ip); + "v=0\r\n" + "o=%s %010u %010u IN %s %s\r\n" + "s=%s\r\n" "c=IN %s %s\r\n" "t=0 0\r\n", username, smh->owner_id, smh->session_id, family, ip, username, family, ip); if (t38_options->T38FaxMaxBuffer) { - switch_snprintf(max_buf, sizeof(max_buf), "a=T38FaxMaxBuffer:%d\n", t38_options->T38FaxMaxBuffer); + switch_snprintf(max_buf, sizeof(max_buf), "a=T38FaxMaxBuffer:%d\r\n", t38_options->T38FaxMaxBuffer); }; if (t38_options->T38FaxMaxDatagram) { - switch_snprintf(max_data, sizeof(max_data), "a=T38FaxMaxDatagram:%d\n", t38_options->T38FaxMaxDatagram); + switch_snprintf(max_data, sizeof(max_data), "a=T38FaxMaxDatagram:%d\r\n", t38_options->T38FaxMaxDatagram); }; if (broken_boolean) { - bit_removal_on = "a=T38FaxFillBitRemoval:1\n"; - bit_removal_off = "a=T38FaxFillBitRemoval:0\n"; + bit_removal_on = "a=T38FaxFillBitRemoval:1\r\n"; + bit_removal_off = "a=T38FaxFillBitRemoval:0\r\n"; - mmr_on = "a=T38FaxTranscodingMMR:1\n"; - mmr_off = "a=T38FaxTranscodingMMR:0\n"; + mmr_on = "a=T38FaxTranscodingMMR:1\r\n"; + mmr_off = "a=T38FaxTranscodingMMR:0\r\n"; - jbig_on = "a=T38FaxTranscodingJBIG:1\n"; - jbig_off = "a=T38FaxTranscodingJBIG:0\n"; + jbig_on = "a=T38FaxTranscodingJBIG:1\r\n"; + jbig_off = "a=T38FaxTranscodingJBIG:0\r\n"; } switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "m=image %d udptl t38\n" - "a=T38FaxVersion:%d\n" - "a=T38MaxBitRate:%d\n" + "m=image %d udptl t38\r\n" + "a=T38FaxVersion:%d\r\n" + "a=T38MaxBitRate:%d\r\n" "%s" "%s" "%s" - "a=T38FaxRateManagement:%s\n" + "a=T38FaxRateManagement:%s\r\n" "%s" "%s" - "a=T38FaxUdpEC:%s\n", - //"a=T38VendorInfo:%s\n", + "a=T38FaxUdpEC:%s\r\n", + //"a=T38VendorInfo:%s\r\n", port, t38_options->T38FaxVersion, t38_options->T38MaxBitRate, @@ -8431,7 +8444,7 @@ SWITCH_DECLARE(void) switch_core_media_set_udptl_image_sdp(switch_core_session_t if (insist) { - switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "m=audio 0 RTP/AVP 19\n"); + switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "m=audio 0 RTP/AVP 19\r\n"); } switch_core_media_set_local_sdp(session, buf, SWITCH_TRUE); @@ -8943,11 +8956,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_se break; case SWITCH_MESSAGE_INDICATE_HARD_MUTE: - { - if (session->bugs) { + if (a_engine->rtp_session) { + if (session->bugs && msg->numeric_arg) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s has a media bug, hard mute not allowed.\n", switch_channel_get_name(session->channel)); - } else if (a_engine->rtp_session) { + } else { if (msg->numeric_arg) { switch_rtp_set_flag(a_engine->rtp_session, SWITCH_RTP_FLAG_MUTE); } else { diff --git a/src/switch_core_media_bug.c b/src/switch_core_media_bug.c index 681bd4d173..3a02f2f441 100644 --- a/src/switch_core_media_bug.c +++ b/src/switch_core_media_bug.c @@ -854,6 +854,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t switch_event_fire(&event); } + switch_core_media_hard_mute(session, SWITCH_FALSE); + return SWITCH_STATUS_SUCCESS; } diff --git a/src/switch_core_video.c b/src/switch_core_video.c index eaa6c9fe74..4ea9669d6c 100644 --- a/src/switch_core_video.c +++ b/src/switch_core_video.c @@ -48,7 +48,9 @@ #include #endif +#ifdef SWITCH_HAVE_YUV static inline void switch_img_get_yuv_pixel(switch_image_t *img, switch_yuv_color_t *yuv, int x, int y); +#endif static inline void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_color_t *rgb, int x, int y); @@ -58,14 +60,18 @@ static inline void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_colo * \param[in] rgb RGB color pointer * \param[out] yuv YUV color pointer */ +#ifdef SWITCH_HAVE_YUV static inline void switch_color_rgb2yuv(switch_rgb_color_t *rgb, switch_yuv_color_t *yuv); +#endif /*!\brief Convert YUV color to RGB * * \param[in] yuv YUV color pointer * \param[out] rgb RGB color pointer */ +#ifdef SWITCH_HAVE_YUV static inline void switch_color_yuv2rgb(switch_yuv_color_t *yuv, switch_rgb_color_t *rgb); +#endif /*!\brief Draw a pixel on an image * @@ -542,17 +548,17 @@ SWITCH_DECLARE(void) switch_img_fill(switch_image_t *img, int x, int y, int w, i #endif } +#ifdef SWITCH_HAVE_YUV static inline void switch_img_get_yuv_pixel(switch_image_t *img, switch_yuv_color_t *yuv, int x, int y) { -#ifdef SWITCH_HAVE_YUV // switch_assert(img->fmt == SWITCH_IMG_FMT_I420); if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return; yuv->y = *(img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * y + x); yuv->u = *(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * y / 2 + x / 2); yuv->v = *(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * y / 2 + x / 2); -#endif } +#endif static inline void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_color_t *rgb, int x, int y) { @@ -699,20 +705,20 @@ SWITCH_DECLARE(void) switch_color_set_rgb(switch_rgb_color_t *color, const char } } +#ifdef SWITCH_HAVE_YUV static inline void switch_color_rgb2yuv(switch_rgb_color_t *rgb, switch_yuv_color_t *yuv) { -#ifdef SWITCH_HAVE_YUV yuv->y = (uint8_t)(((rgb->r * 4897) >> 14) + ((rgb->g * 9611) >> 14) + ((rgb->b * 1876) >> 14)); yuv->u = (uint8_t)(- ((rgb->r * 2766) >> 14) - ((5426 * rgb->g) >> 14) + rgb->b / 2 + 128); yuv->v = (uint8_t)(rgb->r / 2 -((6855 * rgb->g) >> 14) - ((rgb->b * 1337) >> 14) + 128); -#endif } +#endif #define CLAMP(val) MAX(0, MIN(val, 255)) +#ifdef SWITCH_HAVE_YUV static inline void switch_color_yuv2rgb(switch_yuv_color_t *yuv, switch_rgb_color_t *rgb) { -#ifdef SWITCH_HAVE_YUV #if 0 int C = yuv->y - 16; int D = yuv->u - 128; @@ -727,8 +733,8 @@ static inline void switch_color_yuv2rgb(switch_yuv_color_t *yuv, switch_rgb_colo rgb->r = CLAMP( yuv->y + ((22457 * (yuv->v-128)) >> 14)); rgb->g = CLAMP((yuv->y - ((715 * (yuv->v-128)) >> 10) - ((5532 * (yuv->u-128)) >> 14))); rgb->b = CLAMP((yuv->y + ((28384 * (yuv->u-128)) >> 14))); -#endif } +#endif SWITCH_DECLARE(void) switch_color_set_yuv(switch_yuv_color_t *color, const char *str) { diff --git a/src/switch_ivr.c b/src/switch_ivr.c index f1995c52ee..766aa0d8ad 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -159,7 +159,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, if (!switch_channel_media_ready(channel)) { - for (elapsed=0; switch_channel_up(channel) && elapsed<(ms/20); elapsed++) { + for (elapsed=0; switch_channel_ready(channel) && elapsed<(ms/20); elapsed++) { if (switch_channel_test_flag(channel, CF_BREAK)) { switch_channel_clear_flag(channel, CF_BREAK); switch_goto_status(SWITCH_STATUS_BREAK, end); @@ -820,14 +820,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_messages(switch_core_sessio } -static switch_status_t switch_ivr_parse_signal_data(switch_core_session_t *session, switch_bool_t all) +SWITCH_DECLARE(switch_status_t) switch_ivr_parse_signal_data(switch_core_session_t *session, switch_bool_t all, switch_bool_t only_session_thread) { void *data; switch_core_session_message_t msg = { 0 }; int i = 0; switch_channel_t *channel = switch_core_session_get_channel(session); - if (!switch_core_session_in_thread(session)) { + if (only_session_thread && !switch_core_session_in_thread(session)) { return SWITCH_STATUS_FALSE; } @@ -857,11 +857,11 @@ static switch_status_t switch_ivr_parse_signal_data(switch_core_session_t *sessi } SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_signal_data(switch_core_session_t *session) { - return switch_ivr_parse_signal_data(session, SWITCH_TRUE); + return switch_ivr_parse_signal_data(session, SWITCH_TRUE, SWITCH_FALSE); } SWITCH_DECLARE(switch_status_t) switch_ivr_parse_next_signal_data(switch_core_session_t *session) { - return switch_ivr_parse_signal_data(session, SWITCH_FALSE); + return switch_ivr_parse_signal_data(session, SWITCH_FALSE, SWITCH_FALSE); } SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_events(switch_core_session_t *session) diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 9f5b472b19..d55b8ac29b 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -1113,11 +1113,14 @@ static void *SWITCH_THREAD_FUNC recording_thread(switch_thread_t *thread, void * switch_size_t bsize = SWITCH_RECOMMENDED_BUFFER_SIZE, samples = 0, inuse = 0; unsigned char *data; int channels = 1; + switch_codec_implementation_t read_impl = { 0 }; if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) { return NULL; } + switch_core_session_get_read_impl(session, &read_impl); + bsize = read_impl.decoded_bytes_per_packet; rh = switch_core_media_bug_get_user_data(bug); switch_buffer_create_dynamic(&rh->thread_buffer, 1024 * 512, 1024 * 64, 0); rh->thread_ready = 1; diff --git a/src/switch_jitterbuffer.c b/src/switch_jitterbuffer.c index cf6bf6dd69..530db657e3 100644 --- a/src/switch_jitterbuffer.c +++ b/src/switch_jitterbuffer.c @@ -65,6 +65,7 @@ struct switch_jb_s { uint32_t target_ts; uint32_t last_target_ts; uint16_t psuedo_seq; + uint16_t last_psuedo_seq; uint32_t visible_nodes; uint32_t complete_frames; uint32_t frame_len; @@ -581,6 +582,8 @@ static inline void increment_ts(switch_jb_t *jb) { if (!jb->target_ts) return; + jb->last_psuedo_seq = jb->psuedo_seq; + jb->last_target_ts = jb->target_ts; jb->target_ts = htonl((ntohl(jb->target_ts) + jb->samples_per_frame)); jb->psuedo_seq++; } @@ -589,6 +592,7 @@ static inline void set_read_ts(switch_jb_t *jb, uint32_t ts) { if (!ts) return; + jb->last_psuedo_seq = jb->psuedo_seq; jb->last_target_ts = ts; jb->target_ts = htonl((ntohl(jb->last_target_ts) + jb->samples_per_frame)); jb->psuedo_seq++; @@ -597,6 +601,7 @@ static inline void set_read_ts(switch_jb_t *jb, uint32_t ts) static inline void increment_seq(switch_jb_t *jb) { + jb->last_target_seq = jb->target_seq; jb->target_seq = htons((ntohs(jb->target_seq) + 1)); } @@ -828,13 +833,12 @@ SWITCH_DECLARE(void) switch_jb_reset(switch_jb_t *jb) SWITCH_DECLARE(switch_status_t) switch_jb_peek_frame(switch_jb_t *jb, uint32_t ts, uint16_t seq, int peek, switch_frame_t *frame) { switch_jb_node_t *node = NULL; - if (seq) { uint16_t want_seq = seq + peek; - node = switch_core_inthash_find(jb->node_hash, want_seq); + node = switch_core_inthash_find(jb->node_hash, htons(want_seq)); } else if (ts && jb->samples_per_frame) { uint32_t want_ts = ts + (peek * jb->samples_per_frame); - node = switch_core_inthash_find(jb->node_hash_ts, want_ts); + node = switch_core_inthash_find(jb->node_hash_ts, htonl(want_ts)); } if (node) { @@ -842,6 +846,7 @@ SWITCH_DECLARE(switch_status_t) switch_jb_peek_frame(switch_jb_t *jb, uint32_t t frame->timestamp = ntohl(node->packet.header.ts); frame->m = node->packet.header.m; frame->datalen = node->len; + if (frame->data && frame->buflen > node->len) { memcpy(frame->data, node->packet.body, node->len); } @@ -1129,9 +1134,14 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp { switch_jb_node_t *node = NULL; switch_status_t status; - + int plc = 0; + switch_mutex_lock(jb->mutex); + if (jb->complete_frames == 0) { + switch_goto_status(SWITCH_STATUS_BREAK, end); + } + if (jb->complete_frames < jb->frame_len) { jb_debug(jb, 2, "BUFFERING %u/%u\n", jb->complete_frames , jb->frame_len); switch_goto_status(SWITCH_STATUS_MORE_DATA, end); @@ -1230,6 +1240,7 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp switch_goto_status(SWITCH_STATUS_RESTART, end); } else { jb_debug(jb, 2, "%s", "Frame not found suggest PLC\n"); + plc = 1; switch_goto_status(SWITCH_STATUS_NOTFOUND, end); } } @@ -1253,6 +1264,21 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp end: + if (plc) { + uint16_t seq; + uint32_t ts = 0; + + if (jb->samples_per_frame) { + seq = htons(jb->last_psuedo_seq); + ts = jb->last_target_ts; + } else { + seq = jb->last_target_seq; + } + + packet->header.seq = seq; + packet->header.ts = ts; + } + switch_mutex_unlock(jb->mutex); if (status == SWITCH_STATUS_SUCCESS && jb->type == SJB_AUDIO) { diff --git a/src/switch_rtp.c b/src/switch_rtp.c index bc3ca09fc9..ea5fc24504 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -71,6 +71,7 @@ #define WARN_SRTP_ERRS 10 #define MAX_SRTP_ERRS 100 #define NTP_TIME_OFFSET 2208988800UL +#define ZRTP_MAGIC_COOKIE 0x5a525450 static const switch_payload_t INVALID_PT = 255; #define DTMF_SANITY (rtp_session->one_second * 30) @@ -80,7 +81,7 @@ static const switch_payload_t INVALID_PT = 255; static switch_port_t START_PORT = RTP_START_PORT; static switch_port_t END_PORT = RTP_END_PORT; static switch_mutex_t *port_lock = NULL; -static void do_flush(switch_rtp_t *rtp_session, int force); +static switch_size_t do_flush(switch_rtp_t *rtp_session, int force, switch_size_t bytes_in); typedef srtp_hdr_t rtp_hdr_t; @@ -434,6 +435,12 @@ struct switch_rtp { switch_core_session_t *session; payload_map_t **pmaps; payload_map_t *pmap_tail; + int ice_adj; + uint8_t has_rtp; + uint8_t has_rtcp; + uint8_t has_ice; + uint8_t punts; + uint8_t clean; #ifdef ENABLE_ZRTP zrtp_session_t *zrtp_session; zrtp_profile_t *zrtp_profile; @@ -552,7 +559,7 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ */ if (bytes > rtp_header_len && !rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && - rtp_session->recv_msg.header.pt == rtp_session->recv_te) { + rtp_session->last_rtp_hdr.pt == rtp_session->recv_te) { switch_size_t len = bytes - rtp_header_len; unsigned char *packet = (unsigned char *) RTP_BODY(rtp_session); int end; @@ -580,8 +587,8 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ end = packet[1] & 0x80 ? 1 : 0; duration = (packet[2] << 8) + packet[3]; key = switch_rfc2833_to_char(packet[0]); - in_digit_seq = ntohs((uint16_t) rtp_session->recv_msg.header.seq); - ts = htonl(rtp_session->recv_msg.header.ts); + in_digit_seq = ntohs((uint16_t) rtp_session->last_rtp_hdr.seq); + ts = htonl(rtp_session->last_rtp_hdr.ts); if (rtp_session->flags[SWITCH_RTP_FLAG_PASS_RFC2833]) { @@ -610,7 +617,7 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "read: %c %u %u %u %u %d %d %s\n", key, in_digit_seq, rtp_session->dtmf_data.in_digit_seq, - ts, duration, rtp_session->recv_msg.header.m, end, end && !rtp_session->dtmf_data.in_digit_ts ? "ignored" : ""); + ts, duration, rtp_session->last_rtp_hdr.m, end, end && !rtp_session->dtmf_data.in_digit_ts ? "ignored" : ""); #endif if (!rtp_session->dtmf_data.in_digit_queued && rtp_session->dtmf_data.in_digit_ts) { @@ -702,7 +709,7 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ } else { #ifdef DEBUG_2833 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "drop: %c %u %u %u %u %d %d\n", - key, in_digit_seq, rtp_session->dtmf_data.in_digit_seq, ts, duration, rtp_session->recv_msg.header.m, end); + key, in_digit_seq, rtp_session->dtmf_data.in_digit_seq, ts, duration, rtp_session->last_rtp_hdr.m, end); #endif switch_cond_next(); return RESULT_GOTO_RECVFROM; @@ -714,13 +721,13 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ return RESULT_GOTO_END; } - if (!rtp_session->dtmf_data.in_interleaved && rtp_session->recv_msg.header.pt != rtp_session->recv_te) { + if (!rtp_session->dtmf_data.in_interleaved && rtp_session->last_rtp_hdr.pt != rtp_session->recv_te) { /* Drat, they are sending audio still as well as DTMF ok fine..... *sigh* */ rtp_session->dtmf_data.in_interleaved = 1; } if (rtp_session->dtmf_data.in_interleaved || (rtp_session->rtp_bugs & RTP_BUG_IGNORE_DTMF_DURATION)) { - if (rtp_session->recv_msg.header.pt == rtp_session->recv_te) { + if (rtp_session->last_rtp_hdr.pt == rtp_session->recv_te) { return RESULT_GOTO_RECVFROM; } } else { @@ -1184,9 +1191,12 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d ice->last_ok = now; rtp_session->wrong_addrs = 0; } else { - if ((rtp_session->dtls->state != DS_READY || !ice->ready || !ice->rready)) { + if (((rtp_session->dtls && rtp_session->dtls->state != DS_READY) || !ice->ready || !ice->rready) && + rtp_session->wrong_addrs > 2 && rtp_session->ice_adj == 0) { do_adj++; - } else if (rtp_session->wrong_addrs > 5 || elapsed >= 3000) { + rtp_session->ice_adj = 1; + rtp_session->wrong_addrs = 0; + } else if (rtp_session->wrong_addrs > 10 || elapsed >= 10000) { do_adj++; } @@ -2715,7 +2725,8 @@ SWITCH_DECLARE(void) switch_rtp_reset(switch_rtp_t *rtp_session) rtp_session->wrong_addrs = 0; rtp_session->rtcp_sent_packets = 0; rtp_session->rtcp_last_sent = 0; - + rtp_session->ice_adj = 0; + //switch_rtp_del_dtls(rtp_session, DTLS_TYPE_RTP|DTLS_TYPE_RTCP); switch_rtp_set_flag(rtp_session, SWITCH_RTP_FLAG_PAUSE); switch_rtp_set_flag(rtp_session, SWITCH_RTP_FLAG_MUTE); @@ -3994,7 +4005,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_video_buffer_size(switch_rtp_t *r } if (!max_frames) { - max_frames = 30; + max_frames = 50; } if (!rtp_session->vb) { @@ -4052,18 +4063,22 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t * max_queue_frames = queue_frames * 3; } - READ_INC(rtp_session); + if (rtp_session->jb) { status = switch_jb_set_frames(rtp_session->jb, queue_frames, max_queue_frames); } else { + READ_INC(rtp_session); status = switch_jb_create(&rtp_session->jb, SJB_AUDIO, queue_frames, max_queue_frames, rtp_session->pool); switch_jb_set_session(rtp_session->jb, rtp_session->session); - switch_jb_ts_mode(rtp_session->jb, samples_per_packet, samples_per_second); + if (switch_true(switch_channel_get_variable_dup(switch_core_session_get_channel(rtp_session->session), "jb_use_timestamps", SWITCH_FALSE, -1))) { + switch_jb_ts_mode(rtp_session->jb, samples_per_packet, samples_per_second); + } //switch_jb_debug_level(rtp_session->jb, 10); + READ_DEC(rtp_session); } - READ_DEC(rtp_session); + return status; } @@ -4844,14 +4859,15 @@ static int jb_valid(switch_rtp_t *rtp_session) } -static void do_flush(switch_rtp_t *rtp_session, int force) +static switch_size_t do_flush(switch_rtp_t *rtp_session, int force, switch_size_t bytes_in) { int was_blocking = 0; switch_size_t bytes; uint32_t flushed = 0; - + switch_size_t bytes_out = 0; + if (!switch_rtp_ready(rtp_session)) { - return; + return 0; } reset_jitter_seq(rtp_session); @@ -4861,7 +4877,7 @@ static void do_flush(switch_rtp_t *rtp_session, int force) rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] || rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] ) { - return; + return 0; } } @@ -4870,16 +4886,20 @@ static void do_flush(switch_rtp_t *rtp_session, int force) if (switch_rtp_ready(rtp_session) ) { if (rtp_session->jb && !rtp_session->pause_jb && jb_valid(rtp_session)) { - switch_jb_reset(rtp_session->jb); + //switch_jb_reset(rtp_session->jb); + bytes_out = bytes_in; + goto end; } - //if (rtp_session->vb) { - // switch_jb_reset(rtp_session->vb); - //} - if (rtp_session->vbw) { switch_jb_reset(rtp_session->vbw); } + + if (rtp_session->vb) { + //switch_jb_reset(rtp_session->vb); + bytes_out = bytes_in; + goto end; + } if (rtp_session->flags[SWITCH_RTP_FLAG_DEBUG_RTP_READ]) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), @@ -4903,7 +4923,7 @@ static void do_flush(switch_rtp_t *rtp_session, int force) int do_cng = 0; /* Make sure to handle RFC2833 packets, even if we're flushing the packets */ - if (bytes > rtp_header_len && rtp_session->recv_msg.header.pt == rtp_session->recv_te) { + if (bytes > rtp_header_len && rtp_session->last_rtp_hdr.pt == rtp_session->recv_te) { handle_rfc2833(rtp_session, bytes, &do_cng); #ifdef DEBUG_2833 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "*** RTP packet handled in flush loop %d ***\n", do_cng); @@ -4933,8 +4953,12 @@ static void do_flush(switch_rtp_t *rtp_session, int force) switch_core_session_request_video_refresh(rtp_session->session); } } + + end: READ_DEC(rtp_session); + + return bytes_out; } static int check_recv_payload(switch_rtp_t *rtp_session) @@ -4952,7 +4976,7 @@ static int check_recv_payload(switch_rtp_t *rtp_session) continue; } - if (rtp_session->recv_msg.header.pt == pmap->pt) { + if (rtp_session->last_rtp_hdr.pt == pmap->pt) { ok = 1; } } @@ -4962,6 +4986,27 @@ static int check_recv_payload(switch_rtp_t *rtp_session) return ok; } +static int get_recv_payload(switch_rtp_t *rtp_session) +{ + int r = -1; + + if (rtp_session->pmaps && *rtp_session->pmaps) { + payload_map_t *pmap; + + switch_mutex_lock(rtp_session->flag_mutex); + + for (pmap = *rtp_session->pmaps; pmap && pmap->allocated; pmap = pmap->next) { + if (pmap->negotiated) { + r = pmap->pt; + break; + } + } + switch_mutex_unlock(rtp_session->flag_mutex); + } + + return r; +} + #define return_cng_frame() do_cng = 1; goto timer_check static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t *bytes, switch_frame_flag_t *flags, @@ -4973,13 +5018,57 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t int sync = 0; switch_time_t now; switch_size_t xcheck_jitter = 0; - + int tries = 0; + int block = 0; + switch_assert(bytes); more: + tries++; + + if (tries > 20) { + if (rtp_session->jb && !rtp_session->pause_jb && jb_valid(rtp_session)) { + switch_jb_reset(rtp_session->jb); + } + rtp_session->punts++; + rtp_session->clean = 0; + *bytes = 0; + return SWITCH_STATUS_BREAK; + } + + if (block) { + int to = 20000; + int fdr = 0; + + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + to = 100000; + } else { + if (rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] && rtp_session->timer.interval) { + to = rtp_session->timer.interval * 1000; + } + } + + poll_status = switch_poll(rtp_session->read_pollfd, 1, &fdr, to); + + if (rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] && rtp_session->timer.interval) { + switch_core_timer_sync(&rtp_session->timer); + } + + block = 0; + } + *bytes = sizeof(rtp_msg_t); sync = 0; + rtp_session->has_rtp = 0; + rtp_session->has_ice = 0; + rtp_session->has_rtcp = 0; + if (rtp_session->dtls) { + rtp_session->dtls->bytes = 0; + rtp_session->dtls->data = NULL; + } + memset(&rtp_session->last_rtp_hdr, 0, sizeof(rtp_session->last_rtp_hdr)); + if (poll_status == SWITCH_STATUS_SUCCESS) { status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock_input, 0, (void *) &rtp_session->recv_msg, bytes); } else { @@ -4987,15 +5076,58 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } if (*bytes) { - rtp_session->missed_count = 0; - if (rtp_session->recv_msg.header.version == 2) { + b = (unsigned char *) &rtp_session->recv_msg; + + /* version 2 probably rtp, zrtp cookie present means zrtp */ + rtp_session->has_rtp = (rtp_session->recv_msg.header.version == 2 || ntohl(*(int *)(b+4)) == ZRTP_MAGIC_COOKIE); + + if ((*b >= 20) && (*b <= 64)) { + rtp_session->dtls->bytes = *bytes; + rtp_session->dtls->data = (void *) &rtp_session->recv_msg; + rtp_session->has_ice = 0; + rtp_session->has_rtp = 0; + rtp_session->has_rtcp = 0; + } else if (*b == 0 || *b == 1) { + rtp_session->has_ice = 1; + rtp_session->has_rtp = 0; + rtp_session->has_rtcp = 0; + } else { + if (rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) { + switch(rtp_session->recv_msg.header.pt) { + case 64: // 192 Full INTRA-frame request. + case 72: // 200 Sender report. + case 73: // 201 Receiver report. + case 74: // 202 Source description. + case 75: // 203 Goodbye. + case 76: // 204 Application-defined. + case 77: // 205 Transport layer FB message. + case 78: // 206 Payload-specific FB message. + case 79: // 207 Extended report. + rtp_session->has_rtcp = 1; + rtp_session->has_rtp = 0; + rtp_session->has_ice = 0; + break; + default: + if (rtp_session->rtcp_recv_msg_p->header.version == 2 && + rtp_session->rtcp_recv_msg_p->header.type > 199 && rtp_session->rtcp_recv_msg_p->header.type < 208) { + rtp_session->has_rtcp = 1; + rtp_session->has_rtp = 0; + rtp_session->has_ice = 0; + } + break; + } + } + } + + if (rtp_session->has_rtp) { + rtp_session->missed_count = 0; switch_cp_addr(rtp_session->rtp_from_addr, rtp_session->from_addr); rtp_session->last_rtp_hdr = rtp_session->recv_msg.header; } } if (!rtp_session->vb && (!rtp_session->jb || rtp_session->pause_jb || !jb_valid(rtp_session))) { - if (*bytes > rtp_header_len && (rtp_session->recv_msg.header.version == 2 && check_recv_payload(rtp_session))) { + if (*bytes > rtp_header_len && (rtp_session->has_rtp && check_recv_payload(rtp_session))) { xcheck_jitter = *bytes; check_jitter(rtp_session); } @@ -5014,8 +5146,6 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (*bytes) { - b = (unsigned char *) &rtp_session->recv_msg; - *flags &= ~SFF_PROXY_PACKET; //if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { @@ -5023,7 +5153,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t //} - if (*b == 0 || *b == 1) { + if (rtp_session->has_ice) { if (rtp_session->ice.ice_user) { handle_ice(rtp_session, &rtp_session->ice, (void *) &rtp_session->recv_msg, *bytes); } @@ -5040,27 +5170,6 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t do_dtls(rtp_session, rtp_session->rtcp_dtls); } - rtp_session->dtls->bytes = 0; - - if (*bytes) { - char *b = (char *) &rtp_session->recv_msg; - - if ((*b >= 20) && (*b <= 64)) { - rtp_session->dtls->bytes = *bytes; - rtp_session->dtls->data = (void *) &rtp_session->recv_msg; - } else { - rtp_session->dtls->bytes = 0; - rtp_session->dtls->data = NULL; - - if (*b != 0 && *b != 1 && rtp_session->dtls->state != DS_READY) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, - "Drop %s packet %ld bytes (dtls not ready!) b=%u\n", rtp_type(rtp_session), (long)*bytes, *b); - *bytes = 0; - } - - } - } - do_dtls(rtp_session, rtp_session->dtls); if (rtp_session->dtls && rtp_session->dtls->bytes) { @@ -5072,11 +5181,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (status == SWITCH_STATUS_SUCCESS && *bytes) { if (rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) { *flags &= ~SFF_RTCP; - if (!check_recv_payload(rtp_session) && - rtp_session->recv_msg.header.pt != rtp_session->recv_te && - rtp_session->recv_msg.header.pt != rtp_session->cng_pt && - rtp_session->rtcp_recv_msg_p->header.version == 2 && - rtp_session->rtcp_recv_msg_p->header.type > 199 && rtp_session->rtcp_recv_msg_p->header.type < 208) { //rtcp muxed + if (rtp_session->has_rtcp) { *flags |= SFF_RTCP; if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV]) { @@ -5099,7 +5204,11 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } } - if (*bytes && !rtp_write_ready(rtp_session, *bytes, __LINE__)) { + + + if ((*bytes && (!rtp_write_ready(rtp_session, *bytes, __LINE__) || !rtp_session->has_rtp || rtp_session->has_rtcp)) || sync) { + rtp_session->hot_hits = 0; + block = 1; *bytes = 0; goto more; } @@ -5123,8 +5232,8 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t my_host, switch_sockaddr_get_port(rtp_session->local_addr), old_host, rtp_session->remote_port, tx_host, switch_sockaddr_get_port(rtp_session->rtp_from_addr), - rtp_session->recv_msg.header.pt, ntohl(rtp_session->recv_msg.header.ts), ntohs(rtp_session->recv_msg.header.seq), - rtp_session->recv_msg.header.m); + rtp_session->last_rtp_hdr.pt, ntohl(rtp_session->last_rtp_hdr.ts), ntohs(rtp_session->last_rtp_hdr.seq), + rtp_session->last_rtp_hdr.m); } @@ -5133,22 +5242,12 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t int r = (rand() % 10000) + 1; if (r <= 200) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ALERT, - "Simulate dropped packet ......... ts: %u seq: %u\n", ntohl(rtp_session->recv_msg.header.ts), ntohs(rtp_session->recv_msg.header.seq)); + "Simulate dropped packet ......... ts: %u seq: %u\n", ntohl(rtp_session->last_rtp_hdr.ts), ntohs(rtp_session->last_rtp_hdr.seq)); *bytes = 0; } } #endif - - if (sync) { - if (!rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] && rtp_session->timer.interval) { - switch_core_timer_sync(&rtp_session->timer); - reset_jitter_seq(rtp_session); - } - rtp_session->hot_hits = 0; - - goto more; - } udptl: @@ -5158,8 +5257,8 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t now = switch_micro_time_now(); if (*bytes) { - uint16_t seq = ntohs((uint16_t) rtp_session->recv_msg.header.seq); - ts = ntohl(rtp_session->recv_msg.header.ts); + uint16_t seq = ntohs((uint16_t) rtp_session->last_rtp_hdr.seq); + ts = ntohl(rtp_session->last_rtp_hdr.ts); #ifdef DEBUG_MISSED_SEQ if (rtp_session->last_seq && rtp_session->last_seq+1 != seq) { @@ -5213,7 +5312,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } if (!rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && - *bytes && rtp_session->recv_msg.header.pt != rtp_session->recv_te && + *bytes && rtp_session->last_rtp_hdr.pt != rtp_session->recv_te && ts && !rtp_session->jb && !rtp_session->pause_jb && jb_valid(rtp_session) && ts == rtp_session->last_cng_ts) { /* we already sent this frame..... */ *bytes = 0; @@ -5249,11 +5348,11 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t #endif #ifdef ENABLE_SRTP - if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] && rtp_session->recv_msg.header.version == 2 && + if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] && rtp_session->has_rtp && (check_recv_payload(rtp_session) || - rtp_session->recv_msg.header.pt == rtp_session->recv_te || - rtp_session->recv_msg.header.pt == rtp_session->cng_pt)) { - //if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] && (!rtp_session->ice.ice_user || rtp_session->recv_msg.header.version == 2)) { + rtp_session->last_rtp_hdr.pt == rtp_session->recv_te || + rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt)) { + //if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] && (!rtp_session->ice.ice_user || rtp_session->has_rtp)) { int sbytes = (int) *bytes; err_status_t stat = 0; @@ -5319,17 +5418,21 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } - if (rtp_session->recv_msg.header.version == 2) { + if (rtp_session->has_rtp) { + if (rtp_session->recv_msg.header.cc > 0) { /* Contributing Source Identifiers (4 bytes = sizeof CSRC header)*/ + rtp_session->recv_msg.ebody = RTP_BODY(rtp_session) + (rtp_session->recv_msg.header.cc * 4); + } + /* recalculate body length in case rtp extension used */ if (!rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] && - rtp_session->recv_msg.header.version == 2 && rtp_session->recv_msg.header.x) { /* header extensions */ + rtp_session->has_rtp && rtp_session->recv_msg.header.x) { /* header extensions */ uint16_t length; - rtp_session->recv_msg.ext = (switch_rtp_hdr_ext_t *) rtp_session->recv_msg.body; + rtp_session->recv_msg.ext = (switch_rtp_hdr_ext_t *) RTP_BODY(rtp_session); length = ntohs((uint16_t)rtp_session->recv_msg.ext->length); if (length < SWITCH_RTP_MAX_BUF_LEN_WORDS) { - rtp_session->recv_msg.ebody = rtp_session->recv_msg.body + (length * 4) + 4; + rtp_session->recv_msg.ebody = (char *)rtp_session->recv_msg.ext + (length * 4) + 4; if (*bytes > (length * 4 + 4)) { *bytes -= (length * 4 + 4); } else { @@ -5341,17 +5444,17 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t #ifdef DEBUG_CHROME - if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->recv_msg.header.version == 2) { + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->has_rtp) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "VIDEO: seq: %d ts: %u len: %ld %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x mark: %d\n", - ntohs(rtp_session->recv_msg.header.seq), ntohl(rtp_session->recv_msg.header.ts), *bytes, + ntohs(rtp_session->last_rtp_hdr.seq), ntohl(rtp_session->last_rtp_hdr.ts), *bytes, *((uint8_t *)RTP_BODY(rtp_session)), *((uint8_t *)RTP_BODY(rtp_session) + 1), *((uint8_t *)RTP_BODY(rtp_session) + 2), *((uint8_t *)RTP_BODY(rtp_session) + 3), *((uint8_t *)RTP_BODY(rtp_session) + 4), *((uint8_t *)RTP_BODY(rtp_session) + 5), *((uint8_t *)RTP_BODY(rtp_session) + 6), *((uint8_t *)RTP_BODY(rtp_session) + 7), *((uint8_t *)RTP_BODY(rtp_session) + 8), *((uint8_t *)RTP_BODY(rtp_session) + 9), - *((uint8_t *)RTP_BODY(rtp_session) + 10), rtp_session->recv_msg.header.m); + *((uint8_t *)RTP_BODY(rtp_session) + 10), rtp_session->last_rtp_hdr.m); } #endif @@ -5366,9 +5469,9 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t rtp_session->stats.inbound.raw_bytes += *bytes; - if (rtp_session->recv_msg.header.pt == rtp_session->recv_te) { + if (rtp_session->last_rtp_hdr.pt == rtp_session->recv_te) { rtp_session->stats.inbound.dtmf_packet_count++; - } else if (rtp_session->recv_msg.header.pt == rtp_session->cng_pt || rtp_session->recv_msg.header.pt == 13) { + } else if (rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt || rtp_session->last_rtp_hdr.pt == 13) { rtp_session->stats.inbound.cng_packet_count++; } else { rtp_session->stats.inbound.media_packet_count++; @@ -5378,7 +5481,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t rtp_session->stats.inbound.packet_count++; } - if (rtp_session->recv_msg.header.pt == rtp_session->recv_te || + if (rtp_session->last_rtp_hdr.pt == rtp_session->recv_te || (*bytes < rtp_header_len && *bytes > 0) || rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] || rtp_session->flags[SWITCH_RTP_FLAG_UDPTL]) { return SWITCH_STATUS_SUCCESS; @@ -5404,10 +5507,15 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } } - if (rtp_session->recv_msg.header.version == 2 && *bytes) { + if (rtp_session->has_rtp && *bytes) { if (rtp_session->vb && jb_valid(rtp_session)) { - switch_jb_put_packet(rtp_session->vb, (switch_rtp_packet_t *) &rtp_session->recv_msg, *bytes); + status = switch_jb_put_packet(rtp_session->vb, (switch_rtp_packet_t *) &rtp_session->recv_msg, *bytes); + + if (status == SWITCH_STATUS_TOO_LATE) { + goto more; + } + status = SWITCH_STATUS_FALSE; *bytes = 0; @@ -5419,7 +5527,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (rtp_session->jb && !rtp_session->pause_jb && jb_valid(rtp_session)) { uint32_t read_ssrc = ntohl(rtp_session->last_rtp_hdr.ssrc); - if (rtp_session->recv_msg.header.m && rtp_session->recv_msg.header.pt != rtp_session->recv_te && + if (rtp_session->last_rtp_hdr.m && rtp_session->last_rtp_hdr.pt != rtp_session->recv_te && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && !(rtp_session->rtp_bugs & RTP_BUG_IGNORE_MARK_BIT)) { switch_jb_reset(rtp_session->jb); } else if (rtp_session->last_jb_read_ssrc && rtp_session->last_jb_read_ssrc != read_ssrc) { @@ -5433,8 +5541,12 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t reset_jitter_seq(rtp_session); } - switch_jb_put_packet(rtp_session->jb, (switch_rtp_packet_t *) &rtp_session->recv_msg, *bytes); + status = switch_jb_put_packet(rtp_session->jb, (switch_rtp_packet_t *) &rtp_session->recv_msg, *bytes); + if (status == SWITCH_STATUS_TOO_LATE) { + goto more; + } + status = SWITCH_STATUS_FALSE; *bytes = 0; @@ -5444,7 +5556,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } } - if (!*bytes || rtp_session->recv_msg.header.version == 2) { + if (!*bytes || rtp_session->has_rtp) { if (rtp_session->jb && !rtp_session->pause_jb && jb_valid(rtp_session)) { switch_status_t jstatus = switch_jb_get_packet(rtp_session->jb, (switch_rtp_packet_t *) &rtp_session->recv_msg, bytes); @@ -5453,14 +5565,22 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t switch(jstatus) { case SWITCH_STATUS_MORE_DATA: - status = SWITCH_STATUS_BREAK; + if (rtp_session->punts < 4) { + block = 1; + goto more; + } + *bytes = 0; break; case SWITCH_STATUS_NOTFOUND: { + int pt = get_recv_payload(rtp_session); (*flags) |= SFF_PLC; status = SWITCH_STATUS_SUCCESS; - rtp_session->recv_msg.header = rtp_session->last_rtp_hdr; *bytes = switch_jb_get_last_read_len(rtp_session->jb); + rtp_session->last_rtp_hdr = rtp_session->recv_msg.header; + if (pt > -1) { + rtp_session->last_rtp_hdr.pt = pt; + } } break; case SWITCH_STATUS_SUCCESS: @@ -5469,7 +5589,9 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t rtp_session->stats.inbound.jb_packet_count++; status = SWITCH_STATUS_SUCCESS; rtp_session->last_rtp_hdr = rtp_session->recv_msg.header; - + if (++rtp_session->clean > 200) { + rtp_session->punts = 0; + } if (!xcheck_jitter) { check_jitter(rtp_session); xcheck_jitter = *bytes; @@ -6112,9 +6234,9 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ ret = -1; goto end; } - + if (rtp_session->max_missed_packets && read_loops == 1 && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { - if (bytes) { + if (bytes && status == SWITCH_STATUS_SUCCESS) { rtp_session->missed_count = 0; } else if (++rtp_session->missed_count >= rtp_session->max_missed_packets) { ret = -2; @@ -6298,11 +6420,11 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ } - if (bytes && rtp_session->recv_msg.header.version == 2 && + if (bytes && rtp_session->has_rtp && !rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] && - rtp_session->recv_msg.header.pt != 13 && - rtp_session->recv_msg.header.pt != rtp_session->recv_te && - rtp_session->recv_msg.header.pt != rtp_session->cng_pt) { + rtp_session->last_rtp_hdr.pt != 13 && + rtp_session->last_rtp_hdr.pt != rtp_session->recv_te && + rtp_session->last_rtp_hdr.pt != rtp_session->cng_pt) { int accept_packet = 1; @@ -6317,7 +6439,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ continue; } - if (rtp_session->recv_msg.header.pt == pmap->pt) { + if (rtp_session->last_rtp_hdr.pt == pmap->pt) { accept_packet = 1; if (pmapP) { *pmapP = pmap; @@ -6345,8 +6467,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ check = !bytes; if (rtp_session->flags[SWITCH_RTP_FLAG_FLUSH]) { - do_flush(rtp_session, SWITCH_FALSE); - bytes = 0; + bytes = do_flush(rtp_session, SWITCH_FALSE, bytes); switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_FLUSH); } @@ -6370,14 +6491,14 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ goto recvfrom; } - if (bytes && rtp_session->recv_msg.header.m && rtp_session->recv_msg.header.pt != rtp_session->recv_te && + if (bytes && rtp_session->last_rtp_hdr.m && rtp_session->last_rtp_hdr.pt != rtp_session->recv_te && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && !(rtp_session->rtp_bugs & RTP_BUG_IGNORE_MARK_BIT)) { rtp_flush_read_buffer(rtp_session, SWITCH_RTP_FLUSH_ONCE); } - if (rtp_session->recv_msg.header.pt == rtp_session->cng_pt || rtp_session->recv_msg.header.pt == 13) { + if (rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt || rtp_session->last_rtp_hdr.pt == 13) { *flags |= SFF_NOT_AUDIO; } else { *flags &= ~SFF_NOT_AUDIO; /* If this flag was already set, make sure to remove it when we get real audio */ @@ -6386,7 +6507,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ /* ignore packets not meant for us unless the auto-adjust window is open (ice mode has its own alternatives to this) */ if (!using_ice(rtp_session) && bytes) { if (rtp_session->flags[SWITCH_RTP_FLAG_AUTOADJ]) { - if (rtp_session->recv_msg.header.pt == rtp_session->cng_pt || rtp_session->recv_msg.header.pt == 13) { + if (rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt || rtp_session->last_rtp_hdr.pt == 13) { goto recvfrom; } @@ -6465,7 +6586,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (rtp_session->flags[SWITCH_RTP_FLAG_UDPTL]) { #if 0 - if (rtp_session->recv_msg.header.version == 2 && check_recv_payload(rtp_session)) { + if (rtp_session->has_rtp && check_recv_payload(rtp_session)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "Ignoring udptl packet of size of %ld bytes that looks strikingly like a RTP packet.\n", (long)bytes); bytes = 0; @@ -6488,7 +6609,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ goto do_continue; } - if (rtp_session->recv_msg.header.pt && (rtp_session->recv_msg.header.pt == rtp_session->cng_pt || rtp_session->recv_msg.header.pt == 13)) { + if (rtp_session->last_rtp_hdr.pt && (rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt || rtp_session->last_rtp_hdr.pt == 13)) { return_cng_frame(); } } @@ -6500,12 +6621,12 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (bytes && rtp_session->recv_msg.header.version != 2) { uint8_t *data = (uint8_t *) RTP_BODY(rtp_session); - if (rtp_session->recv_msg.header.version == 0) { - if (rtp_session->ice.ice_user) { - handle_ice(rtp_session, &rtp_session->ice, (void *) &rtp_session->recv_msg, bytes); - goto recvfrom; - } - } + //if (rtp_session->recv_msg.header.version == 0) { + // if (rtp_session->ice.ice_user) { + // handle_ice(rtp_session, &rtp_session->ice, (void *) &rtp_session->recv_msg, bytes); + // goto recvfrom; + // } + //} if (rtp_session->invalid_handler) { rtp_session->invalid_handler(rtp_session, rtp_session->sock_input, (void *) &rtp_session->recv_msg, bytes, rtp_session->rtp_from_addr); @@ -6514,9 +6635,9 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ memset(data, 0, 2); data[0] = 65; - rtp_session->recv_msg.header.pt = rtp_session->cng_pt != INVALID_PT ? rtp_session->cng_pt : SWITCH_RTP_CNG_PAYLOAD; + rtp_session->last_rtp_hdr.pt = rtp_session->cng_pt != INVALID_PT ? rtp_session->cng_pt : SWITCH_RTP_CNG_PAYLOAD; *flags |= SFF_CNG; - *payload_type = (switch_payload_t) rtp_session->recv_msg.header.pt; + *payload_type = (switch_payload_t) rtp_session->last_rtp_hdr.pt; ret = 2 + rtp_header_len; goto end; } else if (bytes) { @@ -6556,9 +6677,9 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ memset(data, 0, 2); data[0] = 65; - rtp_session->recv_msg.header.pt = rtp_session->cng_pt != INVALID_PT ? rtp_session->cng_pt : SWITCH_RTP_CNG_PAYLOAD; + rtp_session->last_rtp_hdr.pt = rtp_session->cng_pt != INVALID_PT ? rtp_session->cng_pt : SWITCH_RTP_CNG_PAYLOAD; *flags |= SFF_CNG; - *payload_type = (switch_payload_t) rtp_session->recv_msg.header.pt; + *payload_type = (switch_payload_t) rtp_session->last_rtp_hdr.pt; ret = 2 + rtp_header_len; rtp_session->stats.inbound.skip_packet_count++; goto end; @@ -6598,8 +6719,8 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ return_cng_frame(); } - if (rtp_session->flags[SWITCH_RTP_FLAG_GOOGLEHACK] && rtp_session->recv_msg.header.pt == 102) { - rtp_session->recv_msg.header.pt = 97; + if (rtp_session->flags[SWITCH_RTP_FLAG_GOOGLEHACK] && rtp_session->last_rtp_hdr.pt == 102) { + rtp_session->last_rtp_hdr.pt = 97; } break; @@ -6613,7 +6734,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ } if (switch_rtp_ready(rtp_session)) { - *payload_type = (switch_payload_t) rtp_session->recv_msg.header.pt; + *payload_type = (switch_payload_t) rtp_session->last_rtp_hdr.pt; if (*payload_type == SWITCH_RTP_CNG_PAYLOAD) { *flags |= SFF_CNG; @@ -6803,10 +6924,10 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp if (frame->payload == rtp_session->recv_te) { switch_set_flag(frame, SFF_RFC2833); } - frame->timestamp = ntohl(rtp_session->recv_msg.header.ts); - frame->seq = (uint16_t) ntohs((uint16_t) rtp_session->recv_msg.header.seq); + frame->timestamp = ntohl(rtp_session->last_rtp_hdr.ts); + frame->seq = (uint16_t) ntohs((uint16_t) rtp_session->last_rtp_hdr.seq); frame->ssrc = ntohl(rtp_session->last_rtp_hdr.ssrc); - frame->m = rtp_session->recv_msg.header.m ? SWITCH_TRUE : SWITCH_FALSE; + frame->m = rtp_session->last_rtp_hdr.m ? SWITCH_TRUE : SWITCH_FALSE; } #ifdef ENABLE_ZRTP @@ -7073,11 +7194,11 @@ static int rtp_common_write(switch_rtp_t *rtp_session, send_msg->header.ssrc = htonl(rtp_session->ssrc); if (rtp_session->flags[SWITCH_RTP_FLAG_GOOGLEHACK] && rtp_session->send_msg.header.pt == 97) { - rtp_session->recv_msg.header.pt = 102; + rtp_session->last_rtp_hdr.pt = 102; } if (rtp_session->flags[SWITCH_RTP_FLAG_VAD] && - rtp_session->recv_msg.header.pt == rtp_session->vad_data.read_codec->implementation->ianacode) { + rtp_session->last_rtp_hdr.pt == rtp_session->vad_data.read_codec->implementation->ianacode) { int16_t decoded[SWITCH_RECOMMENDED_BUFFER_SIZE / sizeof(int16_t)] = { 0 }; uint32_t rate = 0; diff --git a/support-d/.bashrc b/support-d/.bashrc index 4b21b517da..4b917c3b5c 100644 --- a/support-d/.bashrc +++ b/support-d/.bashrc @@ -5,6 +5,9 @@ export UNAME=`uname -s` if [ "`id -u`" = "0" ]; then if [ "${UNAME}" = "Linux" ]; then + if [ -d /usr/lib/ccache ]; then + export PATH="/usr/lib/ccache:$PATH" + fi export PATH="$PATH:/opt/bin:/usr/local/bin:/usr/local/sbin:/usr/local/freeswitch/bin" if [ -d /usr/src/freeswitch.git/support-d/utils ]; then export PATH="/usr/src/freeswitch.git/support-d/utils:$PATH" diff --git a/support-d/utils/fixbug.pl b/support-d/utils/fixbug.pl index de3891bf6f..573ef06ee2 100755 --- a/support-d/utils/fixbug.pl +++ b/support-d/utils/fixbug.pl @@ -34,11 +34,16 @@ if ($prog =~ /wget/) { } my $xml = `$cmd $url 2>/dev/null`; +if ($opts{debug}) { + print "URL $url\n"; + print $xml; +} my $xs= new XML::Simple; my $r = $xs->XMLin($xml); my $sum = $r->{channel}->{item}->{summary}; +$sum =~ s/\"/\\"/g; if ($opts{msg} eq "edit") { $auto = 0; diff --git a/support-d/utils/hashfinder b/support-d/utils/hashfinder index 88df4ce5cc..b402f6552b 100755 --- a/support-d/utils/hashfinder +++ b/support-d/utils/hashfinder @@ -35,9 +35,9 @@ # hashfinder - Find origin of a particular line of code # - my $file = shift; my $regex = shift; +my $alt_file = shift; my $delim = " "; $file and $regex or die "missing params. Syntax: "; @@ -58,7 +58,9 @@ sub doit($$) { $linematch = 1; } - open GIT, "git blame -n $file $rev|"; + retry: + + open GIT, "git blame -n $file $rev 2>&1|"; my $mc = 0; my @matches = (); @@ -66,6 +68,14 @@ sub doit($$) { while () { my $matched = 0; + if (/fatal:/) { + if ($alt_file) { + $file = $alt_file; + $alt_file = undef; + goto retry; + } + } + if ($linematch) { $matched = (/^\S+\s+$pattern\s+/); } else { @@ -81,7 +91,6 @@ sub doit($$) { close(GIT); - if ($mc > 5) { print $delim x $loops; print "$mc matches; Maybe more specific?\n"; @@ -100,10 +109,11 @@ sub doit($$) { my ($hash, $lno, $author, $line); my $done = 0; - if (/$file/) { + if (/\//) { ($hash, $lno, $author, $line) = /(\S+)\s+\S+\s+(\S+)\s+(\([^\)]+\))\s*(.*)/; $done = 1; } else { + die $_; ($hash, $lno, $author, $line) = /(\S+)\s+(\S+)\s+(\([^\)]+\))\s*(.*)/; } diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am deleted file mode 100644 index d290dd506d..0000000000 --- a/tests/unit/Makefile.am +++ /dev/null @@ -1,30 +0,0 @@ -AUTOMAKE_OPTIONS = foreign -FSLD = $(top_builddir)/libfreeswitch.la $(top_builddir)/libs/apr/libapr-1.la $(top_builddir)/libs/apr-util/libaprutil-1.la - -TESTS = -check_PROGRAMS = - - -if HAVE_TAP -TESTS += switch_event -check_PROGRAMS += switch_event - -switch_event_SOURCES = switch_event.c -switch_event_CFLAGS = $(SWITCH_AM_CFLAGS) -switch_event_LDADD = $(FSLD) -switch_event_LDFLAGS = $(SWITCH_AM_LDFLAGS) -ltap - -TESTS += switch_hash -check_PROGRAMS += switch_hash - -switch_hash_SOURCES = switch_hash.c -switch_hash_CFLAGS = $(SWITCH_AM_CFLAGS) -switch_hash_LDADD = $(FSLD) -switch_hash_LDFLAGS = $(SWITCH_AM_LDFLAGS) -ltap - -else -check: error -error: - $(error You must install libtap-dev to build these unit tests) -endif - diff --git a/tests/unit/switch_event.c b/tests/unit/switch_event.c index 9b19879d67..a893d493fb 100644 --- a/tests/unit/switch_event.c +++ b/tests/unit/switch_event.c @@ -9,7 +9,7 @@ int main () { switch_bool_t verbose = SWITCH_TRUE; const char *err = NULL; switch_time_t start_ts, end_ts; - int rc = 0, loops = 10; + int rc = 0, loops = 10, x = 0; switch_status_t status = SWITCH_STATUS_SUCCESS; char **index = NULL; unsigned long long micro_total = 0; @@ -31,7 +31,7 @@ int main () { } index = calloc(loops, sizeof(char *)); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { index[x] = switch_mprintf("%d", x); } @@ -42,13 +42,13 @@ int main () { ok( status == SWITCH_STATUS_SUCCESS,"Create Event"); #ifndef BENCHMARK - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { status = switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, index[x], index[x]); ok( status == SWITCH_STATUS_SUCCESS,"Add header to event"); } #else small_start_ts = switch_time_now(); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { if ( switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, index[x], index[x]) != SWITCH_STATUS_SUCCESS) { fail("Failed to add header to event"); } @@ -64,12 +64,12 @@ int main () { #ifndef BENCHMARK - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { is(switch_event_get_header(event, index[x]), index[x], "correct header value returned"); } #else small_start_ts = switch_time_now(); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { if ( !switch_event_get_header(event, index[x])) { fail("Failed to lookup event header value"); } @@ -89,7 +89,7 @@ int main () { end_ts = switch_time_now(); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { free(index[x]); } free(index); @@ -97,7 +97,7 @@ int main () { micro_total = end_ts - start_ts; micro_per = micro_total / (double) loops; rate_per_sec = 1000000 / micro_per; - note("switch_event Total %ldus / %d loops, %.2f us per loop, %.0f loops per second\n", + diag("switch_event Total %ldus / %d loops, %.2f us per loop, %.0f loops per second\n", micro_total, loops, micro_per, rate_per_sec); switch_core_destroy(); diff --git a/tests/unit/switch_hash.c b/tests/unit/switch_hash.c index c3997301d5..522ca6ea1c 100644 --- a/tests/unit/switch_hash.c +++ b/tests/unit/switch_hash.c @@ -13,7 +13,8 @@ int main () { unsigned long long micro_total = 0; double micro_per = 0; double rate_per_sec = 0; - + int x = 0; + #ifdef BENCHMARK switch_time_t small_start_ts, small_end_ts; #endif @@ -42,7 +43,7 @@ int main () { } index = calloc(loops, sizeof(char *)); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { index[x] = switch_mprintf("%d", x); } @@ -51,13 +52,13 @@ int main () { /* Insertion */ #ifndef BENCHMARK - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { status = switch_core_hash_insert(hash, index[x], (void *) index[x]); ok(status == SWITCH_STATUS_SUCCESS, "Insert into the hash"); } #else small_start_ts = switch_time_now(); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { switch_core_hash_insert(hash, index[x], (void *) index[x]); } small_end_ts = switch_time_now(); @@ -72,7 +73,7 @@ int main () { /* Lookup */ #ifndef BENCHMARK - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { char *data = NULL; data = switch_core_hash_find(hash, index[x]); ok(data != NULL, "Successful lookup"); @@ -80,7 +81,7 @@ int main () { } #else small_start_ts = switch_time_now(); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { if ( ! switch_core_hash_find(hash, index[x])) { fail("Failed to properly locate one of the values"); } @@ -97,7 +98,7 @@ int main () { /* Delete */ #ifndef BENCHMARK - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { char *data = NULL; data = switch_core_hash_delete(hash, index[x]); ok(data != NULL, "Create a new hash"); @@ -105,7 +106,7 @@ int main () { } #else small_start_ts = switch_time_now(); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { if ( !switch_core_hash_delete(hash, index[x])) { fail("Failed to delete and return the value"); } @@ -124,7 +125,7 @@ int main () { /* END LOOPS */ switch_core_hash_destroy(&hash); - for ( int x = 0; x < loops; x++) { + for ( x = 0; x < loops; x++) { free(index[x]); } free(index); @@ -132,7 +133,7 @@ int main () { micro_total = end_ts - start_ts; micro_per = micro_total / (double) loops; rate_per_sec = 1000000 / micro_per; - note("switch_hash Total %ldus / %d loops, %.2f us per loop, %.0f loops per second\n", + diag("switch_hash Total %ldus / %d loops, %.2f us per loop, %.0f loops per second\n", micro_total, loops, micro_per, rate_per_sec); switch_core_destroy(); diff --git a/tests/unit/unit.mk b/tests/unit/unit.mk new file mode 100644 index 0000000000..68cd2e5d39 --- /dev/null +++ b/tests/unit/unit.mk @@ -0,0 +1,17 @@ +AUTOMAKE_OPTIONS = foreign +FSLD = $(top_builddir)/libfreeswitch.la $(top_builddir)/libs/apr/libapr-1.la $(top_builddir)/libs/apr-util/libaprutil-1.la + +check_PROGRAMS += tests/unit/switch_event + +tests_unit_switch_event_SOURCES = tests/unit/switch_event.c +tests_unit_switch_event_CFLAGS = $(SWITCH_AM_CFLAGS) +tests_unit_switch_event_LDADD = $(FSLD) +tests_unit_switch_event_LDFLAGS = $(SWITCH_AM_LDFLAGS) -ltap + +check_PROGRAMS += tests/unit/switch_hash + +tests_unit_switch_hash_SOURCES = tests/unit/switch_hash.c +tests_unit_switch_hash_CFLAGS = $(SWITCH_AM_CFLAGS) +tests_unit_switch_hash_LDADD = $(FSLD) +tests_unit_switch_hash_LDFLAGS = $(SWITCH_AM_LDFLAGS) -ltap +