Compare commits

...

205 Commits
ci ... master

Author SHA1 Message Date
Dmytro Bogovych 9a4823e6a9 - optimize audio file reading 2025-12-12 09:59:42 +03:00
Dmytro Bogovych c3deb8378d - promote used libraries commit 2025-12-12 07:14:17 +03:00
Dmytro Bogovych e0f07b6f7a - add opus library as source code 2025-12-12 07:13:59 +03:00
Dmytro Bogovych a7993c8d20 - promote used libraries version 2025-12-12 07:08:50 +03:00
Dmytro Bogovych 1559190bcc - promote used resiprocate version 2025-12-12 07:08:40 +03:00
Dmytro Bogovych 786cefed45 - fix compilation on Windows 2025-12-12 07:06:49 +03:00
Dmytro Bogovych 186ec4ccb4 - no more dependency on static/dynamic MSVC runtime type 2025-12-12 07:06:11 +03:00
Dmytro Bogovych ffd371d6e7 - rewrite .wav audio reader/writer with standard C++ stream and filesystem's path 2025-12-05 12:45:38 +03:00
Dmytro Bogovych f90d64b279 - promote used 'resiprocate' commit 2025-12-05 12:45:13 +03:00
Dmytro Bogovych cf27012d27 - promote used 'resiprocate' commit 2025-11-20 08:52:28 +03:00
Dmytro Bogovych e267415700 - promote used 'libraries' commit 2025-11-20 08:52:15 +03:00
Dmytro Bogovych 3e87b9e7ec - fix various build problems 2025-11-20 08:51:58 +03:00
Dmytro Bogovych 3f9dbda40a - fix SRTP parsing 2025-09-26 14:43:46 +03:00
Dmytro Bogovych eec5aabc42 - cleanups 2025-09-26 12:55:19 +03:00
Dmytro Bogovych 4e00f659e3 - promote used libraries version 2025-09-24 11:07:05 +03:00
Dmytro Bogovych 1599bfc9fc - always use (and depend on) resiprocate 2025-09-24 10:59:24 +03:00
Dmytro Bogovych 6df835bb95 - use usual resip::Security() in macOS builds - we just doesn't need full macOS support for now 2025-09-24 08:31:34 +03:00
Dmytro Bogovych fd878f0d0e - some linker flags for Linux only 2025-09-23 21:17:12 +03:00
Dmytro Bogovych 706339ec97 - promote used libraries commit 2025-09-23 20:54:13 +03:00
Dmytro Bogovych a35db35b48 - commit used resiprocate version 2025-09-13 13:43:01 +03:00
Dmytro Bogovych 46de58ccf4 - fix resiprocate branch 2025-09-13 12:51:50 +03:00
Dmytro Bogovych f891caedcc - promote used resiprocate commit 2025-09-13 12:32:43 +03:00
Dmytro Bogovych cba8e24622 - fix compiler warnings 2025-09-13 12:32:32 +03:00
Dmytro Bogovych 84cc39e669 - refresh used libraries commit 2025-09-11 17:48:47 +03:00
Dmytro Bogovych 916de57660 - fix libsrtp include directory declaration 2025-09-11 17:43:24 +03:00
Dmytro Bogovych e8bebc14c2 - fix Linux builds; now all dependencies are git submodules 2025-09-11 17:43:09 +03:00
Dmytro Bogovych a74f8a6cdf - migrate libsrtp to own fork 2025-09-11 17:30:24 +03:00
Dmytro Bogovych dd242dfa48 - avoid using of libuuid in Linux builds - no need 2025-09-08 11:55:38 +03:00
Dmytro Bogovych 190d4fde54 - prefer static libraries for linking 2025-09-08 10:16:53 +03:00
Dmytro Bogovych d105e582f4 - fix SRTP initialization 2025-08-30 15:19:21 +03:00
Dmytro Bogovych d4a47807d8 - improved SRTP support 2025-08-25 17:30:28 +03:00
Dmytro Bogovych e6cb2a22f7 - improve SRTP decoder API 2025-08-24 14:01:43 +03:00
Dmytro Bogovych 6b7b086bf7 - minor progress in SRTP updates 2025-08-23 14:28:58 +03:00
Dmytro Bogovych 5a05d9d40b - move the media/ice/helper/audio modules into top CMakeLists to ease the management 2025-08-23 12:13:25 +03:00
Dmytro Bogovych 48f6bf21d9 - migrate to the latest libsrtp version 2025-08-22 14:32:31 +03:00
Dmytro Bogovych 6f55c91c7a - remove opus codec sources - for now we will use only binary forms for opus codec 2025-08-22 09:26:04 +03:00
Dmytro Bogovych 906a422865 - remove non-used files in G729 + downgrade to Opus 1.5.1 - conflicting with another header files 2025-08-22 06:53:48 +03:00
Dmytro Bogovych 375bf64275 - upgrade to opus 1.5.2 + fix Opus CMakeLists for macOS ARM + fix rtphone macOS build 2025-08-21 12:17:04 +03:00
Dmytro Bogovych b8c107fb5b - fix path to resiprocate 2025-08-21 11:28:05 +03:00
Dmytro Bogovych 178ebac88a - fix many compiler warnings 2025-08-21 11:04:56 +03:00
Dmytro Bogovych 6b6f4147db - few merges from pcap analyzer project 2025-08-13 14:07:36 +03:00
Dmytro Bogovych 011919f846 - update README + add license file 2025-08-05 11:51:21 +03:00
Dmytro Bogovych 94fea9d5a9 - update README.txt + remove old outdated source code 2025-08-05 10:52:45 +03:00
Dmytro Bogovych 028ce8b982 - fix the AMR parsing 2025-07-30 14:55:41 +03:00
Dmytro Bogovych 5ddd7bbeed - fix octet-aligned mode parser 2025-07-30 13:47:17 +03:00
Dmytro Bogovych e90ae17212 - fixes to AMR decoder 2025-07-28 11:15:39 +03:00
Dmytro Bogovych 1ada2a9159 - promote used resiprocate commit 2025-07-23 14:38:22 +03:00
Dmytro Bogovych 64d2209b02 - fix octet-aligned AMR-WB parsing & decoding 2025-07-23 14:13:30 +03:00
Dmytro Bogovych f9c009c40e - speedup build 2025-07-23 08:37:28 +03:00
Dmytro Bogovych c67319838f - fix source code formatting 2025-07-23 08:37:16 +03:00
Dmytro Bogovych ebc9c3220e - support std::span in ByteBuffer 2025-07-23 08:36:41 +03:00
Dmytro Bogovych 9dc59d5ed6 - fix the RTP SSRC - it was not converted to host byte order 2025-07-23 08:36:23 +03:00
Dmytro Bogovych 8b33a3a7e6 - add strx::lowercase() call 2025-06-17 14:15:39 +03:00
Dmytro Bogovych d852fdde3a - migration to C++ 23 2025-06-05 17:32:45 +03:00
Dmytro Bogovych f63fba6c6a - promote used resiprocate hash 2025-06-02 10:20:32 +03:00
Dmytro Bogovych 80451a1a32 Merge remote-tracking branch 'origin/master' 2025-06-02 10:19:42 +03:00
Dmytro Bogovych 474256bc88 - fix Windows build 2025-06-02 10:18:14 +03:00
Dmytro Bogovych fadaf3541f - remove libraries subrepo 2025-05-10 16:44:02 +03:00
Dmytro Bogovych efa31427ca - remove bad reference to libraries 2025-05-10 16:43:38 +03:00
Dmytro Bogovych 83373cb586 - making rtphone buildable on Windows 2025-04-21 13:12:56 +03:00
Dmytro Bogovych 8d0c8ba4de - source codes cleanups and minor fixes 2025-04-16 09:58:06 +03:00
Dmytro Bogovych a0b7cdcd99 - function to remove double quotes around the string 2025-04-16 09:57:40 +03:00
Dmytro Bogovych 933d52b297 - fix audio resampler 2025-03-04 07:53:20 +03:00
Dmytro Bogovych efbf9c5bf8 - send silence to audio decoder if packet is lost 2025-02-25 15:11:13 +03:00
Dmytro Bogovych f30e111ebe - fix resiprocate commit ID 2025-02-16 14:03:30 +03:00
Dmytro Bogovych a7176fefeb - minor cleanups 2025-02-16 14:03:04 +03:00
Dmytro Bogovych caa2239da1 - fix AMR parsing in payload header and SDP 2025-02-08 07:01:35 +03:00
Dmytro Bogovych 93bf339f00 - comment out EVS parser errors to standard error output 2025-02-07 08:45:22 +03:00
Dmytro Bogovych 7510ff5697 - better checks for RTP packet + enable EVS codec + improve thread pool 2025-02-03 11:51:11 +03:00
Dmytro Bogovych 5ce85e3a09 - minor improvements for pvqa_pcap project 2025-01-30 17:44:12 +03:00
Dmytro Bogovych 34e43bf301 - build ICE stack without OpenSSL dependency 2025-01-27 13:30:08 +03:00
Dmytro Bogovych 84d11d3b04 - add hmac_sha1 and md5 implementations as source code 2025-01-24 13:45:07 +03:00
Dmytro Bogovych cedc44e24c - make the audio file writer easier to debug 2025-01-24 12:53:06 +03:00
Dmytro Bogovych 6fef519a4b - initialize statistics by default values 2025-01-24 12:52:32 +03:00
Dmytro Bogovych f8dd94dfa4 - avoid default SSL linking with ice stack 2025-01-20 10:17:38 +03:00
Dmytro Bogovych c45c686582 - fixes for pvqa_pcap project - lazy creation of codecs + compare timespec structures 2024-12-24 13:19:54 +03:00
Dmytro Bogovych b5d0242c74 - promote used resiprocate version 2024-12-23 17:40:20 +03:00
Dmytro Bogovych 4ba1ea7fa2 - fixes from pvqa_pcap 2024-12-23 17:39:45 +03:00
Dmytro Bogovych bf2d513080 - fix build problem 2024-12-16 10:14:20 +03:00
Dmytro Bogovych fe52356799 - fix build problem 2024-12-16 10:14:15 +03:00
Dmytro Bogovych 0e044faf16 - don't assign any dynamic payload types by default 2024-12-16 10:13:52 +03:00
Dmytro Bogovych 4d7ca3e4f3 - properly handle the badly decoded audio (which can happen due to payload type <-> codec inconsistency) 2024-11-16 16:33:21 +03:00
Dmytro Bogovych 9afbd9e0fd - switch to 48K internal samplerate 2024-11-12 12:37:11 +03:00
Dmytro Bogovych d895385d19 - fix linking problem on Android for Qualtest phone 2024-11-12 10:08:57 +03:00
Dmytro Bogovych cde5cca940 - promote used libraries version 2024-11-12 09:30:49 +03:00
Dmytro Bogovych 34c28e35e1 - use OpenSSL in ice library 2024-11-12 09:30:30 +03:00
Dmytro Bogovych e0b02db39d - fix Opus decoder - now audio is smooth 2024-11-11 12:52:50 +03:00
Dmytro Bogovych 8cb34fad83 - work to allow Opus codec work with both audio and stereo data (fixing old problem) 2024-11-10 21:47:22 +03:00
Dmytro Bogovych f0c37b1bb1 - rename sha1_init / sha1_update / sha1_free with prefix srtp_ to resolve multiple symbol error with libzmq 2024-11-10 13:21:10 +03:00
Dmytro Bogovych 5bf8d665e2 - fix used C++ standard (closer to full C++ 20) 2024-11-05 11:44:53 +03:00
Dmytro Bogovych f3ebe11a42 - comment out - breaks Android compilation 2024-11-05 11:43:12 +03:00
Dmytro Bogovych 3a81626b8e - maybe include file was missed 2024-11-05 11:42:48 +03:00
Dmytro Bogovych 10ec751e43 - format source code (indentation 4 space 2024-11-05 11:42:33 +03:00
Dmytro Bogovych 7c346fbe9b - add libraries from dedicated subrepository 2024-11-05 11:41:10 +03:00
Dmytro Bogovych caf1283f0a - fix UUID generation on Android 2024-11-01 19:19:50 +03:00
Dmytro Bogovych 2c2167c840 - fix cmake warning 2024-11-01 19:19:50 +03:00
Dmytro Bogovych d9b8f4d2d0 - add test build scripts 2024-11-01 19:19:50 +03:00
Dmytro Bogovych 62e4828241 Merge remote-tracking branch 'origin/master' 2024-10-24 12:29:43 +03:00
Dmytro Bogovych 81bc17fbe2 - fix lack of standart integer type definitions in latest gcc 2024-10-24 12:29:33 +03:00
Dmytro Bogovych 5a0a7e1070 - allow Opus spec to use slash as divider + cleanup source code 2024-10-18 09:46:42 +03:00
Dmytro Bogovych 20c2ac3e63 - fix compilation on newer gcc 2024-10-17 10:25:31 +03:00
Dmytro Bogovych d08b2e27a5 - find difference between 'struct timeval' 2024-10-14 08:44:33 +03:00
Dmytro Bogovych 4cff4a0988 - more cleanups - use uuid library on the rtphone level + formatting fixed 2024-10-14 07:25:57 +03:00
Dmytro Bogovych 5e6b4bb929 - avoid USE_NULL_UUID dependency; now cross-platform uuid.h is used (thanks to stduuid project) 2024-08-27 07:45:08 +03:00
Dmytro Bogovych 7aadde3a09 - fixes to handle codec configs properly 2024-08-20 19:15:28 +03:00
Dmytro Bogovych 62f2d996c6 - methods to expand tilda in file pathes + methods to control thread pool 2024-05-16 08:42:55 +03:00
Dmytro Bogovych 05e3cc8cb6 - stamp resiprocate version 2024-04-25 17:43:05 +03:00
Dmytro Bogovych 5030064925 - fixes to avoid dynamic linking problems in Linux + workaround build problems for Opus 2024-04-25 17:42:30 +03:00
Dmytro Bogovych 9f875b5f15 - fix resiprocate-based parser build 2024-04-08 09:59:30 +03:00
Dmytro Bogovych 4ca4e84547 - reduce memory consumption when decoding RTP stream + bring back G722 and G729 2024-04-08 09:59:30 +03:00
Dmytro Bogovych cf9be1c3f2 - temporary workaround to build with qualtest softphone 2024-03-16 22:18:27 +03:00
Dmytro Bogovych 8a851e5e86 - add fmt library 2024-03-13 11:28:30 +03:00
Dmytro Bogovych 62d72fda5c - upgrade to opus 1.5 2024-03-13 11:28:16 +03:00
Dmytro Bogovych 37471d56ff - fixes in logging + allow ICENetworkAddress to be logged in short form 2024-01-15 13:18:59 +03:00
Dmytro Bogovych 69374d2973 - promote used resiprocate version 2023-11-28 10:35:04 +03:00
Dmytro Bogovych 5eb4bf2690 - promote used resiprocate version 2023-11-26 19:32:55 +03:00
Dmytro Bogovych 677faa0615 - rename config.h to engine_config.h to avoid possible problems with include directories order 2023-11-26 16:32:51 +03:00
Dmytro Bogovych 8ec6245496 - make build with no UUID library / functionality default ones 2023-10-06 12:41:24 +03:00
Dmytro Bogovych 4ce7f50964 - refresh Opus codec source code 2023-10-06 09:26:57 +03:00
Dmytro Bogovych 3f33c86949 - remove unused #pragma (no VS development for last years) 2023-10-06 09:26:24 +03:00
Dmytro Bogovych c924329203 Merge branch 'integration_with_pvqa_pcap' 2023-10-06 07:51:10 +03:00
Dmytro Bogovych 75c4888e43 - add missed #include <list> 2023-10-06 07:50:40 +03:00
Dmytro Bogovych 2eb0d17fba - fix MSVC internal compiler error 2023-06-25 15:45:51 +03:00
Dmytro Bogovych 8dd494157f - fix Linux build 2023-06-25 14:56:45 +03:00
Dmytro Bogovych 22653e6241 - fixes for Windows build; opus builds from source code now. 2023-06-24 19:24:51 +03:00
Dmytro Bogovych fa053324fa - fixes for Windows build 2023-06-23 16:35:08 +03:00
Dmytro Bogovych 978079fd55 - fixes for CMakeLists 2023-06-23 11:42:42 +03:00
Dmytro Bogovych ace77323d2 - improve CMakeLists 2023-06-23 10:52:20 +03:00
Dmytro Bogovych 6ff23247ec - minor refactoring - renamed TimeHelper to chronox 2023-06-09 16:47:06 +03:00
Dmytro Bogovych 9ef74113b3 Merge remote-tracking branch 'origin/resiprocate_upgrade' 2023-06-05 11:56:10 +03:00
Dmytro Bogovych 42cc9ee096 - fix compiler warnings 2023-06-05 11:55:40 +03:00
Dmytro Bogovych 3beeac2d0c - use resiprocate as submodule 2023-06-02 11:18:46 +03:00
Dmytro Bogovych 6b8549346c - some logging to help in build diagnostics 2023-06-01 21:52:34 +03:00
Dmytro Bogovych b3c894379e - allow audio file reading without samplerate conversion 2023-06-01 08:46:20 +03:00
Dmytro Bogovych 2aba79353a - allow WavFileReader to read audio without resampling 2023-05-31 22:55:36 +03:00
Dmytro Bogovych 21fb865d80 - remove references to Sevana libraries 2023-05-30 18:26:20 +03:00
Dmytro Bogovych defe8531e7 - ongoing work 2023-05-28 15:42:32 +03:00
Dmytro Bogovych 7653fcf138 - fixes for Android build mostly 2023-05-22 11:01:44 +03:00
Dmytro Bogovych 65e27eec0b - better diagnostics 2023-05-18 15:23:42 +03:00
Dmytro Bogovych 125063b411 - cleanups in build process for Android 2023-05-17 22:04:46 +03:00
Dmytro Bogovych c6e7574976 - add implementation of getifaddrs / freeaddrs for NDK API_LEVEL < 24 2023-05-14 12:05:49 +03:00
Dmytro Bogovych dd6c01ca0d - fixes for Android + ongoing migration to latest resiprocate 2023-05-14 12:04:54 +03:00
Dmytro Bogovych 0eb5ad7cf7 - add build script which targets Android platform 2023-05-14 12:04:02 +03:00
Dmytro Bogovych 95fbaf5379 - switch to latest resiprocate 2023-05-11 15:34:23 +03:00
Dmytro Bogovych 55445f6630 - work to merge latest changes into master branch 2023-05-10 11:03:48 +03:00
Dmytro Bogovych 76aa7861f5 - refactoring 2023-04-21 09:59:18 +03:00
Dmytro Bogovych 7db4c8cb53 - add minimalistic CMakeLists.txt for parser functionality only 2023-04-19 22:05:00 +03:00
Dmytro Bogovych 4f1486b257 - allow usage of jrtplib as network analyzer (accept alien SSRC packets as own ones when calculating the RTT delay) 2023-04-12 13:10:16 +03:00
Dmytro Bogovych bb48e1a777 - comment jitter calculation 2023-04-12 12:19:53 +03:00
Dmytro Bogovych 0607bd1c47 - ongoing work to ease jitter & RTT calculation 2023-04-11 13:43:38 +03:00
Dmytro Bogovych d90940c907 - fix copyright year 2023-04-10 14:36:56 +03:00
Dmytro Bogovych 8ee306fb76 - fix Opus decoder for stereo audio 2023-04-10 13:10:43 +03:00
Dmytro Bogovych 5182cb8379 - compatibility with pcap++ 2023-04-09 15:46:44 +03:00
Dmytro Bogovych f01c5040e1 - cleanup formatting 2023-04-05 17:25:38 +03:00
Dmytro Bogovych b1cc4d1509 - fix the compiler warning + propagate opus decoder return code properly 2023-03-23 11:32:34 +03:00
Dmytro Bogovych 1b41ac9e16 - more RTP manipulation abilities + cleanups 2023-03-13 17:27:58 +03:00
Dmytro Bogovych 5bbdec8452 - better information about decoding results 2022-11-03 17:32:44 +03:00
Dmytro Bogovych 83dd8f88b7 - fix compiler warning + make debugging easier 2022-10-30 18:36:06 +03:00
Dmytro Bogovych 1fbeae18c3 - remove GitLab CI - as now rtphone is checked via Jenkins 2022-10-30 14:30:57 +03:00
Dmytro Bogovych 89de7a7db9 - attempt to fix compiler warning about insufficient buffer size 2022-10-29 21:31:55 +03:00
Dmytro Bogovych 95f50d5b43 - add primitive CI script 2022-10-16 17:08:57 +03:00
Dmytro Bogovych 691501c405 - decouple the Sevana libraries; some improvements for statistics 2022-10-12 12:28:57 +03:00
Dmytro Bogovych c2e9985bc2 - enforce TLS 1.2 usage 2022-09-15 14:33:38 +03:00
Dmytro Bogovych d07eab796f - attempt to fix the CI quickly 2022-09-09 05:25:11 +03:00
Dmytro Bogovych eabc1b5c3d - add stun server IP setting + fix problem when processing incoming audio data 2022-09-08 20:54:01 +03:00
Dmytro Bogovych 93d53957a2 Merge remote-tracking branch 'origin/stable' 2022-06-03 08:46:24 +03:00
Dmytro Bogovych c3c59ddf03 - return NTP timestamp as uint64_t 2022-05-06 08:39:35 +03:00
Dmytro Bogovych 621afead4b - support nested VLANs + fix few clang warnings 2022-05-05 13:34:17 +03:00
Dmytro Bogovych 2fa4d0c3e6 Merge remote-tracking branch 'origin/master' 2022-02-14 22:18:55 +02:00
Dmytro Bogovych ca32a483db - fix build options for Windows 2022-02-14 22:18:48 +02:00
Dmytro Bogovych aeedeb0626 - work to fix secure calls problems 2021-12-22 16:35:41 +02:00
Dmytro Bogovych 616498e8a2 - better logging + initial sips: tests 2021-12-19 13:52:01 +02:00
Dmytro Bogovych 634fc3ac8c - fix cmake 2021-11-14 17:48:27 +02:00
Dmytro Bogovych f03f5fad2b Merge remote-tracking branch 'origin/master' into oboe
# Conflicts:
#	src/CMakeLists.txt
2021-11-14 17:47:02 +02:00
Dmytro Bogovych 2ab0a3c26d - changes for MUSL builds 2021-11-14 15:43:50 +00:00
Dmytro Bogovych 4c1241cd1e - fixes 2021-11-03 15:27:21 +02:00
Dmytro Bogovych 7efef755fa - more fixes from pvqa_server project 2021-09-26 19:50:02 +03:00
Dmytro Bogovych 1bfd84ec34 - more work on RTP early decoding 2021-09-06 15:00:49 +03:00
Dmytro Bogovych 23b4283b89 - initial work to move decoding from audio callback 2021-09-06 14:21:44 +03:00
Dmytro Bogovych ead9979db7 - more work on Android Oboe audio backend 2021-09-03 14:14:25 +03:00
Dmytro Bogovych 74b5aa69cb - ongoing work to fix issue with unstable audio 2021-08-06 18:31:42 +03:00
Dmytro Bogovych ed39725641 - add oboe library snapshot 2021-08-02 16:00:18 +03:00
Dmytro Bogovych cb9c2b693e - android audio subsystem - initial implementation with oboe library 2021-08-02 15:54:55 +03:00
Dmytro Bogovych 4aae4c36e8 - add oboe audio library 2021-07-28 10:38:29 +03:00
Dmytro Bogovych c648db062a - bring back other codecs (Opus was active only before) 2021-07-28 09:55:39 +03:00
Dmytro Bogovych e281a91af6 - missed reference to lround() 2021-07-10 18:31:18 +03:00
Dmytro Bogovych 82cc98f165 - fake change (add space symbol) to trigger build on CI 2021-07-10 18:21:35 +03:00
Dmytro Bogovych cf1f206056 - minor fixes of warnings + counter of dropped packets 2021-07-02 14:08:44 +03:00
Dmytro Bogovych e95aa2274f - switch back to 16KHz 2021-06-14 17:18:56 +03:00
Dmytro Bogovych 84d13da22b - closer to 'classic' AQuA parameters 2021-06-14 17:12:44 +03:00
Dmytro Bogovych 297dfefeac - switch to 48KHz 2021-06-14 10:41:59 +03:00
Dmytro Bogovych ca6fa9f14a - report samplerate for incoming audio dump 2021-06-13 21:15:01 +03:00
Dmytro Bogovych 31fe18df05 - back to 8KHz internal samplerate 2021-06-09 22:04:56 +03:00
Dmytro Bogovych e7467aa154 - fix samplerate to 48000 2021-06-09 21:43:48 +03:00
Dmytro Bogovych fbad7e07d0 - make better AQuA parameters' set + fix odd logging line 2021-06-09 14:41:17 +03:00
Dmytro Bogovych 9479a0f36f - fix opus codec usage 2021-06-09 13:06:26 +03:00
Dmytro Bogovych 61be61b7e3 - missed changes 2021-06-08 19:30:02 +03:00
Dmytro Bogovych ce9912dd8d - fix compilation warning 2021-01-22 11:20:48 +02:00
Dmytro Bogovych 27d59ab676 - fix CI build 2021-01-21 14:16:41 +02:00
Dmytro Bogovych 33465c33a1 - fixes for minimalistic builds (without AMR / EVS / Opus codecs) 2021-01-21 13:53:25 +02:00
Dmytro Bogovych 1007d752bc - replace options with variables - more work to make AMR / EVS / Opus codecs optional 2021-01-20 21:48:29 +02:00
Dmytro Bogovych 24270bac97 - exclude AMR / GSM EFR / EVS / Opus codecs from the default build - to make the new basic builds easier to port to new platforms / projects 2021-01-20 21:06:17 +02:00
Dmytro Bogovych 4d8a167899 - fix building issue (move from Json:: to JsonCpp:: namespace) + minor cleanup in cmake file 2021-01-20 20:38:58 +02:00
Dmytro Bogovych f9acfd47e4 - minor cleanup, just to check if rtphone is builds in CI pipeline 2021-01-20 20:22:25 +02:00
Dmytro Bogovych e7dedc90d4 - switch to JsonCpp:: namespace instead of Json:: 2021-01-15 11:17:32 +03:00
5192 changed files with 913624 additions and 898427 deletions

View File

@ -1,8 +0,0 @@
build_exe:
script:
- mkdir -p build
- cd build
- git clone https://gitlab.com/dmytro.bogovych/libraries.git
- cmake -D LIB_PLATFORM=libraries ../src
- cmake --build .

12
.gitmodules vendored Normal file
View File

@ -0,0 +1,12 @@
[submodule "src/libs/resiprocate"]
path = src/libs/resiprocate
url = git@git.sevana.biz:public/resiprocate.git
branch = sevana
[submodule "src/libs/libsrtp"]
path = src/libs/libsrtp
url = git@git.sevana.biz:public/libsrtp.git
branch = master
[submodule "src/libs/libraries"]
path = src/libs/libraries
url = git@git.sevana.biz:public/libraries.git

373
LICENSE_MPL.txt Normal file
View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

141
README.txt Normal file
View File

@ -0,0 +1,141 @@
# RTPhone Platform
RTPhone is a comprehensive real-time communication (RTC) platform that provides a complete software stack for building VoIP/SIP-based communication applications. Developed by VoIP Objects (Sevana), RTPhone delivers production-ready voice communication capabilities with extensive codec support and cross-platform compatibility.
## Overview
RTPhone serves as a static library (`librtphone.a`) that can be integrated into larger telephony and communication systems. It provides a JSON-based command interface for easy integration and control, making it suitable for building softphones, PBX systems, WebRTC gateways, and carrier-grade voice solutions.
## Key Features
### Audio Codec Support
RTPhone supports an extensive range of audio codecs:
**Standard Codecs:**
- G.711 (A-law/¼-law)
- G.722 (16kHz wideband)
- G.729
- GSM (Full Rate, Half Rate, Enhanced Full Rate)
- iLBC (20ms/30ms)
- ISAC (16kHz/32kHz)
**Advanced Codecs:**
- AMR-NB/AMR-WB (Adaptive Multi-Rate Narrowband/Wideband) - please be aware - there is no patents for AMR codecs usage included ! You should acquire them on your own.
- EVS (Enhanced Voice Services) - 3GPP's latest codec. Again - please be aware - there is no patents for EVS codec usage included ! You should acquire them on your own.
- Opus - Modern low-latency codec
- Speex (with acoustic echo cancellation)
**Codec Features:**
- Bandwidth-efficient and octet-aligned modes
- IuUP (Iu User Plane) protocol support for 3G networks
- Dynamic codec switching
- Packet loss concealment (PLC)
- Comfort noise generation (CNG)
### Network & Protocol Support
**SIP Features:**
- Full SIP 2.0 implementation via reSIProcate
- Multiple transport protocols (UDP, TCP, TLS)
- Registration, authentication, and session management
- SIP MESSAGE, presence, and REFER support
**Media Transport:**
- RTP/RTCP for media streaming
- SRTP for secure media
- ICE for NAT traversal with STUN/TURN support
- WebRTC integration components
- IPv4 and IPv6 support
### Cross-Platform Audio Support
- DirectSound/WMME (Windows)
- Core Audio (macOS/iOS)
- ALSA/PulseAudio (Linux)
- Oboe (Android) for low-latency audio
- PortAudio fallback support
### Audio Quality Features
- 48kHz sample rate support
- Acoustic Echo Cancellation (AEC)
- Audio resampling and format conversion
- Multi-channel audio mixing
- Perceptual Voice Quality Assessment (PVQA)
## Architecture
The platform is organized into several core modules:
- **Engine/Agent**: JSON-based command interface
- **Engine/Endpoint**: SIP user agent implementation
- **Engine/Media**: Audio codec management and processing
- **Engine/Audio**: Cross-platform audio I/O handling
- **Engine/Helper**: Utility functions (networking, logging, threading)
## Supported Platforms
- Linux (x64, ARM/Raspberry Pi)
- Windows (32/64-bit)
- macOS
- Android (with Oboe integration)
- iOS
## Building
RTPhone uses a CMake-based build system with cross-compilation support:
### Linux
```bash
python3 build_linux.py
```
### Android
```bash
python3 build_android.py
# or
./build_android.sh
```
### Dependencies
- CMake 3.10+
- OpenSSL 1.1+
- Boost libraries
- Platform-specific audio libraries
## Recent Updates
Recent development has focused on:
- AMR codec parsing and decoding improvements
- Octet-aligned mode fixes for AMR-WB
- RTP SSRC handling enhancements
- Build system optimizations
- Code modernization to C++20
## Use Cases
RTPhone is good for building:
- VoIP softphones and mobile applications
- PBX and telephony server systems
- RTP proxies
- Carrier-grade voice communication platforms
- 3GPP/IMS-compliant systems
## Security
RTPhone includes comprehensive security features:
- OpenSSL 1.1 integration for encryption
- TLS transport layer security
- SRTP media encryption
- Certificate management support
## Integration
The platform provides a developer-friendly interface with:
- Event-driven architecture
- Comprehensive logging system
- Modern C++20 codebase
For detailed integration instructions and API documentation, please refer to the source code and header files in the `/src/engine/` directory.
## License
Our source code is licensed under the MPL license. Naturally, any third-party components we use are subject to their respective licenses.

44
build_android.py Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/python3
from pathlib import Path
import os
import multiprocessing
import shutil
# Temporary build directory
DIR_BUILD = 'build_android'
# Android NDK home directory
NDK_HOME = os.environ['ANDROID_NDK_HOME']
# CMake toolchain file
TOOLCHAIN_FILE = f'{NDK_HOME}/build/cmake/android.toolchain.cmake'
# This directory
DIR_THIS = Path(__file__).parent.resolve()
# Path to app
DIR_SOURCE = (DIR_THIS / '../src').resolve()
def make_build() -> Path:
if Path(DIR_BUILD).exists():
shutil.rmtree(DIR_BUILD)
os.mkdir(DIR_BUILD)
os.chdir(DIR_BUILD)
cmd = f'cmake -DCMAKE_TOOLCHAIN_FILE={TOOLCHAIN_FILE} '
cmd += f'-DANDROID_NDK=$NDK_HOME '
cmd += f'-DANDROID_PLATFORM=24 '
cmd += f'-DCMAKE_BUILD=Release '
cmd += f'-DANDROID_ABI="arm64-v8a" '
cmd += '../src'
retcode = os.system(cmd)
if retcode != 0:
raise RuntimeError('Problem when configuring the project')
cmd = f'cmake --build . -j {multiprocessing.cpu_count()}'
retcode = os.system(cmd)
if retcode != 0:
raise RuntimeError('Problem when building the project')
if __name__ == '__main__':
make_build()

15
build_android.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
rm -rf build_android
mkdir -p build_android
cd build_android
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$ANDROID_NDK_HOME \
-DANDROID_PLATFORM=24 \
-DCMAKE_BUILD_TYPE=Release \
-DANDROID_ABI="arm64-v8a" \
../src
cmake --build . -j8

37
build_linux.py Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
from pathlib import Path
import os
import multiprocessing
import shutil
# Temporary build directory
DIR_BUILD = 'build_linux'
# This directory
DIR_THIS = Path(__file__).parent.resolve()
# Path to app
DIR_SOURCE = (DIR_THIS / '../src').resolve()
def make_build() -> Path:
if Path(DIR_BUILD).exists():
shutil.rmtree(DIR_BUILD)
os.mkdir(DIR_BUILD)
os.chdir(DIR_BUILD)
cmd = f'cmake ../src -G Ninja'
retcode = os.system(cmd)
if retcode != 0:
raise RuntimeError('Problem when configuring the project')
cmd = f'cmake --build . -j {multiprocessing.cpu_count()}'
retcode = os.system(cmd)
if retcode != 0:
raise RuntimeError('Problem when building the project')
os.chdir('..')
return Path(DIR_BUILD) / 'librtphone.a'
if __name__ == '__main__':
p = make_build()
print (f'Built: {p}')

23
run_ci.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
SEND_MSG="/var/ci/conformance_ci/send_telegram_message.py"
mkdir -p build
cd build
# Configure
cmake ../src
if [ $? -ne 0 ]; then
/usr/bin/python3 $SEND_MSG "rtphone cmake failed. $BUILD_URL"
exit 1
fi
# Build
make -j2
if [ $? -ne 0 ]; then
/usr/bin/python3 $SEND_MSG "rtphone build failed. $BUILD_URL"
exit 1
fi
/usr/bin/python3 $SEND_MSG "rtphone builds ok. $BUILD_URL"

View File

@ -1,198 +1,361 @@
cmake_minimum_required(VERSION 3.20)
project(rtphone) project(rtphone)
cmake_minimum_required(VERSION 3.0) # Rely on C++ 20
set (CMAKE_CXX_STANDARD 20)
macro(configure_msvc_runtime)
if(MSVC)
# Default to statically-linked runtime.
if("${MSVC_RUNTIME}" STREQUAL "")
set(MSVC_RUNTIME "static")
endif()
# Set compiler options.
set(variables
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELWITHDEBINFO
)
if(${MSVC_RUNTIME} STREQUAL "static")
message(STATUS
"rtphone: MSVC -> forcing use of statically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
endif()
endforeach()
else()
message(STATUS
"rtphone: MSVC -> forcing use of dynamically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MT")
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
endif()
endforeach()
endif()
foreach(variable ${variables})
string(REGEX REPLACE "/Z[iI7]" ""
${variable}
"${${variable}}")
set(${variable} "${${variable}} /Zi /Oy-")
endforeach()
endif()
endmacro()
# Rely on C++ 11
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (rtphone_libs libs) set (L libs)
set (rtphone_engine engine) set (E engine)
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." OFF) option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
option (USE_EVS_CODEC "Use EVS codec." OFF) option (USE_EVS_CODEC "Use EVS codec." ON)
option (USE_SEVANA_LIB "Build with Sevana libraries" OFF) option (USE_OPUS_CODEC "Use Opus codec." ON)
option (USE_MUSL "Build with MUSL library" OFF)
# PIC code by default # PIC code by default
set (CMAKE_POSITION_INDEPENDENT_CODE ON) set (CMAKE_POSITION_INDEPENDENT_CODE ON)
set (RUNTIME_CPU_CAPABILITY_DETECTION ON)
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/libs/libraries)
include (${LIB_PLATFORM}/platform_libs.cmake)
if (NOT DEFINED LIB_PLATFORM)
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries)
endif()
message("Libraries: ${LIB_PLATFORM}") message("Libraries: ${LIB_PLATFORM}")
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.0/include) set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.1/include)
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}") message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
message ("Using OpenSSL libs: ${OPENSSL_SSL} and ${OPENSSL_CRYPTO}")
include_directories(${OPENSSL_INCLUDE})
# Used defines for our project
set (DEFINES -DUSE_OPENSSL)
# Libraries for our project
set (LIBS_STATIC "")
set (LIBS_DYNAMIC "")
# Try to prefer static libraries anyway
set (CMAKE_FIND_LIBRARY_SUFFIXES .a .so .dylib)
# Windows-specific definitions
if (CMAKE_SYSTEM MATCHES "Windows*") if (CMAKE_SYSTEM MATCHES "Windows*")
add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS) set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
set (TARGET_WIN ON)
endif() endif()
# Linux-specific definitions
if (CMAKE_SYSTEM MATCHES "Linux*") if (CMAKE_SYSTEM MATCHES "Linux*")
add_definitions (-DTARGET_LINUX) set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H)
set (TARGET_LINUX ON)
set (LIBS_STATIC ${LIBS_STATIC} dl)
endif() endif()
# macOS-specific definitions
if (CMAKE_SYSTEM MATCHES "Darwin*") if (CMAKE_SYSTEM MATCHES "Darwin*")
add_definitions (-DTARGET_OSX) set (DEFINES ${DEFINES} -DTARGET_OSX)
set (TARGET_OSX ON)
set (LIBS_STATIC ${LIBS_STATIC} dl)
endif() endif()
if (USE_SEVANA_LIB) #
add_definitions( -DUSE_AQUA_LIBRARY -DUSE_PVQA_LIBRARY) if (CMAKE_SYSTEM MATCHES "Android")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include) message("Adding the Oboe library")
set (OBOE_DIR libs/oboe)
add_subdirectory (${OBOE_DIR} ./oboe)
include_directories (${OBOE_DIR}/include)
set (DEFINES ${DEFINES} -DTARGET_ANDROID -DHAVE_NETINET_IN_H)
set (TARGET_ANDROID ON)
set (LIBS_STATIC ${LIBS} oboe)
endif()
if (USE_MUSL)
set (DEFINES ${DEFINES} -DTARGET_MUSL)
set (TARGET_MUSL ON)
endif() endif()
set (RTPHONE_SOURCES set (RTPHONE_SOURCES
${rtphone_engine}/media/MT_Statistics.cpp ${E}/engine_config.h
${rtphone_engine}/media/MT_WebRtc.cpp ${E}/media/MT_Statistics.cpp
${rtphone_engine}/media/MT_Stream.cpp ${E}/media/MT_WebRtc.cpp
${rtphone_engine}/media/MT_SrtpHelper.cpp ${E}/media/MT_Stream.cpp
${rtphone_engine}/media/MT_SingleAudioStream.cpp ${E}/media/MT_SrtpHelper.cpp
${rtphone_engine}/media/MT_NativeRtpSender.cpp ${E}/media/MT_SingleAudioStream.cpp
${rtphone_engine}/media/MT_Dtmf.cpp ${E}/media/MT_NativeRtpSender.cpp
${rtphone_engine}/media/MT_CodecList.cpp ${E}/media/MT_Dtmf.cpp
${rtphone_engine}/media/MT_Codec.cpp ${E}/media/MT_CodecList.cpp
${rtphone_engine}/media/MT_Box.cpp ${E}/media/MT_Codec.cpp
${rtphone_engine}/media/MT_AudioStream.cpp ${E}/media/MT_Box.cpp
${rtphone_engine}/media/MT_AudioReceiver.cpp ${E}/media/MT_AudioStream.cpp
${rtphone_engine}/media/MT_AudioCodec.cpp ${E}/media/MT_AudioReceiver.cpp
${rtphone_engine}/media/MT_AmrCodec.cpp ${E}/media/MT_AudioCodec.cpp
${rtphone_engine}/media/MT_EvsCodec.cpp ${E}/media/MT_CngHelper.cpp
${rtphone_engine}/media/MT_CngHelper.cpp ${E}/agent/Agent_Impl.cpp
${rtphone_engine}/agent/Agent_Impl.cpp ${E}/agent/Agent_Impl.h
${rtphone_engine}/agent/Agent_AudioManager.cpp ${E}/agent/Agent_AudioManager.cpp
${rtphone_engine}/endpoint/EP_Account.cpp ${E}/agent/Agent_AudioManager.h
${rtphone_engine}/endpoint/EP_AudioProvider.cpp ${E}/endpoint/EP_Account.cpp
${rtphone_engine}/endpoint/EP_DataProvider.cpp ${E}/endpoint/EP_Account.h
${rtphone_engine}/endpoint/EP_Engine.cpp ${E}/endpoint/EP_AudioProvider.cpp
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp ${E}/endpoint/EP_AudioProvider.h
${rtphone_engine}/endpoint/EP_Observer.cpp ${E}/endpoint/EP_DataProvider.cpp
${rtphone_engine}/endpoint/EP_Session.cpp ${E}/endpoint/EP_DataProvider.h
${E}/endpoint/EP_Engine.cpp
${E}/endpoint/EP_Engine.h
${E}/endpoint/EP_NetworkQueue.cpp
${E}/endpoint/EP_NetworkQueue.h
${E}/endpoint/EP_Observer.cpp
${E}/endpoint/EP_Observer.h
${E}/endpoint/EP_Session.cpp
${E}/endpoint/EP_Session.h
${E}/media/MT_Statistics.h
${E}/media/MT_WebRtc.h
${E}/media/MT_Stream.h
${E}/media/MT_SrtpHelper.h
${E}/media/MT_SingleAudioStream.h
${E}/media/MT_NativeRtpSender.h
${E}/media/MT_Dtmf.h
${E}/media/MT_CodecList.h
${E}/media/MT_Codec.h
${E}/media/MT_Box.h
${E}/media/MT_AudioStream.h
${E}/media/MT_AudioReceiver.h
${E}/media/MT_AudioCodec.h
${E}/media/MT_CngHelper.h
${E}/media/MT_Statistics.cpp
${E}/media/MT_WebRtc.cpp
${E}/media/MT_Stream.cpp
${E}/media/MT_SrtpHelper.cpp
${E}/media/MT_SingleAudioStream.cpp
${E}/media/MT_NativeRtpSender.cpp
${E}/media/MT_Dtmf.cpp
${E}/media/MT_CodecList.cpp
${E}/media/MT_Codec.cpp
${E}/media/MT_Box.cpp
${E}/media/MT_AudioStream.cpp
${E}/media/MT_AudioReceiver.cpp
${E}/media/MT_AudioCodec.cpp
${E}/media/MT_CngHelper.cpp
${E}/media/MT_AmrCodec.cpp
${E}/media/MT_EvsCodec.cpp
${E}/media/MT_Statistics.h
${E}/media/MT_WebRtc.h
${E}/media/MT_Stream.h
${E}/media/MT_SrtpHelper.h
${E}/media/MT_SingleAudioStream.h
${E}/media/MT_NativeRtpSender.h
${E}/media/MT_Dtmf.h
${E}/media/MT_CodecList.h
${E}/media/MT_Codec.h
${E}/media/MT_Box.h
${E}/media/MT_AudioStream.h
${E}/media/MT_AudioReceiver.h
${E}/media/MT_AudioCodec.h
${E}/media/MT_CngHelper.h
${E}/media/MT_AmrCodec.h
${E}/media/MT_EvsCodec.h
${E}/helper/HL_AsyncCommand.cpp
${E}/helper/HL_AsyncCommand.h
${E}/helper/HL_Base64.h
${E}/helper/HL_ByteBuffer.h
${E}/helper/HL_Calculator.cpp
${E}/helper/HL_Calculator.h
${E}/helper/HL_CrashRpt.cpp
${E}/helper/HL_CrashRpt.h
${E}/helper/HL_CsvReader.cpp
${E}/helper/HL_CsvReader.h
${E}/helper/HL_Epoll.cpp
${E}/helper/HL_Epoll.h
${E}/helper/HL_Exception.h
${E}/helper/HL_File.cpp
${E}/helper/HL_File.h
${E}/helper/HL_HepSupport.cpp
${E}/helper/HL_HepSupport.h
${E}/helper/HL_InternetAddress.h
${E}/helper/HL_IuUP.cpp
${E}/helper/HL_IuUP.h
${E}/helper/HL_Log.cpp
${E}/helper/HL_Log.h
${E}/helper/HL_NetworkFrame.cpp
${E}/helper/HL_NetworkFrame.h
${E}/helper/HL_NetworkSocket.cpp
${E}/helper/HL_NetworkSocket.h
${E}/helper/HL_Optional.hpp
${E}/helper/HL_OsVersion.cpp
${E}/helper/HL_OsVersion.h
${E}/helper/HL_Pointer.cpp
${E}/helper/HL_Pointer.h
${E}/helper/HL_Process.cpp
${E}/helper/HL_Process.h
${E}/helper/HL_Rtp.cpp
${E}/helper/HL_Rtp.h
${E}/helper/HL_Singletone.cpp
${E}/helper/HL_Singletone.h
${E}/helper/HL_SocketHeap.cpp
${E}/helper/HL_SocketHeap.h
${E}/helper/HL_Statistics.cpp
${E}/helper/HL_Statistics.h
${E}/helper/HL_StreamState.h
${E}/helper/HL_String.cpp
${E}/helper/HL_String.h
${E}/helper/HL_Sync.cpp
${E}/helper/HL_Sync.h
${E}/helper/HL_ThreadPool.cpp
${E}/helper/HL_ThreadPool.h
${E}/helper/HL_Time.cpp
${E}/helper/HL_Time.h
${E}/helper/HL_Types.h
${E}/helper/HL_Types.cpp
${E}/helper/HL_Usb.cpp
${E}/helper/HL_Usb.h
${E}/helper/HL_Uuid.cpp
${E}/helper/HL_Uuid.h
${E}/helper/HL_VariantMap.cpp
${E}/helper/HL_VariantMap.h
${E}/helper/HL_Xcap.cpp
${E}/helper/HL_Xcap.h
${E}/audio/Audio_Resampler.cpp
${E}/audio/Audio_Resampler.h
${E}/audio/Audio_Quality.cpp
${E}/audio/Audio_Quality.h
${E}/audio/Audio_Mixer.cpp
${E}/audio/Audio_Mixer.h
${E}/audio/Audio_Interface.cpp
${E}/audio/Audio_Interface.h
${E}/audio/Audio_Helper.cpp
${E}/audio/Audio_Helper.h
${E}/audio/Audio_DataWindow.cpp
${E}/audio/Audio_DataWindow.h
${E}/audio/Audio_DevicePair.cpp
${E}/audio/Audio_DevicePair.h
${E}/audio/Audio_Player.cpp
${E}/audio/Audio_Player.h
${E}/audio/Audio_Null.cpp
${E}/audio/Audio_Null.h
${E}/audio/Audio_CoreAudio.cpp
${E}/audio/Audio_CoreAudio.h
${E}/audio/Audio_DirectSound.cpp
${E}/audio/Audio_DirectSound.h
${E}/audio/Audio_AndroidOboe.cpp
${E}/audio/Audio_AndroidOboe.h
${E}/audio/Audio_WavFile.cpp
${E}/audio/Audio_WavFile.h
${L}/ice/hmac_sha1_impl.cpp
${L}/ice/hmac_sha1_impl.h
${L}/ice/ICEAction.h
${L}/ice/ICEAddress.cpp
${L}/ice/ICEAddress.h
${L}/ice/ICEAuthTransaction.cpp
${L}/ice/ICEAuthTransaction.h
${L}/ice/ICEBinding.cpp
${L}/ice/ICEBinding.h
${L}/ice/ICEBox.cpp
${L}/ice/ICEBox.h
${L}/ice/ICEBoxImpl.cpp
${L}/ice/ICEBoxImpl.h
${L}/ice/ICEByteBuffer.cpp
${L}/ice/ICEByteBuffer.h
${L}/ice/ICECandidate.cpp
${L}/ice/ICECandidate.h
${L}/ice/ICECandidatePair.cpp
${L}/ice/ICECandidatePair.h
${L}/ice/ICECheckList.cpp
${L}/ice/ICECheckList.h
${L}/ice/ICECRC32.cpp
${L}/ice/ICECRC32.h
${L}/ice/ICEError.cpp
${L}/ice/ICEError.h
${L}/ice/ICEEvent.h
${L}/ice/ICELog.cpp
${L}/ice/ICELog.h
${L}/ice/ICEMD5.cpp
${L}/ice/ICEMD5.h
${L}/ice/ICENetworkHelper.cpp
${L}/ice/ICENetworkHelper.h
${L}/ice/ICEPacketTimer.cpp
${L}/ice/ICEPacketTimer.h
${L}/ice/ICEPlatform.cpp
${L}/ice/ICEPlatform.h
${L}/ice/ICERelaying.cpp
${L}/ice/ICERelaying.h
${L}/ice/ICESession.cpp
${L}/ice/ICESession.h
${L}/ice/ICESHA1.cpp
${L}/ice/ICESHA1.h
${L}/ice/ICESocket.h
${L}/ice/ICEStream.cpp
${L}/ice/ICEStream.h
${L}/ice/ICEStunAttributes.cpp
${L}/ice/ICEStunAttributes.h
${L}/ice/ICEStunConfig.cpp
${L}/ice/ICEStunConfig.h
${L}/ice/ICEStunMessage.cpp
${L}/ice/ICEStunMessage.h
${L}/ice/ICEStunTransaction.cpp
${L}/ice/ICEStunTransaction.h
${L}/ice/ICESync.cpp
${L}/ice/ICESync.h
${L}/ice/ICETime.cpp
${L}/ice/ICETime.h
${L}/ice/ICETransactionList.cpp
${L}/ice/ICETransactionList.h
${L}/ice/ICETypes.h
${L}/ice/md5_impl.cpp
${L}/ice/md5_impl.h
) )
set (RTPHONE_HEADERS if (USE_OPUS_CODEC)
${rtphone_engine}/media/MT_Statistics.h set (DEFINES ${DEFINES} -DUSE_OPUS_CODEC)
${rtphone_engine}/media/MT_WebRtc.h
${rtphone_engine}/media/MT_Stream.h
${rtphone_engine}/media/MT_SrtpHelper.h
${rtphone_engine}/media/MT_SingleAudioStream.h
${rtphone_engine}/media/MT_NativeRtpSender.h
${rtphone_engine}/media/MT_Dtmf.h
${rtphone_engine}/media/MT_CodecList.h
${rtphone_engine}/media/MT_Codec.h
${rtphone_engine}/media/MT_Box.h
${rtphone_engine}/media/MT_AudioStream.h
${rtphone_engine}/media/MT_AudioReceiver.h
${rtphone_engine}/media/MT_AudioCodec.h
${rtphone_engine}/media/MT_AmrCodec.h
${rtphone_engine}/media/MT_EvsCodec.h
${rtphone_engine}/media/MT_CngHelper.h
${rtphone_engine}/agent/Agent_Impl.h
${rtphone_engine}/agent/Agent_AudioManager.h
${rtphone_engine}/endpoint/EP_Account.h
${rtphone_engine}/endpoint/EP_AudioProvider.h
${rtphone_engine}/endpoint/EP_DataProvider.h
${rtphone_engine}/endpoint/EP_Engine.h
${rtphone_engine}/endpoint/EP_NetworkQueue.h
${rtphone_engine}/endpoint/EP_Observer.h
${rtphone_engine}/endpoint/EP_Session.h
)
add_library (rtphone STATIC ${RTPHONE_SOURCES} ${RTPHONE_HEADERS})
add_subdirectory(${rtphone_libs}/resiprocate)
add_subdirectory(${rtphone_libs}/ice)
add_subdirectory(${rtphone_libs}/jrtplib/src)
add_subdirectory(${rtphone_libs}/libg729)
add_subdirectory(${rtphone_libs}/libevs)
add_subdirectory(${rtphone_libs}/libgsm)
add_subdirectory(${rtphone_libs}/gsmhr)
add_subdirectory(${rtphone_libs}/g722)
add_subdirectory(${rtphone_libs}/speexdsp)
add_subdirectory(${rtphone_libs}/srtp)
add_subdirectory(${rtphone_libs}/webrtc)
add_subdirectory(${rtphone_engine}/helper)
add_subdirectory(${rtphone_engine}/audio)
add_subdirectory(${rtphone_engine}/media)
set (LIBS ice_stack jrtplib g729_codec gsm_codec
gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp
uuid)
if (CMAKE_SYSTEM MATCHES "Win*")
set (LIBS ${LIBS} )
else ()
set (LIBS ${LIBS} dl uuid)
endif() endif()
if (USE_AMR_CODEC) add_library (rtphone STATIC ${RTPHONE_SOURCES})
set (LIBS ${LIBS})
endif (USE_AMR_CODEC) add_subdirectory(${L}/resiprocate)
add_subdirectory(${L}/jrtplib/src)
add_subdirectory(${L}/libg729)
if (USE_EVS_CODEC) if (USE_EVS_CODEC)
add_definitions(-DUSE_EVS_CODEC) add_subdirectory(${L}/libevs)
endif() endif()
target_link_libraries(rtphone add_subdirectory(${L}/libgsm)
ice_stack jrtplib g729_codec gsm_codec add_subdirectory(${L}/gsmhr)
gsmhr_codec g722_codec srtp resiprocate add_subdirectory(${L}/g722)
helper_lib add_subdirectory(${L}/speexdsp)
audio_lib add_subdirectory(${L}/libsrtp)
webrtc add_subdirectory(${L}/webrtc)
speexdsp add_subdirectory(${L}/opus)
# opus
uuid
${OPENSSL_SSL}
${OPENSSL_CRYPTO}
${LIBS})
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
if (USE_AMR_CODEC)
include (${LIB_PLATFORM}/platform_libs.cmake)
message("Media: AMR NB and WB codecs will be included.")
set (DEFINES ${DEFINES} -DUSE_AMR_CODEC)
set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
endif()
if (USE_EVS_CODEC)
message("Media: EVS codec will be included.")
set (DEFINES ${DEFINES} -DUSE_EVS_CODEC)
set (LIBS_STATIC ${LIBS_STATIC} evs_codec)
endif()
target_compile_definitions(rtphone PUBLIC ${DEFINES})
if (TARGET_LINUX)
target_link_options(rtphone PUBLIC -Wl,-Bstatic)
endif()
target_link_libraries(rtphone PUBLIC ${LIBS_STATIC} ${OPENSSL_SSL} ${OPENSSL_CRYPTO})
if (TARGET_LINUX)
target_link_options(rtphone PUBLIC -Wl,-Bdynamic)
endif()
target_include_directories(rtphone target_include_directories(rtphone
PUBLIC PUBLIC
@ -200,15 +363,18 @@ target_include_directories(rtphone
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
${CMAKE_CURRENT_SOURCE_DIR}/libs ${CMAKE_CURRENT_SOURCE_DIR}/libs
${LIB_PLATFORM}/opus/include ${LIB_PLATFORM}/opus/include
${E}/helper
${E}/audio
${E}/media
${L}
${L}/ice
PRIVATE PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/libs/ ${L}/libevs/lib_com
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com ${L}/libevs/lib_enc
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc ${L}/libevs/lib_dec
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec ${L}/speex/include
${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include ${L}/libs/json
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
) )
# For MSVC static builds # For MSVC static builds
configure_msvc_runtime() # set_property(TARGET rtphone PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

View File

@ -1,12 +1,12 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Agent_AudioManager.h" #include "Agent_AudioManager.h"
#include "../engine/audio/Audio_WavFile.h" #include "../engine/audio/Audio_WavFile.h"
#include "../engine/helper/HL_String.h"
#include "../engine/audio/Audio_Null.h" #include "../engine/audio/Audio_Null.h"
#include "HL_String.h"
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
# include "../engine/audio/Audio_Android.h" # include "../engine/audio/Audio_Android.h"
@ -15,11 +15,8 @@
#define LOG_SUBSYSTEM "AudioManager" #define LOG_SUBSYSTEM "AudioManager"
// ---------------- AudioManager -------------
//static AudioManager GAudioManager;
AudioManager::AudioManager() AudioManager::AudioManager()
:mTerminal(nullptr) :mTerminal(nullptr), mAudioMonitoring(nullptr)
{ {
mPlayer.setDelegate(this); mPlayer.setDelegate(this);
} }
@ -49,6 +46,16 @@ MT::Terminal* AudioManager::terminal()
return mTerminal; return mTerminal;
} }
void AudioManager::setAudioMonitoring(Audio::DataConnection* monitoring)
{
mAudioMonitoring = monitoring;
}
Audio::DataConnection* AudioManager::audioMonitoring()
{
return mAudioMonitoring;
}
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard) #define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
void AudioManager::start(int usageId) void AudioManager::start(int usageId)
{ {
@ -68,7 +75,14 @@ void AudioManager::start(int usageId)
// Disable AEC for now - because PVQA conflicts with speex AEC. // Disable AEC for now - because PVQA conflicts with speex AEC.
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull)); std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
if (!mTerminal->audio()) if (!mTerminal->audio())
mTerminal->setAudio(std::make_shared<Audio::DevicePair>(false, true)); {
auto audio = std::make_shared<Audio::DevicePair>();
audio->setAgc(true);
audio->setAec(false);
audio->setMonitoring(mAudioMonitoring);
mTerminal->setAudio(audio);
}
if (!mAudioInput) if (!mAudioInput)
{ {
@ -158,7 +172,7 @@ void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarg
// Check if file exists // Check if file exists
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>(); Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
#ifdef TARGET_WIN #ifdef TARGET_WIN
r->open(StringHelper::makeTstring(path)); r->open(strx::makeTstring(path));
#else #else
r->open(path); r->open(path);
#endif #endif

View File

@ -8,10 +8,7 @@
#include "../engine/audio/Audio_Interface.h" #include "../engine/audio/Audio_Interface.h"
#include "../engine/audio/Audio_Player.h" #include "../engine/audio/Audio_Player.h"
#include "../engine/endpoint/EP_Engine.h"
#include "../engine/media/MT_Box.h" #include "../engine/media/MT_Box.h"
#include "../engine/helper/HL_Log.h"
#include "../engine/helper/HL_Sync.h"
@ -49,6 +46,9 @@ public:
void setTerminal(MT::Terminal* terminal); void setTerminal(MT::Terminal* terminal);
MT::Terminal* terminal(); MT::Terminal* terminal();
void setAudioMonitoring(Audio::DataConnection* monitoring);
Audio::DataConnection* audioMonitoring();
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually // Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
void start(int usageId); void start(int usageId);
void stop(int usageId); void stop(int usageId);
@ -69,6 +69,7 @@ public:
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0); void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
void stopPlayFile(int usageId); void stopPlayFile(int usageId);
void onFilePlayed(Audio::Player::PlaylistItem& item); void onFilePlayed(Audio::Player::PlaylistItem& item);
// Must be called from main loop to release used audio devices // Must be called from main loop to release used audio devices
@ -79,6 +80,7 @@ protected:
Audio::POutputDevice mAudioOutput; Audio::POutputDevice mAudioOutput;
Audio::Player mPlayer; Audio::Player mPlayer;
MT::Terminal* mTerminal; MT::Terminal* mTerminal;
Audio::DataConnection* mAudioMonitoring;
std::map<int, int> UsageMap; std::map<int, int> UsageMap;
UsageCounter mUsage; UsageCounter mUsage;

View File

@ -4,15 +4,9 @@
#include "helper/HL_StreamState.h" #include "helper/HL_StreamState.h"
#include "helper/HL_VariantMap.h" #include "helper/HL_VariantMap.h"
#include "helper/HL_CsvReader.h" #include "helper/HL_CsvReader.h"
#include "helper/HL_Base64.h"
#include <fstream> #include <fstream>
#if defined(USE_PVQA_LIBRARY)
# include "pvqa++.h"
#endif
#if defined(USE_AQUA_LIBRARY)
# include "aqua++.h"
#endif
const std::string Status_Ok = "ok"; const std::string Status_Ok = "ok";
const std::string Status_SessionNotFound = "session not found"; const std::string Status_SessionNotFound = "session not found";
@ -38,6 +32,24 @@ AgentImpl::~AgentImpl()
stopAgentAndThread(); stopAgentAndThread();
} }
// Get access to internal audio manager. Value can be nullptr.
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
{
return mAudioManager;
}
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
{
mAudioMonitoring = monitoring;
if (mAudioManager)
mAudioManager->setAudioMonitoring(monitoring);
}
Audio::DataConnection* AgentImpl::monitoring() const
{
return mAudioMonitoring;
}
void AgentImpl::run() void AgentImpl::run()
{ {
while (!mShutdown) while (!mShutdown)
@ -55,10 +67,10 @@ std::string AgentImpl::command(const std::string& command)
if (command.empty()) if (command.empty())
return ""; return "";
Json::Value d, answer; JsonCpp::Value d, answer;
try try
{ {
Json::Reader r; JsonCpp::Reader r;
if (!r.parse(command, d)) if (!r.parse(command, d))
return ""; return "";
@ -138,7 +150,9 @@ std::string AgentImpl::command(const std::string& command)
{ {
answer["status"] = e.what(); answer["status"] = e.what();
} }
return answer.toStyledString(); std::string result = answer.toStyledString();
return result;
} }
bool AgentImpl::waitForData(int /*milliseconds*/) bool AgentImpl::waitForData(int /*milliseconds*/)
@ -151,29 +165,18 @@ std::string AgentImpl::read()
return ""; return "";
} }
void AgentImpl::processConfig(Json::Value &d, Json::Value &answer) void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
#if !defined(TARGET_ANDROID) && defined(USE_PVQA_LIBRARY)
// It works for desktop OSes only
// Because Android requires special initializing procedure (valid JNI environment context)
std::string pvqaLicense = d["pvqa-license"].asString(), pvqaConfig = d["pvqa-config"].asString();
if (!pvqaLicense.empty() && !pvqaConfig.empty())
sevana::pvqa::initialize(pvqaLicense, pvqaConfig);
#endif
#if !defined(TARGET_ANDROID) && defined(USE_AQUA_LIBRARY)
std::string aquaLicense = d["aqua-license"].asString();
if (!aquaLicense.empty())
sevana::aqua::initialize(aquaLicense.c_str(), aquaLicense.size());
#endif
std::string transport = d["transport"].asString(); std::string transport = d["transport"].asString();
config()[CONFIG_TRANSPORT] = (transport == "any") ? 0 : (transport == "udp" ? 1 : (transport == "tcp" ? 2 : 3)); config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
config()[CONFIG_IPV4] = d["ipv4"].asBool(); config()[CONFIG_IPV4] = d["ipv4"].asBool();
config()[CONFIG_IPV6] = d["ipv6"].asBool(); config()[CONFIG_IPV6] = d["ipv6"].asBool();
if (transport == "tls")
config()[CONFIG_SIPS] = true;
// Log file // Log file
std::string logfile = d["logfile"].asString(); std::string logfile = d["logfile"].asString();
ice::Logger& logger = ice::GLogger; ice::Logger& logger = ice::GLogger;
@ -185,10 +188,13 @@ void AgentImpl::processConfig(Json::Value &d, Json::Value &answer)
mUseNativeAudio = d["nativeaudio"].asBool(); mUseNativeAudio = d["nativeaudio"].asBool();
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString(); config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
config()[CONFIG_SIPS] = d["secure"].asBool();
config()[CONFIG_STUNSERVER_IP] = d["stun_server"].asString();
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processStart(Json::Value& /*request*/, Json::Value &answer) void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
if (mThread) if (mThread)
@ -197,6 +203,9 @@ void AgentImpl::processStart(Json::Value& /*request*/, Json::Value &answer)
return; // Started already return; // Started already
} }
// Process config (can be sent via start command as well)
// processConfig(request, answer);
// Start socket thread // Start socket thread
SocketHeap::instance().start(); SocketHeap::instance().start();
@ -208,18 +217,23 @@ void AgentImpl::processStart(Json::Value& /*request*/, Json::Value &answer)
PVariantMap priorityConfig = std::make_shared<VariantMap>(); PVariantMap priorityConfig = std::make_shared<VariantMap>();
MT::CodecList& cl = mTerminal->codeclist(); MT::CodecList& cl = mTerminal->codeclist();
for (int i=0; i<cl.count(); i++) for (int i=0; i<cl.count(); i++)
if (cl.codecAt(i).payloadType() < 96) priorityConfig->at(i) = i;
// Disable dynamic payload codec types - commented for now
/*if (cl.codecAt(i).payloadType() < 96)
priorityConfig->at(i) = i; priorityConfig->at(i) = i;
else else
priorityConfig->at(i) = -1; priorityConfig->at(i) = -1;*/
config()[CONFIG_CODEC_PRIORITY] = priorityConfig; config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
// Enable audio // Enable audio
mAudioManager = std::make_shared<AudioManager>(); mAudioManager = std::make_shared<AudioManager>();
mAudioManager->setTerminal(mTerminal.get()); mAudioManager->setTerminal(mTerminal.get());
if (mAudioMonitoring)
mAudioManager->setAudioMonitoring(mAudioMonitoring);
// Do not start here. Start right before call. // Do not start audio manager here. Start right before call.
// Initialize endpoint // Initialize endpoint
start(); start();
@ -230,13 +244,13 @@ void AgentImpl::processStart(Json::Value& /*request*/, Json::Value &answer)
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processStop(Json::Value& /*request*/, Json::Value& answer) void AgentImpl::processStop(JsonCpp::Value& /*request*/, JsonCpp::Value& answer)
{ {
stopAgentAndThread(); stopAgentAndThread();
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processCreateAccount(Json::Value &d, Json::Value& answer) void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
PVariantMap c = std::make_shared<VariantMap>(); PVariantMap c = std::make_shared<VariantMap>();
@ -246,7 +260,7 @@ void AgentImpl::processCreateAccount(Json::Value &d, Json::Value& answer)
(*c)[CONFIG_DOMAIN] = d["domain"].asString(); (*c)[CONFIG_DOMAIN] = d["domain"].asString();
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool(); (*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
auto nameAndPort = StringHelper::parseHost(d["stun_server"].asString(), 3478); auto nameAndPort = strx::parseHost(d["stun_server"].asString(), 3478);
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first; (*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second; (*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
@ -256,7 +270,7 @@ void AgentImpl::processCreateAccount(Json::Value &d, Json::Value& answer)
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processStartAccount(Json::Value& request, Json::Value& answer) void AgentImpl::processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map // Locate account in map
@ -270,7 +284,7 @@ void AgentImpl::processStartAccount(Json::Value& request, Json::Value& answer)
answer["status"] = Status_AccountNotFound; answer["status"] = Status_AccountNotFound;
} }
void AgentImpl::processSetUserInfoToAccount(Json::Value &request, Json::Value &answer) void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map // Locate account in map
@ -278,7 +292,7 @@ void AgentImpl::processSetUserInfoToAccount(Json::Value &request, Json::Value &a
if (accountIter != mAccountMap.end()) if (accountIter != mAccountMap.end())
{ {
Account::UserInfo info; Account::UserInfo info;
Json::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys) for (const std::string& k: keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
@ -290,7 +304,7 @@ void AgentImpl::processSetUserInfoToAccount(Json::Value &request, Json::Value &a
answer["status"] = Status_AccountNotFound; answer["status"] = Status_AccountNotFound;
} }
void AgentImpl::processCreateSession(Json::Value &request, Json::Value &answer) void AgentImpl::processCreateSession(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto accountIter = mAccountMap.find(request["account_id"].asInt()); auto accountIter = mAccountMap.find(request["account_id"].asInt());
@ -305,7 +319,7 @@ void AgentImpl::processCreateSession(Json::Value &request, Json::Value &answer)
answer["status"] = Status_AccountNotFound; answer["status"] = Status_AccountNotFound;
} }
void AgentImpl::processStartSession(Json::Value& request, Json::Value& answer) void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -328,29 +342,32 @@ void AgentImpl::processStartSession(Json::Value& request, Json::Value& answer)
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal); PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing)); audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
#if defined(USE_AQUA_LIBRARY) /*#if defined(USE_AQUA_LIBRARY)
std::string path_faults = request["path_faults"].asString(); std::string path_faults = request["path_faults"].asString();
sevana::aqua::config config = { sevana::aqua::config config = {
{"avlp", "off"}, {"avlp", "off"},
{"smtnrm", "off"},
{"decor", "off"}, {"decor", "off"},
{"mprio", "off"}, {"mprio", "off"},
{ "miter", "1" }, {"npnt", "auto"},
{ "enorm", "off" },
{"voip", "on"}, {"voip", "on"},
{ "g711", "on" }, {"enorm", "rms"},
{"g711", "off"},
{"spfrcor", "on"}, {"spfrcor", "on"},
{"grad", "off"}, {"grad", "off"},
{ "ratem", "%%m" }, {"tmc", "on"},
{ "trim", "a 2" }, {"miter", "1"},
{ "ratem", "%m" },
{ "trim", "a 10" },
{ "output", "json" }, { "output", "json" },
{ "fau", path_faults}, { "fau", path_faults},
{ "specp", "32"} { "specp", "32"}
}; };
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json"; // std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
/*if (temp_path.size()) // if (temp_path.size())
config += " -fau " + temp_path; */ // config += " -fau " + temp_path;
auto qc = std::make_shared<sevana::aqua>(); auto qc = std::make_shared<sevana::aqua>();
if (!qc->is_open()) if (!qc->is_open())
@ -363,12 +380,13 @@ void AgentImpl::processStartSession(Json::Value& request, Json::Value& answer)
mAquaMap[sessionIter->first] = qc; mAquaMap[sessionIter->first] = qc;
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get()); dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
#endif #endif
*/
// TODO: support SRTP via StreamState::Srtp option in audio provider state // TODO: support SRTP via StreamState::Srtp option in audio provider state
// Get user headers // Get user headers
Session::UserHeaders info; Session::UserHeaders info;
Json::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys) for (const std::string& k: keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
@ -384,7 +402,7 @@ void AgentImpl::processStartSession(Json::Value& request, Json::Value& answer)
} }
} }
void AgentImpl::processStopSession(Json::Value& request, Json::Value& answer) void AgentImpl::processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -399,7 +417,7 @@ void AgentImpl::processStopSession(Json::Value& request, Json::Value& answer)
answer["status"] = Status_SessionNotFound; answer["status"] = Status_SessionNotFound;
} }
void AgentImpl::processAcceptSession(Json::Value& request, Json::Value& answer) void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto sessionIter = mSessionMap.find(request["session_id"].asInt()); auto sessionIter = mSessionMap.find(request["session_id"].asInt());
@ -413,7 +431,7 @@ void AgentImpl::processAcceptSession(Json::Value& request, Json::Value& answer)
// Get user headers // Get user headers
Session::UserHeaders info; Session::UserHeaders info;
Json::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys) for (const std::string& k: keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
@ -429,7 +447,7 @@ void AgentImpl::processAcceptSession(Json::Value& request, Json::Value& answer)
} }
void AgentImpl::processDestroySession(Json::Value& request, Json::Value& answer) void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -437,13 +455,13 @@ void AgentImpl::processDestroySession(Json::Value& request, Json::Value& answer)
auto sessionIter = mSessionMap.find(sessionId); auto sessionIter = mSessionMap.find(sessionId);
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
mSessionMap.erase(sessionIter); mSessionMap.erase(sessionIter);
#if defined(USE_AQUA_LIBRARY) //#if defined(USE_AQUA_LIBRARY)
closeAqua(sessionId); // closeAqua(sessionId);
#endif //#endif
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processWaitForEvent(Json::Value &request, Json::Value &answer) void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -463,43 +481,8 @@ void AgentImpl::processWaitForEvent(Json::Value &request, Json::Value &answer)
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
#if defined(USE_PVQA_LIBRARY)
static Json::Value CsvReportToJson(const std::string& report)
{
Json::Value detectorValues;
std::istringstream iss(report);
CsvReader reader(iss);
std::vector<std::string> cells;
if (reader.readLine(cells))
{
Json::Value detectorNames;
for (size_t nameIndex = 0; nameIndex < cells.size(); nameIndex++)
detectorNames[static_cast<int>(nameIndex)] = StringHelper::trim(cells[nameIndex]);
// Put first line name of columns
detectorValues[0] = detectorNames;
int rowIndex = 1; void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
while (reader.readLine(cells))
{
// Skip last column for now
Json::Value row;
for (size_t valueIndex = 0; valueIndex < cells.size(); valueIndex++)
{
bool isFloat = true;
float v = StringHelper::toFloat(cells[valueIndex], 0.0, &isFloat);
if (isFloat)
row[static_cast<int>(valueIndex)] = static_cast<double>(v);
else
row[static_cast<int>(valueIndex)] = cells[valueIndex];
}
detectorValues[rowIndex++] = row;
}
}
return detectorValues;
}
#endif
void AgentImpl::processGetMediaStats(Json::Value& request, Json::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
int sessionId = request["session_id"].asInt(); int sessionId = request["session_id"].asInt();
@ -508,26 +491,18 @@ void AgentImpl::processGetMediaStats(Json::Value& request, Json::Value& answer)
{ {
PSession session = sessionIter->second; PSession session = sessionIter->second;
VariantMap result; VariantMap result;
bool includePvqa = request["include_pvqa"].asBool(); session->getSessionInfo(Session::InfoOptions::Detailed,
#if defined(USE_AQUA_LIBRARY)
bool includeAqua = request["include_aqua"].asBool();
std::string aquaReference = request["aqua_reference_audio"].asString();
#endif
session->getSessionInfo(includePvqa ? Session::InfoOptions::Detailed : Session::InfoOptions::Standard,
result); result);
if (result.exists(SessionInfo_AudioCodec)) if (result.exists(SessionInfo_AudioCodec))
answer["codec"] = result[SessionInfo_AudioCodec].asStdString(); answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
if (result.exists(SessionInfo_NetworkMos)) if (result.exists(SessionInfo_NetworkMos))
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat(); answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
#if defined(USE_PVQA_LIBRARY)
if (result.exists(SessionInfo_PvqaMos))
answer["pvqa_mos"] = result[SessionInfo_PvqaMos].asFloat();
if (result.exists(SessionInfo_PvqaReport))
answer["pvqa_report"] = CsvReportToJson(result[SessionInfo_PvqaReport].asStdString());
#endif
if (result.exists(SessionInfo_PacketLoss)) if (result.exists(SessionInfo_PacketLoss))
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt(); answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
if (result.exists(SessionInfo_DroppedRtp))
answer["rtp_dropped"] = result[SessionInfo_DroppedRtp].asInt();
if (result.exists(SessionInfo_SentRtp)) if (result.exists(SessionInfo_SentRtp))
answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt(); answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt();
if (result.exists(SessionInfo_ReceivedRtp)) if (result.exists(SessionInfo_ReceivedRtp))
@ -545,73 +520,13 @@ void AgentImpl::processGetMediaStats(Json::Value& request, Json::Value& answer)
if (result.exists(SessionInfo_RemotePeer)) if (result.exists(SessionInfo_RemotePeer))
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString(); answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
#if defined(USE_AQUA_LIBRARY)
if (includeAqua)
{
answer["incoming_audio"] = mAquaIncoming.hexstring();
ICELogInfo(<< "Running AQuA analyzer.");
ByteBuffer referenceAudio;
// Read AQuA reference audio from file if available
if (aquaReference.empty())
{
ICELogCritical(<< "AQuA reference audio file is not set, skipping analyzing.");
}
else {
auto sa = mAquaMap[sessionIter->first];
if (sa) {
Audio::WavFileReader reader;
reader.open(StringHelper::makeTstring(aquaReference));
if (reader.isOpened()) {
char buffer[1024];
int wasRead = 0;
do {
wasRead = reader.read(buffer, 1024);
if (wasRead > 0)
referenceAudio.appendBuffer(buffer, wasRead);
} while (wasRead == 1024);
}
else {
ICELogCritical(<< "Failed to read AQuA reference audio, error code: " << reader.lastError());
}
sevana::aqua::audio_buffer test(mAquaIncoming.data(), mAquaIncoming.size()),
reference(referenceAudio.data(), referenceAudio.size());
test.mRate = AUDIO_SAMPLERATE;
reference.mRate = AUDIO_SAMPLERATE;
test.mChannels = AUDIO_CHANNELS;
reference.mChannels = AUDIO_CHANNELS;
ICELogInfo(
<< "Comparing test audio " << mAquaIncoming.size() << " bytes with reference audio " << referenceAudio.size() << " bytes.");
auto r = sa->compare(reference, test);
if (r.mErrorCode) {
ICELogInfo(
<< "Error code: " << r.mErrorCode << ", msg: " << r.mErrorMessage);
} else {
ICELogInfo(<< "MOS: " << r.mMos << ", faults: " << r.mFaultsText);
}
answer["aqua_mos"] = r.mMos;
answer["aqua_report"] = r.mFaultsText;
if (r.mErrorCode) {
answer["aqua_error_code"] = r.mErrorCode;
answer["aqua_error_message"] = r.mErrorMessage;
}
closeAqua(sessionIter->first);
}
}
// Remove test audio
mAquaIncoming.clear(); mAquaOutgoing.clear();
}
#endif
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
else else
answer["status"] = Status_SessionNotFound; answer["status"] = Status_SessionNotFound;
} }
void AgentImpl::processNetworkChanged(Json::Value& /*request*/, Json::Value& /*answer*/) void AgentImpl::processNetworkChanged(JsonCpp::Value& /*request*/, JsonCpp::Value& /*answer*/)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
} }
@ -619,30 +534,28 @@ void AgentImpl::processNetworkChanged(Json::Value& /*request*/, Json::Value& /*a
const std::string BeginCertificate = "-----BEGIN CERTIFICATE-----"; const std::string BeginCertificate = "-----BEGIN CERTIFICATE-----";
const std::string EndCertificate = "-----END CERTIFICATE-----"; const std::string EndCertificate = "-----END CERTIFICATE-----";
void AgentImpl::processAddRootCert(Json::Value& request, Json::Value& answer) void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
std::string pem = request["cert"].asString(); std::string pem = request["cert"].asString();
std::string::size_type pb = 0, pe = 0; std::string::size_type pb = 0, pe = 0;
while (pb != std::string::npos && pe != std::string::npos) {
pb = pem.find(BeginCertificate, pb);
pe = pem.find(EndCertificate, pe);
for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe); if (pb != std::string::npos && pe != std::string::npos && pe > pb) {
pb != std::string::npos && pe != std::string::npos; std::string cert = pem.substr(pb, pe - pb + EndCertificate.size());
pb = pem.find(BeginCertificate, pb + BeginCertificate.size()), pe = pem.find(EndCertificate, pe + EndCertificate.size()))
{
// Get single certificate
std::string cert = pem.substr(pb, pe + EndCertificate.size());
//int size = cert.size();
addRootCert(ByteBuffer(cert.c_str(), cert.size())); addRootCert(ByteBuffer(cert.c_str(), cert.size()));
// Delete processed part pb = ++pe;
pem.erase(0, pe + EndCertificate.size()); }
} }
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processLogMessage(Json::Value &request, Json::Value &answer) void AgentImpl::processLogMessage(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
int level = request["level"].asInt(); int level = request["level"].asInt();
std::string message = request["message"].asString(); std::string message = request["message"].asString();
@ -692,7 +605,7 @@ void AgentImpl::stopAgentAndThread()
SocketHeap::instance().stop(); SocketHeap::instance().stop();
} }
void AgentImpl::processUseStreamForSession(Json::Value& request, Json::Value& answer) void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
SessionMap::iterator sessionIter = mSessionMap.find(request["session_id"].asInt()); SessionMap::iterator sessionIter = mSessionMap.find(request["session_id"].asInt());
@ -724,7 +637,7 @@ void AgentImpl::processUseStreamForSession(Json::Value& request, Json::Value& an
else else
{ {
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>(); Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
if (!reader->open(StringHelper::makeTstring(path))) if (!reader->open(strx::makeTstring(path)))
answer["status"] = Status_FailedToOpenFile; answer["status"] = Status_FailedToOpenFile;
else else
{ {
@ -745,7 +658,7 @@ void AgentImpl::processUseStreamForSession(Json::Value& request, Json::Value& an
else else
{ {
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>(); Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
if (!writer->open(StringHelper::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS)) if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
answer["status"] = Status_FailedToOpenFile; answer["status"] = Status_FailedToOpenFile;
else else
{ {
@ -768,29 +681,18 @@ void AgentImpl::processUseStreamForSession(Json::Value& request, Json::Value& an
} }
} }
#if defined(USE_AQUA_LIBRARY)
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
{ {
/* if (mIncomingAudioDump && direction == MT::Stream::MediaDirection::Incoming) /*switch (direction)
mIncomingAudioDump->write(data, length);
if (mOutgoingAudioDump && direction == MT::Stream::MediaDirection::Outgoing)
mOutgoingAudioDump->write(data, length);*/
// User tag points to accumulator object which includes
// auto* sa = reinterpret_cast<sevana::aqua*>(userTag);
switch (direction)
{ {
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break; case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break; case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
}*/
} }
}
#endif
// Called on new incoming session; providers shoukld // Called on new incoming session; providers shoukld
#define EVENT_WITH_NAME(X) Json::Value v; v["event_name"] = X; #define EVENT_WITH_NAME(X) JsonCpp::Value v; v["event_name"] = X;
PDataProvider AgentImpl::onProviderNeeded(const std::string& name) PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
{ {
@ -935,7 +837,7 @@ void AgentImpl::onSipConnectionFailed()
addEvent(v); addEvent(v);
} }
void AgentImpl::addEvent(const Json::Value& v) void AgentImpl::addEvent(const JsonCpp::Value& v)
{ {
std::unique_lock<std::mutex> lock(mEventListMutex); std::unique_lock<std::mutex> lock(mEventListMutex);
mEventList.push_back(v); mEventList.push_back(v);
@ -943,12 +845,12 @@ void AgentImpl::addEvent(const Json::Value& v)
} }
#if defined(USE_AQUA_LIBRARY) #if defined(USE_AQUA_LIBRARY)
void AgentImpl::closeAqua(int sessionId) /*void AgentImpl::closeAqua(int sessionId)
{ {
auto aquaIter = mAquaMap.find(sessionId); auto aquaIter = mAquaMap.find(sessionId);
if (aquaIter != mAquaMap.end()) { if (aquaIter != mAquaMap.end()) {
aquaIter->second->close(); aquaIter->second->close();
mAquaMap.erase(aquaIter); mAquaMap.erase(aquaIter);
} }
} }*/
#endif #endif

View File

@ -13,24 +13,15 @@
#include "Agent_AudioManager.h" #include "Agent_AudioManager.h"
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#if defined(USE_AQUA_LIBRARY)
# include "aqua++.h"
#endif
#if defined(USE_PVQA_LIBRARY)
# include "pvqa++.h"
#endif
class AgentImpl: public UserAgent class AgentImpl: public UserAgent, public MT::Stream::MediaObserver
#if defined(USE_AQUA_LIBRARY)
, public MT::Stream::MediaObserver
#endif
{ {
protected: protected:
std::recursive_mutex mAgentMutex; std::recursive_mutex mAgentMutex;
std::mutex mEventListMutex; std::mutex mEventListMutex;
std::condition_variable mEventListChangeCondVar; std::condition_variable mEventListChangeCondVar;
std::vector<Json::Value> mEventList; std::vector<JsonCpp::Value> mEventList;
bool mUseNativeAudio = false; bool mUseNativeAudio = false;
typedef std::map<int, PAccount> AccountMap; typedef std::map<int, PAccount> AccountMap;
@ -39,39 +30,32 @@ protected:
typedef std::map<int, PSession> SessionMap; typedef std::map<int, PSession> SessionMap;
SessionMap mSessionMap; SessionMap mSessionMap;
#if defined(USE_AQUA_LIBRARY)
// Keys are the same as used in mSessionMap
typedef std::map<int, std::shared_ptr<sevana::aqua>> AquaMap;
AquaMap mAquaMap;
ByteBuffer mAquaIncoming, mAquaOutgoing;
void closeAqua(int sessionId);
#endif
std::shared_ptr<std::thread> mThread; std::shared_ptr<std::thread> mThread;
volatile bool mShutdown; volatile bool mShutdown;
std::shared_ptr<MT::Terminal> mTerminal; std::shared_ptr<MT::Terminal> mTerminal;
std::shared_ptr<AudioManager> mAudioManager; std::shared_ptr<AudioManager> mAudioManager;
//Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump; Audio::DataConnection* mAudioMonitoring = nullptr;
void run(); void run();
void addEvent(const Json::Value& v); void addEvent(const JsonCpp::Value& v);
void processConfig(Json::Value& request, Json::Value& answer); void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStart(Json::Value& request, Json::Value& answer); void processStart(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStop(Json::Value& request, Json::Value& answer); void processStop(JsonCpp::Value& request, JsonCpp::Value& answer);
void processCreateAccount(Json::Value& request, Json::Value& answer); void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStartAccount(Json::Value& request, Json::Value& answer); void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processSetUserInfoToAccount(Json::Value& request, Json::Value& answer); void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processCreateSession(Json::Value& request, Json::Value& answer); void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStartSession(Json::Value& request, Json::Value& answer); void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStopSession(Json::Value& request, Json::Value& answer); void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processAcceptSession(Json::Value& request, Json::Value& answer); void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processDestroySession(Json::Value& request, Json::Value& answer); void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processWaitForEvent(Json::Value& request, Json::Value& answer); void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer);
void processGetMediaStats(Json::Value& request, Json::Value& answer); void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer);
void processUseStreamForSession(Json::Value& request, Json::Value& answer); void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processNetworkChanged(Json::Value& request, Json::Value& answer); void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer);
void processAddRootCert(Json::Value& request, Json::Value& answer); void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer);
void processLogMessage(Json::Value& request, Json::Value& answer); void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer);
void stopAgentAndThread(); void stopAgentAndThread();
public: public:
@ -82,6 +66,12 @@ public:
bool waitForData(int milliseconds); bool waitForData(int milliseconds);
std::string read(); std::string read();
// Get access to internal audio manager. Value can be nullptr.
const std::shared_ptr<AudioManager>& audioManager() const;
void setAudioMonitoring(Audio::DataConnection* monitoring);
Audio::DataConnection* monitoring() const;
// UserAgent overrides // UserAgent overrides
// Called on new incoming session; providers shoukld // Called on new incoming session; providers shoukld
PDataProvider onProviderNeeded(const std::string& name) override; PDataProvider onProviderNeeded(const std::string& name) override;
@ -131,10 +121,8 @@ public:
// Called when problem with SIP connection(s) detected // Called when problem with SIP connection(s) detected
void onSipConnectionFailed() override; void onSipConnectionFailed() override;
#if defined(USE_AQUA_LIBRARY)
// Called on incoming & outgoing audio for voice sessions // Called on incoming & outgoing audio for voice sessions
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override; void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
#endif
}; };
#endif #endif

View File

@ -365,8 +365,9 @@ void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void *
// ------------ AndroidOutputDevice ----------------- // ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId) AndroidOutputDevice::AndroidOutputDevice(int devId)
{ {
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this)); ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
} }
AndroidOutputDevice::~AndroidOutputDevice() AndroidOutputDevice::~AndroidOutputDevice()
{ {
ICELogDebug(<< "Deleting AndroidOutputDevice."); ICELogDebug(<< "Deleting AndroidOutputDevice.");

View File

@ -25,7 +25,6 @@
namespace Audio namespace Audio
{ {
class AndroidEnumerator: public Enumerator class AndroidEnumerator: public Enumerator
{ {
public: public:

View File

@ -0,0 +1,245 @@
#include "Audio_AndroidOboe.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Log.h"
#include <mutex>
#include <iostream>
#include <stdexcept>
#include "../helper/HL_String.h"
#include "../helper/HL_Time.h"
#ifdef TARGET_ANDROID
#define LOG_SUBSYSTEM "Audio"
using namespace Audio;
// -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator()
{}
AndroidEnumerator::~AndroidEnumerator()
{}
int AndroidEnumerator::indexOfDefaultDevice()
{
return 0;
}
int AndroidEnumerator::count()
{
return 1;
}
int AndroidEnumerator::idAt(int index)
{
return 0;
}
std::string AndroidEnumerator::nameAt(int index)
{
return "Audio";
}
void AndroidEnumerator::open(int direction)
{}
void AndroidEnumerator::close()
{}
// --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId)
{}
AndroidInputDevice::~AndroidInputDevice()
{
close();
}
bool AndroidInputDevice::open()
{
if (active())
return true;
oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Input);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setFormat(oboe::AudioFormat::I16);
builder.setChannelCount(oboe::ChannelCount::Mono);
builder.setCallback(this);
oboe::Result rescode = builder.openStream(&mRecordingStream);
if (rescode != oboe::Result::OK)
return false;
mDeviceRate = mRecordingStream->getSampleRate();
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
mActive = true;
rescode = mRecordingStream->requestStart();
if (rescode != oboe::Result::OK)
{
close();
mActive = false;
}
return mActive;
}
void AndroidInputDevice::close()
{
// There is no check for active() value because close() can be called to cleanup after bad open() call.
if (mRecordingStream != nullptr)
{
mRecordingStream->close();
delete mRecordingStream; mRecordingStream = nullptr;
}
mActive = false;
}
oboe::DataCallbackResult
AndroidInputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
{
std::unique_lock<std::mutex> l(mMutex);
// Send data to AudioPair
if (mConnection)
mConnection->onMicData(getFormat(), audioData, numFrames);
return oboe::DataCallbackResult::Continue;
}
Format AndroidInputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidInputDevice::active() const
{
return mActive;
}
bool AndroidInputDevice::fakeMode()
{
return false;
}
void AndroidInputDevice::setFakeMode(bool fakemode)
{}
int AndroidInputDevice::readBuffer(void* buffer)
{
throw std::runtime_error("AndroidInputDevice::readBuffer() is not implemented.");
}
// ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId)
{
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
}
AndroidOutputDevice::~AndroidOutputDevice()
{
ICELogDebug(<< "Deleting AndroidOutputDevice.");
close();
}
bool AndroidOutputDevice::open()
{
std::unique_lock<std::mutex> l(mMutex);
if (mActive)
return true;
mRequestedFrames = 0;
mStartTime = 0.0;
mEndTime = 0.0;
oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Output);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setFormat(oboe::AudioFormat::I16);
builder.setChannelCount(oboe::ChannelCount::Mono);
// builder.setDataCallback(this);
builder.setCallback(this);
//builder.setErrorCallback(this)
oboe::Result rescode = builder.openStream(&mPlayingStream);
if (rescode != oboe::Result::OK)
return false;
mDeviceRate = mPlayingStream->getSampleRate();
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
mActive = true;
rescode = mPlayingStream->requestStart();
if (rescode != oboe::Result::OK)
{
close();
mActive = false;
}
return mActive;
}
void AndroidOutputDevice::close()
{
std::unique_lock<std::mutex> l(mMutex);
if (!mActive)
return;
if (mPlayingStream != nullptr)
{
mPlayingStream->close();
delete mPlayingStream; mPlayingStream = nullptr;
}
mEndTime = now_ms();
mActive = false;
ICELogInfo(<< "For time " << mEndTime - mStartTime << " ms was requested "
<< float(mRequestedFrames) / getFormat().mRate * 1000 << " ms");
}
Format AndroidOutputDevice::getFormat()
{
return {mDeviceRate, 1};
}
bool AndroidOutputDevice::fakeMode()
{
return false;
}
void AndroidOutputDevice::setFakeMode(bool /*fakemode*/)
{
}
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
{
if (mInShutdown)
return oboe::DataCallbackResult::Stop;
if (mStartTime == 0.0)
mStartTime = now_ms();
// Ask producer about data
memset(audioData, 0, numFrames * 2);
if (mConnection)
{
Format f = getFormat();
if (f.mRate != 0)
mConnection->onSpkData(f, audioData, numFrames * 2);
}
mRequestedFrames += numFrames;
return oboe::DataCallbackResult::Continue;
}
// TODO - special case https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result) {
if (result == oboe::Result::ErrorDisconnected) {
// LOGI("Restarting AudioStream after disconnect");
// soundEngine.restart(); // please check oboe samples for soundEngine.restart(); call
}
}
#endif // TARGET_ANDROID

View File

@ -0,0 +1,109 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_ANDROID_OBOE_H
#define __AUDIO_ANDROID_OBOE_H
#ifdef TARGET_ANDROID
#include "Audio_Interface.h"
#include "Audio_Helper.h"
#include "Audio_Resampler.h"
#include "Audio_DataWindow.h"
#include "../helper/HL_Pointer.h"
#include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Exception.h"
#include "../helper/HL_Statistics.h"
#include <memory>
#include <string>
#include "oboe/Oboe.h"
namespace Audio
{
class AndroidEnumerator: public Enumerator
{
public:
AndroidEnumerator();
~AndroidEnumerator();
void open(int direction);
void close();
int count();
std::string nameAt(int index);
int idAt(int index);
int indexOfDefaultDevice();
protected:
};
class AndroidInputDevice: public InputDevice, public oboe::AudioStreamCallback
{
public:
AndroidInputDevice(int devId);
~AndroidInputDevice();
bool open();
void close();
Format getFormat();
bool fakeMode();
void setFakeMode(bool fakemode);
int readBuffer(void* buffer);
bool active() const;
oboe::DataCallbackResult
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
protected:
bool mActive = false;
oboe::AudioStream* mRecordingStream = nullptr;
PResampler mResampler;
DataWindow mDeviceRateCache, mSdkRateCache;
int mDeviceRate; // Actual rate of opened recorder
int mBufferSize; // Size of buffer used for recording (at native sample rate)
DataWindow mRecorderBuffer;
std::condition_variable mDataCondVar;
int mRecorderBufferIndex;
std::mutex mMutex;
};
class AndroidOutputDevice: public OutputDevice, public oboe::AudioStreamCallback
{
public:
AndroidOutputDevice(int devId);
~AndroidOutputDevice();
bool open();
void close();
Format getFormat();
bool fakeMode();
void setFakeMode(bool fakemode);
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result);
protected:
std::mutex mMutex;
int mDeviceRate = 0;
oboe::AudioStream* mPlayingStream = nullptr;
DataWindow mPlayBuffer;
int mBufferIndex = 0, mBufferSize = 0;
bool mInShutdown = false;
bool mActive = false;
// Statistics
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
};
}
#endif // TARGET_ANDROID
#endif // __AUDIO_ANDROID_H

View File

@ -56,17 +56,25 @@ void DataWindow::add(const void* data, int length)
if (length > mCapacity) if (length > mCapacity)
{ {
// Use latest bytes from data buffer in this case.
data = (char*)data + length - mCapacity; data = (char*)data + length - mCapacity;
length = mCapacity; length = mCapacity;
} }
// Check how much free space we have
int avail = mCapacity - mFilled; int avail = mCapacity - mFilled;
if (avail < length) if (avail < length)
{ {
memmove(mData, mData + length - avail, mFilled - (length - avail)); // Find the portion of data to move & save
mFilled -= length - avail; int delta = length - avail;
// Move the data
if (mFilled - delta > 0)
memmove(mData, mData + delta, mFilled - delta);
mFilled -= delta;
} }
memcpy(mData + mFilled, data, length); memcpy(mData + mFilled, data, length);
mFilled += length; mFilled += length;
} }

View File

@ -13,8 +13,9 @@
using namespace Audio; using namespace Audio;
// --- DevicePair --- // --- DevicePair ---
DevicePair::DevicePair(bool aec, bool agc) DevicePair::DevicePair()
:mConfig(NULL), mDelegate(NULL), mAec(aec), mAgc(agc), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS) :mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS),
mMonitoring(nullptr)
{ {
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1)); mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1)); mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
@ -28,26 +29,50 @@ DevicePair::~DevicePair()
if (mInput) if (mInput)
{ {
if (mInput->connection() == this) if (mInput->connection() == this)
mInput->setConnection(NULL); mInput->setConnection(nullptr);
mInput.reset(); mInput.reset();
} }
if (mOutput) if (mOutput)
{ {
if (mOutput->connection() == this) if (mOutput->connection() == this)
mOutput->setConnection(NULL); mOutput->setConnection(nullptr);
mOutput.reset(); mOutput.reset();
} }
} }
DevicePair& DevicePair::setAec(bool aec)
{
mAec = aec;
return *this;
}
bool DevicePair::aec()
{
return mAec;
}
DevicePair& DevicePair::setAgc(bool agc)
{
mAgc = agc;
return *this;
}
bool DevicePair::agc()
{
return mAgc;
}
VariantMap* DevicePair::config() VariantMap* DevicePair::config()
{ {
return mConfig; return mConfig;
} }
void DevicePair::setConfig(VariantMap* config) DevicePair& DevicePair::setConfig(VariantMap* config)
{ {
mConfig = config; mConfig = config;
return *this;
} }
PInputDevice DevicePair::input() PInputDevice DevicePair::input()
@ -55,15 +80,17 @@ PInputDevice DevicePair::input()
return mInput; return mInput;
} }
void DevicePair::setInput(PInputDevice input) DevicePair& DevicePair::setInput(PInputDevice input)
{ {
if (mInput == input) if (mInput == input)
return; return *this;
mInput = input; mInput = input;
mInput->setConnection(this); mInput->setConnection(this);
if (mDelegate) if (mDelegate)
mDelegate->deviceChanged(this); mDelegate->deviceChanged(this);
return *this;
} }
POutputDevice DevicePair::output() POutputDevice DevicePair::output()
@ -71,14 +98,17 @@ POutputDevice DevicePair::output()
return mOutput; return mOutput;
} }
void DevicePair::setOutput(POutputDevice output) DevicePair& DevicePair::setOutput(POutputDevice output)
{ {
if (output == mOutput) if (output == mOutput)
return; return *this;
mOutput = output; mOutput = output;
mOutput->setConnection(this); mOutput->setConnection(this);
if (mDelegate) if (mDelegate)
mDelegate->deviceChanged(this); mDelegate->deviceChanged(this);
return *this;
} }
bool DevicePair::start() bool DevicePair::start()
@ -88,6 +118,7 @@ bool DevicePair::start()
result = mInput->open(); result = mInput->open();
if (mOutput && result) if (mOutput && result)
result &= mOutput->open(); result &= mOutput->open();
return result; return result;
} }
@ -99,9 +130,10 @@ void DevicePair::stop()
mOutput->close(); mOutput->close();
} }
void DevicePair::setDelegate(Delegate* dc) DevicePair& DevicePair::setDelegate(Delegate* dc)
{ {
mDelegate = dc; mDelegate = dc;
return *this;
} }
DevicePair::Delegate* DevicePair::delegate() DevicePair::Delegate* DevicePair::delegate()
@ -109,6 +141,17 @@ DevicePair::Delegate* DevicePair::delegate()
return mDelegate; return mDelegate;
} }
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
{
mMonitoring = monitoring;
return *this;
}
DataConnection* DevicePair::monitoring()
{
return mMonitoring;
}
Player& DevicePair::player() Player& DevicePair::player()
{ {
return mPlayer; return mPlayer;
@ -185,6 +228,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
{ {
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity()); memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
// Ask audio data on main AUDIO_SAMPLERATE frequency
if (mDelegate) if (mDelegate)
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity()); mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
@ -197,8 +241,12 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
// Resample these 10 milliseconds it to native format // Resample these 10 milliseconds it to native format
size_t wasProcessed = 0; size_t wasProcessed = 0;
size_t wasProduced = mSpkResampler.resample(AUDIO_SAMPLERATE, mOutput10msBuffer.data(), mOutput10msBuffer.capacity(), wasProcessed, f.mRate, size_t wasProduced = mSpkResampler.resample(Format().mRate,
mOutputNativeData.mutableData() + mOutputNativeData.filled(), mOutputNativeData.capacity() - mOutputNativeData.filled()); mOutput10msBuffer.data(),
mOutput10msBuffer.capacity(),
wasProcessed, f.mRate,
mOutputNativeData.mutableData() + mOutputNativeData.filled(),
mOutputNativeData.capacity() - mOutputNativeData.filled());
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced); mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
#ifdef CONSOLE_LOGGING #ifdef CONSOLE_LOGGING
printf("Resampled %d to %d\n", wasProcessed, wasProduced); printf("Resampled %d to %d\n", wasProcessed, wasProduced);
@ -206,7 +254,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
} }
} }
assert(mOutputNativeData.filled() >= length); // assert(mOutputNativeData.filled() >= length);
#ifdef DUMP_NATIVEOUTPUT #ifdef DUMP_NATIVEOUTPUT
if (mNativeOutputDump) if (mNativeOutputDump)
mNativeOutputDump->write(mOutputNativeData.data(), length); mNativeOutputDump->write(mOutputNativeData.data(), length);
@ -214,6 +262,10 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
mOutputNativeData.read(buffer, length); mOutputNativeData.read(buffer, length);
// Send data to monitoring if needed
if (mMonitoring)
mMonitoring->onSpkData(f, buffer, length);
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short)) #define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
// AEC filter wants frames. // AEC filter wants frames.
@ -224,7 +276,6 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex); mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE); mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
} }
//ICELogMedia(<< "Audio::DevicePair::onSpkData() end")
} }
void DevicePair::processMicData(const Format& f, void* buffer, int length) void DevicePair::processMicData(const Format& f, void* buffer, int length)

View File

@ -26,29 +26,32 @@ namespace Audio
virtual void deviceChanged(DevicePair* dpair) = 0; virtual void deviceChanged(DevicePair* dpair) = 0;
}; };
DevicePair(bool aec = true, bool agc = true); DevicePair();
virtual ~DevicePair(); virtual ~DevicePair();
void setAec(bool aec); DevicePair& setAec(bool aec);
bool aec(); bool aec();
void setAgc(bool agc); DevicePair& setAgc(bool agc);
bool agc(); bool agc();
VariantMap* config(); VariantMap* config();
void setConfig(VariantMap* config); DevicePair& setConfig(VariantMap* config);
PInputDevice input(); PInputDevice input();
void setInput(PInputDevice input); DevicePair& setInput(PInputDevice input);
POutputDevice output(); POutputDevice output();
void setOutput(POutputDevice output); DevicePair& setOutput(POutputDevice output);
bool start(); bool start();
void stop(); void stop();
void setDelegate(Delegate* dc); DevicePair& setDelegate(Delegate* dc);
Delegate* delegate(); Delegate* delegate();
DevicePair& setMonitoring(DataConnection* monitoring);
DataConnection* monitoring();
Player& player(); Player& player();
protected: protected:
@ -63,6 +66,7 @@ namespace Audio
Player mPlayer; Player mPlayer;
UniversalResampler mMicResampler, mSpkResampler; UniversalResampler mMicResampler, mSpkResampler;
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer; DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
DataConnection* mMonitoring;
#ifdef DUMP_NATIVEOUTPUT #ifdef DUMP_NATIVEOUTPUT
std::shared_ptr<WavFileWriter> mNativeOutputDump; std::shared_ptr<WavFileWriter> mNativeOutputDump;

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -6,7 +6,7 @@
#ifndef __AUDIO_DSOUND_H #ifndef __AUDIO_DSOUND_H
#define __AUDIO_DSOUND_H #define __AUDIO_DSOUND_H
#include "../config.h" #include "../engine_config.h"
#include <winsock2.h> #include <winsock2.h>
#include <windows.h> #include <windows.h>

View File

@ -29,10 +29,6 @@ TimeSource::TimeSource(int quantTime, int nrOfQuants)
mTailTime = 0; mTailTime = 0;
} }
TimeSource::~TimeSource()
{
}
void TimeSource::start() void TimeSource::start()
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
@ -102,11 +98,12 @@ unsigned TimeSource::time()
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
assert(0); assert(0);
#endif #endif
return 0;
} }
// --- StubTimer --- // --- StubTimer ---
StubTimer::StubTimer(int bufferTime, int bufferCount) StubTimer::StubTimer(int bufferTime, int bufferCount)
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false) :mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false), mCurrentTime(0)
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);

View File

@ -47,7 +47,7 @@ namespace Audio
public: public:
TimeSource(int quantTime, int nrOfQuants); TimeSource(int quantTime, int nrOfQuants);
~TimeSource(); ~TimeSource() = default;
void start(); void start();
void stop(); void stop();

View File

@ -146,7 +146,8 @@ OsEngine* OsEngine::instance()
#endif #endif
#ifdef TARGET_ANDROID #ifdef TARGET_ANDROID
return &OpenSLEngine::instance(); return nullptr; // As we use Oboe library for now
//return &OpenSLEngine::instance();
#endif #endif
return nullptr; return nullptr;

View File

@ -7,7 +7,7 @@
#define __AUDIO_INTERFACE_H #define __AUDIO_INTERFACE_H
#include <string> #include <string>
#include "../config.h" #include "../engine_config.h"
#include "../helper/HL_Types.h" #include "../helper/HL_Types.h"
#include "../helper/HL_VariantMap.h" #include "../helper/HL_VariantMap.h"
#include "../helper/HL_Pointer.h" #include "../helper/HL_Pointer.h"
@ -57,6 +57,27 @@ namespace Audio
sprintf(buffer, "%dHz %dch", mRate, mChannels); sprintf(buffer, "%dHz %dch", mRate, mChannels);
return std::string(buffer); return std::string(buffer);
} }
bool operator == (const Format& rhs) const
{
return mRate == rhs.mRate && mChannels == rhs.mChannels;
}
bool operator != (const Format& rhs) const
{
return mRate != rhs.mRate || mChannels != rhs.mChannels;
}
int rate() const
{
return mRate;
}
int channels() const
{
return mChannels;
}
}; };
class DataConnection class DataConnection

View File

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h" #include "../engine_config.h"
#include "../helper/HL_Exception.h" #include "../helper/HL_Exception.h"
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
@ -227,7 +227,11 @@ void Mixer::mix()
{ {
// Copy much samples as we have // Copy much samples as we have
Stream& audio = *channelList[0]; Stream& audio = *channelList[0];
// Copy the decoded data
mOutput.add(audio.data().data(), audio.data().filled()); mOutput.add(audio.data().data(), audio.data().filled());
// Erase copied audio samples
audio.data().erase(audio.data().filled()); audio.data().erase(audio.data().filled());
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled()); //ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
} }

View File

@ -6,7 +6,7 @@
#ifndef _RX_MIXER_H #ifndef _RX_MIXER_H
#define _RX_MIXER_H #define _RX_MIXER_H
#include "../config.h" #include "../engine_config.h"
#include "../helper/HL_ByteBuffer.h" #include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include "Audio_Resampler.h" #include "Audio_Resampler.h"

View File

@ -1,6 +1,7 @@
#include "Audio_Null.h" #include "Audio_Null.h"
#include "helper/HL_Log.h" #include "helper/HL_Log.h"
#include <assert.h> #include <assert.h>
#include <chrono>
#define LOG_SUBSYSTEM "NULL audio" #define LOG_SUBSYSTEM "NULL audio"
using namespace Audio; using namespace Audio;
@ -59,7 +60,7 @@ NullInputDevice::NullInputDevice()
NullInputDevice::~NullInputDevice() NullInputDevice::~NullInputDevice()
{ {
close(); internalClose();
} }
bool NullInputDevice::open() bool NullInputDevice::open()
@ -72,7 +73,7 @@ bool NullInputDevice::open()
return true; return true;
} }
void NullInputDevice::close() void NullInputDevice::internalClose()
{ {
mTimer.reset(); mTimer.reset();
if (mBuffer) if (mBuffer)
@ -83,6 +84,10 @@ void NullInputDevice::close()
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes."); ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
} }
void NullInputDevice::close()
{
internalClose();
}
Format NullInputDevice::getFormat() Format NullInputDevice::getFormat()
{ {
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE); assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
@ -105,7 +110,7 @@ NullOutputDevice::NullOutputDevice()
NullOutputDevice::~NullOutputDevice() NullOutputDevice::~NullOutputDevice()
{ {
close(); internalClose();
} }
@ -118,13 +123,18 @@ bool NullOutputDevice::open()
return true; return true;
} }
void NullOutputDevice::close() void NullOutputDevice::internalClose()
{ {
mTimer.reset(); mTimer.reset();
free(mBuffer); mBuffer = nullptr; free(mBuffer); mBuffer = nullptr;
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes."); ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
} }
void NullOutputDevice::close()
{
internalClose();
}
Format NullOutputDevice::getFormat() Format NullOutputDevice::getFormat()
{ {
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE); assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);

View File

@ -38,6 +38,8 @@ namespace Audio
void* mBuffer = nullptr; void* mBuffer = nullptr;
std::shared_ptr<NullTimer> mTimer; std::shared_ptr<NullTimer> mTimer;
int64_t mTimeCounter = 0, mDataCounter = 0; int64_t mTimeCounter = 0, mDataCounter = 0;
void internalClose();
public: public:
NullInputDevice(); NullInputDevice();
virtual ~NullInputDevice(); virtual ~NullInputDevice();
@ -55,6 +57,8 @@ namespace Audio
std::shared_ptr<NullTimer> mTimer; std::shared_ptr<NullTimer> mTimer;
void* mBuffer = nullptr; void* mBuffer = nullptr;
int64_t mDataCounter = 0, mTimeCounter = 0; int64_t mDataCounter = 0, mTimeCounter = 0;
void internalClose();
public: public:
NullOutputDevice(); NullOutputDevice();
virtual ~NullOutputDevice(); virtual ~NullOutputDevice();

View File

@ -1,14 +1,18 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Audio_Player.h" #include "Audio_Player.h"
#include "../helper/HL_Log.h"
#define LOG_SUBSYSTEM "Player"
using namespace Audio; using namespace Audio;
// -------------- Player ----------- // -------------- Player -----------
Player::Player() Player::Player()
:mDelegate(NULL), mPlayedTime(0) :mDelegate(nullptr), mPlayedTime(0)
{ {
} }
@ -99,7 +103,7 @@ void Player::onFilePlayed()
void Player::obtain(int usage) void Player::obtain(int usage)
{ {
Lock l(mGuard); Lock l(mGuard);
UsageMap::iterator usageIter = mUsage.find(usage); auto usageIter = mUsage.find(usage);
if (usageIter == mUsage.end()) if (usageIter == mUsage.end())
mUsage[usage] = 1; mUsage[usage] = 1;
else else
@ -132,7 +136,7 @@ int Player::releasePlayed()
{ {
Lock l(mGuard); Lock l(mGuard);
int result = mFinishedUsages.size(); int result = mFinishedUsages.size();
while (mFinishedUsages.size()) while (!mFinishedUsages.empty())
{ {
release(mFinishedUsages.front()); release(mFinishedUsages.front());
mFinishedUsages.erase(mFinishedUsages.begin()); mFinishedUsages.erase(mFinishedUsages.begin());

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -8,6 +8,7 @@
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include "../helper/HL_Statistics.h"
#include "Audio_Interface.h" #include "Audio_Interface.h"
#include <deque> #include <deque>
#include <map> #include <map>
@ -48,15 +49,18 @@ namespace Audio
void onMicData(const Format& f, const void* buffer, int length); void onMicData(const Format& f, const void* buffer, int length);
void onSpkData(const Format& f, void* buffer, int length); void onSpkData(const Format& f, void* buffer, int length);
void onFilePlayed(); void onFilePlayed();
void scheduleRelease();
void obtain(int usageId); void obtain(int usageId);
public: public:
Player(); Player();
~Player(); ~Player();
void setDelegate(EndOfAudioDelegate* d); void setDelegate(EndOfAudioDelegate* d);
EndOfAudioDelegate* getDelegate() const; EndOfAudioDelegate* getDelegate() const;
void setOutput(POutputDevice output); void setOutput(POutputDevice output);
POutputDevice getOutput() const; POutputDevice getOutput() const;
void add(int usageId, PWavFileReader file, bool loop, int timelength); void add(int usageId, PWavFileReader file, bool loop, int timelength);
void release(int usageId); void release(int usageId);
void clear(); void clear();

View File

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h" #include "../engine_config.h"
#include "Audio_Quality.h" #include "Audio_Quality.h"
#include "../helper/HL_Exception.h" #include "../helper/HL_Exception.h"
#include "../helper/HL_Types.h" #include "../helper/HL_Types.h"
@ -17,7 +17,10 @@
using namespace Audio; using namespace Audio;
#ifndef SHRT_MAX
# define SHRT_MAX 32767 /* maximum (signed) short value */ # define SHRT_MAX 32767 /* maximum (signed) short value */
#endif
AgcFilter::AgcFilter(int channels) AgcFilter::AgcFilter(int channels)
{ {
static const float DefaultLevel = 0.8f; static const float DefaultLevel = 0.8f;

View File

@ -5,7 +5,7 @@
#ifndef __AUDIO_QUALITY_H #ifndef __AUDIO_QUALITY_H
#define __AUDIO_QUALITY_H #define __AUDIO_QUALITY_H
#include "../config.h" #include "../engine_config.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include <vector> #include <vector>

View File

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h" #include "../engine_config.h"
#include "Audio_Resampler.h" #include "Audio_Resampler.h"
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
@ -18,7 +18,7 @@ namespace Audio
SpeexResampler::SpeexResampler() SpeexResampler::SpeexResampler()
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0) :mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0), mChannels(0)
{ {
} }
@ -125,12 +125,13 @@ int SpeexResampler::destRate()
size_t SpeexResampler::getDestLength(size_t sourceLen) size_t SpeexResampler::getDestLength(size_t sourceLen)
{ {
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f) / 2 * 2; return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f);
} }
size_t SpeexResampler::getSourceLength(size_t destLen) size_t SpeexResampler::getSourceLength(size_t destLen)
{ {
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f) / 2 * 2; // Here we want to get 'destLen' number of samples
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f);
} }
// Returns instance + speex resampler size in bytes // Returns instance + speex resampler size in bytes
@ -273,7 +274,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
PResampler r; PResampler r;
if (resamplerIter == mResamplerMap.end()) if (resamplerIter == mResamplerMap.end())
{ {
r = PResampler(new Resampler()); r = std::make_shared<Resampler>();
r->start(AUDIO_CHANNELS, sourceRate, destRate); r->start(AUDIO_CHANNELS, sourceRate, destRate);
mResamplerMap[RatePair(sourceRate, destRate)] = r; mResamplerMap[RatePair(sourceRate, destRate)] = r;
} }

View File

@ -7,9 +7,10 @@
#include "helper/HL_Exception.h" #include "helper/HL_Exception.h"
#include "helper/HL_String.h" #include "helper/HL_String.h"
#include "helper/HL_Log.h" #include "helper/HL_Log.h"
#include "../config.h" #include "../engine_config.h"
#include <memory.h> #include <memory.h>
#include <assert.h>
#ifndef WORD #ifndef WORD
# define WORD unsigned short # define WORD unsigned short
@ -39,7 +40,7 @@ using namespace Audio;
// ---------------------- WavFileReader ------------------------- // ---------------------- WavFileReader -------------------------
WavFileReader::WavFileReader() WavFileReader::WavFileReader()
:mHandle(nullptr), mRate(0), mLastError(0) :mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
{ {
mDataOffset = 0; mDataOffset = 0;
} }
@ -52,38 +53,51 @@ WavFileReader::~WavFileReader()
std::string WavFileReader::readChunk() std::string WavFileReader::readChunk()
{ {
char name[5]; char name[5] = {0};
if (fread(name, 1, 4, mHandle) != 4) readBuffer(name, 4);
THROW_READERROR;
name[4] = 0;
std::string result = name; std::string result = name;
unsigned size; uint32_t size = 0;
if (fread(&size, 4, 1, mHandle) != 1) readBuffer(&size, 4);
THROW_READERROR;
if (result == "fact") if (result == "fact")
fread(&mDataLength, 4, 1, mHandle); {
uint32_t dataLength = 0;
readBuffer(&dataLength, sizeof dataLength);
mDataLength = dataLength;
}
else else
if (result != "data") if (result != "data")
fseek(mHandle, size, SEEK_CUR); mInput->seekg(size, std::ios_base::beg);
else else
mDataLength = size; mDataLength = size;
return result; return result;
} }
bool WavFileReader::open(const std::tstring& filename) void WavFileReader::readBuffer(void* buffer, size_t sz)
{
auto p = mInput->tellg();
mInput->read(reinterpret_cast<char*>(buffer), sz);
if (mInput->tellg() - p != sz)
throw Exception(ERR_WAVFILE_FAILED);
}
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
{
auto p = mInput->tellg();
mInput->read(reinterpret_cast<char*>(buffer), sz);
return mInput->tellg() - p;
}
bool WavFileReader::open(const std::filesystem::path& p)
{ {
LOCK; LOCK;
try try
{ {
#ifdef WIN32 mPath = p;
mHandle = _wfopen(filename.c_str(), L"rb"); mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in);
#else if (!mInput->is_open())
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "rb");
#endif
if (NULL == mHandle)
{ {
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX) #if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
mLastError = errno; mLastError = errno;
@ -97,76 +111,62 @@ bool WavFileReader::open(const std::tstring& filename)
// Read the .WAV header // Read the .WAV header
char riff[4]; char riff[4];
if (fread(riff, 4, 1, mHandle) < 1) readBuffer(riff, sizeof riff);
THROW_READERROR;
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F')) if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
THROW_READERROR; THROW_READERROR;
// Read the file size // Read the file size
unsigned int filesize = 0; uint32_t filesize = 0;
if (fread(&filesize, 4, 1, mHandle) < 1) readBuffer(&filesize, sizeof(filesize));
THROW_READERROR;
char wavefmt[9]; char wavefmt[9] = {0};
if (fread(wavefmt, 8, 1, mHandle) < 1) readBuffer(wavefmt, 8);
THROW_READERROR;
wavefmt[8] = 0;
if (strcmp(wavefmt, "WAVEfmt ") != 0) if (strcmp(wavefmt, "WAVEfmt ") != 0)
THROW_READERROR; THROW_READERROR;
unsigned fmtSize = 0; uint32_t fmtSize = 0;
if (fread(&fmtSize, 4, 1, mHandle) < 1) readBuffer(&fmtSize, sizeof(fmtSize));
THROW_READERROR;
unsigned fmtStart = ftell(mHandle); auto fmtStart = mInput->tellg();
unsigned short formattag = 0; uint16_t formattag = 0;
if (fread(&formattag, 2, 1, mHandle) < 1) readBuffer(&formattag, sizeof(formattag));
THROW_READERROR;
if (formattag != 1/*WAVE_FORMAT_PCM*/) if (formattag != 1/*WAVE_FORMAT_PCM*/)
THROW_READERROR; THROW_READERROR;
mChannels = 0; mChannels = 0;
if (fread(&mChannels, 2, 1, mHandle) < 1) readBuffer(&mChannels, sizeof(mChannels));
THROW_READERROR;
mRate = 0; mSamplerate = 0;
if (fread(&mRate, 4, 1, mHandle) < 1) readBuffer(&mSamplerate, sizeof(mSamplerate));
THROW_READERROR;
unsigned int avgbytespersec = 0; uint32_t avgbytespersec = 0;
if (fread(&avgbytespersec, 4, 1, mHandle) < 1) readBuffer(&avgbytespersec, sizeof(avgbytespersec));
THROW_READERROR;
unsigned short blockalign = 0; uint16_t blockalign = 0;
if (fread(&blockalign, 2, 1, mHandle) < 1) readBuffer(&blockalign, sizeof(blockalign));
THROW_READERROR;
mBits = 0; mBits = 0;
if (fread(&mBits, 2, 1, mHandle) < 1) readBuffer(&mBits, sizeof(mBits));
THROW_READERROR;
if (mBits !=8 && mBits != 16) if (mBits !=8 && mBits != 16)
THROW_READERROR; THROW_READERROR;
// Read the "chunk" // Look for the chunk 'data'
fseek(mHandle, fmtStart + fmtSize, SEEK_SET); mInput->seekg(fmtStart + std::streampos(fmtSize));
//unsigned pos = ftell(mHandle);
mDataLength = 0; mDataLength = 0;
while (readChunk() != "data") while (readChunk() != "data")
; ;
mFileName = filename; mDataOffset = mInput->tellg();
mDataOffset = ftell(mHandle); mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
} }
catch(...) catch(...)
{ {
fclose(mHandle); mHandle = nullptr; mInput.reset();
mLastError = static_cast<unsigned>(-1); mLastError = static_cast<unsigned>(-1);
} }
return isOpened(); return isOpened();
@ -175,78 +175,117 @@ bool WavFileReader::open(const std::tstring& filename)
void WavFileReader::close() void WavFileReader::close()
{ {
LOCK; LOCK;
mInput.reset();
if (nullptr != mHandle)
fclose(mHandle);
mHandle = nullptr;
} }
int WavFileReader::rate() const int WavFileReader::samplerate() const
{ {
return mRate; return mSamplerate;
} }
unsigned WavFileReader::read(void* buffer, unsigned bytes) int WavFileReader::channels() const
{
return mChannels;
}
size_t WavFileReader::read(void* buffer, size_t bytes)
{ {
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2; return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
} }
unsigned WavFileReader::read(short* buffer, unsigned samples) size_t WavFileReader::readRaw(void* buffer, size_t bytes)
{
return readRaw((short*)buffer, bytes / channels() / sizeof(short)) * channels() * sizeof(short);
}
size_t WavFileReader::read(short* buffer, size_t samples)
{ {
LOCK; LOCK;
if (!mHandle) if (!mInput)
return 0; return 0;
// Get number of samples that must be read from source file // Get number of samples that must be read from source file
int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8; size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
void* temp = alloca(requiredBytes); bool useHeap = requiredBytes > sizeof mTempBuffer;
void* temp;
if (useHeap)
temp = malloc(requiredBytes);
else
temp = mTempBuffer;
memset(temp, 0, requiredBytes); memset(temp, 0, requiredBytes);
// Find required size of input buffer // Find required size of input buffer
if (mDataLength) if (mDataLength)
{ {
unsigned filePosition = ftell(mHandle); auto filePosition = mInput->tellg();
// Check how much data we can read // Check how much data we can read
unsigned fileAvailable = mDataLength + mDataOffset - filePosition; size_t fileAvailable = mDataLength + mDataOffset - filePosition;
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
}
size_t readBytes = tryReadBuffer(temp, requiredBytes);
size_t processedBytes = 0;
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes,
buffer, samples * 2 * AUDIO_CHANNELS);
if (useHeap)
free(temp);
return result / 2 / AUDIO_CHANNELS;
}
size_t WavFileReader::readRaw(short* buffer, size_t samples)
{
LOCK;
if (!mInput)
return 0;
// Get number of samples that must be read from source file
size_t requiredBytes = samples * channels() * sizeof(short);
// Find required size of input buffer
if (mDataLength)
{
auto filePosition = mInput->tellg();
// Check how much data we can read
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes; requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
} }
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8); size_t readBytes = tryReadBuffer(buffer, requiredBytes);
size_t processedBytes = 0; return readBytes / channels() / sizeof(short);
size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
buffer, samples * 2 * AUDIO_CHANNELS);
return result / 2 / AUDIO_CHANNELS;
} }
bool WavFileReader::isOpened() bool WavFileReader::isOpened()
{ {
LOCK; LOCK;
if (!mInput)
return (mHandle != 0); return false;
return mInput->is_open();
} }
void WavFileReader::rewind() void WavFileReader::rewind()
{ {
LOCK; LOCK;
if (mInput)
if (mHandle) mInput->seekg(mDataOffset);
fseek(mHandle, mDataOffset, SEEK_SET);
} }
std::tstring WavFileReader::filename() const std::filesystem::path WavFileReader::path() const
{ {
LOCK; LOCK;
return mPath;
return mFileName;
} }
unsigned WavFileReader::size() const size_t WavFileReader::size() const
{ {
LOCK; LOCK;
return mDataLength; return mDataLength;
} }
@ -260,9 +299,8 @@ unsigned WavFileReader::lastError() const
#define BITS_PER_CHANNEL 16 #define BITS_PER_CHANNEL 16
WavFileWriter::WavFileWriter() WavFileWriter::WavFileWriter()
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0) :mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
{ {}
}
WavFileWriter::~WavFileWriter() WavFileWriter::~WavFileWriter()
{ {
@ -275,68 +313,74 @@ void WavFileWriter::checkWriteResult(int result)
throw Exception(ERR_WAVFILE_FAILED, errno); throw Exception(ERR_WAVFILE_FAILED, errno);
} }
bool WavFileWriter::open(const std::tstring& filename, int rate, int channels) void WavFileWriter::writeBuffer(const void* buffer, size_t sz)
{
if (!mOutput)
return;
auto p = mOutput->tellp();
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
if (mOutput->tellp() - p != sz)
throw Exception(ERR_WAVFILE_FAILED);
}
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
{ {
LOCK; LOCK;
close(); close();
mSamplerate = samplerate;
mRate = rate;
mChannels = channels; mChannels = channels;
#ifdef WIN32 mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc);
mHandle = _wfopen(filename.c_str(), L"wb"); if (!mOutput)
#else
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "wb");
#endif
if (nullptr == mHandle)
{ {
ICELogError(<< "Failed to create .wav file: filename = " << StringHelper::makeUtf8(filename) << " , error = " << errno); int errorcode = errno;
ICELogError(<< "Failed to create .wav file: filename = " << p << " , error = " << errorcode);
return false; return false;
} }
// Write the .WAV header // Write the .WAV header
const char* riff = "RIFF"; const char* riff = "RIFF";
checkWriteResult( fwrite(riff, 4, 1, mHandle) ); writeBuffer(riff, 4);
// Write the file size // Write the file size
unsigned int filesize = 0; uint32_t filesize = 0;
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) ); writeBuffer(&filesize, sizeof filesize);
const char* wavefmt = "WAVEfmt "; const char* wavefmt = "WAVEfmt ";
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) ); writeBuffer(wavefmt, 8);
// Set the format description // Set the format description
DWORD dwFmtSize = 16; /*= 16L*/; uint32_t dwFmtSize = 16; /*= 16L*/;
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) ); writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
WaveFormatEx format; WaveFormatEx format;
format.wFormatTag = WAVE_FORMAT_PCM; format.wFormatTag = WAVE_FORMAT_PCM;
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) ); writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag));
format.nChannels = mChannels; format.nChannels = mChannels;
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) ); writeBuffer(&format.nChannels, sizeof(format.nChannels));
format.nSamplesPerSec = mRate; format.nSamplesPerSec = mSamplerate;
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) ); writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec));
format.nAvgBytesPerSec = mRate * 2 * mChannels; format.nAvgBytesPerSec = mSamplerate * 2 * mChannels;
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) ); writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec));
format.nBlockAlign = 2 * mChannels; format.nBlockAlign = 2 * mChannels;
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) ); writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign));
format.wBitsPerSample = BITS_PER_CHANNEL; format.wBitsPerSample = BITS_PER_CHANNEL;
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) ); writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample));
const char* data = "data"; const char* data = "data";
checkWriteResult( fwrite(data, 4, 1, mHandle)); writeBuffer(data, 4);
mFileName = filename; mPath = p;
mWritten = 0; mWritten = 0;
mLengthOffset = ftell(mHandle); mLengthOffset = mOutput->tellp();
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) ); writeBuffer(&mWritten, sizeof mWritten);
return isOpened(); return isOpened();
} }
@ -344,51 +388,44 @@ bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
void WavFileWriter::close() void WavFileWriter::close()
{ {
LOCK; LOCK;
mOutput.reset();
if (mHandle)
{
fclose(mHandle);
mHandle = nullptr;
}
} }
size_t WavFileWriter::write(const void* buffer, size_t bytes) size_t WavFileWriter::write(const void* buffer, size_t bytes)
{ {
LOCK; LOCK;
if (!mHandle) if (!mOutput)
return 0; return 0;
// Seek the end of file // Seek the end of file - here new data will be written
fseek(mHandle, 0, SEEK_END); mOutput->seekp(0, std::ios_base::end);
mWritten += bytes; mWritten += bytes;
// Write the data // Write the data
fwrite(buffer, bytes, 1, mHandle); writeBuffer(buffer, bytes);
// Write file length // Write file length
fseek(mHandle, 4, SEEK_SET); mOutput->seekp(4, std::ios_base::beg);
int32_t fl = mWritten + 36; uint32_t fl = mWritten + 36;
fwrite(&fl, sizeof(fl), 1, mHandle); writeBuffer(&fl, sizeof(fl));
// Write data length // Write data length
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET); mOutput->seekp(mLengthOffset, std::ios_base::beg);
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) ); writeBuffer(&mWritten, sizeof(mWritten));
return bytes; return bytes;
} }
bool WavFileWriter::isOpened() bool WavFileWriter::isOpened() const
{ {
LOCK; LOCK;
return mOutput.get();
return (mHandle != nullptr);
} }
std::tstring WavFileWriter::filename() std::filesystem::path WavFileWriter::path() const
{ {
LOCK; LOCK;
return mPath;
return mFileName;
} }

View File

@ -12,6 +12,8 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <filesystem>
#include <fstream>
namespace Audio namespace Audio
@ -20,35 +22,43 @@ namespace Audio
class WavFileReader class WavFileReader
{ {
protected: protected:
FILE* mHandle; uint16_t mChannels = 0;
short mChannels; uint16_t mBits = 0;
short mBits; int mSamplerate = 0;
int mRate; std::filesystem::path mPath;
std::tstring mFileName;
mutable std::recursive_mutex mFileMtx; mutable std::recursive_mutex mFileMtx;
unsigned mDataOffset; size_t mDataOffset = 0;
unsigned mDataLength; size_t mDataLength = 0;
Resampler mResampler; Resampler mResampler;
unsigned mLastError; unsigned mLastError = 0;
std::unique_ptr<std::ifstream> mInput;
uint8_t mTempBuffer[16384];
std::string readChunk(); std::string readChunk();
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
public: public:
WavFileReader(); WavFileReader();
~WavFileReader(); ~WavFileReader();
bool open(const std::tstring& filename); bool open(const std::filesystem::path& p);
void close(); void close();
bool isOpened(); bool isOpened();
void rewind(); void rewind();
int rate() const; int samplerate() const;
int channels() const;
// This method returns number of read bytes // This method returns number of read bytes
unsigned read(void* buffer, unsigned bytes); size_t read(void* buffer, size_t bytes);
size_t readRaw(void* buffer, size_t bytes);
// This method returns number of read samples // This method returns number of read samples
unsigned read(short* buffer, unsigned samples); size_t read(short* buffer, size_t samples);
std::tstring filename() const; size_t readRaw(short* buffer, size_t samples);
unsigned size() const;
std::filesystem::path path() const;
size_t size() const;
unsigned lastError() const; unsigned lastError() const;
}; };
@ -58,25 +68,25 @@ namespace Audio
class WavFileWriter class WavFileWriter
{ {
protected: protected:
FILE* mHandle; /// Handle of audio file. std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
std::tstring mFileName; /// Path to requested audio file. std::filesystem::path mPath; /// Path to requested audio file.
std::recursive_mutex mFileMtx; /// Mutex to protect this instance. mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
int mWritten; /// Amount of written data (in bytes) size_t mWritten = 0; /// Amount of written data (in bytes)
int mLengthOffset; /// Position of length field. size_t mLengthOffset = 0; /// Position of length field.
int mRate, int mSamplerate = 0,
mChannels; mChannels = 0;
void checkWriteResult(int result); void checkWriteResult(int result);
void writeBuffer(const void* buffer, size_t sz);
public: public:
WavFileWriter(); WavFileWriter();
~WavFileWriter(); ~WavFileWriter();
bool open(const std::tstring& filename, int rate, int channels); bool open(const std::filesystem::path& p, int samplerate, int channels);
void close(); void close();
bool isOpened(); bool isOpened() const;
size_t write(const void* buffer, size_t bytes); size_t write(const void* buffer, size_t bytes);
std::tstring filename(); std::filesystem::path path() const;
}; };
typedef std::shared_ptr<WavFileWriter> PWavFileWriter; typedef std::shared_ptr<WavFileWriter> PWavFileWriter;

View File

@ -8,7 +8,7 @@
#ifdef TARGET_WIN #ifdef TARGET_WIN
#include "../config.h" #include "../engine_config.h"
#include <winsock2.h> #include <winsock2.h>
#include <windows.h> #include <windows.h>

View File

@ -1,27 +1,42 @@
project (audio_lib) project (audio_lib)
# Rely on C++ 11 # Rely on C++ 11
set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set (AUDIOLIB_SOURCES set (AUDIOLIB_SOURCES
Audio_Resampler.cpp Audio_Resampler.cpp
Audio_Resampler.h
Audio_Quality.cpp Audio_Quality.cpp
Audio_Quality.h
Audio_Mixer.cpp Audio_Mixer.cpp
Audio_Mixer.h
Audio_Interface.cpp Audio_Interface.cpp
Audio_Interface.h
Audio_Helper.cpp Audio_Helper.cpp
Audio_Helper.h
Audio_DataWindow.cpp Audio_DataWindow.cpp
Audio_DataWindow.h
Audio_DevicePair.cpp Audio_DevicePair.cpp
Audio_DevicePair.h
Audio_Player.cpp Audio_Player.cpp
Audio_Player.h
Audio_Null.cpp Audio_Null.cpp
Audio_Null.h
Audio_CoreAudio.cpp Audio_CoreAudio.cpp
Audio_CoreAudio.h
Audio_DirectSound.cpp Audio_DirectSound.cpp
Audio_DirectSound.h
Audio_AndroidOboe.cpp
Audio_AndroidOboe.h
Audio_WavFile.cpp Audio_WavFile.cpp
Audio_WavFile.h
) )
add_library(audio_lib ${AUDIOLIB_SOURCES}) add_library(audio_lib ${AUDIOLIB_SOURCES})
set_property(TARGET audio_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
## ##
target_include_directories(audio_lib target_include_directories(audio_lib

View File

@ -1,113 +0,0 @@
/* Copyright(C) 2007-2020 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __TOOLKIT_CONFIG_H
#define __TOOLKIT_CONFIG_H
#define USE_SPEEX_AEC
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
//#define USE_WEBRTC_AEC
#define USER
#define AUDIO_SAMPLE_WIDTH 16
#define AUDIO_CHANNELS 1
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
#define AUDIO_SAMPLERATE 8000
#define AUDIO_MIC_BUFFER_COUNT 16
#define AUDIO_MIC_BUFFER_LENGTH 10
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_SPK_BUFFER_COUNT 16
#define AUDIO_SPK_BUFFER_LENGTH 10
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_MIX_CHANNEL_COUNT 16
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
#define AUDIO_RESAMPLER_QUALITY 1
#define AEC_FRAME_TIME 10
#define AEC_TAIL_TIME 160
// Defined these two lines to get dumping of audio input/output
//#define AUDIO_DUMPINPUT
//#define AUDIO_DUMPOUTPUT
#define UA_REGISTRATION_TIME 3600
#define UA_MEDIA_PORT_START 20000
#define UA_MEDIA_PORT_FINISH 30000
#define UA_MAX_UDP_PACKET_SIZE 576
#define UA_PUBLICATION_ID "314"
#define MT_SAMPLERATE AUDIO_SAMPLERATE
#define MT_MAXAUDIOFRAME 1440
#define MT_MAXRTPPACKET 1500
#define MT_DTMF_END_PACKETS 3
#define RTP_BUFFER_HIGH 480
#define RTP_BUFFER_LOW 10
#define RTP_BUFFER_PREBUFFER 80
#define RTP_DECODED_CAPACITY 2048
#define DEFAULT_SUBSCRIPTION_TIME 1200
#define DEFAULT_SUBSCRIPTION_REFRESHTIME 500
#define PRESENCE_IN_REG_HEADER "PresenceInReg"
// Maximum UDP packet length
#define MAX_UDPPACKET_SIZE 65535
#define MAX_VALID_UDPPACKET_SIZE 2048
// AMR codec defines - it requires USE_AMR_CODEC defined
// #define USE_AMR_CODEC
#define MT_AMRNB_PAYLOADTYPE 122
#define MT_AMRNB_CODECNAME "amr"
#define MT_AMRNB_OCTET_PAYLOADTYPE 123
#define MT_AMRWB_PAYLOADTYPE 124
#define MT_AMRWB_CODECNAME "amr-wb"
#define MT_AMRWB_OCTET_PAYLOADTYPE 125
#define MT_GSMEFR_PAYLOADTYPE 126
#define MT_GSMEFR_CODECNAME "GERAN-EFR"
#define MT_EVS_PAYLOADTYPE 127
#define MT_EVS_CODECNAME "EVS"
// OPUS codec defines
// #define USE_OPUS_CODEC
#define MT_OPUS_CODEC_PT -1
// ILBC codec defines
#define MT_ILBC20_PAYLOADTYPE -1
#define MT_ILBC30_PAYLOADTYPE -1
// ISAC codec defines
#define MT_ISAC16K_PAYLOADTYPE -1
#define MT_ISAC32K_PAYLOADTYPE -1
// GSM HR payload type
#define MT_GSMHR_PAYLOADTYPE -1
// Mirror buffer capacity
#define MT_MIRROR_CAPACITY 32768
// Mirror buffer readiness threshold - 50 milliseconds
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
# define TEXT(X) X
#endif
// In milliseconds
#define MT_SEVANA_FRAME_TIME 680
#endif

View File

@ -156,7 +156,7 @@ Account::Account(PVariantMap config, UserAgent& agent)
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None), :mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
mRegistration(NULL) mRegistration(NULL)
{ {
mProfile = resip::SharedPtr<resip::UserProfile>(new resip::UserProfile(agent.mProfile)); mProfile = std::make_shared<resip::UserProfile>(agent.mProfile);
mId = Account::generateId(); mId = Account::generateId();
setup(*config); setup(*config);
} }
@ -201,7 +201,7 @@ void Account::setup(VariantMap &config)
} }
// NAT decorator // NAT decorator
mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent))); mProfile->setOutboundDecorator(std::make_shared<NATDecorator>(mAgent));
// Rinstance // Rinstance
if (config.exists(CONFIG_INSTANCE_ID)) if (config.exists(CONFIG_INSTANCE_ID))
@ -266,7 +266,7 @@ void Account::start()
// Create registration // Create registration
mRegistration = new ResipSession(*mAgent.mDum); mRegistration = new ResipSession(*mAgent.mDum);
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration); auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++) for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str())); regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
@ -380,7 +380,7 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
observer->mSessionId = observer->mSession->sessionId(); observer->mSessionId = observer->mSession->sessionId();
observer->mPeer = target; observer->mPeer = target;
resip::SharedPtr<resip::SipMessage> msg; std::shared_ptr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME; int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME)) if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt(); expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
@ -498,7 +498,7 @@ void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
//config.mDetectNetworkChange = true; //config.mDetectNetworkChange = true;
//config.mNetworkCheckInterval = 5000; //config.mNetworkCheckInterval = 5000;
session->mIceStack = resip::SharedPtr<ice::Stack>(ice::Stack::makeICEBox(config)); session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config));
session->mIceStack->setEventHandler(session, this); session->mIceStack->setEventHandler(session, this);
session->mIceStack->setRole(icerole); session->mIceStack->setRole(icerole);
} }
@ -552,9 +552,8 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip())); mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
} }
const resip::Transport* transport = response.getReceivedTransport(); mUsedTransport = response.getReceivedTransportTuple().getType();
mUsedTransport = transport->transport(); //bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS;
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
// Retry registration for stream based transport too // Retry registration for stream based transport too
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool()) if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
@ -596,7 +595,7 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
//mProfile->setDefaultFrom(from); //mProfile->setDefaultFrom(from);
} }
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt()); mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME); auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++) for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str())); regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
@ -740,8 +739,8 @@ Account::UserInfo Account::getUserInfo() const
return mUserInfo; return mUserInfo;
} }
resip::AtomicCounter Account::IdGenerator; std::atomic_int Account::IdGenerator;
int Account::generateId() int Account::generateId()
{ {
return IdGenerator.increment(); return ++IdGenerator;
} }

View File

@ -65,7 +65,7 @@ public:
void setup(VariantMap& config); void setup(VariantMap& config);
/* Returns corresponding resiprocate profile */ /* Returns corresponding resiprocate profile */
resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; } std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; }
typedef std::map<std::string, std::string> UserInfo; typedef std::map<std::string, std::string> UserInfo;
void setUserInfo(const UserInfo& info); void setUserInfo(const UserInfo& info);
@ -83,7 +83,7 @@ protected:
RegistrationState mRegistrationState; RegistrationState mRegistrationState;
ice::NetworkAddress mExternalAddress; ice::NetworkAddress mExternalAddress;
resip::SharedPtr<resip::UserProfile> mProfile; std::shared_ptr<resip::UserProfile> mProfile;
UserAgent& mAgent; UserAgent& mAgent;
bool mPresenceOnline; bool mPresenceOnline;
std::string mPresenceContent; std::string mPresenceContent;
@ -135,7 +135,7 @@ protected:
#pragma endregion #pragma endregion
static int generateId(); static int generateId();
static resip::AtomicCounter IdGenerator; static std::atomic_int IdGenerator;
}; };
typedef std::shared_ptr<Account> PAccount; typedef std::shared_ptr<Account> PAccount;

View File

@ -26,7 +26,7 @@ AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY)) if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap()); mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
mSrtpSuite = SRTP_NONE; mSrtpSuite = SRTP_NONE;
setState((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending); setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending);
} }
AudioProvider::~AudioProvider() AudioProvider::~AudioProvider()
@ -64,7 +64,7 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer,
} }
// Processes incoming data // Processes incoming data
void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source) void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source)
{ {
if (!mActiveStream) if (!mActiveStream)
return; return;
@ -77,7 +77,7 @@ void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int
} }
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
void AudioProvider::sendData(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size) void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer, unsigned int size)
{ {
s->sendDatagram(destination, buffer, size); s->sendDatagram(destination, buffer, size);
} }
@ -99,7 +99,6 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
else else
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite))); sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
} }
#if defined(USE_RESIP_INTEGRATION)
// Use CodecListPriority mCodecPriority adapter to work with codec priorities // Use CodecListPriority mCodecPriority adapter to work with codec priorities
if (mAvailableCodecs.empty()) if (mAvailableCodecs.empty())
@ -114,7 +113,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
if (mRemoteTelephoneCodec) if (mRemoteTelephoneCodec)
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent); sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
} }
#endif
// Publish stream state // Publish stream state
const char* attr = nullptr; const char* attr = nullptr;
@ -125,6 +124,8 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
{ {
case msSendonly: attr = "recvonly"; break; case msSendonly: attr = "recvonly"; break;
case msInactive: attr = "recvonly"; break; case msInactive: attr = "recvonly"; break;
case msRecvonly:
case msSendRecv: break; // Do nothing here
} }
break; break;
@ -182,6 +183,7 @@ void AudioProvider::sessionEstablished(int conntype)
{ {
RemoteCodec& rc = mAvailableCodecs.front(); RemoteCodec& rc = mAvailableCodecs.front();
mActiveStream->setTransmittingCodec(*rc.mFactory, rc.mRemotePayloadType); mActiveStream->setTransmittingCodec(*rc.mFactory, rc.mRemotePayloadType);
auto codec = dynamic_cast<MT::AudioStream*>(mActiveStream.get())->transmittingCodec();
dynamic_cast<MT::AudioStream*>(mActiveStream.get())->setTelephoneCodec(mRemoteTelephoneCodec); dynamic_cast<MT::AudioStream*>(mActiveStream.get())->setTelephoneCodec(mRemoteTelephoneCodec);
} }
} }
@ -226,10 +228,8 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++) for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
{ {
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex); MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
#if defined(USE_RESIP_INTEGRATION)
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1) if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
mAvailableCodecs.push_back(RemoteCodec(&factory, pt)); mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
#endif
} }
if (!mAvailableCodecs.size()) if (!mAvailableCodecs.size())
@ -271,11 +271,10 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
return true; return true;
} }
void AudioProvider::setState(unsigned state) void AudioProvider::setState(unsigned state)
{ {
mState = state; setStateImpl(state);
if (mActiveStream)
mActiveStream->setState(state);
} }
unsigned AudioProvider::state() unsigned AudioProvider::state()
@ -301,26 +300,12 @@ std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
if (!mActiveStream) if (!mActiveStream)
return ""; return "";
// Use tag 1 - it is ok, as we use only single crypto attribute
int srtpTag = 1;
// Print key to base64 string // Print key to base64 string
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first; PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
resip::Data d(keyBuffer->data(), keyBuffer->size()); resip::Data d(keyBuffer->data(), keyBuffer->size());
resip::Data keyText = d.base64encode(); resip::Data keyText = d.base64encode();
// Create "crypto" attribute value return std::format("{} {} inline:{}", 1, toString(suite), keyText.c_str());
char buffer[512];
const char* suiteName = NULL;
switch (suite)
{
case SRTP_AES_128_AUTH_80: suiteName = SRTP_SUITE_NAME_1; break;
case SRTP_AES_256_AUTH_80: suiteName = SRTP_SUITE_NAME_2; break;
default: assert(0);
}
sprintf(buffer, "%d %s inline:%s", srtpTag, suiteName, keyText.c_str());
return buffer;
} }
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key) SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
@ -341,15 +326,7 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu
resip::Data rawkey = keyText.base64decode(); resip::Data rawkey = keyText.base64decode();
key = ByteBuffer(rawkey.c_str(), rawkey.size()); key = ByteBuffer(rawkey.c_str(), rawkey.size());
// Open srtp return toSrtpSuite(suite);
SrtpSuite result = SRTP_NONE;
if (strcmp(suite, SRTP_SUITE_NAME_1) == 0)
result = SRTP_AES_128_AUTH_80;
else
if (strcmp(suite, SRTP_SUITE_NAME_2) == 0)
result = SRTP_AES_256_AUTH_80;
return result;
} }
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs) void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
@ -381,3 +358,10 @@ void AudioProvider::setupMirror(bool enable)
if (mActiveStream) if (mActiveStream)
mActiveStream->setupMirror(enable); mActiveStream->setupMirror(enable);
} }
void AudioProvider::setStateImpl(unsigned int state) {
mState = state;
if (mActiveStream)
mActiveStream->setState(state);
}

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -12,9 +12,7 @@
#include "../media/MT_Box.h" #include "../media/MT_Box.h"
#include "../media/MT_Stream.h" #include "../media/MT_Stream.h"
#include "../media/MT_Codec.h" #include "../media/MT_Codec.h"
#include "../audio/Audio_Interface.h"
#include "rutil/ThreadIf.hxx"
#include <vector> #include <vector>
#include <string> #include <string>
@ -28,46 +26,47 @@ public:
virtual ~AudioProvider(); virtual ~AudioProvider();
// Returns provider RTP name // Returns provider RTP name
std::string streamName(); std::string streamName() override;
// Returns provider RTP profile name // Returns provider RTP profile name
std::string streamProfile(); std::string streamProfile() override;
// Sets destination IP address // Sets destination IP address
void setDestinationAddress(const RtpPair<InternetAddress>& addr); void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
// Processes incoming data // Processes incoming data
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source); void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize); void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
// Updates SDP offer // Updates SDP offer
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction); void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
// Called by user agent when session is deleted. // Called by user agent when session is deleted.
void sessionDeleted(); void sessionDeleted() override;
// Called by user agent when session is terminated. // Called by user agent when session is terminated.
void sessionTerminated(); void sessionTerminated() override;
// Called by user agent when session is started. // Called by user agent when session is started.
void sessionEstablished(int conntype); void sessionEstablished(int conntype) override;
// Called by user agent to save media socket for this provider // Called by user agent to save media socket for this provider
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6); void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override;
// Called by user agent to get media socket for this provider // Called by user agent to get media socket for this provider
RtpPair<PDatagramSocket>& socket(int family); RtpPair<PDatagramSocket>& socket(int family) override;
// Called by user agent to process media stream description from remote peer. // Called by user agent to process media stream description from remote peer.
// Returns true if description is processed succesfully. Otherwise method returns false. // Returns true if description is processed succesfully. Otherwise method returns false.
// myAnswer sets if the answer will be sent after. // myAnswer sets if the answer will be sent after.
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection); bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) override;
void setState(unsigned state);
unsigned state(); void setState(unsigned state) override;
MT::Statistics getStatistics(); unsigned state() override;
MT::Statistics getStatistics() override;
MT::PStream activeStream(); MT::PStream activeStream();
void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction); void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction);
@ -75,6 +74,7 @@ public:
void setupMirror(bool enable); void setupMirror(bool enable);
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag); void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
protected: protected:
// SDP's stream name // SDP's stream name
@ -110,8 +110,11 @@ protected:
void* mMediaObserverTag = nullptr; void* mMediaObserverTag = nullptr;
std::string createCryptoAttribute(SrtpSuite suite); std::string createCryptoAttribute(SrtpSuite suite);
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs); void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
void setStateImpl(unsigned state);
}; };
#endif #endif

View File

@ -10,7 +10,6 @@
#include <vector> #include <vector>
#include "resip/stack/SdpContents.hxx" #include "resip/stack/SdpContents.hxx"
#include "rutil/SharedPtr.hxx"
#include "../helper/HL_InternetAddress.h" #include "../helper/HL_InternetAddress.h"
#include "../helper/HL_NetworkSocket.h" #include "../helper/HL_NetworkSocket.h"
@ -46,10 +45,10 @@ public:
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0; virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
// Processes incoming data // Processes incoming data
virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0; virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
virtual void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0; virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
// Updates SDP offer // Updates SDP offer
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0; virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;

View File

@ -44,6 +44,60 @@
typedef resip::SdpContents::Session::Medium Medium; typedef resip::SdpContents::Session::Medium Medium;
typedef resip::SdpContents::Session::MediumContainer MediumContainer; typedef resip::SdpContents::Session::MediumContainer MediumContainer;
class TransportLogger: public resip::Transport::SipMessageLoggingHandler
{
public:
void outboundMessage(const resip::Tuple &source, const resip::Tuple &destination, const resip::SipMessage &msg) override
{
std::ostringstream dest_buffer; dest_buffer << destination;
std::ostringstream msg_buffer; msg_buffer << msg;
std::string msg_text = msg_buffer.str();
#if defined(TARGET_ANDROID)
if (msg_text.size() > 512)
{
ICELogDebug(<< "Sent to " << dest_buffer.str() << " :");
msg_text = strx::prefixLines(msg_text, "<---");
auto lines = strx::split(msg_text);
for (const auto& l: lines)
ICELogDebug(<< l);
}
else
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
#else
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
#endif
}
// Note: retransmissions store already encoded messages, so callback doesn't send SipMessage it sends
// the encoded version of the SipMessage instead. If you need a SipMessage you will need to
// re-parse back into a SipMessage in the callback handler.
void outboundRetransmit(const resip::Tuple &source, const resip::Tuple &destination, const resip::SendData &data) override
{}
void inboundMessage(const resip::Tuple& source, const resip::Tuple& destination, const resip::SipMessage &msg) override
{
std::ostringstream source_buffer; source_buffer << source;
std::ostringstream msg_buffer; msg_buffer << msg;
std::string msg_text = msg_buffer.str();
#if defined(TARGET_ANDROID)
if (msg_text.size() > 512)
{
ICELogDebug(<< "Received from " << source_buffer.str() << " :");
msg_text = strx::prefixLines(msg_text, "--->");
auto lines = strx::split(msg_text);
for (const auto& l: lines)
ICELogDebug(<< l);
}
else
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
#else
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_buffer.str(), "--->"));
#endif
}
};
//-------------- UserAgent ----------------------- //-------------- UserAgent -----------------------
UserAgent::UserAgent() UserAgent::UserAgent()
{ {
@ -54,7 +108,7 @@ UserAgent::UserAgent()
mConfig[CONFIG_RTCP_ATTR] = true; mConfig[CONFIG_RTCP_ATTR] = true;
// Create master profile // Create master profile
mProfile = resip::SharedPtr<resip::MasterProfile>(new resip::MasterProfile()); mProfile = std::make_shared<resip::MasterProfile>();
mProfile->clearSupportedMethods(); mProfile->clearSupportedMethods();
mProfile->addSupportedMethod(resip::INVITE); mProfile->addSupportedMethod(resip::INVITE);
mProfile->addSupportedMethod(resip::BYE); mProfile->addSupportedMethod(resip::BYE);
@ -105,7 +159,7 @@ void UserAgent::start()
while (std::getline(ss, line)) while (std::getline(ss, line))
{ {
line = StringHelper::trim(line); line = strx::trim(line);
ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized
switch (addr.family()) switch (addr.family())
{ {
@ -120,7 +174,7 @@ void UserAgent::start()
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
s = new resip::WinSecurity(); s = new resip::WinSecurity();
#elif defined(TARGET_OSX) #elif defined(TARGET_OSX)
s = new resip::MacSecurity(); s = new resip::Security();
#elif defined(TARGET_LINUX) #elif defined(TARGET_LINUX)
s = new resip::Security("/etc/ssl/certs"); s = new resip::Security("/etc/ssl/certs");
#elif defined(TARGET_ANDROID) #elif defined(TARGET_ANDROID)
@ -142,16 +196,18 @@ void UserAgent::start()
} }
} }
mStack->setTransportSipMessageLoggingHandler(std::make_shared<TransportLogger>());
// Add transports // Add transports
mTransportList.clear(); mTransportList.clear();
resip::InternalTransport* t; resip::InternalTransport* t;
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { t->setTransportLogger(this); mTransportList.push_back(t);} #define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { t->setTransportLogger(this); mTransportList.push_back(t);} #define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
switch (mConfig[CONFIG_TRANSPORT].asInt()) switch (mConfig[CONFIG_TRANSPORT].asInt())
{ {
case 0: case TransportType_Any:
if (mConfig[CONFIG_IPV4].asBool()) if (mConfig[CONFIG_IPV4].asBool())
{ {
ADD_TRANSPORT4(resip::TCP) ADD_TRANSPORT4(resip::TCP)
@ -166,21 +222,21 @@ void UserAgent::start()
} }
break; break;
case 1: case TransportType_Udp:
if (mConfig[CONFIG_IPV4].asBool()) if (mConfig[CONFIG_IPV4].asBool())
ADD_TRANSPORT4(resip::UDP); ADD_TRANSPORT4(resip::UDP);
if (mConfig[CONFIG_IPV6].asBool()) if (mConfig[CONFIG_IPV6].asBool())
ADD_TRANSPORT6(resip::UDP); ADD_TRANSPORT6(resip::UDP);
break; break;
case 2: case TransportType_Tcp:
if (mConfig[CONFIG_IPV4].asBool()) if (mConfig[CONFIG_IPV4].asBool())
ADD_TRANSPORT4(resip::TCP); ADD_TRANSPORT4(resip::TCP);
if (mConfig[CONFIG_IPV6].asBool()) if (mConfig[CONFIG_IPV6].asBool())
ADD_TRANSPORT6(resip::TCP); ADD_TRANSPORT6(resip::TCP);
break; break;
case 3: case TransportType_Tls:
if (mConfig[CONFIG_IPV4].asBool()) if (mConfig[CONFIG_IPV4].asBool())
ADD_TRANSPORT4(resip::TLS); ADD_TRANSPORT4(resip::TLS);
if (mConfig[CONFIG_IPV6].asBool()) if (mConfig[CONFIG_IPV6].asBool())
@ -262,16 +318,16 @@ void UserAgent::shutdown()
{ {
LOCK; LOCK;
for (auto observerIter: mClientObserverMap) for (auto& observerIter: mClientObserverMap)
observerIter.second->stop(); observerIter.second->stop();
for (auto observerIter: mServerObserverMap) for (auto& observerIter: mServerObserverMap)
observerIter.second->stop(); observerIter.second->stop();
for (auto sessionIter: mSessionMap) for (auto& sessionIter: mSessionMap)
sessionIter.second->stop(); sessionIter.second->stop();
for (auto accountIter: mAccountSet) for (auto& accountIter: mAccountSet)
accountIter->stop(); accountIter->stop();
} }
} }
@ -279,24 +335,24 @@ void UserAgent::shutdown()
bool UserAgent::active() bool UserAgent::active()
{ {
LOCK; LOCK;
return mStack != NULL; return mStack != nullptr;
} }
void UserAgent::refresh() void UserAgent::refresh()
{ {
LOCK; LOCK;
for (auto acc: mAccountSet) for (auto& acc: mAccountSet)
acc->refresh(); acc->refresh();
for (auto observer: mClientObserverMap) for (auto& observer: mClientObserverMap)
observer.second->refresh(); observer.second->refresh();
} }
void UserAgent::onDumCanBeDeleted() void UserAgent::onDumCanBeDeleted()
{ {
delete mDum; mDum = NULL; delete mDum; mDum = nullptr;
delete mStack; mStack = NULL; delete mStack; mStack = nullptr;
mClientObserverMap.clear(); mClientObserverMap.clear();
mServerObserverMap.clear(); mServerObserverMap.clear();
@ -315,7 +371,10 @@ void UserAgent::stop()
mTransportList.clear(); mTransportList.clear();
// Dump statistics here // Dump statistics here
ICELogInfo(<< "Remaining " << Session::InstanceCounter.value() << " session(s), " << ResipSession::InstanceCounter.value() << " resip DialogSet(s), " << resip::ClientRegistration::InstanceCounter.value() << " ClientRegistration(s)"); ICELogInfo(<< "Remaining "
<< Session::InstanceCounter << " session(s), "
<< ResipSession::InstanceCounter << " resip DialogSet(s), "
<< resip::ClientRegistration::InstanceCounter << " ClientRegistration(s)");
mDum->shutdown(this); mDum->shutdown(this);
onDumCanBeDeleted(); onDumCanBeDeleted();
@ -379,15 +438,14 @@ void UserAgent::process()
// Find all sessions // Find all sessions
std::set<int> idSet; std::set<int> idSet;
SessionMap::iterator sessionIter; for (auto sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
for (sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
idSet.insert(sessionIter->first); idSet.insert(sessionIter->first);
// Now process session one by one checking if current is available yet // Now process session one by one checking if current is available yet
std::set<int>::iterator resipIter; std::set<int>::iterator resipIter;
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter) for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
{ {
SessionMap::iterator sessionIter = mSessionMap.find(*resipIter); auto sessionIter = mSessionMap.find(*resipIter);
if (sessionIter == mSessionMap.end()) if (sessionIter == mSessionMap.end())
continue; continue;
@ -428,9 +486,16 @@ void UserAgent::process()
void UserAgent::addRootCert(const ByteBuffer& data) void UserAgent::addRootCert(const ByteBuffer& data)
{ {
LOCK; LOCK;
if (!mStack)
return;
resip::Data b(data.data(), data.size()); resip::Data b(data.data(), data.size());
try {
mStack->getSecurity()->addRootCertPEM(b); mStack->getSecurity()->addRootCertPEM(b);
} }
catch(...) {
// Ignore silently
}
}
PAccount UserAgent::createAccount(PVariantMap config) PAccount UserAgent::createAccount(PVariantMap config)
{ {
@ -464,7 +529,7 @@ PSession UserAgent::createSession(PAccount account)
return session; return session;
} }
std::string UserAgent::formatSipAddress(std::string sip) std::string UserAgent::formatSipAddress(const std::string& sip)
{ {
std::string result; std::string result;
if (sip.size()) if (sip.size())
@ -480,17 +545,18 @@ std::string UserAgent::formatSipAddress(std::string sip)
return result; return result;
} }
bool UserAgent::isSipAddressValid(std::string sip) bool UserAgent::isSipAddressValid(const std::string& sip)
{ {
bool result = false; bool result = false;
try try
{ {
if (sip.find('<') == std::string::npos) std::string s = sip;
sip = "<" + sip; if (s.find('<') == std::string::npos)
if (sip.find('>') == std::string::npos) s = "<" + s;
sip += ">"; if (s.find('>') == std::string::npos)
s += ">";
resip::Data d(formatSipAddress(sip)); resip::Data d(formatSipAddress(s));
resip::Uri uri(d); resip::Uri uri(d);
result = uri.isWellFormed(); result = uri.isWellFormed();
if (result) if (result)
@ -540,7 +606,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
return result; return result;
} }
bool UserAgent::compareSipAddresses(std::string sip1, std::string sip2) bool UserAgent::compareSipAddresses(const std::string& sip1, const std::string& sip2)
{ {
if (sip1.empty() || sip2.empty()) if (sip1.empty() || sip2.empty())
return false; return false;
@ -585,7 +651,7 @@ void UserAgent::sendOffer(Session* session)
if (session->mOriginVersion == 1) if (session->mOriginVersion == 1)
{ {
// Construct INVITE session // Construct INVITE session
resip::SharedPtr<resip::SipMessage> msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession); auto msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
// Include user headers // Include user headers
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++) for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++)
@ -655,13 +721,19 @@ void UserAgent::onFailure(resip::ClientRegistrationHandle h, const resip::SipMes
#pragma endregion #pragma endregion
bool UserAgent::operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName, const char* file, bool UserAgent::operator()(resip::Log::Level level,
int line, const resip::Data& message, const resip::Data& messageWithHeaders) const resip::Subsystem& subsystem,
const resip::Data& appName,
const char* file,
int line,
const resip::Data& message,
const resip::Data& messageWithHeaders,
const resip::Data& instanceName)
{ {
std::string filename = file; std::string filename = file;
std::stringstream ss; std::stringstream ss;
ss << "File " << StringHelper::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str(); ss << "File " << strx::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
if (level <= resip::Log::Crit) if (level <= resip::Log::Crit)
ICELogCritical(<< ss.str()) ICELogCritical(<< ss.str())
else else
@ -948,7 +1020,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
// See if remote stream has "rtcp" or "rtcp-mux" attributes // See if remote stream has "rtcp" or "rtcp-mux" attributes
if (remoteStream.exists("rtcp")) if (remoteStream.exists("rtcp"))
addr2.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) ); addr2.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
else else
if (remoteStream.exists("rtcp-mux")) if (remoteStream.exists("rtcp-mux"))
addr2.setPort( remoteDefaultPort ); addr2.setPort( remoteDefaultPort );
@ -990,7 +1062,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
s->mIceStack->clear(); s->mIceStack->clear();
} }
UInt64 version = sdp.session().origin().getVersion(); uint64_t version = sdp.session().origin().getVersion();
s->mRemoteOriginVersion = version; s->mRemoteOriginVersion = version;
} }
@ -1015,12 +1087,12 @@ void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& m
if (sdp.session().exists("ice-ufrag")) if (sdp.session().exists("ice-ufrag"))
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str(); iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
ice::Stack& ice = *s->mIceStack; //ice::Stack& ice = *s->mIceStack;
UInt64 version = sdp.session().origin().getVersion(); uint64_t version = sdp.session().origin().getVersion();
std::string remoteIp = sdp.session().connection().getAddress().c_str(); std::string remoteIp = sdp.session().connection().getAddress().c_str();
int code; int code;
if ((UInt64)-1 == s->mRemoteOriginVersion) if ((uint64_t)-1 == s->mRemoteOriginVersion)
{ {
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media()); code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
} }
@ -1521,7 +1593,7 @@ VariantMap& UserAgent::config()
return mConfig; return mConfig;
} }
static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output) /*static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
{ {
resip::Data::size_type startLine = 0, endLine = content.find("\r\n"); resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
while (endLine != resip::Data::npos) while (endLine != resip::Data::npos)
@ -1533,9 +1605,9 @@ static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& outpu
} }
if (0 == startLine) if (0 == startLine)
output.push_back(content); output.push_back(content);
} }*/
static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value) /*static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
{ {
resip::Data::size_type p = input.find(":"); resip::Data::size_type p = input.find(":");
if (p == resip::Data::npos) if (p == resip::Data::npos)
@ -1556,7 +1628,7 @@ static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& valu
} }
} }
return true; return true;
} }*/
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
{ {
@ -1564,17 +1636,17 @@ void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, con
ice::NetworkAddress address(*addr, addrlen); ice::NetworkAddress address(*addr, addrlen);
std::string addressText = address.toStdString(); std::string addressText = address.toStdString();
switch (flow) /*switch (flow)
{ {
case resip::InternalTransport::TransportLogger::Flow_Received: case resip::InternalTransport::TransportLogger::Flow_Received:
ICELogDebug(<< "Received from " << addressText << ":" << "\n" ICELogDebug(<< "Received from " << addressText << ":" << "\n"
<< StringHelper::prefixLines(d, "--->")); << strx::prefixLines(d, "--->"));
break; break;
case resip::InternalTransport::TransportLogger::Flow_Sent: case resip::InternalTransport::TransportLogger::Flow_Sent:
ICELogDebug(<< "Sent to " << addressText << "\n" << StringHelper::prefixLines(d, "<---")); ICELogDebug(<< "Sent to " << addressText << "\n" << strx::prefixLines(d, "<---"));
break; break;
} }*/
} }
PSession UserAgent::getUserSession(int sessionId) PSession UserAgent::getUserSession(int sessionId)

View File

@ -47,7 +47,7 @@
#include "../ice/ICETime.h" #include "../ice/ICETime.h"
#include <sstream> #include <sstream>
#include <time.h> #include <time.h>
#include "../config.h" #include "../engine_config.h"
#include "EP_Session.h" #include "EP_Session.h"
#include "EP_Observer.h" #include "EP_Observer.h"
#include "EP_DataProvider.h" #include "EP_DataProvider.h"
@ -60,6 +60,14 @@
#define RESIPROCATE_SUBSYSTEM Subsystem::TEST #define RESIPROCATE_SUBSYSTEM Subsystem::TEST
using namespace std; using namespace std;
enum
{
TransportType_Any,
TransportType_Udp,
TransportType_Tcp,
TransportType_Tls
};
enum enum
{ {
CONFIG_IPV4 = 0, // Use IP4 CONFIG_IPV4 = 0, // Use IP4
@ -144,8 +152,8 @@ class UserAgent: public resip::ClientRegistrationHandler,
public resip::ServerSubscriptionHandler, public resip::ServerSubscriptionHandler,
public resip::ClientPagerMessageHandler, public resip::ClientPagerMessageHandler,
public resip::ServerPagerMessageHandler, public resip::ServerPagerMessageHandler,
public resip::ClientPublicationHandler, public resip::ClientPublicationHandler
public resip::InternalTransport::TransportLogger //public resip::InternalTransport::TransportLogger
{ {
friend class Account; friend class Account;
friend class Session; friend class Session;
@ -154,9 +162,9 @@ class UserAgent: public resip::ClientRegistrationHandler,
friend class WatcherQueue; friend class WatcherQueue;
public: public:
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */ /* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
static bool compareSipAddresses(std::string sip1, std::string sip2); static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
static std::string formatSipAddress(std::string sip); static std::string formatSipAddress(const std::string& sip);
static bool isSipAddressValid(std::string sip); static bool isSipAddressValid(const std::string& sip);
struct SipAddress struct SipAddress
{ {
bool mValid; bool mValid;
@ -375,7 +383,8 @@ public:
const char* file, const char* file,
int line, int line,
const resip::Data& message, const resip::Data& message,
const resip::Data& messageWithHeaders) override; const resip::Data& messageWithHeaders,
const resip::Data& instanceName) override;
#pragma endregion #pragma endregion
#pragma region DnsResultSink implementation #pragma region DnsResultSink implementation
@ -389,7 +398,7 @@ public:
#pragma endregion #pragma endregion
#pragma region TransportLogger implementation #pragma region TransportLogger implementation
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override; void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
#pragma endregion #pragma endregion
#pragma region ClientPublicationHandler #pragma region ClientPublicationHandler
@ -439,7 +448,7 @@ protected:
Mutex mGuard; Mutex mGuard;
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here. // Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
resip::SharedPtr<resip::MasterProfile> mProfile; std::shared_ptr<resip::MasterProfile> mProfile;
// Resiprocate's SIP stack object pointer // Resiprocate's SIP stack object pointer
resip::SipStack* mStack; resip::SipStack* mStack;

View File

@ -85,7 +85,7 @@ void WatcherQueue::process()
if (i == mItemList.end()) if (i == mItemList.end())
return; return;
resip::SharedPtr<resip::SipMessage> msg; std::shared_ptr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME; int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
switch (i->mState) switch (i->mState)

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -7,11 +7,7 @@
#include "EP_Engine.h" #include "EP_Engine.h"
#include "EP_AudioProvider.h" #include "EP_AudioProvider.h"
#include "../media/MT_Stream.h" #include "../media/MT_Stream.h"
#include "../media/MT_AudioStream.h"
#include "../media/MT_Dtmf.h"
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
#include "../helper/HL_Exception.h"
#include "../helper/HL_StreamState.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include "../helper/HL_String.h" #include "../helper/HL_String.h"
@ -20,7 +16,7 @@
typedef resip::SdpContents::Session::Medium Medium; typedef resip::SdpContents::Session::Medium Medium;
typedef resip::SdpContents::Session::MediumContainer MediumContainer; typedef resip::SdpContents::Session::MediumContainer MediumContainer;
#define DOMULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing #define IS_MULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
//------------ ResipSessionAppDialog ------------ //------------ ResipSessionAppDialog ------------
@ -37,12 +33,12 @@ ResipSessionAppDialog::~ResipSessionAppDialog()
#pragma region ResipSession #pragma region ResipSession
resip::AtomicCounter ResipSession::InstanceCounter; std::atomic_int ResipSession::InstanceCounter;
ResipSession::ResipSession(resip::DialogUsageManager& dum) ResipSession::ResipSession(resip::DialogUsageManager& dum)
: resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0) : resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
{ {
ResipSession::InstanceCounter.increment(); ResipSession::InstanceCounter++;
mTag = NULL; mTag = NULL;
mTerminated = false; mTerminated = false;
mOnWatchingStartSent = false; mOnWatchingStartSent = false;
@ -62,7 +58,7 @@ ResipSession::~ResipSession()
{ {
} }
ResipSession::InstanceCounter.decrement(); ResipSession::InstanceCounter--;
} }
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg) resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
@ -167,12 +163,12 @@ int ResipSession::sessionId()
return mSessionId; return mSessionId;
} }
void ResipSession::setUASProfile(std::shared_ptr<resip::UserProfile> profile) void ResipSession::setUASProfile(const std::shared_ptr<resip::UserProfile>& profile)
{ {
mUASProfile = profile; mUASProfile = profile;
} }
resip::SharedPtr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg) std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
{ {
assert(mUserAgent != nullptr); assert(mUserAgent != nullptr);
@ -184,7 +180,7 @@ resip::SharedPtr<resip::UserProfile> ResipSession::selectUASUserProfile(const re
else else
return mUserAgent->mProfile; return mUserAgent->mProfile;
} }
return resip::SharedPtr<resip::UserProfile>(); return std::shared_ptr<resip::UserProfile>();
} }
#pragma endregion #pragma endregion
@ -262,11 +258,11 @@ void Session::Stream::setRtcpMuxAttr(bool value)
#pragma endregion #pragma endregion
#pragma region Session #pragma region Session
resip::AtomicCounter Session::InstanceCounter; std::atomic_int Session::InstanceCounter;
Session::Session(PAccount account) Session::Session(PAccount account)
{ {
InstanceCounter.increment(); InstanceCounter++;
mAccount = account; mAccount = account;
mSessionId = Session::generateId(); mSessionId = Session::generateId();
mTag = NULL; mTag = NULL;
@ -278,7 +274,7 @@ Session::Session(PAccount account)
mRole = Acceptor; mRole = Acceptor;
mGatheredCandidates = false; mGatheredCandidates = false;
mTerminated = false; mTerminated = false;
mRemoteOriginVersion = (UInt64)-1; mRemoteOriginVersion = (uint64_t)-1;
mResipSession = NULL; mResipSession = NULL;
mRefCount = 1; mRefCount = 1;
mOfferAnswerCounter = 0; mOfferAnswerCounter = 0;
@ -296,7 +292,7 @@ Session::~Session()
} }
catch(...) catch(...)
{} {}
InstanceCounter.decrement(); InstanceCounter--;
} }
void Session::start(const std::string& peer) void Session::start(const std::string& peer)
@ -449,39 +445,30 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
MT::Statistics stat; MT::Statistics stat;
// Iterate all session providers // Iterate all session providers
stat.reset(); Stream* media = nullptr;
Stream* media = NULL; for (Stream& stream: mStreamList)
for (unsigned streamIndex = 0; streamIndex < mStreamList.size(); streamIndex++)
{
Stream& stream = mStreamList[streamIndex];
if (stream.provider())
{ {
if (!stream.provider())
continue;
media = &stream; media = &stream;
MT::Statistics s = stream.provider()->getStatistics(); MT::Statistics s = stream.provider()->getStatistics();
#if defined(USE_PVQA_LIBRARY) && !defined(TARGET_SERVER)
if (options != InfoOptions::Standard)
{
// This information is available AFTER audio stream is deleted
info[SessionInfo_PvqaMos] = s.mPvqaMos;
info[SessionInfo_PvqaReport] = s.mPvqaReport;
}
#endif
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14)); info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
info[SessionInfo_AudioCodec] = s.mCodecName; info[SessionInfo_AudioCodec] = s.mCodecName;
stat += s; stat += s;
} }
}
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived); info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent); info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
info[SessionInfo_ReceivedRtp] = static_cast<int>(stat.mReceivedRtp); info[SessionInfo_ReceivedRtp] = static_cast<int>(stat.mReceivedRtp);
info[SessionInfo_ReceivedRtcp] = static_cast<int>(stat.mReceivedRtcp); info[SessionInfo_ReceivedRtcp] = static_cast<int>(stat.mReceivedRtcp);
info[SessionInfo_LostRtp] = static_cast<int>(stat.mPacketLoss); info[SessionInfo_LostRtp] = static_cast<int>(stat.mPacketLoss);
info[SessionInfo_DroppedRtp] = static_cast<int>(stat.mPacketDropped);
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp); info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp); info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
if (stat.mFirstRtpTime) if (stat.mFirstRtpTime)
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - *(stat.mFirstRtpTime)).count()); info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - *(stat.mFirstRtpTime)).count());
else else
info[SessionInfo_Duration] = 0; info[SessionInfo_Duration] = 0;
@ -548,12 +535,11 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
{ {
// Try to process incoming data by ICE stack // Try to process incoming data by ICE stack
int component = -1, stream = -1; int component = -1, stream = -1;
bool processed;
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component)) if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
{ {
ice::ByteBuffer buffer(receivedPtr, receivedSize); ice::ByteBuffer buffer(receivedPtr, receivedSize);
buffer.setRemoteAddress(src); buffer.setRemoteAddress(src);
processed = mIceStack->processIncomingData(stream, component, buffer); /*bool processed = */mIceStack->processIncomingData(stream, component, buffer);
} }
} }
else else
@ -726,7 +712,7 @@ void Session::buildSdp(resip::SdpContents &sdp, SdpDirection sdpDirection)
if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool()) if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
media.addAttribute("rtcp-mux"); media.addAttribute("rtcp-mux");
// Ask provider about specific information // Ask provider about specific information - codecs are filled here
provider.updateSdpOffer(media, sdpDirection); provider.updateSdpOffer(media, sdpDirection);
// Add ICE information // Add ICE information
@ -791,8 +777,8 @@ void Session::addProvider(PDataProvider provider)
s.setProvider( provider ); s.setProvider( provider );
// Allocate socket for provider // Allocate socket for provider
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()) ); s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()) );
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()) ); s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()) );
s.provider()->setSocket(s.socket4(), s.socket6()); s.provider()->setSocket(s.socket4(), s.socket6());
// Create ICE stream/component // Create ICE stream/component
@ -828,10 +814,10 @@ int Session::sessionId()
return mSessionId; return mSessionId;
} }
resip::AtomicCounter Session::IdGenerator; std::atomic_int Session::IdGenerator;
int Session::generateId() int Session::generateId()
{ {
return (int)IdGenerator.increment(); return ++IdGenerator;
} }
std::string Session::remoteAddress() const std::string Session::remoteAddress() const
@ -895,8 +881,8 @@ void Session::refreshMediaPath()
SocketHeap::instance().freeSocketPair(p->socket(AF_INET)); SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
// Bring new socket to provider and stream // Bring new socket to provider and stream
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX() ), RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ),
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()); s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX());
p->setSocket(s4, s6); p->setSocket(s4, s6);
s.setSocket4(s4); s.setSocket4(s4);
@ -913,7 +899,7 @@ void Session::refreshMediaPath()
} }
// Received offer with new SDP // Received offer with new SDP
int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag, int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media) std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
{ {
bool iceRestart = false; bool iceRestart = false;
@ -958,7 +944,7 @@ int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, s
targetAddr.mRtcp.setPort( remoteStream.port() ); targetAddr.mRtcp.setPort( remoteStream.port() );
else else
if (stream.rtcpAttr()) if (stream.rtcpAttr())
targetAddr.mRtcp.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) ); targetAddr.mRtcp.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
else else
targetAddr.mRtcp.setPort( remoteStream.port() + 1); targetAddr.mRtcp.setPort( remoteStream.port() + 1);
@ -975,8 +961,8 @@ int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, s
{ {
try try
{ {
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX())); stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX())); stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
} }
catch(...) catch(...)
{ {

View File

@ -28,15 +28,14 @@
#include "rutil/Logger.hxx" #include "rutil/Logger.hxx"
#include "rutil/Random.hxx" #include "rutil/Random.hxx"
#include "rutil/WinLeakCheck.hxx" #include "rutil/WinLeakCheck.hxx"
#include "rutil/AtomicCounter.hxx"
#include "../ice/ICEBox.h" #include "../ice/ICEBox.h"
#include <sstream> #include <sstream>
#include <atomic>
#include <time.h> #include <time.h>
#include "../config.h" #include "../engine_config.h"
#include "EP_Session.h"
#include "EP_Account.h" #include "EP_Account.h"
#include "EP_DataProvider.h" #include "EP_DataProvider.h"
#include "EP_AudioProvider.h" #include "EP_AudioProvider.h"
@ -66,12 +65,13 @@ enum SessionInfo
SessionInfo_ReceivedRtp, SessionInfo_ReceivedRtp,
SessionInfo_ReceivedRtcp, SessionInfo_ReceivedRtcp,
SessionInfo_LostRtp, SessionInfo_LostRtp,
SessionInfo_DroppedRtp,
SessionInfo_Duration, SessionInfo_Duration,
SessionInfo_Jitter, SessionInfo_Jitter,
SessionInfo_Rtt, SessionInfo_Rtt,
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
SessionInfo_RemotePeer, SessionInfo_RemotePeer,
SessionInfo_SSRC SessionInfo_SSRC,
}; };
@ -213,7 +213,8 @@ public:
void refreshMediaPath(); void refreshMediaPath();
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error). // Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag, // There are passing string objects by value; this is correct; this values will modified on the stack.
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media); std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
// Session ID // Session ID
@ -223,7 +224,7 @@ public:
std::vector<Stream> mStreamList; std::vector<Stream> mStreamList;
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method // Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
resip::SharedPtr<ice::Stack> mIceStack; std::shared_ptr<ice::Stack> mIceStack;
// Pointer to owner user agent instance // Pointer to owner user agent instance
UserAgent* mUserAgent; UserAgent* mUserAgent;
@ -236,7 +237,7 @@ public:
// SDP's origin version for sending // SDP's origin version for sending
int mOriginVersion; int mOriginVersion;
UInt64 mRemoteOriginVersion; uint64_t mRemoteOriginVersion;
// SDP's session version // SDP's session version
int mSessionVersion; int mSessionVersion;
@ -317,8 +318,8 @@ public:
void enqueueOffer(); void enqueueOffer();
void processQueuedOffer(); void processQueuedOffer();
static int generateId(); static int generateId();
static resip::AtomicCounter IdGenerator; static std::atomic_int IdGenerator;
static resip::AtomicCounter InstanceCounter; static std::atomic_int InstanceCounter;
}; };
typedef std::shared_ptr<Session> PSession; typedef std::shared_ptr<Session> PSession;
@ -353,13 +354,13 @@ public:
Type_Call, Type_Call,
Type_Auto Type_Auto
}; };
static resip::AtomicCounter InstanceCounter; static std::atomic_int InstanceCounter;
ResipSession(resip::DialogUsageManager& dum); ResipSession(resip::DialogUsageManager& dum);
virtual ~ResipSession(); virtual ~ResipSession();
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg); virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg); virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
void setType(Type type); void setType(Type type);
Type type(); Type type();
@ -383,7 +384,7 @@ public:
void runTerminatedEvent(Type type, int code = 0, int reason = 0); void runTerminatedEvent(Type type, int code = 0, int reason = 0);
void setUASProfile(std::shared_ptr<resip::UserProfile> profile); void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
protected: protected:
bool mTerminated; bool mTerminated;

117
src/engine/engine_config.h Normal file
View File

@ -0,0 +1,117 @@
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __TOOLKIT_CONFIG_H
#define __TOOLKIT_CONFIG_H
#define USE_SPEEX_AEC
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
//#define USE_WEBRTC_AEC
#define USER
#define AUDIO_SAMPLE_WIDTH 16
#define AUDIO_CHANNELS 1
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
#define AUDIO_SAMPLERATE 48000
#define AUDIO_MIC_BUFFER_COUNT 16
#define AUDIO_MIC_BUFFER_LENGTH 10
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_SPK_BUFFER_COUNT 16
#define AUDIO_SPK_BUFFER_LENGTH 10
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_MIX_CHANNEL_COUNT 16
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
#define AUDIO_RESAMPLER_QUALITY 1
#define AEC_FRAME_TIME 10
#define AEC_TAIL_TIME 160
// Defined these two lines to get dumping of audio input/output
//#define AUDIO_DUMPINPUT
//#define AUDIO_DUMPOUTPUT
#define UA_REGISTRATION_TIME 3600
#define UA_MEDIA_PORT_START 20000
#define UA_MEDIA_PORT_FINISH 30000
#define UA_MAX_UDP_PACKET_SIZE 576
#define UA_PUBLICATION_ID "314"
#define MT_SAMPLERATE AUDIO_SAMPLERATE
#define MT_MAXAUDIOFRAME 1440
#define MT_MAXRTPPACKET 1500
#define MT_DTMF_END_PACKETS 3
#define RTP_BUFFER_HIGH 0
#define RTP_BUFFER_LOW 0
#define RTP_BUFFER_PREBUFFER 0
// #define RTP_BUFFER_HIGH 160
// #define RTP_BUFFER_LOW 10
// #define RTP_BUFFER_PREBUFFER 160
#define RTP_DECODED_CAPACITY 2048
#define DEFAULT_SUBSCRIPTION_TIME 1200
#define DEFAULT_SUBSCRIPTION_REFRESHTIME 500
#define PRESENCE_IN_REG_HEADER "PresenceInReg"
// Maximum UDP packet length
#define MAX_UDPPACKET_SIZE 65535
#define MAX_VALID_UDPPACKET_SIZE 2048
// AMR codec defines - it requires USE_AMR_CODEC defined
// #define USE_AMR_CODEC
#define MT_AMRNB_PAYLOADTYPE 112
#define MT_AMRNB_CODECNAME "amr"
#define MT_AMRNB_OCTET_PAYLOADTYPE 113
#define MT_AMRWB_PAYLOADTYPE 96
#define MT_AMRWB_CODECNAME "amr-wb"
#define MT_AMRWB_OCTET_PAYLOADTYPE 97
#define MT_GSMEFR_PAYLOADTYPE 126
#define MT_GSMEFR_CODECNAME "GERAN-EFR"
#define MT_EVS_PAYLOADTYPE 127
#define MT_EVS_CODECNAME "EVS"
// OPUS codec defines
// #define USE_OPUS_CODEC
#define MT_OPUS_CODEC_PT 106
// ILBC codec defines
#define MT_ILBC20_PAYLOADTYPE -1
#define MT_ILBC30_PAYLOADTYPE -1
// ISAC codec defines
#define MT_ISAC16K_PAYLOADTYPE -1
#define MT_ISAC32K_PAYLOADTYPE -1
// GSM HR payload type
#define MT_GSMHR_PAYLOADTYPE -1
// Mirror buffer capacity
#define MT_MIRROR_CAPACITY 32768
// Mirror buffer readiness threshold - 50 milliseconds
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
# define TEXT(X) X
#endif
// In milliseconds
#define MT_SEVANA_FRAME_TIME 680
#endif

View File

@ -1,14 +1,20 @@
cmake_minimum_required (VERSION 3.15)
project (helper_lib) project (helper_lib)
# Rely on C++ 11 # Rely on C++ 11
set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (USE_NULL_UUID OFF CACHE BOOL "When enabled linking to libuuid is avoided")
set (CMAKE_POSITION_INDEPENDENT_CODE ON) set (CMAKE_POSITION_INDEPENDENT_CODE ON)
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h") file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
add_library(helper_lib ${HELPER_LIB_SOURCES}) add_library(helper_lib ${HELPER_LIB_SOURCES})
target_include_directories(helper_lib PRIVATE ../../libs/ ../../engine ../) set_property(TARGET helper_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# Private include directories
target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .)
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE) target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
if (TARGET_LINUX)
target_link_libraries (helper_lib PUBLIC uuid)
endif()

View File

@ -312,7 +312,7 @@ namespace Calc
{ {
// Dot is here - is it float // Dot is here - is it float
bool isFloat = false; bool isFloat = false;
StringHelper::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat); strx::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
if (isFloat) if (isFloat)
mCurrentLexem.mType = LexemType::Float; mCurrentLexem.mType = LexemType::Float;
else else
@ -488,7 +488,7 @@ namespace Calc
case LexemType::Hex: case LexemType::Hex:
result->mType = Ast::Type::Number; result->mType = Ast::Type::Number;
result->mValue = StringHelper::fromHex2Int(l.mValue); result->mValue = strx::fromHex2Int(l.mValue);
break; break;
case LexemType::Float: case LexemType::Float:

View File

@ -20,10 +20,10 @@ bool CsvReader::readLine(std::vector<std::string>& cells)
std::string line; std::string line;
if (!std::getline(mInputStream, line)) if (!std::getline(mInputStream, line))
return false; return false;
StringHelper::trim(line); strx::trim(line);
if (line.empty()) if (line.empty())
return false; return false;
StringHelper::split(line, cells, ",;"); strx::split(line, cells, ",;");
return true; return true;
} }

View File

@ -1,7 +1,7 @@
#include "HL_File.h" #include "HL_File.h"
#include <fstream> #include <fstream>
#if defined(TARGET_LINUX) || defined(TARGET_OSX) #if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
# include <unistd.h> # include <unistd.h>
# include <sys/statvfs.h> # include <sys/statvfs.h>
# include <memory.h> # include <memory.h>
@ -32,23 +32,24 @@ void FileHelper::remove(const char* s)
::remove(s); ::remove(s);
} }
std::string FileHelper::gettempname() // std::string FileHelper::gettempname()
{ // {
#if defined(TARGET_LINUX) // #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp"; // char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
mkstemp(template_filename); // int code = mkstemp(template_filename);
return template_filename;
#elif defined(TARGET_WIN)
char buffer[L_tmpnam];
tmpnam(buffer);
return buffer; // return template_filename;
#elif defined(TARGET_OSX) // #elif defined(TARGET_WIN)
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp"; // char buffer[L_tmpnam];
mktemp(template_filename); // tmpnam(buffer);
return template_filename;
#endif // return buffer;
} // #elif defined(TARGET_OSX)
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
// mktemp(template_filename);
// return template_filename;
// #endif
// }
bool FileHelper::isAbsolute(const std::string& s) bool FileHelper::isAbsolute(const std::string& s)
{ {
@ -64,7 +65,7 @@ std::string FileHelper::getCurrentDir()
return std::string(); return std::string();
#endif #endif
#if defined(TARGET_LINUX) || defined(TARGET_OSX) #if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
char buf[512]; char buf[512];
if (getcwd(buf, sizeof buf) != nullptr) if (getcwd(buf, sizeof buf) != nullptr)
return buf; return buf;
@ -109,3 +110,32 @@ size_t FileHelper::getFreespace(const std::string& path)
#endif #endif
return r; return r;
} }
std::string FileHelper::expandUserHome(const std::string &path)
{
if (path.empty() || path[0] != '~')
return path; // No expansion needed
const char* home_dir = nullptr;
#ifdef TARGET_WIN
home_dir = std::getenv("USERPROFILE");
if (!home_dir)
{
home_dir = std::getenv("HOMEDRIVE");
const char* homepath = std::getenv("HOMEPATH");
if (home_dir && homepath) {
std::string fullpath(home_dir);
fullpath += homepath;
return fullpath + path.substr(1);
}
}
#else
home_dir = std::getenv("HOME");
#endif
if (!home_dir)
throw std::runtime_error("Unable to determine the home directory");
return std::string(home_dir) + path.substr(1);
}

View File

@ -12,7 +12,7 @@ public:
static void remove(const std::string& s); static void remove(const std::string& s);
static void remove(const char* s); static void remove(const char* s);
static std::string gettempname(); // static std::string gettempname();
static bool isAbsolute(const std::string& s); static bool isAbsolute(const std::string& s);
static std::string getCurrentDir(); static std::string getCurrentDir();
@ -23,6 +23,7 @@ public:
// Returns free space on volume for path // Returns free space on volume for path
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too) // Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
static size_t getFreespace(const std::string& path); static size_t getFreespace(const std::string& path);
static std::string expandUserHome(const std::string& path);
}; };
#endif #endif

View File

@ -1208,7 +1208,7 @@ static const uint16_t byte_crc10_table[256] = {
/* Update the data block's CRC-10 remainder one byte at a time */ /* Update the data block's CRC-10 remainder one byte at a time */
uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size) uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size)
{ {
register int i; /*register*/ int i;
for (i = 0; i < data_blk_size; i++) { for (i = 0; i < data_blk_size; i++) {
crc10_accum = ((crc10_accum << 8) & 0x3ff) crc10_accum = ((crc10_accum << 8) & 0x3ff)

View File

@ -11,25 +11,51 @@
#define MPLS_STACK_MASK (0x00000100) #define MPLS_STACK_MASK (0x00000100)
#define MPLS_STACK_SHIFT (8) #define MPLS_STACK_SHIFT (8)
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForRaw(const Packet& data)
{ {
PacketData result(packet); const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(packet.mData); if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return Payload();
switch (ip4->version())
{
case 4:
return GetUdpPayloadForIp4(data);
case 6:
return GetUdpPayloadForIp6(data);
default:
return Payload();
}
}
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
{
Packet result(data);
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(data.mData);
// Skip ethernet header // Skip ethernet header
packet.mData += sizeof(EthernetHeader); result.mData += sizeof(EthernetHeader);
packet.mLength -= sizeof(EthernetHeader); result.mLength -= sizeof(EthernetHeader);
// See if there is Vlan header // See if there is Vlan header
uint16_t proto = 0; uint16_t proto = 0;
if (ethernet->mEtherType == 129) if (ethernet->mEtherType == 129)
{ {
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData); // Skip 1 or more VLAN headers
packet.mData += sizeof(VlanHeader); do
packet.mLength -= sizeof(VlanHeader); {
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(result.mData);
result.mData += sizeof(VlanHeader);
result.mLength -= sizeof(VlanHeader);
proto = ntohs(vlan->mData); proto = ntohs(vlan->mData);
} }
while (proto == 0x8100);
}
// Skip MPLS headers // Skip MPLS headers
switch (proto) switch (proto)
@ -38,10 +64,10 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::Pa
case ETHERTYPE_MPLS_MC: case ETHERTYPE_MPLS_MC:
// Parse MPLS here until marker "bottom of mpls stack" // Parse MPLS here until marker "bottom of mpls stack"
for(bool bottomOfStack = false; !bottomOfStack; for(bool bottomOfStack = false; !bottomOfStack;
bottomOfStack = ((ntohl(*(uint32_t*)(packet.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0) bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
{ {
packet.mData += 4; result.mData += 4;
packet.mLength -=4; result.mLength -=4;
} }
break; break;
@ -54,86 +80,86 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::Pa
break; break;
} }
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData); const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(result.mData);
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return PacketData(); return Payload();
switch (ip4->version()) switch (ip4->version())
{ {
case 4: case 4:
return GetUdpPayloadForIp4(packet, source, destination); return GetUdpPayloadForIp4(result);
case 6: case 6:
return GetUdpPayloadForIp6(packet, source, destination); return GetUdpPayloadForIp6(result);
default: default:
return PacketData(); return Payload();
} }
} }
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForSLL(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForSLL(const Packet& data)
{ {
PacketData result(packet); Packet result(data);
if (packet.mLength < 16) if (result.mLength < 16)
return PacketData(); return Payload();
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(packet.mData); const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(result.mData);
packet.mData += sizeof(LinuxSllHeader); result.mData += sizeof(LinuxSllHeader);
packet.mLength -= sizeof(LinuxSllHeader); result.mLength -= sizeof(LinuxSllHeader);
switch (ntohs(sll->mProtocolType)) switch (ntohs(sll->mProtocolType))
{ {
case 0x0800: case 0x0800:
return GetUdpPayloadForIp4(packet, source, destination); return GetUdpPayloadForIp4(result);
case 0x86DD: case 0x86DD:
return GetUdpPayloadForIp6(packet, source, destination); return GetUdpPayloadForIp6(result);
default: default:
return PacketData(); return Payload();
} }
} }
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForLoopback(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForLoopback(const Packet& data)
{ {
PacketData result(packet); Packet result(data);
if (packet.mLength < 16) if (result.mLength < 16)
return PacketData(); return Payload();
struct LoopbackHeader struct LoopbackHeader
{ {
uint32_t mProtocolType; uint32_t mProtocolType;
}; };
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(packet.mData); const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(result.mData);
packet.mData += sizeof(LoopbackHeader); result.mData += sizeof(LoopbackHeader);
packet.mLength -= sizeof(LoopbackHeader); result.mLength -= sizeof(LoopbackHeader);
switch (lh->mProtocolType) switch (lh->mProtocolType)
{ {
case AF_INET: case AF_INET:
return GetUdpPayloadForIp4(packet, source, destination); return GetUdpPayloadForIp4(result);
case AF_INET6: case AF_INET6:
return GetUdpPayloadForIp6(packet, source, destination); return GetUdpPayloadForIp6(result);
default: default:
return PacketData(); return Payload();
} }
} }
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
{ {
PacketData result(packet); Packet result(data);
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData); const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return PacketData(nullptr, 0); return Payload();
result.mData += ip4->headerLength(); result.mData += ip4->headerLength();
result.mLength -= ip4->headerLength(); result.mLength -= ip4->headerLength();
@ -147,13 +173,15 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketD
if (length - sizeof(UdpHeader) < (size_t)result.mLength) if (length - sizeof(UdpHeader) < (size_t)result.mLength)
result.mLength = length - sizeof(UdpHeader); result.mLength = length - sizeof(UdpHeader);
source.setIp(ip4->mSource); InternetAddress addr_source;
source.setPort(ntohs(udp->mSourcePort)); addr_source.setIp(ip4->mSource);
addr_source.setPort(ntohs(udp->mSourcePort));
destination.setIp(ip4->mDestination); InternetAddress addr_dest;
destination.setPort(ntohs(udp->mDestinationPort)); addr_dest.setIp(ip4->mDestination);
addr_dest.setPort(ntohs(udp->mDestinationPort));
return result; return {.data = result, .source = addr_source, .dest = addr_dest};
} }
struct Ip6Header struct Ip6Header
@ -183,10 +211,10 @@ struct Ip6Header
struct in6_addr dst_ip; struct in6_addr dst_ip;
}; };
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
{ {
PacketData result(packet); Packet result(data);
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(packet.mData); const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData);
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) /*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return PacketData(nullptr, 0); return PacketData(nullptr, 0);
*/ */
@ -198,18 +226,13 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketD
result.mData += sizeof(UdpHeader); result.mData += sizeof(UdpHeader);
result.mLength -= sizeof(UdpHeader); result.mLength -= sizeof(UdpHeader);
/* InternetAddress addr_source;
if (result.mLength != ntohs(udp->mDatagramLength)) addr_source.setIp(ip6->src_ip);
return PacketData(nullptr, 0); addr_source.setPort(ntohs(udp->mSourcePort));
*/
source.setIp(ip6->src_ip); InternetAddress addr_dest;
source.setPort(ntohs(udp->mSourcePort)); addr_dest.setIp(ip6->dst_ip);
//std::cout << source.toStdString() << " - "; addr_dest.setPort(ntohs(udp->mDestinationPort));
destination.setIp(ip6->dst_ip); return {.data = result, .source = addr_source, .dest = addr_dest};
destination.setPort(ntohs(udp->mDestinationPort));
//std::cout << destination.toStdString() << std::endl;
return result;
} }

View File

@ -7,25 +7,38 @@
class NetworkFrame class NetworkFrame
{ {
public: public:
struct PacketData struct Packet
{ {
const uint8_t* mData; const uint8_t* mData;
size_t mLength; size_t mLength;
PacketData(const uint8_t* data, size_t length) Packet(const uint8_t* data, size_t length)
:mData(data), mLength(length) :mData(data), mLength(length)
{} {}
PacketData() Packet()
:mData(nullptr), mLength(0) :mData(nullptr), mLength(0)
{} {}
bool is_empty() const
{
return mData == nullptr || mLength == 0;
}
}; };
static PacketData GetUdpPayloadForEthernet(PacketData& packet, InternetAddress& source, InternetAddress& destination); struct Payload
static PacketData GetUdpPayloadForIp4(PacketData& packet, InternetAddress& source, InternetAddress& destination); {
static PacketData GetUdpPayloadForIp6(PacketData& packet, InternetAddress& source, InternetAddress& destination); Packet data;
static PacketData GetUdpPayloadForSLL(PacketData& packet, InternetAddress& source, InternetAddress& destination); InternetAddress source;
static PacketData GetUdpPayloadForLoopback(PacketData& packet, InternetAddress& source, InternetAddress& destination); InternetAddress dest;
};
static Payload GetUdpPayloadForEthernet(const Packet& data);
static Payload GetUdpPayloadForIp4(const Packet& data);
static Payload GetUdpPayloadForIp6(const Packet& data);
static Payload GetUdpPayloadForSLL(const Packet& data);
static Payload GetUdpPayloadForLoopback(const Packet& data);
static Payload GetUdpPayloadForRaw(const Packet& data);
struct EthernetHeader struct EthernetHeader
{ {

View File

@ -7,7 +7,7 @@
# include <asm/ioctls.h> # include <asm/ioctls.h>
#endif #endif
#include "../config.h" #include "../engine_config.h"
#include "HL_NetworkSocket.h" #include "HL_NetworkSocket.h"
#if defined(TARGET_OSX) || defined(TARGET_LINUX) #if defined(TARGET_OSX) || defined(TARGET_LINUX)
@ -27,7 +27,7 @@ DatagramSocket::DatagramSocket()
DatagramSocket::~DatagramSocket() DatagramSocket::~DatagramSocket()
{ {
closeSocket(); internalClose();
} }
void DatagramSocket::open(int family) void DatagramSocket::open(int family)
@ -61,7 +61,7 @@ void DatagramSocket::sendDatagram(InternetAddress &dest, const void *packetData,
if (mHandle == INVALID_SOCKET) if (mHandle == INVALID_SOCKET)
return; return;
int sent = ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen()); /*int sent = */::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
} }
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity) unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
@ -85,7 +85,7 @@ unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer,
return 0; return 0;
} }
void DatagramSocket::closeSocket() void DatagramSocket::internalClose()
{ {
if (mHandle != INVALID_SOCKET) if (mHandle != INVALID_SOCKET)
{ {
@ -98,6 +98,11 @@ void DatagramSocket::closeSocket()
} }
} }
void DatagramSocket::closeSocket()
{
internalClose();
}
bool DatagramSocket::isValid() const bool DatagramSocket::isValid() const
{ {
return mHandle != INVALID_SOCKET; return mHandle != INVALID_SOCKET;
@ -163,7 +168,7 @@ unsigned DatagramAgreggator::count()
bool DatagramAgreggator::hasDataAtIndex(unsigned index) bool DatagramAgreggator::hasDataAtIndex(unsigned index)
{ {
PDatagramSocket socket = mSocketVector[index]; PDatagramSocket socket = mSocketVector[index];
return (FD_ISSET(socket->mHandle, &mReadSet) != 0); return FD_ISSET(socket->mHandle, &mReadSet);
} }
PDatagramSocket DatagramAgreggator::socketAt(unsigned index) PDatagramSocket DatagramAgreggator::socketAt(unsigned index)

View File

@ -36,10 +36,12 @@ public:
virtual SOCKET socket() const; virtual SOCKET socket() const;
virtual void open(int family); virtual void open(int family);
protected: protected:
int mFamily; int mFamily;
SOCKET mHandle; SOCKET mHandle;
int mLocalPort; int mLocalPort;
void internalClose();
}; };
typedef std::shared_ptr<DatagramSocket> PDatagramSocket; typedef std::shared_ptr<DatagramSocket> PDatagramSocket;

View File

@ -39,7 +39,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
PROCESS_INFORMATION pi = { 0 }; PROCESS_INFORMATION pi = { 0 };
char* cmdline = (char*)_alloca(cmd.size()+1); char* cmdline = (char*)_alloca(cmd.size()+1);
strcpy(cmdline, StringHelper::replace(cmd, "/", "\\").c_str()); strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str());
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (! fSuccess) if (! fSuccess)
@ -114,7 +114,7 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
memset(&pi, 0, sizeof pi); memset(&pi, 0, sizeof pi);
char* cmdbuffer = (char*)_alloca(cmdline.size()+1); char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
strcpy(cmdbuffer, StringHelper::replace(cmdline, "/", "\\").c_str()); strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str());
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE, BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
@ -160,14 +160,14 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
{ {
std::string line(buf, cr - buf -1); std::string line(buf, cr - buf -1);
if (callback) if (callback)
callback(StringHelper::trim(line)); callback(strx::trim(line));
memmove(buf, cr + 1, strlen(cr+1) + 1); memmove(buf, cr + 1, strlen(cr+1) + 1);
} }
} }
} //for } //for
if (buf[0]) if (buf[0])
callback(StringHelper::trim(std::string(buf))); callback(strx::trim(std::string(buf)));
char ctrlc = 3; char ctrlc = 3;
//if (finish_flag) //if (finish_flag)
@ -278,16 +278,16 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
} }
while (r == sizeof(buffer) - 1); while (r == sizeof(buffer) - 1);
if (lines.find("\n") != std::string::npos && line_callback) if (lines.find('\n') != std::string::npos && line_callback)
{ {
std::string::size_type p = 0; std::string::size_type p = 0;
while (p < lines.size()) while (p < lines.size())
{ {
std::string::size_type d = lines.find("\n", p); std::string::size_type d = lines.find('\n', p);
if (d != std::string::npos) if (d != std::string::npos)
{ {
if (line_callback) if (line_callback)
line_callback(StringHelper::trim(lines.substr(p, d-p))); line_callback(strx::trim(lines.substr(p, d-p)));
p = d + 1; p = d + 1;
} }
} }
@ -311,6 +311,8 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
return t; return t;
} }
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
pid_t OsProcess::findPid(const std::string& cmdline) pid_t OsProcess::findPid(const std::string& cmdline)
{ {
try try
@ -332,5 +334,6 @@ void OsProcess::killByPid(pid_t pid)
return; return;
execSystem("kill -9 " + std::to_string(pid) + " &"); execSystem("kill -9 " + std::to_string(pid) + " &");
} }
#endif
#endif #endif

View File

@ -8,6 +8,10 @@
# include <Windows.h> # include <Windows.h>
#endif #endif
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
# include <arpa/inet.h>
#endif
#include "HL_Rtp.h" #include "HL_Rtp.h"
#include "HL_Exception.h" #include "HL_Exception.h"
#include "HL_String.h" #include "HL_String.h"
@ -52,8 +56,12 @@ bool RtpHelper::isRtp(const void* buffer, size_t length)
if (length < 12) if (length < 12)
return false; return false;
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt; const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35); if (h->version != 0b10)
return false;
unsigned char pt = h->pt;
bool rtp = ( (pt & 0x7F) >= 96 && (pt & 0x7F) <= 127) || ((pt & 0x7F) < 35);
return rtp; return rtp;
} }
@ -62,9 +70,8 @@ bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
{ {
if (length < 12) if (length < 12)
return false; return false;
unsigned char b = ((const unsigned char*)buffer)[0]; const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
return h->version == 0b10;
return (b & 0xC0 ) == 128;
} }
bool RtpHelper::isRtcp(const void* buffer, size_t length) bool RtpHelper::isRtcp(const void* buffer, size_t length)
@ -75,9 +82,17 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
unsigned RtpHelper::findSsrc(const void* buffer, size_t length) unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
{ {
if (isRtp(buffer, length)) if (isRtp(buffer, length))
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc; return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
else else
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc; return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc);
}
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
{
if (isRtp(buffer, length))
reinterpret_cast<RtpHeader*>(buffer)->ssrc = htonl(ssrc);
else
reinterpret_cast<RtcpHeader*>(buffer)->ssrc = htonl(ssrc);
} }
int RtpHelper::findPtype(const void* buffer, size_t length) int RtpHelper::findPtype(const void* buffer, size_t length)
@ -184,64 +199,3 @@ void RtpDump::flush()
} }
#endif #endif
// -------------- MediaStreamId --------------------
bool MediaStreamId::operator < (const MediaStreamId& right) const
{
if (mSsrcIsId)
return std::tie(mSSRC, mSource, mDestination) < std::tie(right.mSSRC, right.mSource, right.mDestination);
else
return std::tie(mSource, mDestination) < std::tie(right.mSource, right.mDestination);
}
bool MediaStreamId::operator == (const MediaStreamId& right) const
{
if (mSsrcIsId)
return std::tie(mSSRC, mSource, mDestination) == std::tie(right.mSSRC, right.mSource, right.mDestination);
else
return std::tie(mSource, mDestination) == std::tie(right.mSource, right.mDestination);
}
std::string MediaStreamId::toString() const
{
std::ostringstream oss;
oss << "src: " << mSource.toStdString() <<
" dst: " << mDestination.toStdString() <<
" ssrc: " << StringHelper::toHex(mSSRC);
return oss.str();
}
void writeToJson(const MediaStreamId& id, std::ostringstream& oss)
{
oss << " \"src\": \"" << id.mSource.toStdString() << "\"," << std::endl
<< " \"dst\": \"" << id.mDestination.toStdString() << "\"," << std::endl
<< " \"ssrc\": \"" << StringHelper::toHex(id.mSSRC) << "\"," << std::endl
#if !defined(USE_NULL_UUID)
<< " \"link_id\": \"" << id.mLinkId.toString() << "\"" << std::endl
#endif
;
}
std::string MediaStreamId::getDetectDescription() const
{
std::ostringstream oss;
oss << "{\"event\": \"stream_detected\"," << std::endl;
writeToJson(*this, oss);
oss << "}";
return oss.str();
}
std::string MediaStreamId::getFinishDescription() const
{
std::ostringstream oss;
oss << "{" << std::endl
<< " \"event\": \"stream_finished\", " << std::endl;
writeToJson(*this, oss);
oss << "}";
return oss.str();
}

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -10,11 +10,8 @@
# include "jrtplib/src/rtppacket.h" # include "jrtplib/src/rtppacket.h"
#endif #endif
#include "HL_Uuid.h" #include <cstdint>
#include "HL_InternetAddress.h" #include <stdlib.h>
#include <vector>
#include <string>
// Class to carry rtp/rtcp socket pair // Class to carry rtp/rtcp socket pair
template<class T> template<class T>
@ -42,6 +39,7 @@ public:
static bool isRtpOrRtcp(const void* buffer, size_t length); static bool isRtpOrRtcp(const void* buffer, size_t length);
static bool isRtcp(const void* buffer, size_t length); static bool isRtcp(const void* buffer, size_t length);
static unsigned findSsrc(const void* buffer, size_t length); static unsigned findSsrc(const void* buffer, size_t length);
static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
static int findPayloadLength(const void* buffer, size_t length); static int findPayloadLength(const void* buffer, size_t length);
}; };
@ -72,19 +70,4 @@ public:
}; };
#endif #endif
struct MediaStreamId
{
InternetAddress mSource;
InternetAddress mDestination;
uint32_t mSSRC = 0;
bool mSsrcIsId = true;
Uuid mLinkId;
bool operator < (const MediaStreamId& s2) const;
bool operator == (const MediaStreamId& right) const;
std::string toString() const;
std::string getDetectDescription() const;
std::string getFinishDescription() const;
};
#endif #endif

View File

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h" #include "../engine_config.h"
#ifdef WIN32 #ifdef WIN32
#include <winsock2.h> #include <winsock2.h>
#include <windows.h> #include <windows.h>
@ -127,7 +127,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
if (sock == INVALID_SOCKET) if (sock == INVALID_SOCKET)
{ {
// Return null socket // Return null socket
PDatagramSocket result(new DatagramSocket()); auto result = std::make_shared<DatagramSocket>();
result->mLocalPort = port; result->mLocalPort = port;
result->mFamily = family; result->mFamily = family;
return result; return result;
@ -170,7 +170,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
closesocket(sock); closesocket(sock);
throw Exception(ERR_NET_FAILED, WSAGetLastError()); throw Exception(ERR_NET_FAILED, WSAGetLastError());
} }
PDatagramSocket resultObject(new DatagramSocket()); auto resultObject = std::make_shared<DatagramSocket>();
resultObject->mLocalPort = testport; resultObject->mLocalPort = testport;
resultObject->mHandle = sock; resultObject->mHandle = sock;
if (!resultObject->setBlocking(false)) if (!resultObject->setBlocking(false))

View File

@ -6,7 +6,7 @@
#ifndef __SOCKET_HEAP_H #ifndef __SOCKET_HEAP_H
#define __SOCKET_HEAP_H #define __SOCKET_HEAP_H
#include "../config.h" #include "../engine_config.h"
#include <map> #include <map>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>

View File

@ -0,0 +1,82 @@
#ifndef __HELPER_STATISTICS_H
#define __HELPER_STATISTICS_H
template<typename T>
struct Average
{
int mCount = 0;
T mSum = 0;
T average() const
{
if (!mCount)
return 0;
return mSum / mCount;
}
T value() const
{
return average();
}
void process(T value)
{
mCount++;
mSum += value;
}
};
template<typename T, int minimum = 100000, int maximum = 0, int default_value = 0>
struct TestResult
{
T mMin = minimum;
T mMax = maximum;
Average<T> mAverage;
T mCurrent = default_value;
void process(T value)
{
if (mMin > value)
mMin = value;
if (mMax < value)
mMax = value;
mCurrent = value;
mAverage.process(value);
}
bool is_initialized() const
{
return mAverage.mCount > 0;
}
T current() const
{
if (is_initialized())
return mCurrent;
else
return 0;
}
T value() const
{
return current();
}
T average() const
{
return mAverage.average();
}
TestResult<T>& operator = (T value)
{
process(value);
return *this;
}
operator T()
{
return mCurrent;
}
};
#endif

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -8,6 +8,7 @@
#include <iomanip> #include <iomanip>
#include <memory.h> #include <memory.h>
#include <algorithm> #include <algorithm>
#include <inttypes.h>
#ifdef TARGET_WIN #ifdef TARGET_WIN
# include <WinSock2.h> # include <WinSock2.h>
@ -15,7 +16,7 @@
# include <cctype> # include <cctype>
#endif #endif
std::string StringHelper::extractFilename(const std::string& path) std::string strx::extractFilename(const std::string& path)
{ {
if (path.empty()) if (path.empty())
return std::string(); return std::string();
@ -30,7 +31,7 @@ std::string StringHelper::extractFilename(const std::string& path)
return path.substr(p_bs + 1); return path.substr(p_bs + 1);
} }
std::string StringHelper::appendPath(const std::string& s1, const std::string& s2) std::string strx::appendPath(const std::string& s1, const std::string& s2)
{ {
std::string result = s1; std::string result = s1;
if (!endsWith(result, "/") && !endsWith(result, "\\")) if (!endsWith(result, "/") && !endsWith(result, "\\"))
@ -44,7 +45,7 @@ std::string StringHelper::appendPath(const std::string& s1, const std::string& s
return result + s2; return result + s2;
} }
std::string StringHelper::makeUtf8(const std::tstring &arg) std::string strx::makeUtf8(const std::tstring &arg)
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
size_t required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL); size_t required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
@ -56,12 +57,12 @@ std::string StringHelper::makeUtf8(const std::tstring &arg)
#endif #endif
} }
std::string StringHelper::toUtf8(const std::tstring &arg) std::string strx::toUtf8(const std::tstring &arg)
{ {
return makeUtf8(arg); return makeUtf8(arg);
} }
std::tstring StringHelper::makeTstring(const std::string& arg) std::tstring strx::makeTstring(const std::string& arg)
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
size_t count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0); size_t count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0);
@ -73,7 +74,7 @@ std::tstring StringHelper::makeTstring(const std::string& arg)
#endif #endif
} }
int StringHelper::toInt(const char *s, int defaultValue, bool* isOk) int strx::toInt(const char *s, int defaultValue, bool* isOk)
{ {
int result; int result;
if (sscanf(s, "%d", &result) != 1) if (sscanf(s, "%d", &result) != 1)
@ -89,7 +90,7 @@ int StringHelper::toInt(const char *s, int defaultValue, bool* isOk)
return result; return result;
} }
uint64_t StringHelper::toUint64(const char* s, uint64_t def, bool *isOk) uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
{ {
uint64_t result = def; uint64_t result = def;
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
@ -109,24 +110,24 @@ uint64_t StringHelper::toUint64(const char* s, uint64_t def, bool *isOk)
return result; return result;
} }
std::string StringHelper::toHex(unsigned int value) std::string strx::toHex(unsigned int value)
{ {
char buffer[32]; char buffer[32];
sprintf(buffer, "%x", value); sprintf(buffer, "%x", value);
return buffer; return buffer;
} }
std::string StringHelper::toHex(const void *ptr) std::string strx::toHex(const void *ptr)
{ {
std::ostringstream oss; std::ostringstream oss;
oss << std::hex << ptr; oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr;
return oss.str(); return oss.str();
} }
//must be lowercase for MD5 //must be lowercase for MD5
static const char hexmap[] = "0123456789abcdef"; static const char hexmap[] = "0123456789abcdef";
std::string StringHelper::toHex(const uint8_t* input, size_t inputLength) std::string strx::toHex(const uint8_t* input, size_t inputLength)
{ {
std::string result; result.resize(inputLength * 2); std::string result; result.resize(inputLength * 2);
@ -147,7 +148,7 @@ std::string StringHelper::toHex(const uint8_t* input, size_t inputLength)
return result; return result;
} }
std::string StringHelper::prefixLines(const std::string &source, const std::string &prefix) std::string strx::prefixLines(const std::string &source, const std::string &prefix)
{ {
// Read source line by line // Read source line by line
std::istringstream iss(source); std::istringstream iss(source);
@ -160,7 +161,7 @@ std::string StringHelper::prefixLines(const std::string &source, const std::stri
return oss.str(); return oss.str();
} }
std::string StringHelper::doubleToString(double value, int precision) std::string strx::doubleToString(double value, int precision)
{ {
std::stringstream ss; std::stringstream ss;
ss << std::fixed << std::setprecision(precision) << value; ss << std::fixed << std::setprecision(precision) << value;
@ -168,7 +169,7 @@ std::string StringHelper::doubleToString(double value, int precision)
} }
const char* StringHelper::findSubstring(const char* buffer, const char* substring, size_t bufferLength) const char* strx::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
return (const char*)strstr(buffer, substring); return (const char*)strstr(buffer, substring);
@ -178,7 +179,7 @@ const char* StringHelper::findSubstring(const char* buffer, const char* substrin
} }
void StringHelper::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims) void strx::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
{ {
dst.clear(); dst.clear();
std::string::size_type p = 0; std::string::size_type p = 0;
@ -202,21 +203,21 @@ void StringHelper::split(const std::string& src, std::vector<std::string>& dst,
} }
} }
std::vector<std::string> StringHelper::split(const std::string& src, const std::string& delims) std::vector<std::string> strx::split(const std::string& src, const std::string& delims)
{ {
std::vector<std::string> r; std::vector<std::string> r;
split(src, r, delims); split(src, r, delims);
return r; return r;
} }
std::pair<std::string, int> StringHelper::parseHost(const std::string& host, int defaultPort) std::pair<std::string, int> strx::parseHost(const std::string& host, int defaultPort)
{ {
std::pair<std::string, int> result; std::pair<std::string, int> result;
std::size_t p = host.find(':'); std::size_t p = host.find(':');
if (p != std::string::npos) if (p != std::string::npos)
{ {
result.first = host.substr(0, p); result.first = host.substr(0, p);
result.second = StringHelper::toInt(host.c_str() + p + 1, defaultPort); result.second = strx::toInt(host.c_str() + p + 1, defaultPort);
} }
else else
{ {
@ -226,15 +227,15 @@ std::pair<std::string, int> StringHelper::parseHost(const std::string& host, int
return result; return result;
} }
std::pair<std::string, std::string> StringHelper::parseAssignment(const std::string& s, bool trimQuotes) std::pair<std::string, std::string> strx::parseAssignment(const std::string& s, bool trimQuotes)
{ {
std::pair<std::string, std::string> result; std::pair<std::string, std::string> result;
std::string::size_type p = s.find('='); std::string::size_type p = s.find('=');
if (p != std::string::npos) if (p != std::string::npos)
{ {
result.first = StringHelper::trim(s.substr(0, p)); result.first = strx::trim(s.substr(0, p));
result.second = StringHelper::trim(s.substr(p+1)); result.second = strx::trim(s.substr(p+1));
if (trimQuotes && result.second.size() >= 2) if (trimQuotes && result.second.size() >= 2)
{ {
if ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') || if ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') ||
@ -243,19 +244,19 @@ std::pair<std::string, std::string> StringHelper::parseAssignment(const std::str
} }
} }
else else
result.first = StringHelper::trim(s); result.first = strx::trim(s);
return result; return result;
} }
std::string StringHelper::intToString(int value) std::string strx::intToString(int value)
{ {
char buffer[32]; char buffer[32];
sprintf(buffer, "%d", value); sprintf(buffer, "%d", value);
return buffer; return buffer;
} }
float StringHelper::toFloat(const std::string &s, float v, bool* isOk) float strx::toFloat(const std::string &s, float v, bool* isOk)
{ {
float result = 0.0; float result = 0.0;
int code = sscanf(s.c_str(), "%f", &result); int code = sscanf(s.c_str(), "%f", &result);
@ -274,14 +275,14 @@ float StringHelper::toFloat(const std::string &s, float v, bool* isOk)
return result; return result;
} }
std::string StringHelper::trim(const std::string &s) std::string strx::trim(const std::string &s)
{ {
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }); auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; });
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }).base(); auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }).base();
return (wsback <= wsfront ? std::string() : std::string(wsfront,wsback)); return (wsback <= wsfront ? std::string() : std::string(wsfront,wsback));
} }
std::string StringHelper::timeToString(time_t t) std::string strx::timeToString(time_t t)
{ {
char buffer[128] = ""; char buffer[128] = "";
struct tm lt; struct tm lt;
@ -295,12 +296,12 @@ std::string StringHelper::timeToString(time_t t)
return buffer; return buffer;
} }
std::string StringHelper::millisecondsToString(uint64_t t) std::string strx::millisecondsToString(uint64_t t)
{ {
return timeToString(t/1000); return timeToString(t/1000);
} }
int StringHelper::fromHex2Int(const std::string &s) int strx::fromHex2Int(const std::string &s)
{ {
int result = 0; int result = 0;
sscanf(s.c_str(), "%x", &result); sscanf(s.c_str(), "%x", &result);
@ -318,12 +319,12 @@ static int hex2code(char s)
return 0; return 0;
} }
static int hex2code(const char* s) /*static int hex2code(const char* s)
{ {
return (hex2code(s[0]) << 4) + hex2code(s[1]); return (hex2code(s[0]) << 4) + hex2code(s[1]);
} }*/
std::string StringHelper::fromHex2String(const std::string& s) std::string strx::fromHex2String(const std::string& s)
{ {
std::string result; result.resize(s.size() / 2); std::string result; result.resize(s.size() / 2);
const char* t = s.c_str(); const char* t = s.c_str();
@ -333,7 +334,7 @@ std::string StringHelper::fromHex2String(const std::string& s)
return result; return result;
} }
std::string StringHelper::replace(const std::string& s, char f, char r) std::string strx::replace(const std::string& s, char f, char r)
{ {
std::string result(s); std::string result(s);
for (std::string::size_type i = 0; i < result.size(); i++) for (std::string::size_type i = 0; i < result.size(); i++)
@ -343,7 +344,7 @@ std::string StringHelper::replace(const std::string& s, char f, char r)
return result; return result;
} }
std::string StringHelper::replace(const std::string& s, const std::string& tmpl, const std::string& n) std::string strx::replace(const std::string& s, const std::string& tmpl, const std::string& n)
{ {
std::string result(s); std::string result(s);
std::string::size_type p = 0; std::string::size_type p = 0;
@ -356,7 +357,7 @@ std::string StringHelper::replace(const std::string& s, const std::string& tmpl,
return result; return result;
} }
std::string StringHelper::decodeUri(const std::string& s) std::string strx::decodeUri(const std::string& s)
{ {
std::string ret; std::string ret;
ret.reserve(s.size()); ret.reserve(s.size());
@ -379,19 +380,19 @@ std::string StringHelper::decodeUri(const std::string& s)
return ret; return ret;
} }
bool StringHelper::startsWith(const std::string& s, const std::string& prefix) bool strx::startsWith(const std::string& s, const std::string& prefix)
{ {
std::string::size_type p = s.find(prefix); std::string::size_type p = s.find(prefix);
return p == 0; return p == 0;
} }
bool StringHelper::endsWith(const std::string& s, const std::string& suffix) bool strx::endsWith(const std::string& s, const std::string& suffix)
{ {
std::string::size_type p = s.rfind(suffix); std::string::size_type p = s.rfind(suffix);
return (p == s.size() - suffix.size()); return (p == s.size() - suffix.size());
} }
int StringHelper::stringToDuration(const std::string& s) int strx::stringToDuration(const std::string& s)
{ {
if (endsWith(s, "ms")) if (endsWith(s, "ms"))
return std::stoi(s.substr(0, s.size()-2)); return std::stoi(s.substr(0, s.size()-2));
@ -405,92 +406,52 @@ int StringHelper::stringToDuration(const std::string& s)
return std::stoi(s) * 1000; return std::stoi(s) * 1000;
} }
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" std::string strx::uppercase(const std::string& s)
// --------------------- XcapHelper -----------------
std::string XcapHelper::buildBuddyList(std::string listName, std::vector<std::string> buddies)
{ {
std::ostringstream result; std::string r(s);
result << XML_HEADER << std::transform(r.begin(), r.end(), r.begin(), ::toupper);
"<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">" << return r;
"<list name=\"" << listName.c_str() << "\">";
// to test CT only!
//result << "<entry uri=\"" << "sip:dbogovych1@10.11.1.25" << "\"/>";
//result << "<entry uri=\"" << "sip:dbogovych2@10.11.1.25" << "\"/>";
for (unsigned i = 0; i<buddies.size(); i++)
{
result << "<entry uri=\"" << normalizeSipUri(buddies[i]).c_str() << "\"/>";
}
result << "</list></resource-lists>";
return result.str();
} }
std::string XcapHelper::buildRules(std::vector<std::string> buddies) std::string strx::lowercase(const std::string& s)
{ {
std::ostringstream result; std::string r(s);
result << XML_HEADER << std::transform(r.begin(), r.end(), r.begin(), ::tolower);
"<ruleset xmlns=\"urn:ietf:params:xml:ns:common-policy\">" << return r;
"<rule id=\"presence_allow\">" <<
"<conditions>";
for (unsigned i = 0; i<buddies.size(); i++)
{
result << "<identity><one id=\"" <<
normalizeSipUri(buddies[i]).c_str() << "\"/></identity>";
}
result << "</conditions>" <<
"<actions>" <<
"<sub-handling xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"allow" <<
"</sub-handling>" <<
"</actions>" <<
"<transformations>" <<
"<provide-devices xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-devices/>" <<
"</provide-devices>" <<
"<provide-persons xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-persons/>" <<
"</provide-persons>" <<
"<provide-services xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-services/>" <<
"</provide-services>" <<
"</transformations>" <<
"</rule>" <<
"</ruleset>";
return result.str();
} }
std::string XcapHelper::buildServices(std::string serviceUri, std::string listRef) std::string strx::removeQuotes(const std::string& s)
{ {
std::ostringstream result; std::string r(s);
if (s.empty())
return s;
result << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl << if (r.front() == '"')
"<rls-services xmlns=\"urn:ietf:params:xml:ns:rls-services\"" << std::endl << r = r.substr(1);
"xmlns:rl=\"urn:ietf:params:xml:ns:resource-lists\"" << std::endl <<
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << std::endl <<
"<service uri=\"" << normalizeSipUri(serviceUri).c_str() << "\">" << std::endl <<
"<resource-list>" << listRef.c_str() << "</resource-list>" << std::endl <<
"<packages>" << std::endl <<
"<package>presence</package>" << std::endl <<
"</packages>" << std::endl <<
"</service>" << std::endl <<
"</rls-services>";
return result.str(); if (r.back() == '"')
r = r.substr(0, r.size()-1);
return r;
} }
std::string XcapHelper::normalizeSipUri(std::string uri) #if defined(TARGET_WIN)
// MSVC++ lacks memmem support
const void *memmem(const void *haystack, size_t haystack_len,
const void * const needle, const size_t needle_len)
{ {
if (uri.length()) if (!haystack || !haystack_len || !needle || !needle_len)
{ return nullptr;
if (uri[0] == '<')
uri.erase(0,1); for (const char *h = (const char*)haystack;
if (uri.length()) haystack_len >= needle_len;
{ ++h, --haystack_len) {
if (uri[uri.length()-1] == '>') if (!memcmp(h, needle, needle_len)) {
uri.erase(uri.length()-1, 1); return h;
} }
} }
return uri; return nullptr;
} }
#endif

View File

@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <cstdint>
#include "HL_Types.h" #include "HL_Types.h"
#ifdef TARGET_OSX #ifdef TARGET_OSX
@ -16,7 +17,7 @@
#endif #endif
class StringHelper class strx
{ {
public: public:
static std::string extractFilename(const std::string& path); static std::string extractFilename(const std::string& path);
@ -25,6 +26,7 @@ public:
static std::string makeUtf8(const std::tstring& arg); static std::string makeUtf8(const std::tstring& arg);
static std::string toUtf8(const std::tstring& arg); static std::string toUtf8(const std::tstring& arg);
static std::tstring makeTstring(const std::string& arg); static std::tstring makeTstring(const std::string& arg);
static int toInt(const char* s, int defaultValue, bool* isOk = nullptr); static int toInt(const char* s, int defaultValue, bool* isOk = nullptr);
static uint64_t toUint64(const char* s, uint64_t def, bool *isOk = nullptr); static uint64_t toUint64(const char* s, uint64_t def, bool *isOk = nullptr);
static std::string toHex(unsigned int value); static std::string toHex(unsigned int value);
@ -33,8 +35,13 @@ public:
static std::string intToString(int value); static std::string intToString(int value);
static std::string prefixLines(const std::string& source, const std::string& prefix); static std::string prefixLines(const std::string& source, const std::string& prefix);
static std::string doubleToString(double value, int precision); static std::string doubleToString(double value, int precision);
static int fromHex2Int(const std::string& s);
static std::string fromHex2String(const std::string& s);
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength); static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength);
static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims); static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
static std::vector<std::string> split(const std::string& src, const std::string& delims = "\n"); static std::vector<std::string> split(const std::string& src, const std::string& delims = "\n");
@ -44,7 +51,7 @@ public:
std::ostringstream s; std::ostringstream s;
for (const auto& i : v) for (const auto& i : v)
{ {
if (&i != &v[0]) if (&i != &v.front())
s << delimiter; s << delimiter;
s << i; s << i;
} }
@ -53,18 +60,19 @@ public:
static std::pair<std::string, int> parseHost(const std::string& host, int defaultPort); static std::pair<std::string, int> parseHost(const std::string& host, int defaultPort);
static std::pair<std::string, std::string> parseAssignment(const std::string& s, bool trimQuotes = true); static std::pair<std::string, std::string> parseAssignment(const std::string& s, bool trimQuotes = true);
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
static std::string trim(const std::string& s); static std::string trim(const std::string& s);
static std::string timeToString(time_t t); static std::string timeToString(time_t t);
static std::string millisecondsToString(uint64_t t); static std::string millisecondsToString(uint64_t t);
static int fromHex2Int(const std::string& s);
static std::string fromHex2String(const std::string& s);
static std::string replace(const std::string& s, char f, char r); static std::string replace(const std::string& s, char f, char r);
static std::string replace(const std::string& s, const std::string& tmpl, const std::string& n); static std::string replace(const std::string& s, const std::string& tmpl, const std::string& n);
static std::string decodeUri(const std::string& s); static std::string decodeUri(const std::string& s);
static bool startsWith(const std::string& s, const std::string& prefix); static bool startsWith(const std::string& s, const std::string& prefix);
static bool endsWith(const std::string& s, const std::string& suffix); static bool endsWith(const std::string& s, const std::string& suffix);
static int stringToDuration(const std::string& s); static int stringToDuration(const std::string& s);
static std::string uppercase(const std::string& s);
static std::string lowercase(const std::string& s);
static std::string removeQuotes(const std::string& s);
}; };
class XcapHelper class XcapHelper
@ -77,4 +85,11 @@ public:
}; };
#if defined(TARGET_WIN)
// MSVC++ lacks memmem support
extern const void *memmem(const void *haystack, size_t haystack_len,
const void * const needle, const size_t needle_len);
#endif
#endif #endif

View File

@ -77,19 +77,21 @@ uint64_t ThreadHelper::getCurrentId()
// ------------------- TimeHelper --------------- // ------------------- TimeHelper ---------------
using namespace std::chrono; using namespace std::chrono;
// Milliseconds starting from the epoch
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count(); static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
// Seconds starting from the epoch
static time_t TimestampBase = time(nullptr); static time_t TimestampBase = time(nullptr);
uint64_t TimeHelper::getTimestamp() // Returns number of milliseconds starting from 01 Jan 1970 GMT
uint64_t chronox::getTimestamp()
{ {
time_point<steady_clock> t = steady_clock::now(); time_point<steady_clock> t = steady_clock::now();
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count(); uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
return ms - TimestampStartPoint + TimestampBase * 1000; return ms - TimestampStartPoint + TimestampBase * 1000;
} }
uint64_t TimeHelper::getUptime() uint64_t chronox::getUptime()
{ {
time_point<steady_clock> t = steady_clock::now(); time_point<steady_clock> t = steady_clock::now();
@ -98,7 +100,7 @@ uint64_t TimeHelper::getUptime()
return ms - TimestampStartPoint; return ms - TimestampStartPoint;
} }
uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier) uint32_t chronox::getDelta(uint32_t later, uint32_t earlier)
{ {
if (later > earlier) if (later > earlier)
return later - earlier; return later - earlier;
@ -109,14 +111,42 @@ uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier)
return 0; return 0;
} }
TimeHelper::ExecutionTime::ExecutionTime() timespec chronox::toTimespec(uint64_t milliseconds)
{ {
mStart = TimeHelper::getTimestamp(); timespec r;
r.tv_sec = milliseconds / 1000;
r.tv_nsec = milliseconds % 1000;
r.tv_nsec *= 1000 * 1000;
return r;
} }
uint64_t TimeHelper::ExecutionTime::getSpentTime() const uint64_t chronox::toTimestamp(const timeval& ts)
{ {
return TimeHelper::getTimestamp() - mStart; return ts.tv_sec * 1000 + ts.tv_usec / 1000;
}
int64_t chronox::getDelta(const timespec& a, const timespec& b)
{
uint64_t ms_a = a.tv_sec * 1000 + a.tv_nsec / 10000000;
uint64_t ms_b = b.tv_sec * 1000 + a.tv_nsec / 10000000;
return ms_a - ms_b;
}
int64_t chronox::getDelta(const timeval& a, const timeval& b)
{
int64_t diff_seconds = a.tv_sec - b.tv_sec;
int64_t diff_microseconds = a.tv_usec - b.tv_usec;
return diff_seconds * 1000 + diff_microseconds / 1000;
}
chronox::ExecutionTime::ExecutionTime()
{
mStart = chronox::getTimestamp();
}
uint64_t chronox::ExecutionTime::getSpentTime() const
{
return chronox::getTimestamp() - mStart;
} }
// --------------- BufferQueue ----------------- // --------------- BufferQueue -----------------

View File

@ -14,6 +14,11 @@
#include <functional> #include <functional>
#include <assert.h> #include <assert.h>
#if defined(TARGET_WIN)
# include <WinSock2.h>
# include <Windows.h>
#endif
typedef std::recursive_mutex Mutex; typedef std::recursive_mutex Mutex;
typedef std::unique_lock<std::recursive_mutex> Lock; typedef std::unique_lock<std::recursive_mutex> Lock;
@ -46,7 +51,7 @@ public:
static uint64_t getCurrentId(); static uint64_t getCurrentId();
}; };
class TimeHelper class chronox
{ {
public: public:
// Returns current timestamp in milliseconds // Returns current timestamp in milliseconds
@ -59,6 +64,14 @@ public:
// Handles cases when clock is wrapped. // Handles cases when clock is wrapped.
static uint32_t getDelta(uint32_t later, uint32_t earlier); static uint32_t getDelta(uint32_t later, uint32_t earlier);
// Converts number of milliseconds starting from Epoch begin to timespec.
static timespec toTimespec(uint64_t milliseconds);
static uint64_t toTimestamp(const timeval& ts);
// Returns difference between timestamps in milliseconds
static int64_t getDelta(const timespec& a, const timespec& b);
static int64_t getDelta(const timeval& a, const timeval& b);
class ExecutionTime class ExecutionTime
{ {
public: public:

View File

@ -7,7 +7,7 @@ thread_pool::thread_pool(size_t num_of_threads, const std::string& name)
num_of_threads = std::thread::hardware_concurrency(); num_of_threads = std::thread::hardware_concurrency();
for(size_t idx = 0; idx < num_of_threads; idx++) for(size_t idx = 0; idx < num_of_threads; idx++)
this->workers.push_back(std::thread(&thread_pool::run_worker, this)); this->workers.emplace_back(std::thread(&thread_pool::run_worker, this));
} }
// Add new work item to the pool // Add new work item to the pool
@ -20,6 +20,22 @@ void thread_pool::enqueue(const thread_pool::task& t)
this->condition.notify_one(); this->condition.notify_one();
} }
void thread_pool::wait(std::chrono::milliseconds interval)
{
while (size() != 0)
std::this_thread::sleep_for(interval);
}
size_t thread_pool::size()
{
std::unique_lock l(this->queue_mutex);
return this->tasks.size();
}
size_t thread_pool::threads()
{
return this->workers.size();
}
// the destructor joins all threads // the destructor joins all threads
thread_pool::~thread_pool() thread_pool::~thread_pool()

View File

@ -18,10 +18,13 @@ class thread_pool
public: public:
typedef std::function<void()> task; typedef std::function<void()> task;
thread_pool(size_t num_of_threads, const std::string&); thread_pool(size_t num_of_threads, const std::string& thread_name);
~thread_pool(); ~thread_pool();
void enqueue(const task& task); void enqueue(const task& task);
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
size_t size();
size_t threads();
private: private:
// need to keep track of threads so we can join them // need to keep track of threads so we can join them
@ -33,7 +36,7 @@ private:
// synchronization // synchronization
std::mutex queue_mutex; std::mutex queue_mutex;
std::condition_variable condition; std::condition_variable condition;
bool stop = false; std::atomic_bool stop = false;
// thread name prefix for worker threads // thread name prefix for worker threads
std::string name; std::string name;

View File

@ -0,0 +1,53 @@
#include "HL_Time.h"
#include <time.h>
#include <chrono>
/* return current time in milliseconds */
double now_ms(void)
{
#if defined(TARGET_WIN)
auto tp = std::chrono::steady_clock::now();
auto result = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count();
return result;
#else
struct timespec res;
clock_gettime(CLOCK_MONOTONIC, &res);
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
#endif
}
int compare_timespec(const timespec& lhs, const timespec& rhs)
{
if (lhs.tv_sec < rhs.tv_sec)
return -1;
if (lhs.tv_sec > rhs.tv_sec)
return 1;
if (lhs.tv_nsec < rhs.tv_nsec)
return -1;
if (lhs.tv_nsec > rhs.tv_nsec)
return 1;
return 0;
}
bool operator < (const timespec& lhs, const timespec& rhs)
{
return compare_timespec(lhs, rhs) < 0;
}
bool operator == (const timespec& lhs, const timespec& rhs)
{
return compare_timespec(lhs, rhs) == 0;
}
bool operator > (const timespec& lhs, const timespec& rhs)
{
return compare_timespec(lhs, rhs) > 0;
}
bool is_zero(const timespec& ts)
{
return !ts.tv_sec && !ts.tv_nsec;
}

View File

@ -0,0 +1,17 @@
#ifndef __HELPER_TIME_H
#define __HELPER_TIME_H
#include <time.h>
// Return current monotonic number of milliseconds starting from some point
extern double now_ms();
// Compare the timespec.
// Returns -1 if lhs < rhs, 1 if lhs > rhs, 0 if equal
extern int compare_timespec(const timespec& lhs, const timespec& rhs);
extern bool operator < (const timespec& lhs, const timespec& rhs);
extern bool operator == (const timespec& lhs, const timespec& rhs);
extern bool operator > (const timespec& lhs, const timespec& rhs);
extern bool is_zero(const timespec& ts);
#endif

View File

@ -0,0 +1 @@
#include "HL_Types.h"

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -26,4 +26,139 @@ enum SdpDirection
Sdp_Offer Sdp_Offer
}; };
#include <unordered_map>
#include <utility>
#include <stdexcept>
#include <map>
template<
class K, class V,
class HashK = std::hash<K>, class EqK = std::equal_to<K>,
class HashV = std::hash<V>, class EqV = std::equal_to<V>
>
class BiMap {
public:
using key_type = K;
using mapped_type = V;
BiMap(const std::map<K,V>& initializers) {
for (const auto& item: initializers) {
insert(item.first, item.second);
}
}
// Insert a new (key, value) pair. Returns false if either key or value already exists.
bool insert(const K& k, const V& v) {
if (contains_key(k) || contains_value(v)) return false;
auto ok = forward_.emplace(k, v);
try {
auto ov = reverse_.emplace(v, k);
if (!ov.second) { // shouldn't happen given the guard above
forward_.erase(k);
return false;
}
} catch (...) {
forward_.erase(k);
throw;
}
return ok.second;
}
bool insert(K&& k, V&& v) {
if (contains_key(k) || contains_value(v)) return false;
auto ok = forward_.emplace(std::move(k), std::move(v));
try {
auto ov = reverse_.emplace(ok.first->second, ok.first->first); // use stored refs
if (!ov.second) {
forward_.erase(ok.first);
return false;
}
} catch (...) {
forward_.erase(ok.first);
throw;
}
return ok.second;
}
// Replace value for existing key (and update reverse map). Returns false if value is already bound elsewhere.
bool replace_by_key(const K& k, const V& new_v) {
auto it = forward_.find(k);
if (it == forward_.end()) return false;
if (contains_value(new_v)) return false;
// remove old reverse, insert new reverse, then update forward
reverse_.erase(it->second);
reverse_.emplace(new_v, k);
it->second = new_v;
return true;
}
// Replace key for existing value (and update forward map). Returns false if key is already bound elsewhere.
bool replace_by_value(const V& v, const K& new_k) {
auto it = reverse_.find(v);
if (it == reverse_.end()) return false;
if (contains_key(new_k)) return false;
forward_.erase(it->second);
forward_.emplace(new_k, v);
it->second = new_k;
return true;
}
// Erase by key/value. Return number erased (0 or 1).
size_t erase_key(const K& k) {
auto it = forward_.find(k);
if (it == forward_.end()) return 0;
reverse_.erase(it->second);
forward_.erase(it);
return 1;
}
size_t erase_value(const V& v) {
auto it = reverse_.find(v);
if (it == reverse_.end()) return 0;
forward_.erase(it->second);
reverse_.erase(it);
return 1;
}
// Lookup
bool contains_key(const K& k) const { return forward_.find(k) != forward_.end(); }
bool contains_value(const V& v) const { return reverse_.find(v) != reverse_.end(); }
const V* find_by_key(const K& k) const {
auto it = forward_.find(k);
return (it == forward_.end()) ? nullptr : &it->second;
}
const K* find_by_value(const V& v) const {
auto it = reverse_.find(v);
return (it == reverse_.end()) ? nullptr : &it->second;
}
// at() variants throw std::out_of_range on missing entries
const V& at_key(const K& k) const { return forward_.at(k); }
const K& at_value(const V& v) const { return reverse_.at(v); }
void clear() noexcept {
forward_.clear();
reverse_.clear();
}
bool empty() const noexcept { return forward_.empty(); }
size_t size() const noexcept { return forward_.size(); }
// Reserve buckets for performance (optional)
void reserve(size_t n) {
forward_.reserve(n);
reverse_.reserve(n);
}
private:
std::unordered_map<K, V, HashK, EqK> forward_;
std::unordered_map<V, K, HashV, EqV> reverse_;
};
#include <chrono>
typedef std::chrono::steady_clock::time_point timepoint_t;
typedef std::chrono::steady_clock monoclock_t;
#endif #endif

View File

@ -1,76 +1,57 @@
#include "HL_Uuid.h" #include "HL_Uuid.h"
#include <memory.h> #include <memory.h>
#include <random>
#include <span>
#if defined(TARGET_LINUX)
#define UUID_SYSTEM_GENERATOR
#endif
#include "uuid.h"
Uuid::Uuid() Uuid::Uuid()
{ {
#if defined(TARGET_WIN) || defined(TARGET_LINUX) || defined(TARGET_OSX)
memset(&mUuid, 0, sizeof mUuid); memset(&mUuid, 0, sizeof mUuid);
#endif
} }
Uuid Uuid::generateOne() Uuid Uuid::generateOne()
{ {
Uuid result; Uuid result;
#if !defined(USE_NULL_UUID) // #if defined(TARGET_LINUX)
#if defined(TARGET_LINUX) || defined(TARGET_OSX) // auto id = uuids::uuid_system_generator{}();
uuid_generate(result.mUuid); // #else
#endif std::random_device rd;
#if defined(TARGET_WIN) auto seed_data = std::array<int, std::mt19937::state_size> {};
UuidCreate(&result.mUuid); std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
#endif std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
#endif std::mt19937 generator(seq);
uuids::uuid_random_generator gen{generator};
auto id = gen();
// #endif
memcpy(result.mUuid, id.as_bytes().data(), id.as_bytes().size_bytes());
return result; return result;
} }
Uuid Uuid::parse(const std::string &s) Uuid Uuid::parse(const std::string &s)
{ {
Uuid result; Uuid result;
#if !defined(USE_NULL_UUID) auto id = uuids::uuid::from_string(s);
#if defined(TARGET_LINUX) || defined(TARGET_OSX) if (id)
uuid_parse(s.c_str(), result.mUuid); memcpy(result.mUuid, id->as_bytes().data(), id->as_bytes().size_bytes());
#endif
#if defined(TARGET_WIN)
UuidFromStringA((RPC_CSTR)s.c_str(), &result.mUuid);
#endif
#endif
return result; return result;
} }
std::string Uuid::toString() const std::string Uuid::toString() const
{ {
char buf[64]; auto s = std::span<uuids::uuid::value_type, 16>{(uuids::uuid::value_type*)mUuid, 16};
#if defined(USE_NULL_UUID) uuids::uuid id(s);
return "UUID_disabled"; return uuids::to_string(id);
#else
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
uuid_unparse_lower(mUuid, buf);
#endif
#if defined(TARGET_WIN)
RPC_CSTR s = nullptr;
UuidToStringA(&mUuid, &s);
if (s)
{
strcpy(buf, (const char*)s);
RpcStringFreeA(&s);
s = nullptr;
}
#endif
return buf;
#endif
} }
bool Uuid::operator < (const Uuid& right) const bool Uuid::operator < (const Uuid& right) const
{ {
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0; return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
#endif
#if defined(TARGET_WIN)
return memcmp(&mUuid, &right.mUuid, sizeof(mUuid)) < 0;
#endif
return false; return false;
} }

View File

@ -2,14 +2,7 @@
#define __HL_UUID_H #define __HL_UUID_H
#include <string> #include <string>
#include <stdint.h>
#if (defined(TARGET_LINUX) || defined(TARGET_OSX)) && !defined(USE_NULL_UUID)
// Please do not forget "sudo apt install uuid-dev" on Ubuntu
# include <uuid/uuid.h>
#endif
#if defined(TARGET_WIN)
# include <rpc.h>
#endif
class Uuid class Uuid
{ {
@ -21,20 +14,7 @@ public:
bool operator < (const Uuid& right) const; bool operator < (const Uuid& right) const;
protected: protected:
#if defined(USE_NULL_UUID) uint8_t mUuid[16];
unsigned char mUuid[16];
#else
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
uuid_t mUuid;
#endif
#if defined(TARGET_WIN)
UUID mUuid;
#endif
#if defined(TARGET_ANDROID)
// Stub only
#endif
#endif
}; };

View File

@ -0,0 +1,109 @@
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HL_Xcap.h"
#include <sstream>
#include <iomanip>
#include <memory.h>
#include <algorithm>
#include <inttypes.h>
#ifdef TARGET_WIN
# include <WinSock2.h>
# include <Windows.h>
# include <cctype>
#endif
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
// --------------------- XcapHelper -----------------
std::string XcapHelper::buildBuddyList(const std::string& listName, const std::vector<std::string>& buddies)
{
std::ostringstream result;
result << XML_HEADER <<
"<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">" <<
"<list name=\"" << listName.c_str() << "\">";
// to test CT only!
//result << "<entry uri=\"" << "sip:dbogovych1@10.11.1.25" << "\"/>";
//result << "<entry uri=\"" << "sip:dbogovych2@10.11.1.25" << "\"/>";
for (unsigned i = 0; i<buddies.size(); i++)
{
result << "<entry uri=\"" << normalizeSipUri(buddies[i]).c_str() << "\"/>";
}
result << "</list></resource-lists>";
return result.str();
}
std::string XcapHelper::buildRules(const std::vector<std::string>& buddies)
{
std::ostringstream result;
result << XML_HEADER <<
"<ruleset xmlns=\"urn:ietf:params:xml:ns:common-policy\">" <<
"<rule id=\"presence_allow\">" <<
"<conditions>";
for (unsigned i = 0; i<buddies.size(); i++)
{
result << "<identity><one id=\"" <<
normalizeSipUri(buddies[i]).c_str() << "\"/></identity>";
}
result << "</conditions>" <<
"<actions>" <<
"<sub-handling xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"allow" <<
"</sub-handling>" <<
"</actions>" <<
"<transformations>" <<
"<provide-devices xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-devices/>" <<
"</provide-devices>" <<
"<provide-persons xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-persons/>" <<
"</provide-persons>" <<
"<provide-services xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-services/>" <<
"</provide-services>" <<
"</transformations>" <<
"</rule>" <<
"</ruleset>";
return result.str();
}
std::string XcapHelper::buildServices(const std::string& serviceUri, const std::string& listRef)
{
std::ostringstream result;
result << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl <<
"<rls-services xmlns=\"urn:ietf:params:xml:ns:rls-services\"" << std::endl <<
"xmlns:rl=\"urn:ietf:params:xml:ns:resource-lists\"" << std::endl <<
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << std::endl <<
"<service uri=\"" << normalizeSipUri(serviceUri).c_str() << "\">" << std::endl <<
"<resource-list>" << listRef.c_str() << "</resource-list>" << std::endl <<
"<packages>" << std::endl <<
"<package>presence</package>" << std::endl <<
"</packages>" << std::endl <<
"</service>" << std::endl <<
"</rls-services>";
return result.str();
}
std::string XcapHelper::normalizeSipUri(const std::string& uri)
{
std::string t(uri);
if (t.length())
{
if (t[0] == '<')
t.erase(0,1);
if (t.length())
{
if (t[t.length()-1] == '>')
t.erase(uri.length()-1, 1);
}
}
return t;
}

View File

@ -0,0 +1,23 @@
/* Copyright(C) 2007-2024 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HELPER_STRING_H
#define __HELPER_STRING_H
#include <vector>
#include <string>
#include <sstream>
class XcapHelper
{
public:
static std::string buildBuddyList(const std::string& listName, const std::vector<std::string>& buddies);
static std::string buildRules(const std::vector<std::string>& buddies);
static std::string buildServices(const std::string& serviceUri, const std::string& listRef);
static std::string normalizeSipUri(const std::string& uri);
};
#endif

View File

@ -1,31 +1,97 @@
project (media_lib) project (media_lib)
# Rely on C++ 11 if (NOT DEFINED LIB_PLATFORM)
set (CMAKE_CXX_STANDARD 11) message(FATAL_ERROR media_lib project requires LIB_PLATFORM to be set - it uses libraries from that directory)
endif()
# Rely on C++ 20
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_STANDARD_REQUIRED ON)
# Produce PIC code always # Produce PIC code always
set (CMAKE_POSITION_INDEPENDENT_CODE ON) set (CMAKE_POSITION_INDEPENDENT_CODE ON)
#set (USE_RESIP_INTEGRATION CACHE BOOL OFF "Integrate with resip structures") set (SOURCES
file(GLOB MEDIA_LIB_SOURCES "*.cpp" "*.h") MT_Statistics.cpp
MT_WebRtc.cpp
MT_Stream.cpp
MT_SrtpHelper.cpp
MT_SingleAudioStream.cpp
MT_NativeRtpSender.cpp
MT_Dtmf.cpp
MT_CodecList.cpp
MT_Codec.cpp
MT_Box.cpp
MT_AudioStream.cpp
MT_AudioReceiver.cpp
MT_AudioCodec.cpp
MT_CngHelper.cpp
MT_AmrCodec.cpp
MT_EvsCodec.cpp
if(CMAKE_SYSTEM MATCHES "Linux*") MT_Statistics.h
add_definitions(-DHAVE_NETINET_IN_H) MT_WebRtc.h
MT_Stream.h
MT_SrtpHelper.h
MT_SingleAudioStream.h
MT_NativeRtpSender.h
MT_Dtmf.h
MT_CodecList.h
MT_Codec.h
MT_Box.h
MT_AudioStream.h
MT_AudioReceiver.h
MT_AudioCodec.h
MT_CngHelper.h
MT_AmrCodec.h
MT_EvsCodec.h
)
add_library(media_lib ${SOURCES})
set (LIBS_CODEC)
if (USE_AMR_CODEC)
include (${LIB_PLATFORM}/platform_libs.cmake)
message("Media: AMR NB and WB codecs will be included.")
target_compile_definitions(media_lib PUBLIC USE_AMR_CODEC)
list (APPEND LIBS_CODEC ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
endif() endif()
if(CMAKE_SYSTEM MATCHES "Darwin*") if (USE_EVS_CODEC)
# OS X Specific flags message("Media: EVS codec will be included.")
add_definitions(-DHAVE_NETINET_IN_H) target_compile_definitions (media_lib PUBLIC USE_EVS_CODEC)
list (APPEND LIBS_CODEC evs_codec)
endif() endif()
if (USE_OPUS_CODEC)
message("Media: Opus codec will be included.")
target_compile_definitions(media_lib PUBLIC USE_OPUS_CODEC)
list (APPEND LIBS_CODEC opus)
endif()
if(CMAKE_SYSTEM MATCHES "Linux*" OR CMAKE_SYSTEM MATCHES "Darwin*")
target_compile_definitions(media_lib PUBLIC HAVE_NETINET_IN_H)
endif()
if (CMAKE_SYSTEM MATCHES "Windows*") if (CMAKE_SYSTEM MATCHES "Windows*")
# Windows Specific flags - MSVC expected # Windows Specific flags - MSVC expected
add_definitions(-D_CRT_SECURE_NO_WARNINGS -DHAVE_WINSOCK2_H target_compile_definitions(media_lib PRIVATE
-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -DUNICODE -D_UNICODE ) _CRT_SECURE_NO_WARNINGS
HAVE_WINSOCK2_H
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
UNICODE
_UNICODE )
endif() endif()
add_library(media_lib ${MEDIA_LIB_SOURCES})
# Dependency on ice_stack - Linux build requires it
# Codec libraries as well
target_link_libraries(media_lib ice_stack ${LIBS_CODEC})
set_property(TARGET media_lib PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_include_directories(media_lib target_include_directories(media_lib
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/
@ -33,9 +99,9 @@ target_include_directories(media_lib
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/opus/include/
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/ ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/ ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs
${LIB_PLATFORM}/opus/include
) )
target_include_directories(media_lib target_include_directories(media_lib
@ -45,8 +111,5 @@ target_include_directories(media_lib
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_op ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_op
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_math ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_math
) )
if (USE_RESIP_INTEGRATION)
message("USE_RESIP_INTEGRATION is turned on!")
target_compile_definitions(media_lib PUBLIC -DUSE_RESIP_INTEGRATION)
endif()

View File

@ -5,85 +5,30 @@
#include "../helper/HL_ByteBuffer.h" #include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
#include "../helper/HL_IuUP.h" #include "../helper/HL_IuUP.h"
#include "../helper/HL_Exception.h"
#include <iostream>
#define LOG_SUBSYSTEM "AmrCodec" #define LOG_SUBSYSTEM "AmrCodec"
using namespace MT; using namespace MT;
static const uint8_t amr_block_size[16]={ 13, 14, 16, 18, 20, 21, 27, 32, // Constant of AMR-NB frame lengths in bytes.
6 , 0 , 0 , 0 , 0 , 0 , 0 , 1 }; const uint8_t amrnb_framelen[9] =
{12, 13, 15, 17, 19, 20, 26, 31, 5};
/**
* Constant of AMR-NB frame lengths in bytes.
*/
const uint8_t amrnb_framelen[16] =
{12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
/**
* Constant of AMR-NB frame lengths in bits.
*/
const uint16_t amrnb_framelenbits[9] = const uint16_t amrnb_framelenbits[9] =
{95, 103, 118, 134, 148, 159, 204, 244, 39}; {95, 103, 118, 134, 148, 159, 204, 244, 39};
/**
* Constant of AMR-NB bitrates.
*/
const uint16_t amrnb_bitrates[8] =
{4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200};
/** // Constant of AMR-WB frame lengths in bytes.
* Constant of AMR-WB frame lengths in bytes. const uint8_t amrwb_framelen[10] =
*/ {17, 23, 32, 37, 40, 46, 50, 58, 60, 5 /* SID packet */};
const uint8_t amrwb_framelen[16] =
{17, 23, 32, 37, 40, 46, 50, 58, 60, 5, 0, 0, 0, 0, 0, 0};
/**
* Constant of AMR-WB frame lengths in bits.
*/
const uint16_t amrwb_framelenbits[10] = const uint16_t amrwb_framelenbits[10] =
{132, 177, 253, 285, 317, 365, 397, 461, 477, 40}; {132, 177, 253, 285, 317, 365, 397, 461, 477, 40 /* SID packet */};
/**
* Constant of AMR-WB bitrates.
*/
const uint16_t amrwb_bitrates[9] =
{6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850};
// Helper routines // Helper routines
/*static int8_t bitrateToMode(uint16_t bitrate)
{
int8_t mode = -1;
switch (bitrate)
{
// AMR NB
case 4750: mode = 0; break;
case 5150: mode = 1; break;
case 5900: mode = 2; break;
case 6700: mode = 3; break;
case 7400: mode = 4; break;
case 7950: mode = 5; break;
case 10200: mode = 6; break;
case 12200: mode = 7; break;
// AMRWB
case 6600: mode = 0; break;
case 8850: mode = 1; break;
case 12650: mode = 2; break;
case 14250: mode = 3; break;
case 15850: mode = 4; break;
case 18250: mode = 5; break;
case 19850: mode = 6; break;
case 23050: mode = 7; break;
case 23850: mode = 8; break;
}
return mode;
}*/
struct AmrPayloadInfo struct AmrPayloadInfo
{ {
const uint8_t* mPayload; const uint8_t* mPayload;
@ -119,12 +64,13 @@ struct AmrPayload
static AmrPayload parseAmrPayload(AmrPayloadInfo& input) static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
{ {
AmrPayload result; AmrPayload result;
// Do not skip packet by default; I suppose packet is good enough by default. // Do not skip packet by default; I suppose packet is good enough by default.
result.mDiscardPacket = false; result.mDiscardPacket = false;
// Wrap incoming data with ByteArray to make bit dequeuing easy // Wrap incoming data with ByteArray to make bit dequeuing easy
ByteBuffer dataIn(input.mPayload, static_cast<size_t>(input.mPayloadLength)); ByteBuffer byte_reader(input.mPayload, static_cast<size_t>(input.mPayloadLength));
BitReader br(input.mPayload, static_cast<size_t>(input.mPayloadLength)); BitReader bit_reader (input.mPayload, static_cast<size_t>(input.mPayloadLength));
// In bandwidth-efficient mode, the payload header simply consists of a // In bandwidth-efficient mode, the payload header simply consists of a
// 4-bit codec mode request: // 4-bit codec mode request:
@ -135,16 +81,15 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
// AMR, as defined in Table 1a in [2], or 0-8 for AMR-WB, as defined // AMR, as defined in Table 1a in [2], or 0-8 for AMR-WB, as defined
// in Table 1a in [4]. CMR value 15 indicates that no mode request // in Table 1a in [4]. CMR value 15 indicates that no mode request
// is present, and other values are for future use. // is present, and other values are for future use.
result.mCodeModeRequest = static_cast<uint8_t>(br.readBits(4)); result.mCodeModeRequest = static_cast<uint8_t>(bit_reader.readBits(4));
//ICELogMedia(<< "CMR: " << result.mCodeModeRequest);
// Consume extra 4 bits for octet aligned profile // Consume extra 4 bits for octet aligned profile
if (input.mOctetAligned) if (input.mOctetAligned)
br.readBits(4); bit_reader.readBits(4);
// Skip interleaving flags for now for octet aligned mode // Skip interleaving flags for now for octet aligned mode
if (input.mInterleaving && input.mOctetAligned) if (input.mInterleaving && input.mOctetAligned)
br.readBits(8); bit_reader.readBits(8);
// Silence codec mode constant (it differs for wideband and narrowband codecs) // Silence codec mode constant (it differs for wideband and narrowband codecs)
uint8_t SID_FT = input.mWideband ? 9 : 8; uint8_t SID_FT = input.mWideband ? 9 : 8;
@ -158,105 +103,121 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
// F (1 bit): If set to 1, indicates that this frame is followed by // F (1 bit): If set to 1, indicates that this frame is followed by
// another speech frame in this payload; if set to 0, indicates that // another speech frame in this payload; if set to 0, indicates that
// this frame is the last frame in this payload. // this frame is the last frame in this payload.
F = br.readBit(); F = bit_reader.readBit();
// FT (4 bits): Frame type index, indicating either the AMR or AMR-WB // FT (4 bits): Frame type index, indicating either the AMR or AMR-WB
// speech coding mode or comfort noise (SID) mode of the // speech coding mode or comfort noise (SID) mode of the
// corresponding frame carried in this payload. // corresponding frame carried in this payload.
FT = static_cast<uint8_t>(br.readBits(4)); FT = static_cast<uint8_t>(bit_reader.readBits(4));
// Q (1 bit): Frame quality indicator. If set to 0, indicates the // Q (1 bit): Frame quality indicator. If set to 0, indicates the
// corresponding frame is severely damaged, and the receiver should // corresponding frame is severely damaged, and the receiver should
// set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD // set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD
// depending on the frame type (FT). // depending on the frame type (FT).
Q = br.readBit(); Q = bit_reader.readBit();
// Handle padding for octet alignment
if (input.mOctetAligned)
bit_reader.readBits(2);
AmrFrame frame;
frame.mFrameType = FT;
frame.mSTI = 0;
frame.mMode = FT < SID_FT ? FT : 0xFF;
frame.mGoodQuality = Q == 1;
frame.mTimestamp = input.mCurrentTimestamp;
result.mFrames.push_back(frame);
input.mCurrentTimestamp += input.mWideband ? 320 : 160;
}
while (F != 0);
for (size_t frameIndex=0; frameIndex < result.mFrames.size() && !result.mDiscardPacket; frameIndex++)
{
AmrFrame& f = result.mFrames[frameIndex];
// If receiving a ToC entry with a FT value in the range 9-14 for AMR or // If receiving a ToC entry with a FT value in the range 9-14 for AMR or
// 10-13 for AMR-WB, the whole packet SHOULD be discarded. This is to // 10-13 for AMR-WB, the whole packet SHOULD be discarded. This is to
// avoid the loss of data synchronization in the depacketization // avoid the loss of data synchronization in the depacketization
// process, which can result in a huge degradation in speech quality. // process, which can result in a huge degradation in speech quality.
if ((input.mWideband && (FT >= 10 && FT <= 13)) || bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14);
(!input.mWideband && (FT >= 9 && FT <= 14))) // discard |= input.mWideband ? f.mFrameType >= 14 : f.mFrameType >= 15;
if (discard)
{ {
ICELogMedia(<< "Discard corrupted packet");
// Discard bad packet
result.mDiscardPacket = true; result.mDiscardPacket = true;
return result; continue;
} }
// Handle padding for octet alignment if (input.mWideband && f.mFrameType == 15)
if (input.mOctetAligned)
br.readBits(2);
AmrFrame frame;
frame.mFrameType = FT;
frame.mMode = FT < SID_FT ? FT : 0xFF;
frame.mGoodQuality = Q == 1;
frame.mTimestamp = input.mCurrentTimestamp;
result.mFrames.push_back(frame);
input.mCurrentTimestamp += input.mWideband ? 320 : 160;
}
while (F != 0);
for (size_t frameIndex=0; frameIndex < result.mFrames.size(); frameIndex++)
{ {
AmrFrame& frame = result.mFrames[frameIndex]; // DTX, no sense to decode the data
size_t bitsLength = input.mWideband ? amrwb_framelenbits[frame.mFrameType] : amrnb_framelenbits[frame.mFrameType]; continue;
size_t byteLength = input.mWideband ? amrwb_framelen[frame.mFrameType] : amrnb_framelen[frame.mFrameType]; }
ICELogMedia(<< "New AMR speech frame: frame type = " << FT <<
", mode = " << frame.mMode << if (input.mWideband && f.mFrameType == 14)
", timestamp = " << static_cast<int>(frame.mTimestamp) << {
", bits length = " << static_cast<int>(bitsLength) << // Speech lost code only
", byte length =" << static_cast<int>(byteLength) << continue;
", remaining packet length = " << static_cast<int>(dataIn.size())); }
if (!f.mGoodQuality)
{
// Bad quality, frame is damaged
continue;
}
size_t bitsLength = input.mWideband ? amrwb_framelenbits[f.mFrameType] : amrnb_framelenbits[f.mFrameType];
size_t byteLength = input.mWideband ? amrwb_framelen[f.mFrameType] : amrnb_framelen[f.mFrameType];
if (bitsLength > 0) if (bitsLength > 0)
{ {
if (input.mOctetAligned) if (input.mOctetAligned)
{ {
if (dataIn.size() < byteLength) if (byte_reader.size() < byteLength)
frame.mGoodQuality = false; f.mGoodQuality = false;
else else
{ {
// It is octet aligned scheme, so we are on byte boundary now // It is octet aligned scheme, so we are on byte boundary now
size_t byteOffset = br.count() / 8; size_t byteOffset = bit_reader.position() / 8;
// Copy data of AMR frame // Copy data of AMR frame
frame.mData = std::make_shared<ByteBuffer>(input.mPayload + byteOffset, byteLength); if (byteOffset + byteLength <= input.mPayloadLength)
{
f.mData = std::make_shared<ByteBuffer>();
f.mData->resize(byteLength + 1); // payload + header
memcpy(f.mData->mutableData() + 1, input.mPayload + byteOffset, byteLength);
// Add header for decoder
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
}
else
{
ICELogError(<< "Problem parsing AMR header: octet-aligned is set, available " << int(input.mPayloadLength - byteOffset)
<< " bytes but requested " << (int)byteLength);
result.mDiscardPacket = true;
continue;
}
} }
} }
else else
{ {
// Allocate place for copying // Allocate place for copying
frame.mData = std::make_shared<ByteBuffer>(); f.mData = std::make_shared<ByteBuffer>();
frame.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1); f.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
// Add header for decoder // Add header for decoder
frame.mData->mutableData()[0] = (frame.mFrameType << 3) | (1 << 2); f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
// Read bits // Read bits
if (br.readBits(frame.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength) if (bit_reader.readBits(f.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
frame.mGoodQuality = false; f.mGoodQuality = false;
} }
} }
} }
// Padding bits are skipped // Padding bits are skipped
/*if (br.count() / 8 != br.position() / 8 &&
br.count() / 8 != br.position() / 8 + 1)
throw std::runtime_error("Failed to parse AMR frame");*/
return result; return result;
} }
/*static void predecodeAmrFrame(AmrFrame& frame, ByteBuffer& data)
{
// Data are already moved into
}*/
AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config) AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
:mConfig(config) :mConfig(config)
{ {
@ -278,12 +239,9 @@ int AmrNbCodec::CodecFactory::payloadType()
return mConfig.mPayloadType; return mConfig.mPayloadType;
} }
#ifdef USE_RESIP_INTEGRATION
void AmrNbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) void AmrNbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {}
}
int AmrNbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) int AmrNbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
@ -295,7 +253,6 @@ void AmrNbCodec::CodecFactory::create(CodecMap& codecs)
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig)); codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig));
} }
#endif
PCodec AmrNbCodec::CodecFactory::create() PCodec AmrNbCodec::CodecFactory::create()
{ {
return PCodec(new AmrNbCodec(mConfig)); return PCodec(new AmrNbCodec(mConfig));
@ -378,6 +335,9 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
#define AMR_BITRATE_DTX 15 #define AMR_BITRATE_DTX 15
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
if (mConfig.mOctetAligned)
return 0;
if (mConfig.mIuUP) if (mConfig.mIuUP)
{ {
// Try to parse IuUP frame // Try to parse IuUP frame
@ -485,7 +445,7 @@ int AmrNbCodec::plc(int lostFrames, void* output, int outputCapacity)
for (int i=0; i < lostFrames; i++) for (int i=0; i < lostFrames; i++)
{ {
unsigned char buffer[32]; uint8_t buffer[32];
buffer[0] = (AMR_BITRATE_DTX << 3)|4; buffer[0] = (AMR_BITRATE_DTX << 3)|4;
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
dataOut += L_FRAME; dataOut += L_FRAME;
@ -519,12 +479,8 @@ int AmrWbCodec::CodecFactory::payloadType()
return mConfig.mPayloadType; return mConfig.mPayloadType;
} }
#ifdef USE_RESIP_INTEGRATION
void AmrWbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) void AmrWbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {}
}
int AmrWbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) int AmrWbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
@ -536,19 +492,17 @@ void AmrWbCodec::CodecFactory::create(CodecMap& codecs)
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig)); codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig));
} }
#endif
PCodec AmrWbCodec::CodecFactory::create() PCodec AmrWbCodec::CodecFactory::create()
{ {
return PCodec(new AmrWbCodec(mConfig)); return PCodec(new AmrWbCodec(mConfig));
} }
AmrWbStatistics MT::GAmrWbStatistics;
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config) AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config), :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config),
mSwitchCounter(0), mPreviousPacketLength(0) mSwitchCounter(0), mPreviousPacketLength(0)
{ {
//mEncoderCtx = E_IF_init();
mDecoderCtx = D_IF_init(); mDecoderCtx = D_IF_init();
mCurrentDecoderTimestamp = 0; mCurrentDecoderTimestamp = 0;
} }
@ -595,36 +549,16 @@ int AmrWbCodec::samplerate()
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
if (inputBytes % pcmLength()) throw Exception(ERR_NOT_IMPLEMENTED);
return 0;
// Declare the data input pointer
const short *dataIn = (const short *)input;
// Declare the data output pointer
unsigned char *dataOut = (unsigned char *)output;
// Find how much RTP frames will be generated
unsigned int frames = inputBytes / pcmLength();
// Generate frames
for (unsigned int i = 0; i < frames; i++)
{
// dataOut += Encoder_Interface_Encode(mEncoderCtx, Mode::MRDTX, dataIn, dataOut, 1);
// dataIn += pcmLength() / 2;
}
return frames * rtpLength();
} }
#define L_FRAME 160 #define L_FRAME 160
#define AMR_BITRATE_DTX 15 #define AMR_BITRATE_DTX 15
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
{ int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output)
if (mConfig.mIuUP)
{ {
IuUP::Frame frame; IuUP::Frame frame;
if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame)) if (!IuUP::parse2(input.data(), input.size(), frame))
return 0; return 0;
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk) if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
@ -633,9 +567,6 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
return 0; return 0;
} }
// Build first byte to help decoder
//ICELogDebug(<< "Decoding AMR frame length = " << frame.mPayloadSize);
// Reserve space // Reserve space
ByteBuffer dataToDecode; ByteBuffer dataToDecode;
dataToDecode.resize(1 + frame.mPayloadSize); dataToDecode.resize(1 + frame.mPayloadSize);
@ -652,16 +583,17 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2); dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0); D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0);
return pcmLength(); return pcmLength();
} }
else
int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
AmrPayloadInfo info; AmrPayloadInfo info;
info.mCurrentTimestamp = mCurrentDecoderTimestamp; info.mCurrentTimestamp = mCurrentDecoderTimestamp;
info.mOctetAligned = mConfig.mOctetAligned; info.mOctetAligned = mConfig.mOctetAligned;
info.mPayload = (const uint8_t*)input; info.mPayload = input.data();
info.mPayloadLength = inputBytes; info.mPayloadLength = input.size();
info.mWideband = true; info.mWideband = true;
info.mInterleaving = false; info.mInterleaving = false;
@ -672,6 +604,7 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
} }
catch(...) catch(...)
{ {
GAmrWbStatistics.mNonParsed++;
ICELogDebug(<< "Failed to decode AMR payload"); ICELogDebug(<< "Failed to decode AMR payload");
return 0; return 0;
} }
@ -680,13 +613,17 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
// Check if packet is corrupted // Check if packet is corrupted
if (ap.mDiscardPacket) if (ap.mDiscardPacket)
{
GAmrWbStatistics.mDiscarded++;
return 0; return 0;
}
// Check for output buffer capacity // Check for output buffer capacity
if (outputCapacity < (int)ap.mFrames.size() * pcmLength()) if (output.size() < (int)ap.mFrames.size() * pcmLength())
return 0; return 0;
short* dataOut = (short*)output; short* dataOut = (short*)output.data();
size_t dataOutSizeInBytes = 0;
for (AmrFrame& frame: ap.mFrames) for (AmrFrame& frame: ap.mFrames)
{ {
memset(dataOut, 0, static_cast<size_t>(pcmLength())); memset(dataOut, 0, static_cast<size_t>(pcmLength()));
@ -695,11 +632,22 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
{ {
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0); D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
dataOut += pcmLength() / 2; dataOut += pcmLength() / 2;
dataOutSizeInBytes += pcmLength();
} }
} }
return pcmLength() * ap.mFrames.size(); return dataOutSizeInBytes;
} }
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
{
auto inputBuffer = std::span<const uint8_t>((uint8_t*)input, (size_t)inputBytes);
auto outputBuffer = std::span<uint8_t>((uint8_t*)output, (size_t)outputCapacity);
if (mConfig.mIuUP)
return decodeIuup(inputBuffer, outputBuffer);
else
return decodePlain(inputBuffer, outputBuffer);
return 0; return 0;
} }
@ -750,8 +698,6 @@ int GsmEfrCodec::GsmEfrFactory::payloadType()
return mPayloadType; return mPayloadType;
} }
#ifdef USE_RESIP_INTEGRATION
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
@ -767,8 +713,6 @@ void GsmEfrCodec::GsmEfrFactory::create(CodecMap& codecs)
codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP)); codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP));
} }
#endif
PCodec GsmEfrCodec::GsmEfrFactory::create() PCodec GsmEfrCodec::GsmEfrFactory::create()
{ {
return PCodec(new GsmEfrCodec(mIuUP)); return PCodec(new GsmEfrCodec(mIuUP));

View File

@ -1,8 +1,10 @@
#ifndef MT_AMRCODEC_H #ifndef MT_AMRCODEC_H
#define MT_AMRCODEC_H #define MT_AMRCODEC_H
#include "../config.h" #include "../engine_config.h"
#include <map> #include <map>
#include <span>
#include "MT_Codec.h" #include "MT_Codec.h"
#include "../helper/HL_Pointer.h" #include "../helper/HL_Pointer.h"
@ -16,9 +18,9 @@ namespace MT
{ {
struct AmrCodecConfig struct AmrCodecConfig
{ {
bool mIuUP; bool mIuUP = false;
bool mOctetAligned; bool mOctetAligned = false;
int mPayloadType; int mPayloadType = -1;
}; };
class AmrNbCodec : public Codec class AmrNbCodec : public Codec
@ -41,11 +43,10 @@ namespace MT
int samplerate() override; int samplerate() override;
int payloadType() override; int payloadType() override;
#ifdef USE_RESIP_INTEGRATION
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
void create(CodecMap& codecs) override; void create(CodecMap& codecs) override;
#endif
PCodec create() override; PCodec create() override;
protected: protected:
@ -66,6 +67,13 @@ namespace MT
int getSwitchCounter() const; int getSwitchCounter() const;
}; };
struct AmrWbStatistics
{
int mDiscarded = 0;
int mNonParsed = 0;
};
extern AmrWbStatistics GAmrWbStatistics;
class AmrWbCodec : public Codec class AmrWbCodec : public Codec
{ {
protected: protected:
@ -76,6 +84,9 @@ namespace MT
int mSwitchCounter; int mSwitchCounter;
int mPreviousPacketLength; int mPreviousPacketLength;
int decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output);
int decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output);
public: public:
class CodecFactory: public Factory class CodecFactory: public Factory
{ {
@ -86,11 +97,10 @@ namespace MT
int samplerate() override; int samplerate() override;
int payloadType() override; int payloadType() override;
#ifdef USE_RESIP_INTEGRATION
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
void create(CodecMap& codecs) override; void create(CodecMap& codecs) override;
#endif
PCodec create() override; PCodec create() override;
protected: protected:
@ -128,11 +138,10 @@ namespace MT
int samplerate() override; int samplerate() override;
int payloadType() override; int payloadType() override;
#ifdef USE_RESIP_INTEGRATION
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
void create(CodecMap& codecs) override; void create(CodecMap& codecs) override;
#endif
PCodec create() override; PCodec create() override;
protected: protected:

View File

@ -1,11 +1,11 @@
/* Copyright(C) 2007-2018 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define NOMINMAX #define NOMINMAX
#include "../config.h" #include "../engine_config.h"
#include "MT_AudioCodec.h" #include "MT_AudioCodec.h"
#include "MT_CodecList.h" #include "MT_CodecList.h"
#include "../helper/HL_Exception.h" #include "../helper/HL_Exception.h"
@ -58,18 +58,14 @@ int G729Codec::G729Factory::payloadType()
return 18; return 18;
} }
#if defined(USE_RESIP_INTEGRATION)
void G729Codec::G729Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) void G729Codec::G729Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {}
}
int G729Codec::G729Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) int G729Codec::G729Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
return 0; return 0;
} }
#endif
PCodec G729Codec::G729Factory::create() PCodec G729Codec::G729Factory::create()
{ {
return std::make_shared<G729Codec>(); return std::make_shared<G729Codec>();
@ -125,7 +121,7 @@ int G729Codec::channels()
return 1; return 1;
} }
static const int SamplesPerFrame = 80; // static const int SamplesPerFrame = 80;
int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity) int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
// Create encoder if it is not done yet // Create encoder if it is not done yet
@ -226,12 +222,12 @@ int G729Codec::plc(int lostFrames, void* output, int outputCapacity)
return 0; return 0;
} }
#if defined(USE_OPUS_CODEC)
// -------------- Opus ------------------- // -------------- Opus -------------------
#define OPUS_CODEC_NAME "OPUS" #define OPUS_CODEC_NAME "OPUS"
#define OPUS_CODEC_RATE 16000 #define OPUS_CODEC_RATE 16000
#define OPUS_TARGET_BITRATE 64000 #define OPUS_TARGET_BITRATE 64000
#define OPUS_PACKET_LOSS 30 #define OPUS_PACKET_LOSS 10
#define OPUS_CODEC_COMPLEXITY 2 #define OPUS_CODEC_COMPLEXITY 2
OpusCodec::Params::Params() OpusCodec::Params::Params()
@ -241,7 +237,6 @@ OpusCodec::Params::Params()
mTargetBitrate = OPUS_TARGET_BITRATE; mTargetBitrate = OPUS_TARGET_BITRATE;
} }
#if defined(USE_RESIP_INTEGRATION)
resip::Data OpusCodec::Params::toString() const resip::Data OpusCodec::Params::toString() const
{ {
std::ostringstream oss; std::ostringstream oss;
@ -321,10 +316,10 @@ void OpusCodec::Params::parse(const resip::Data &params)
mStereo = paramIter->mValue == "1"; mStereo = paramIter->mValue == "1";
else else
if (paramIter->mName == "ptime") if (paramIter->mName == "ptime")
mPtime = StringHelper::toInt(paramIter->mValue.c_str(), 20); mPtime = strx::toInt(paramIter->mValue.c_str(), 20);
} }
} }
#endif
OpusCodec::OpusFactory::OpusFactory(int samplerate, int channels, int ptype) OpusCodec::OpusFactory::OpusFactory(int samplerate, int channels, int ptype)
{ {
mSamplerate = samplerate; mSamplerate = samplerate;
@ -352,7 +347,6 @@ int OpusCodec::OpusFactory::payloadType()
return mPType; return mPType;
} }
#if defined(USE_RESIP_INTEGRATION)
void OpusCodec::OpusFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) void OpusCodec::OpusFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
// Put opus codec record // Put opus codec record
@ -394,7 +388,6 @@ int OpusCodec::OpusFactory::processSdp(const resip::SdpContents::Session::Medium
} }
return -1; return -1;
} }
#endif
PCodec OpusCodec::OpusFactory::create() PCodec OpusCodec::OpusFactory::create()
{ {
@ -402,19 +395,18 @@ PCodec OpusCodec::OpusFactory::create()
result->applyParams(mParams); result->applyParams(mParams);
PCodec c(result); PCodec c(result);
mCodecList.push_back(c); mCodecList.push_back(c);
return c; return c;
} }
OpusCodec::OpusCodec(int samplerate, int channels, int ptime) OpusCodec::OpusCodec(int samplerate, int channels, int ptime)
:mEncoderCtx(NULL), mDecoderCtx(NULL), mChannels(channels), mPTime(ptime), mSamplerate(samplerate) :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(channels), mPTime(ptime), mSamplerate(samplerate), mDecoderChannels(0)
{ {
int status; int status;
mEncoderCtx = opus_encoder_create(48000, mChannels, OPUS_APPLICATION_VOIP, &status); mEncoderCtx = opus_encoder_create(mSamplerate, mChannels, OPUS_APPLICATION_VOIP, &status);
if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_COMPLEXITY(OPUS_CODEC_COMPLEXITY))) if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_COMPLEXITY(OPUS_CODEC_COMPLEXITY)))
ICELogError(<< "Failed to set Opus encoder complexity"); ICELogError(<< "Failed to set Opus encoder complexity");
//if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_FORCE_CHANNELS(AUDIO_CHANNELS))) // Decoder creation is postponed until first packet arriving (because it may use different channel number
// ICELogCritical(<<"Failed to set channel number in Opus encoder");
mDecoderCtx = opus_decoder_create(48000, mChannels, &status);
} }
void OpusCodec::applyParams(const Params &params) void OpusCodec::applyParams(const Params &params)
@ -432,7 +424,7 @@ void OpusCodec::applyParams(const Params &params)
if (OPUS_OK != (error = opus_encoder_ctl(mEncoderCtx, OPUS_SET_PACKET_LOSS_PERC(params.mExpectedPacketLoss)))) if (OPUS_OK != (error = opus_encoder_ctl(mEncoderCtx, OPUS_SET_PACKET_LOSS_PERC(params.mExpectedPacketLoss))))
ICELogError(<< "Failed to (un)set expected packet loss. Error " << opus_strerror(error)); ICELogError(<< "Failed to (un)set expected packet loss. Error " << opus_strerror(error));
mDecodeResampler.start(channels(), 48000, mSamplerate); // mDecodeResampler.start(channels(), 48000, mSamplerate);
} }
OpusCodec::~OpusCodec() OpusCodec::~OpusCodec()
@ -440,13 +432,13 @@ OpusCodec::~OpusCodec()
if (mDecoderCtx) if (mDecoderCtx)
{ {
opus_decoder_destroy(mDecoderCtx); opus_decoder_destroy(mDecoderCtx);
mDecoderCtx = NULL; mDecoderCtx = nullptr;
} }
if (mEncoderCtx) if (mEncoderCtx)
{ {
opus_encoder_destroy(mEncoderCtx); opus_encoder_destroy(mEncoderCtx);
mEncoderCtx = NULL; mEncoderCtx = nullptr;
} }
} }
@ -492,47 +484,123 @@ int OpusCodec::encode(const void* input, int inputBytes, void* output, int outpu
int OpusCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) int OpusCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
int nrOfChannelsInPacket = opus_packet_get_nb_channels((const unsigned char*)input); int result = 0;
assert(nrOfChannelsInPacket == 1);
int nrOfSamplesInPacket = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char*)input, inputBytes); // Examine the number of channels available in incoming packet
if (nrOfSamplesInPacket > 0) int nr_of_channels = opus_packet_get_nb_channels((const unsigned char *) input);
// Recreate decoder if needed
if (mDecoderChannels != nr_of_channels)
{ {
// Send number of bytes for input and number of samples for output if (mDecoderCtx)
int capacity = nrOfSamplesInPacket * sizeof(opus_int16) * channels(); {
opus_int16* buffer = (opus_int16*)alloca(capacity); opus_decoder_destroy(mDecoderCtx);
int decoded = opus_decode(mDecoderCtx, (const unsigned char*)input, inputBytes, (opus_int16*)buffer, capacity / (sizeof(short) * channels()), 1); mDecoderCtx = nullptr;
}
mDecoderChannels = nr_of_channels;
}
if (!mDecoderCtx)
{
int status = 0;
mDecoderCtx = opus_decoder_create(mSamplerate, mDecoderChannels, &status);
if (status)
return 0;
}
int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char *) input,
inputBytes);
if (nr_of_frames <= 0)
return 0;
// We support stereo and mono here.
int buffer_capacity = nr_of_frames * sizeof(opus_int16) * nr_of_channels;
opus_int16 *buffer_decode = (opus_int16 *)alloca(buffer_capacity);
int decoded = opus_decode(mDecoderCtx,
reinterpret_cast<const unsigned char *>(input), inputBytes,
buffer_decode, nr_of_frames, 0);
if (decoded < 0) if (decoded < 0)
return 0;
else
{ {
// Resample 48000 to requested samplerate ICELogCritical(<< "opus_decode() returned " << decoded);
size_t processed = 0;
return mDecodeResampler.processBuffer(buffer, decoded * sizeof(short) * channels(), processed, output, outputCapacity);
}
}
else
return 0; return 0;
} }
int OpusCodec::plc(int lostFrames, void* output, int outputCapacity) opus_int16 *buffer_stereo = nullptr;
int buffer_stereo_capacity = buffer_capacity * 2;
switch (nr_of_channels) {
case 1:
// Convert to stereo before
buffer_stereo = (opus_int16 *) alloca(buffer_stereo_capacity);
for (int i = 0; i < nr_of_frames; i++) {
buffer_stereo[i * 2 + 1] = buffer_decode[i];
buffer_stereo[i * 2] = buffer_decode[i];
}
assert(buffer_stereo_capacity <= outputCapacity);
memcpy(output, buffer_stereo, buffer_stereo_capacity);
result = buffer_stereo_capacity;
break;
case 2:
assert(buffer_capacity <= outputCapacity);
memcpy(output, buffer_decode, buffer_capacity);
result = buffer_capacity;
break;
default:
assert(0);
}
return result;
}
int OpusCodec::plc(int lostPackets, void* output, int outputCapacity)
{ {
int nrOfSamplesInFrame = pcmLength() / (sizeof(short) * channels()); // Find how much frames do we need to produce and prefill it with silence
int frames_per_packet = (int)pcmLength() / (sizeof(opus_int16) * channels());
memset(output, 0, outputCapacity); memset(output, 0, outputCapacity);
for (int i=0; i<lostFrames; i++)
{
/*int decodedSamples = */opus_decode(mDecoderCtx, NULL, 0, (opus_int16*)output + nrOfSamplesInFrame * i, nrOfSamplesInFrame, 0);
// Use this pointer as output
opus_int16* data_output = reinterpret_cast<opus_int16*>(output);
int nr_of_decoded_frames = 0;
// Buffer for single lost frame
opus_int16* buffer_plc = (opus_int16*)alloca(frames_per_packet * mDecoderChannels * sizeof(opus_int16));
for (int i=0; i<lostPackets; i++)
{
nr_of_decoded_frames = opus_decode(mDecoderCtx, nullptr, 0, buffer_plc, frames_per_packet, 0);
assert(nr_of_decoded_frames == frames_per_packet);
switch (mDecoderChannels)
{
case 1:
// Convert mono to stereo
for (int i=0; i < nr_of_decoded_frames; i++)
{
data_output[i * 2] = buffer_plc[i];
data_output[i * 2 + 1] = buffer_plc[i+1];
} }
return lostFrames * pcmLength(); data_output += frames_per_packet * mChannels;
break;
case 2:
// Just copy data
memcpy(data_output, buffer_plc, frames_per_packet * sizeof(opus_int16) * mDecoderChannels);
data_output += frames_per_packet * mChannels;
break;
} }
}
return ((char*)data_output - (char*)output) * sizeof(opus_int16);
}
#endif
// -------------- ILBC ------------------- // -------------- ILBC -------------------
#define ILBC_CODEC_NAME "ILBC" #define ILBC_CODEC_NAME "ILBC"
IlbcCodec::IlbcCodec(int packetTime) IlbcCodec::IlbcCodec(int packetTime)
:mPacketTime(packetTime) :mPacketTime(packetTime), mEncoderCtx(nullptr), mDecoderCtx(nullptr)
{ {
WebRtcIlbcfix_EncoderCreate(&mEncoderCtx); WebRtcIlbcfix_EncoderCreate(&mEncoderCtx);
WebRtcIlbcfix_DecoderCreate(&mDecoderCtx); WebRtcIlbcfix_DecoderCreate(&mDecoderCtx);
@ -646,7 +714,6 @@ PCodec IlbcCodec::IlbcFactory::create()
return PCodec(new IlbcCodec(mPtime)); return PCodec(new IlbcCodec(mPtime));
} }
#if defined(USE_RESIP_INTEGRATION)
void IlbcCodec::IlbcFactory::create(CodecMap& codecs) void IlbcCodec::IlbcFactory::create(CodecMap& codecs)
{ {
codecs[mPType20ms] = PCodec(create()); codecs[mPType20ms] = PCodec(create());
@ -708,8 +775,6 @@ int IlbcCodec::IlbcFactory::processSdp(const resip::SdpContents::Session::Medium
return pt; return pt;
} }
#endif
// --- IsacCodec(s) --- // --- IsacCodec(s) ---
#define ISAC_CODEC_NAME "ISAC" #define ISAC_CODEC_NAME "ISAC"
@ -1087,8 +1152,8 @@ int GsmCodec::plc(int lostFrames, void* output, int outputCapacity)
G722Codec::G722Codec() G722Codec::G722Codec()
{ {
mEncoder = g722_encode_init(NULL, 64000, 0); mEncoder = g722_encode_init(nullptr, 64000, 0);
mDecoder = g722_decode_init(NULL, 64000, 0); mDecoder = g722_decode_init(nullptr, 64000, 0);
} }

View File

@ -6,7 +6,7 @@
#ifndef __AUDIO_CODEC_H #ifndef __AUDIO_CODEC_H
#define __AUDIO_CODEC_H #define __AUDIO_CODEC_H
#include "../config.h" #include "../engine_config.h"
#include <map> #include <map>
#include "MT_Codec.h" #include "MT_Codec.h"
#include "../audio/Audio_Resampler.h" #include "../audio/Audio_Resampler.h"
@ -24,7 +24,9 @@ extern "C"
#include "libg729/g729_typedef.h" #include "libg729/g729_typedef.h"
#include "libg729/g729_ld8a.h" #include "libg729/g729_ld8a.h"
#if defined(USE_OPUS_CODEC)
# include "opus.h" # include "opus.h"
#endif
namespace MT namespace MT
{ {
@ -44,10 +46,9 @@ public:
int samplerate() override; int samplerate() override;
int payloadType() override; int payloadType() override;
#if defined(USE_RESIP_INTEGRATION)
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
#endif
PCodec create() override; PCodec create() override;
}; };
G729Codec(); G729Codec();
@ -64,13 +65,15 @@ public:
int plc(int lostFrames, void* output, int outputCapacity) override; int plc(int lostFrames, void* output, int outputCapacity) override;
}; };
#if defined(USE_OPUS_CODEC)
class OpusCodec: public Codec class OpusCodec: public Codec
{ {
protected: protected:
OpusEncoder *mEncoderCtx; OpusEncoder *mEncoderCtx;
OpusDecoder *mDecoderCtx; OpusDecoder *mDecoderCtx;
int mPTime, mSamplerate, mChannels; int mPTime, mSamplerate, mChannels;
Audio::SpeexResampler mDecodeResampler; // Audio::SpeexResampler mDecodeResampler;
int mDecoderChannels;
public: public:
struct Params struct Params
{ {
@ -78,10 +81,8 @@ public:
int mPtime, mTargetBitrate, mExpectedPacketLoss; int mPtime, mTargetBitrate, mExpectedPacketLoss;
Params(); Params();
#if defined(USE_RESIP_INTEGRATION)
resip::Data toString() const; resip::Data toString() const;
void parse(const resip::Data& params); void parse(const resip::Data& params);
#endif
}; };
class OpusFactory: public Factory class OpusFactory: public Factory
@ -99,10 +100,8 @@ public:
int channels() override; int channels() override;
int samplerate() override; int samplerate() override;
int payloadType() override; int payloadType() override;
#if defined(USE_RESIP_INTEGRATION)
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
#endif
PCodec create() override; PCodec create() override;
}; };
@ -120,6 +119,7 @@ public:
int decode(const void* input, int inputBytes, void* output, int outputCapacity); int decode(const void* input, int inputBytes, void* output, int outputCapacity);
int plc(int lostFrames, void* output, int outputCapacity); int plc(int lostFrames, void* output, int outputCapacity);
}; };
#endif
class IlbcCodec: public Codec class IlbcCodec: public Codec
{ {
@ -141,11 +141,9 @@ public:
const char* name(); const char* name();
int samplerate(); int samplerate();
int payloadType(); int payloadType();
#if defined(USE_RESIP_INTEGRATION)
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction); void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction); int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
void create(CodecMap& codecs); void create(CodecMap& codecs);
#endif
PCodec create(); PCodec create();
}; };

View File

@ -1,19 +1,24 @@
/* Copyright(C) 2007-2018 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2021 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(TARGET_WIN) && !defined(NOMINMAX)
# define NOMINMAX # define NOMINMAX
#endif
#include "../config.h" #include "../engine_config.h"
#include "MT_AudioReceiver.h" #include "MT_AudioReceiver.h"
#include "MT_AudioCodec.h" #include "MT_AudioCodec.h"
#include "MT_CngHelper.h" #include "MT_CngHelper.h"
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
#include "../helper/HL_Time.h"
#include "../audio/Audio_Interface.h" #include "../audio/Audio_Interface.h"
#include "../audio/Audio_Resampler.h" #include "../audio/Audio_Resampler.h"
#include <cmath>
#include <iostream>
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) #if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC)
# include "MT_AmrCodec.h" # include "MT_AmrCodec.h"
#endif #endif
@ -26,7 +31,7 @@
using namespace MT; using namespace MT;
// ----------------- RtpBuffer::Packet -------------- // ----------------- RtpBuffer::Packet --------------
RtpBuffer::Packet::Packet(std::shared_ptr<RTPPacket> packet, int timelength, int rate) RtpBuffer::Packet::Packet(const std::shared_ptr<RTPPacket>& packet, int timelength, int rate)
:mRtp(packet), mTimelength(timelength), mRate(rate) :mRtp(packet), mTimelength(timelength), mRate(rate)
{ {
} }
@ -46,12 +51,22 @@ int RtpBuffer::Packet::rate() const
return mRate; return mRate;
} }
const std::vector<short>& RtpBuffer::Packet::pcm() const
{
return mPcm;
}
std::vector<short>& RtpBuffer::Packet::pcm()
{
return mPcm;
}
// ------------ RtpBuffer ---------------- // ------------ RtpBuffer ----------------
RtpBuffer::RtpBuffer(Statistics& stat) RtpBuffer::RtpBuffer(Statistics& stat)
:mStat(stat), mSsrc(0), mHigh(RTP_BUFFER_HIGH), mLow(RTP_BUFFER_LOW), mPrebuffer(RTP_BUFFER_PREBUFFER), :mStat(stat)
mFirstPacketWillGo(true),
mReturnedCounter(0), mAddCounter(0), mFetchedPacket(std::shared_ptr<RTPPacket>(), 0, 0)
{ {
if (mStat.mPacketLoss)
std::cout << "Warning: packet loss is not zero" << std::endl;
} }
RtpBuffer::~RtpBuffer() RtpBuffer::~RtpBuffer()
@ -95,19 +110,27 @@ int RtpBuffer::getCount() const
return static_cast<int>(mPacketList.size()); return static_cast<int>(mPacketList.size());
} }
bool SequenceSort(const RtpBuffer::Packet& p1, const RtpBuffer::Packet& p2) bool SequenceSort(const std::shared_ptr<RtpBuffer::Packet>& p1, const std::shared_ptr<RtpBuffer::Packet>& p2)
{ {
return p1.rtp()->GetExtendedSequenceNumber() < p2.rtp()->GetExtendedSequenceNumber(); return p1->rtp()->GetExtendedSequenceNumber() < p2->rtp()->GetExtendedSequenceNumber();
} }
bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate) std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate)
{ {
if (!packet) if (!packet)
return false; return std::shared_ptr<Packet>();
Lock l(mGuard); Lock l(mGuard);
// Update statistics // Update statistics
if (mLastAddTime == 0.0)
mLastAddTime = now_ms();
else
{
float t = now_ms();
mStat.mPacketInterval.process(t - mLastAddTime);
mLastAddTime = t;
}
mStat.mSsrc = static_cast<uint16_t>(packet->GetSSRC()); mStat.mSsrc = static_cast<uint16_t>(packet->GetSSRC());
// Update jitter // Update jitter
@ -120,16 +143,15 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
// New sequence number // New sequence number
unsigned newSeqno = packet->GetExtendedSequenceNumber(); unsigned newSeqno = packet->GetExtendedSequenceNumber();
for (PacketList::iterator iter = mPacketList.begin(); iter != mPacketList.end(); iter++) for (std::shared_ptr<Packet>& p: mPacketList)
{ {
Packet& p = *iter; unsigned seqno = p->rtp()->GetExtendedSequenceNumber();
unsigned seqno = p.rtp()->GetExtendedSequenceNumber();
if (seqno == newSeqno) if (seqno == newSeqno)
{ {
mStat.mDuplicatedRtp++; mStat.mDuplicatedRtp++;
ICELogMedia(<< "Discovered duplicated packet, skipping"); ICELogMedia(<< "Discovered duplicated packet, skipping");
return false; return std::shared_ptr<Packet>();
} }
if (seqno > maxno) if (seqno > maxno)
@ -138,12 +160,16 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
minno = seqno; minno = seqno;
} }
// Get amount of available audio (in milliseconds) in jitter buffer
int available = findTimelength(); int available = findTimelength();
if (newSeqno > minno || (available < mHigh)) if (newSeqno > minno || (available < mHigh))
{ {
Packet p(packet, timelength, rate); // Insert into queue
auto p = std::make_shared<Packet>(packet, timelength, rate);
mPacketList.push_back(p); mPacketList.push_back(p);
// Sort again
std::sort(mPacketList.begin(), mPacketList.end(), SequenceSort); std::sort(mPacketList.begin(), mPacketList.end(), SequenceSort);
// Limit by max timelength // Limit by max timelength
@ -151,21 +177,18 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
if (available > mHigh) if (available > mHigh)
ICELogMedia(<< "Available " << available << "ms with limit " << mHigh << "ms"); ICELogMedia(<< "Available " << available << "ms with limit " << mHigh << "ms");
/*while (available > mHigh && mPacketList.size())
{ return p;
ICELogDebug( << "Dropping RTP packet from jitter buffer");
available -= mPacketList.front().timelength();
mPacketList.erase(mPacketList.begin());
}*/
} }
else else
{ {
ICELogMedia(<< "Too old packet, skipping"); ICELogMedia(<< "Too old packet, skipping");
mStat.mOldRtp++; mStat.mOldRtp++;
return false;
return std::shared_ptr<Packet>();
} }
return true; return std::shared_ptr<Packet>();
} }
RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
@ -178,78 +201,65 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
// See if there is enough information in buffer // See if there is enough information in buffer
int total = findTimelength(); int total = findTimelength();
while (total > mHigh && mPacketList.size()) while (total > mHigh && mPacketList.size() && 0 != mHigh)
{ {
ICELogMedia( << "Dropping RTP packets from jitter buffer"); ICELogMedia( << "Dropping RTP packets from jitter buffer");
total -= mPacketList.front().timelength(); total -= mPacketList.front()->timelength();
// Save it as last packet however - to not confuse loss packet counter // Save it as last packet however - to not confuse loss packet counter
mFetchedPacket = mPacketList.front(); mFetchedPacket = mPacketList.front();
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Erase from packet list // Erase from packet list
mPacketList.erase(mPacketList.begin()); mPacketList.erase(mPacketList.begin());
// Increase number in statistics
mStat.mPacketDropped++;
} }
if (total < mLow) if (total < mLow)
{
// Still not prebuffered
result = FetchResult::NoPacket; result = FetchResult::NoPacket;
}
else else
{ {
if (mFetchedPacket.rtp()) // Did we fetch any packet before ?
bool is_fetched_packet = mFetchedPacket.get() != nullptr;
if (is_fetched_packet)
is_fetched_packet &= mFetchedPacket->rtp().get() != nullptr;
if (mLastSeqno.has_value())
{ {
if (mPacketList.empty()) if (mPacketList.empty())
{ {
result = FetchResult::NoPacket; result = FetchResult::NoPacket;
mStat.mPacketLoss++; // Don't increase counter of lost packets here; maybe it is DTX
} }
else else
{ {
// Current sequence number ? // Current sequence number ?
unsigned seqno = mPacketList.front().rtp()->GetExtendedSequenceNumber(); uint32_t seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Gap between new packet and previous on // Gap between new packet and previous on
int gap = seqno - mFetchedPacket.rtp()->GetSequenceNumber() - 1; int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
gap = std::min(gap, 127); gap = std::min(gap, 127);
if (gap > 0 && mPacketList.empty())
{
result = FetchResult::Gap;
mStat.mPacketLoss += gap;
mStat.mLoss[gap]++;
}
else
{
if (gap > 0) if (gap > 0)
{ {
mStat.mPacketLoss += gap; // std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl;
mStat.mLoss[gap]++; mStat.mPacketLoss++;
//mStat.mLoss[gap]++;
mLastSeqno = *mLastSeqno + 1;
result = FetchResult::Gap;
} }
result = FetchResult::RegularPacket;
Packet& p = mPacketList.front();
rl.push_back(p.rtp());
// Maybe it is time to replay packet right now ? For case of AMR SID packets
/*if (mFetchedPacket.rtp() && gap == 0 && mFetchedPacket.timelength() >= 10 && p.timelength() >= 10)
{
int timestampDelta;
// Timestamp difference
if (p.rtp()->GetTimestamp() > mFetchedPacket.rtp()->GetTimestamp())
timestampDelta = TimeHelper::getDelta(p.rtp()->GetTimestamp(), mFetchedPacket.rtp()->GetTimestamp());
else else
timestampDelta = TimeHelper::getDelta(mFetchedPacket.rtp()->GetTimestamp(), p.rtp()->GetTimestamp());
// Timestamp units per packet
int nrOfPackets = timestampDelta / (p.timelength() * (p.rate() / 1000));
// Add more copies of SID (most probably) packets
for (int i = 0; i < nrOfPackets - 1; i++)
{ {
//assert(false); result = FetchResult::RegularPacket;
rl.push_back(p.rtp()); rl.push_back(mPacketList.front());
}
}*/
// Save last returned normal packet // Save last returned normal packet
mFetchedPacket = mPacketList.front(); mFetchedPacket = mPacketList.front();
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Remove returned packet from the list // Remove returned packet from the list
mPacketList.erase(mPacketList.begin()); mPacketList.erase(mPacketList.begin());
@ -259,16 +269,17 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
else else
{ {
// See if prebuffer limit is reached // See if prebuffer limit is reached
if (findTimelength() >= mPrebuffer) if (findTimelength() >= mPrebuffer && !mPacketList.empty())
{ {
// Normal packet will be returned // Normal packet will be returned
result = FetchResult::RegularPacket; result = FetchResult::RegularPacket;
// Put it to output list // Put it to output list
rl.push_back(mPacketList.front().rtp()); rl.push_back(mPacketList.front());
// Remember returned packet // Remember returned packet
mFetchedPacket = mPacketList.front(); mFetchedPacket = mPacketList.front();
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Remove returned packet from buffer list // Remove returned packet from buffer list
mPacketList.erase(mPacketList.begin()); mPacketList.erase(mPacketList.begin());
@ -291,7 +302,7 @@ int RtpBuffer::findTimelength()
{ {
int available = 0; int available = 0;
for (unsigned i = 0; i < mPacketList.size(); i++) for (unsigned i = 0; i < mPacketList.size(); i++)
available += mPacketList[i].timelength(); available += mPacketList[i]->timelength();
return available; return available;
} }
@ -317,7 +328,7 @@ Receiver::~Receiver()
//-------------- AudioReceiver ---------------- //-------------- AudioReceiver ----------------
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat) AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
:Receiver(stat), mBuffer(stat), mFrameCount(0), mFailedCount(0), mCodecSettings(settings), :Receiver(stat), mBuffer(stat), mCodecSettings(settings),
mCodecList(settings) mCodecList(settings)
{ {
// Init resamplers // Init resamplers
@ -327,6 +338,7 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE); mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
// Init codecs // Init codecs
mCodecList.setSettings(settings);
mCodecList.fillCodecMap(mCodecMap); mCodecList.fillCodecMap(mCodecMap);
#if defined(DUMP_DECODED) #if defined(DUMP_DECODED)
@ -337,12 +349,6 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
AudioReceiver::~AudioReceiver() AudioReceiver::~AudioReceiver()
{ {
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
if (mPVQA && mPvqaBuffer)
{
mStat.mPvqaMos = calculatePvqaMos(AUDIO_SAMPLERATE, mStat.mPvqaReport);
}
#endif
mResampler8.stop(); mResampler8.stop();
mResampler16.stop(); mResampler16.stop();
mResampler32.stop(); mResampler32.stop();
@ -350,63 +356,145 @@ AudioReceiver::~AudioReceiver()
mDecodedDump.reset(); mDecodedDump.reset();
} }
// Update codec settings
bool AudioReceiver::add(std::shared_ptr<jrtplib::RTPPacket> p, Codec** codec) void AudioReceiver::setCodecSettings(const CodecList::Settings& codecSettings)
{ {
if (mCodecSettings == codecSettings)
return;
mCodecSettings = codecSettings;
mCodecList.setSettings(mCodecSettings); // This builds factory list with proper payload types according to payload types in settings
// Rebuild codec map from factory list
mCodecList.fillCodecMap(mCodecMap);
}
CodecList::Settings& AudioReceiver::getCodecSettings()
{
return mCodecSettings;
}
size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t output_capacity)
{
// How much data was produced
size_t result = 0;
// Handle here regular RTP packets
// Check if payload length is ok
int tail = codec.rtpLength() ? p.GetPayloadLength() % codec.rtpLength() : 0;
if (!tail)
{
// Find number of frames
int frame_count = codec.rtpLength() ? p.GetPayloadLength() / codec.rtpLength() : 1;
int frame_length = codec.rtpLength() ? codec.rtpLength() : (int)p.GetPayloadLength();
// Save last packet time length
// mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
// Decode
for (int i=0; i < frame_count; i++)
{
auto decoded_length = codec.decode(p.GetPayloadData() + i * codec.rtpLength(),
frame_length,
output_buffer,
output_capacity);
result += decoded_length;
}
}
else
ICELogMedia(<< "RTP packet with tail.");
return result;
}
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** detectedCodec)
{
// Estimate time length
int time_length = 0,
samplerate = 8000,
payloadLength = p->GetPayloadLength(),
ptype = p->GetPayloadType();
// ICELogInfo(<< "Adding packet No " << p->GetSequenceNumber());
// Increase codec counter // Increase codec counter
mStat.mCodecCount[p->GetPayloadType()]++; mStat.mCodecCount[ptype]++;
// Check if codec can be handled // Check if codec can be handled
CodecMap::iterator codecIter = mCodecMap.find(p->GetPayloadType()); Codec* codec = nullptr;
CodecMap::iterator codecIter = mCodecMap.find(ptype);
if (codecIter == mCodecMap.end()) if (codecIter == mCodecMap.end())
{ {
ICELogMedia(<< "Cannot find codec in available codecs"); time_length = 10;
return false; // Reject packet with unknown payload type
} }
else
// Check if codec is created actually {
// Check if codec is creating lazily
if (!codecIter->second) if (!codecIter->second)
{ {
// Look for ptype codecIter->second = mCodecList.createCodecByPayloadType(ptype);
for (int codecIndex = 0; codecIndex < mCodecList.count(); codecIndex++) }
if (mCodecList.codecAt(codecIndex).payloadType() == p->GetPayloadType()) codec = codecIter->second.get();
codecIter->second = mCodecList.codecAt(codecIndex).create();
// Return pointer to codec if needed.get()
if (detectedCodec)
*detectedCodec = codec;
if (mStat.mCodecName.empty() && codec)
mStat.mCodecName = codec->name();
if (!codec)
time_length = 10;
else
if (!codec->rtpLength())
time_length = codec->frameTime();
else
time_length = lround(double(payloadLength) / codec->rtpLength() * codec->frameTime());
if (codec)
samplerate = codec->samplerate();
} }
// Return pointer to codec if needed
if (codec)
*codec = codecIter->second.get();
if (mStat.mCodecName.empty())
mStat.mCodecName = codecIter->second.get()->name();
// Estimate time length
int timelen = 0, payloadLength = p->GetPayloadLength(), ptype = p->GetPayloadType();
if (!codecIter->second->rtpLength())
timelen = codecIter->second->frameTime();
else
timelen = int(double(payloadLength) / codecIter->second->rtpLength() * codecIter->second->frameTime() + 0.5);
// Process jitter // Process jitter
mJitterStats.process(p.get(), codecIter->second->samplerate()); mJitterStats.process(p.get(), samplerate);
mStat.mJitter = static_cast<float>(mJitterStats.get()); mStat.mJitter = static_cast<float>(mJitterStats.get());
// Check if packet is CNG // Check if packet is CNG
if (payloadLength >= 1 && payloadLength <= 6 && (ptype == 0 || ptype == 8)) if (payloadLength >= 1 && payloadLength <= 6 && (ptype == 0 || ptype == 8))
timelen = mLastPacketTimeLength ? mLastPacketTimeLength : 20; time_length = mLastPacketTimeLength ? mLastPacketTimeLength : 20;
else else
// Check if packet is too short from time length side // Check if packet is too short from time length side
if (timelen < 2) if (time_length < 2)
{ {
// It will cause statistics to report about bad RTP packet // It will cause statistics to report about bad RTP packet
// I have to replay last packet payload here to avoid report about lost packet // I have to replay last packet payload here to avoid report about lost packet
mBuffer.add(p, timelen, codecIter->second->samplerate()); mBuffer.add(p, time_length, samplerate);
return false; return false;
} }
// Queue packet to buffer // Queue packet to buffer
return mBuffer.add(p, timelen, codecIter->second->samplerate()); auto packet = mBuffer.add(p, time_length, samplerate).get();
if (packet)
{
// Check if early decoding configured
if (mEarlyDecode && codec)
{
// Move data to packet buffer
size_t available = decode_packet(*codec, *p, mDecodedFrame, sizeof mDecodedFrame);
if (available > 0)
{
packet->pcm().resize(available / 2);
memcpy(packet->pcm().data(), mDecodedFrame, available / 2);
}
}
return true;
}
else
return false;
} }
void AudioReceiver::processDecoded(Audio::DataWindow& output, int options) void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
@ -420,18 +508,14 @@ void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
makeMonoAndResample(resample ? mCodec->samplerate() : 0, makeMonoAndResample(resample ? mCodec->samplerate() : 0,
mCodec->channels()); mCodec->channels());
// Update PVQA with stats
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
updatePvqa(mResampledFrame, mResampledLength);
#endif
// Send to output // Send to output
output.add(mResampledFrame, mResampledLength); output.add(mResampledFrame, mResampledLength);
} }
bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate) AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
{ {
bool result = false; DecodeResult result = DecodeResult_Skip;
bool had_decode = false;
// Get next packet from buffer // Get next packet from buffer
RtpBuffer::ResultList rl; RtpBuffer::ResultList rl;
@ -439,7 +523,7 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
switch (fr) switch (fr)
{ {
case RtpBuffer::FetchResult::Gap: case RtpBuffer::FetchResult::Gap:
ICELogInfo(<< "Gap detected."); ICELogDebug(<< "Gap detected.");
mDecodedLength = mResampledLength = 0; mDecodedLength = mResampledLength = 0;
if (mCngPacket && mCodec) if (mCngPacket && mCodec)
@ -456,36 +540,41 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
if (options & DecodeOptions_SkipDecode) if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0; mDecodedLength = 0;
else else
{
mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame); mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame);
if (!mDecodedLength)
{
// PLC is not support or failed
// So substitute the silence
size_t nr_of_samples = mCodec->frameTime() * mCodec->samplerate() / 1000 * sizeof(short);
mDecodedLength = nr_of_samples * sizeof(short);
memset(mDecodedFrame, 0, mDecodedLength);
}
}
} }
if (mDecodedLength) if (mDecodedLength)
{ {
processDecoded(output, options); processDecoded(output, options);
result = true; result = DecodeResult_Ok;
} }
break; break;
case RtpBuffer::FetchResult::NoPacket: case RtpBuffer::FetchResult::NoPacket:
ICELogDebug(<< "No packet available in jitter buffer"); ICELogDebug(<< "No packet available in jitter buffer");
mFailedCount++; mFailedCount++;
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
if (mResampledLength > 0)
updatePvqa(nullptr, mResampledLength);
#endif
break; break;
case RtpBuffer::FetchResult::RegularPacket: case RtpBuffer::FetchResult::RegularPacket:
mFailedCount = 0; mFailedCount = 0;
for (std::shared_ptr<RtpBuffer::Packet>& p: rl)
for (std::shared_ptr<RTPPacket>& p: rl)
{ {
assert(p); assert(p);
// Check if previously CNG packet was detected. Emit CNG audio here if needed. // Check if previously CNG packet was detected. Emit CNG audio here if needed.
if (options & DecodeOptions_FillCngGap && mCngPacket && mCodec) if (options & DecodeOptions_FillCngGap && mCngPacket && mCodec)
{ {
// Fill CNG audio is server mode is present // Fill CNG audio is server mode is present
int units = p->GetTimestamp() - mCngPacket->GetTimestamp(); int units = p->rtp()->GetTimestamp() - mCngPacket->GetTimestamp();
int milliseconds = units / (mCodec->samplerate() / 1000); int milliseconds = units / (mCodec->samplerate() / 1000);
if (milliseconds > mLastPacketTimeLength) if (milliseconds > mLastPacketTimeLength)
{ {
@ -514,33 +603,41 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
if (mDecodedLength) if (mDecodedLength)
processDecoded(output, options); processDecoded(output, options);
} }
result = true; result = DecodeResult_Ok;
} }
} }
// Find codec if (mEarlyDecode)
mCodec = mCodecMap[p->GetPayloadType()]; {
// ToDo - copy the decoded data to output buffer
}
else
{
// Find codec by payload type
int ptype = p->rtp()->GetPayloadType();
mCodec = mCodecMap[ptype];
if (mCodec) if (mCodec)
{ {
if (rate) if (rate)
*rate = mCodec->samplerate(); *rate = mCodec->samplerate();
// Check if it is CNG packet // Check if it is CNG packet
if ((p->GetPayloadType() == 0 || p->GetPayloadType() == 8) && p->GetPayloadLength() >= 1 && p->GetPayloadLength() <= 6) if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6)
{ {
if (options & DecodeOptions_SkipDecode) if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0; mDecodedLength = 0;
else else
{ {
mCngPacket = p; mCngPacket = p->rtp();
mCngDecoder.decode3389(p->GetPayloadData(), p->GetPayloadLength()); mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength());
// Emit CNG mLastPacketLength milliseconds // Emit CNG mLastPacketLength milliseconds
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength,
(short*)mDecodedFrame, true); (short*)mDecodedFrame, true);
if (mDecodedLength) if (mDecodedLength)
processDecoded(output, options); processDecoded(output, options);
} }
result = true; result = DecodeResult_Ok;
} }
else else
{ {
@ -549,13 +646,16 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
// Handle here regular RTP packets // Handle here regular RTP packets
// Check if payload length is ok // Check if payload length is ok
int tail = mCodec->rtpLength() ? p->GetPayloadLength() % mCodec->rtpLength() : 0; size_t payload_length = p->rtp()->GetPayloadLength();
size_t rtp_frame_length = mCodec->rtpLength();
int tail = rtp_frame_length ? payload_length % rtp_frame_length : 0;
if (!tail) if (!tail)
{ {
// Find number of frames // Find number of frames
mFrameCount = mCodec->rtpLength() ? p->GetPayloadLength() / mCodec->rtpLength() : 1; mFrameCount = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() / mCodec->rtpLength() : 1;
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->GetPayloadLength(); int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->rtp()->GetPayloadLength();
// Save last packet time length // Save last packet time length
mLastPacketTimeLength = mFrameCount * mCodec->frameTime(); mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
@ -567,20 +667,27 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
mDecodedLength = 0; mDecodedLength = 0;
else else
{ {
// Trigger the statistics
had_decode = true;
// Decode frame by frame // Decode frame by frame
mDecodedLength = mCodec->decode(p->GetPayloadData() + i*mCodec->rtpLength(), mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
frameLength, mDecodedFrame, sizeof mDecodedFrame); frameLength, mDecodedFrame, sizeof mDecodedFrame);
if (mDecodedLength) if (mDecodedLength > 0)
processDecoded(output, options); processDecoded(output, options);
} }
} }
result = mFrameCount > 0; result = mFrameCount > 0 ? DecodeResult_Ok : DecodeResult_Skip;
// Check for bitrate counter // Check for bitrate counter
processStatisticsWithAmrCodec(mCodec.get()); processStatisticsWithAmrCodec(mCodec.get());
} }
else else
ICELogMedia(<< "RTP packet with tail."); {
result = DecodeResult_BadPacket;
// ICELogMedia(<< "RTP packet with tail.");
}
}
} }
} }
} }
@ -590,6 +697,18 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
assert(0); assert(0);
} }
if (had_decode)
{
// mStat.mDecodeRequested++;
if (mLastDecodeTime == 0.0)
mLastDecodeTime = now_ms();
else
{
float t = now_ms();
mStat.mDecodingInterval.process(t - mLastDecodeTime);
mLastDecodeTime = t;
}
}
return result; return result;
} }
@ -635,66 +754,10 @@ Codec* AudioReceiver::findCodec(int payloadType)
return codecIter->second.get(); return codecIter->second.get();
} }
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
void AudioReceiver::initPvqa()
{
// Allocate space for 20 seconds audio
if (!mPvqaBuffer)
{
mPvqaBuffer = std::make_shared<Audio::DataWindow>();
mPvqaBuffer->setCapacity(Audio::Format().sizeFromTime(30000));
}
// Instantiate & open PVQA analyzer
if (!mPVQA)
{
mPVQA = std::make_shared<sevana::pvqa>();
mPVQA->open(AUDIO_SAMPLERATE, 1, PVQA_INTERVAL);
}
}
void AudioReceiver::updatePvqa(const void *data, int size)
{
if (!mPVQA)
initPvqa();
if (mPVQA)
{
if (data)
mPvqaBuffer->add(data, size);
else
mPvqaBuffer->addZero(size);
Audio::Format fmt;
int frames = (int)fmt.timeFromSize(mPvqaBuffer->filled()) / (PVQA_INTERVAL * 1000);
if (frames > 0)
{
int time4pvqa = (int)(frames * PVQA_INTERVAL * 1000);
int size4pvqa = (int)fmt.sizeFromTime(time4pvqa);
ICELogInfo(<< "PVQA buffer has " << time4pvqa << " milliseconds of audio.");
mPVQA->update(mPvqaBuffer->data(), size4pvqa);
mPvqaBuffer->erase(size4pvqa);
}
}
}
float AudioReceiver::calculatePvqaMos(int rate, std::string& report)
{
if (mPVQA && mPvqaBuffer)
{
sevana::pvqa::result result;
if (mPVQA->get_result(result)) {
report = result.mReport;
return result.mMos;
}
}
return 0.0f;
}
#endif
void AudioReceiver::processStatisticsWithAmrCodec(Codec* c) void AudioReceiver::processStatisticsWithAmrCodec(Codec* c)
{ {
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) #if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC)
AmrNbCodec* nb = dynamic_cast<AmrNbCodec*>(c); AmrNbCodec* nb = dynamic_cast<AmrNbCodec*>(c);
AmrWbCodec* wb = dynamic_cast<AmrWbCodec*>(c); AmrWbCodec* wb = dynamic_cast<AmrWbCodec*>(c);

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -8,25 +8,16 @@
#include "MT_Stream.h" #include "MT_Stream.h"
#include "MT_CodecList.h" #include "MT_CodecList.h"
#include "MT_AudioCodec.h"
#include "MT_CngHelper.h" #include "MT_CngHelper.h"
#include "../helper/HL_Pointer.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include "../helper/HL_Optional.hpp"
#include "jrtplib/src/rtppacket.h" #include "jrtplib/src/rtppacket.h"
#include "jrtplib/src/rtcppacket.h"
#include "jrtplib/src/rtpsourcedata.h" #include "jrtplib/src/rtpsourcedata.h"
#include "../audio/Audio_DataWindow.h" #include "../audio/Audio_DataWindow.h"
#include "../audio/Audio_Resampler.h" #include "../audio/Audio_Resampler.h"
#if defined(USE_PVQA_LIBRARY) #include <optional>
# include "pvqa++.h"
#endif
#include <map>
// #define DUMP_DECODED
namespace MT namespace MT
{ {
@ -45,14 +36,19 @@ namespace MT
class Packet class Packet
{ {
public: public:
Packet(std::shared_ptr<RTPPacket> packet, int timelen, int rate); Packet(const std::shared_ptr<RTPPacket>& packet, int timelen, int rate);
std::shared_ptr<RTPPacket> rtp() const; std::shared_ptr<RTPPacket> rtp() const;
int timelength() const; int timelength() const;
int rate() const; int rate() const;
const std::vector<short>& pcm() const;
std::vector<short>& pcm();
protected: protected:
std::shared_ptr<RTPPacket> mRtp; std::shared_ptr<RTPPacket> mRtp;
int mTimelength = 0, mRate = 0; int mTimelength = 0, mRate = 0;
std::vector<short> mPcm;
}; };
RtpBuffer(Statistics& stat); RtpBuffer(Statistics& stat);
@ -60,35 +56,49 @@ namespace MT
unsigned ssrc(); unsigned ssrc();
void setSsrc(unsigned ssrc); void setSsrc(unsigned ssrc);
void setHigh(int milliseconds); void setHigh(int milliseconds);
int high(); int high();
void setLow(int milliseconds); void setLow(int milliseconds);
int low(); int low();
void setPrebuffer(int milliseconds); void setPrebuffer(int milliseconds);
int prebuffer(); int prebuffer();
int getNumberOfReturnedPackets() const; int getNumberOfReturnedPackets() const;
int getNumberOfAddPackets() const; int getNumberOfAddPackets() const;
int findTimelength(); int findTimelength();
int getCount() const; int getCount() const;
// Returns false if packet was not add - maybe too old or too new or duplicate
bool add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
typedef std::vector<std::shared_ptr<RTPPacket>> ResultList; // Returns false if packet was not add - maybe too old or too new or duplicate
std::shared_ptr<Packet> add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
typedef std::vector<std::shared_ptr<Packet>> ResultList;
typedef std::shared_ptr<ResultList> PResultList; typedef std::shared_ptr<ResultList> PResultList;
FetchResult fetch(ResultList& rl); FetchResult fetch(ResultList& rl);
protected: protected:
unsigned mSsrc; unsigned mSsrc = 0;
int mHigh, mLow, mPrebuffer; int mHigh = RTP_BUFFER_HIGH,
int mReturnedCounter, mAddCounter; mLow = RTP_BUFFER_LOW,
mPrebuffer = RTP_BUFFER_PREBUFFER;
int mReturnedCounter = 0,
mAddCounter = 0;
mutable Mutex mGuard; mutable Mutex mGuard;
typedef std::vector<Packet> PacketList; typedef std::vector<std::shared_ptr<Packet>> PacketList;
PacketList mPacketList; PacketList mPacketList;
Statistics& mStat; Statistics& mStat;
bool mFirstPacketWillGo; bool mFirstPacketWillGo = true;
jrtplib::RTPSourceStats mRtpStats; jrtplib::RTPSourceStats mRtpStats;
Packet mFetchedPacket; std::shared_ptr<Packet> mFetchedPacket;
std::optional<uint32_t> mLastSeqno;
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
float mLastAddTime = 0.0;
}; };
class Receiver class Receiver
@ -107,9 +117,12 @@ namespace MT
AudioReceiver(const CodecList::Settings& codecSettings, Statistics& stat); AudioReceiver(const CodecList::Settings& codecSettings, Statistics& stat);
~AudioReceiver(); ~AudioReceiver();
// Update codec settings
void setCodecSettings(const CodecList::Settings& codecSettings);
CodecList::Settings& getCodecSettings();
// Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding. // Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding.
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container). // Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
bool add(std::shared_ptr<jrtplib::RTPPacket> p, Codec** codec = nullptr); bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
// Returns false when there is no rtp data from jitter // Returns false when there is no rtp data from jitter
enum DecodeOptions enum DecodeOptions
@ -120,7 +133,14 @@ namespace MT
DecodeOptions_SkipDecode = 4 DecodeOptions_SkipDecode = 4
}; };
bool getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr); enum DecodeResult
{
DecodeResult_Ok,
DecodeResult_Skip,
DecodeResult_BadPacket
};
DecodeResult getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr);
// Looks for codec by payload type // Looks for codec by payload type
Codec* findCodec(int payloadType); Codec* findCodec(int payloadType);
@ -146,6 +166,9 @@ namespace MT
std::shared_ptr<jrtplib::RTPPacket> mCngPacket; std::shared_ptr<jrtplib::RTPPacket> mCngPacket;
CngDecoder mCngDecoder; CngDecoder mCngDecoder;
// Decode RTP early, do not wait for speaker callback
bool mEarlyDecode = false;
// Buffer to hold decoded data // Buffer to hold decoded data
char mDecodedFrame[65536]; char mDecodedFrame[65536];
int mDecodedLength = 0; int mDecodedLength = 0;
@ -161,26 +184,23 @@ namespace MT
// Last packet time length // Last packet time length
int mLastPacketTimeLength = 0; int mLastPacketTimeLength = 0;
int mFailedCount; int mFailedCount = 0;
Audio::Resampler mResampler8, mResampler16, Audio::Resampler mResampler8, mResampler16,
mResampler32, mResampler48; mResampler32, mResampler48;
Audio::PWavFileWriter mDecodedDump; Audio::PWavFileWriter mDecodedDump;
float mLastDecodeTime = 0.0; // Time last call happened to codec->decode()
float mIntervalSum = 0.0;
int mIntervalCount = 0;
// Zero rate will make audio mono but resampling will be skipped // Zero rate will make audio mono but resampling will be skipped
void makeMonoAndResample(int rate, int channels); void makeMonoAndResample(int rate, int channels);
// Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame // Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame
void processDecoded(Audio::DataWindow& output, int options); void processDecoded(Audio::DataWindow& output, int options);
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
std::shared_ptr<sevana::pvqa> mPVQA;
void initPvqa();
void updatePvqa(const void* data, int size);
float calculatePvqaMos(int rate, std::string& report);
std::shared_ptr<Audio::DataWindow> mPvqaBuffer;
#endif
void processStatisticsWithAmrCodec(Codec* c); void processStatisticsWithAmrCodec(Codec* c);
}; };

View File

@ -97,7 +97,7 @@ AudioStream::~AudioStream()
if (mFinalStatistics) if (mFinalStatistics)
*mFinalStatistics = mStat; *mFinalStatistics = mStat;
ICELogInfo(<< mStat.toShortString()); ICELogInfo(<< mStat.toString());
} }
void AudioStream::setDestination(const RtpPair<InternetAddress>& dest) void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)
@ -110,6 +110,7 @@ void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)
void AudioStream::setTransmittingCodec(Codec::Factory& factory, int payloadType) void AudioStream::setTransmittingCodec(Codec::Factory& factory, int payloadType)
{ {
ICELogInfo(<< "Selected codec " << factory.name() << "/" << factory.samplerate() << " for transmitting"); ICELogInfo(<< "Selected codec " << factory.name() << "/" << factory.samplerate() << " for transmitting");
Lock l(mMutex); Lock l(mMutex);
mTransmittingCodec = factory.create(); mTransmittingCodec = factory.create();
mTransmittingPayloadType = payloadType; mTransmittingPayloadType = payloadType;
@ -145,9 +146,11 @@ void AudioStream::addData(const void* buffer, int bytes)
{ {
Lock l(mMutex); Lock l(mMutex);
codec = mTransmittingCodec.get(); codec = mTransmittingCodec.get();
if (!codec) if (nullptr == codec) {
// ICELogDebug(<< "No transmitting codec selected.");
return; return;
} }
}
// Resample // Resample
unsigned dstlen = unsigned(float(codec->samplerate() / float(AUDIO_SAMPLERATE)) * bytes); unsigned dstlen = unsigned(float(codec->samplerate() / float(AUDIO_SAMPLERATE)) * bytes);
@ -202,7 +205,7 @@ void AudioStream::addData(const void* buffer, int bytes)
int packetTime = mPacketTime ? mPacketTime : codec->frameTime(); int packetTime = mPacketTime ? mPacketTime : codec->frameTime();
// Make stereo version if required // Make stereo version if required
for (int i=0; i<mCapturedAudio.filled() / mTransmittingCodec->pcmLength(); i++) for (int i=0; i<mCapturedAudio.filled() / codec->pcmLength(); i++)
{ {
if (mSendingDump) if (mSendingDump)
mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength()); mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength());
@ -230,6 +233,7 @@ void AudioStream::addData(const void* buffer, int bytes)
} }
} }
} }
if (processed > 0)
mCapturedAudio.erase(processed); mCapturedAudio.erase(processed);
} }
@ -292,9 +296,9 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
assert(info); assert(info);
// Drop RTP packets if stream is not receiving now; let RTCP go // Drop RTP packets if stream is not receiving now; let RTCP go
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtp(buffer, length)) if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtpOrRtcp(buffer, length))
{ {
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the packet"); ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the RT(C)P packet");
return; return;
} }
@ -302,21 +306,23 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
int receiveLength = length; int receiveLength = length;
memcpy(mReceiveBuffer, buffer, length); memcpy(mReceiveBuffer, buffer, length);
bool srtpResult;
if (mSrtpSession.active()) if (mSrtpSession.active())
{ {
bool srtpResult;
size_t srcLength = length; size_t dstLength = sizeof mSrtpDecodeBuffer;
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength)) if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength); srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
else else
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength); srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
if (!srtpResult) if (!srtpResult)
{ {
ICELogError(<<"Cannot decrypt SRTP packet."); ICELogError(<<"Cannot decrypt SRTP packet.");
return; return;
} }
}
//ICELogDebug(<< "Packet no: " << RtpHelper::findPacketNo(mReceiveBuffer, receiveLength)); memcpy(mReceiveBuffer, mSrtpDecodeBuffer, dstLength);
receiveLength = dstLength;
}
switch (source.family()) switch (source.family())
{ {
@ -341,8 +347,8 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
mStat.mReceived += length; mStat.mReceived += length;
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength)) if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
{ {
if (!mStat.mFirstRtpTime.is_initialized()) if (!mStat.mFirstRtpTime)
mStat.mFirstRtpTime = std::chrono::system_clock::now(); mStat.mFirstRtpTime = std::chrono::steady_clock::now();
mStat.mReceivedRtp++; mStat.mReceivedRtp++;
} }
else else
@ -356,6 +362,7 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
if (packet) if (packet)
{ {
ICELogMedia(<< "jrtplib returned packet"); ICELogMedia(<< "jrtplib returned packet");
// Find right handler for rtp stream // Find right handler for rtp stream
SingleAudioStream* rtpStream = nullptr; SingleAudioStream* rtpStream = nullptr;
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC()); AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());

View File

@ -6,7 +6,7 @@
#ifndef __MT_AUDIOSTREAM_H #ifndef __MT_AUDIOSTREAM_H
#define __MT_AUDIOSTREAM_H #define __MT_AUDIOSTREAM_H
#include "../config.h" #include "../engine_config.h"
#include "MT_Stream.h" #include "MT_Stream.h"
#include "MT_NativeRtpSender.h" #include "MT_NativeRtpSender.h"
#include "MT_SingleAudioStream.h" #include "MT_SingleAudioStream.h"
@ -64,7 +64,7 @@ namespace MT
Audio::DataWindow mStereoCapturedAudio; Audio::DataWindow mStereoCapturedAudio;
char mIncomingPcmBuffer[AUDIO_MIC_BUFFER_SIZE]; // Temporary buffer to allow reading from file char mIncomingPcmBuffer[AUDIO_MIC_BUFFER_SIZE]; // Temporary buffer to allow reading from file
char mResampleBuffer[AUDIO_MIC_BUFFER_SIZE*8]; // Temporary buffer to hold data char mResampleBuffer[AUDIO_MIC_BUFFER_SIZE*8]; // Temporary buffer to hold data
char mStereoBuffer[AUDIO_MIC_BUFFER_SIZE*8]; // Temporary buffer to hold data converted to stereo char mStereoBuffer[AUDIO_MIC_BUFFER_SIZE*16]; // Temporary buffer to hold data converted to stereo
PCodec mTransmittingCodec; // Current encoding codec PCodec mTransmittingCodec; // Current encoding codec
int mTransmittingPayloadType; // Payload type to mark outgoing packets int mTransmittingPayloadType; // Payload type to mark outgoing packets
int mPacketTime; // Required packet time int mPacketTime; // Required packet time
@ -87,7 +87,8 @@ namespace MT
mCaptureResampler32, mCaptureResampler32,
mCaptureResampler48; mCaptureResampler48;
DtmfContext mDtmfContext; DtmfContext mDtmfContext;
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE]; char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE],
mSrtpDecodeBuffer[MAX_VALID_UDPPACKET_SIZE];
struct struct
{ {

View File

@ -34,13 +34,13 @@ Terminal::~Terminal()
mAudioPair.reset(); mAudioPair.reset();
} }
PStream Terminal::createStream(int type, VariantMap& config) PStream Terminal::createStream(int type, VariantMap& /*config*/)
{ {
PStream result; PStream result;
switch (type) switch (type)
{ {
case Stream::Audio: case Stream::Audio:
result = PStream(new AudioStream(MT::CodecList::Settings::DefaultSettings)); result = std::make_shared<AudioStream>(MT::CodecList::Settings::DefaultSettings);
mAudioList.add(result); mAudioList.add(result);
break; break;
@ -52,7 +52,7 @@ PStream Terminal::createStream(int type, VariantMap& config)
return result; return result;
} }
void Terminal::freeStream(PStream stream) void Terminal::freeStream(const PStream& stream)
{ {
if (AudioStream* audio = dynamic_cast<AudioStream*>(stream.get())) if (AudioStream* audio = dynamic_cast<AudioStream*>(stream.get()))
{ {

View File

@ -26,10 +26,11 @@ namespace MT
CodecList& codeclist(); CodecList& codeclist();
PStream createStream(int type, VariantMap& config); PStream createStream(int type, VariantMap& config);
void freeStream(PStream s); void freeStream(const PStream& s);
Audio::PDevicePair audio(); Audio::PDevicePair audio();
void setAudio(const Audio::PDevicePair& audio); void setAudio(const Audio::PDevicePair& audio);
protected: protected:
StreamList mAudioList; StreamList mAudioList;
std::mutex mAudioListMutex; std::mutex mAudioListMutex;

View File

@ -1,4 +1,4 @@
#include "../config.h" #include "../engine_config.h"
#include "MT_CngHelper.h" #include "MT_CngHelper.h"
#include <stdlib.h> #include <stdlib.h>

View File

@ -11,8 +11,6 @@ int Codec::Factory::channels()
{ {
return 1; return 1;
} }
#if defined(USE_RESIP_INTEGRATION)
void Codec::Factory::create(CodecMap& codecs) void Codec::Factory::create(CodecMap& codecs)
{ {
codecs[payloadType()] = std::shared_ptr<Codec>(create()); codecs[payloadType()] = std::shared_ptr<Codec>(create());
@ -38,4 +36,4 @@ int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecC
} }
return -1; return -1;
} }
#endif

View File

@ -6,9 +6,7 @@
#ifndef __MT_CODEC_H #ifndef __MT_CODEC_H
#define __MT_CODEC_H #define __MT_CODEC_H
#if defined(USE_RESIP_INTEGRATION)
#include "resiprocate/resip/stack/SdpContents.hxx" #include "resiprocate/resip/stack/SdpContents.hxx"
#endif
#include "../helper/HL_Types.h" #include "../helper/HL_Types.h"
#include <map> #include <map>
#include "../helper/HL_Pointer.h" #include "../helper/HL_Pointer.h"
@ -36,14 +34,12 @@ public:
virtual PCodec create() = 0; virtual PCodec create() = 0;
virtual int channels(); virtual int channels();
#if defined(USE_RESIP_INTEGRATION)
typedef std::map<int, PCodec > CodecMap; typedef std::map<int, PCodec > CodecMap;
virtual void create(CodecMap& codecs); virtual void create(CodecMap& codecs);
virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction); virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
// Returns payload type from chosen codec if success. -1 is returned for negative result. // Returns payload type from chosen codec if success. -1 is returned for negative result.
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction); virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
resip::Codec resipCodec(); resip::Codec resipCodec();
#endif
}; };
virtual ~Codec() {} virtual ~Codec() {}
virtual const char* name() = 0; virtual const char* name() = 0;
@ -63,8 +59,13 @@ public:
virtual int channels() { return 1; } virtual int channels() { return 1; }
// Returns size of encoded data (RTP) in bytes
virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0; virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
// Returns size of decoded data (PCM signed short) in bytes
virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0; virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
// Returns size of produced data (PCM signed short) in bytes
virtual int plc(int lostFrames, void* output, int outputCapacity) = 0; virtual int plc(int lostFrames, void* output, int outputCapacity) = 0;
// Returns size of codec in memory // Returns size of codec in memory

View File

@ -1,25 +1,147 @@
/* Copyright(C) 2007-2020 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <algorithm> #include <algorithm>
#include <list>
#include "../config.h" #include "../engine_config.h"
#include "MT_CodecList.h" #include "MT_CodecList.h"
#include "MT_AudioCodec.h" #include "MT_AudioCodec.h"
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI) #if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI)
# if defined(USE_AMR_CODEC)
# include "MT_AmrCodec.h" # include "MT_AmrCodec.h"
# endif
# if defined(USE_EVS_CODEC)
# include "MT_EvsCodec.h" # include "MT_EvsCodec.h"
# endif # endif
#endif
#include "helper/HL_String.h" #include "helper/HL_String.h"
using namespace MT; using namespace MT;
using strx = StringHelper; using strx = strx;
bool CodecList::Settings::contains(int ptype) const
{
if (ptype >= 0 && ptype < 96)
return true;
if (mAmrNbOctetPayloadType.contains(ptype))
return true;
if (mAmrNbPayloadType.contains(ptype))
return true;
if (mAmrWbOctetPayloadType.contains(ptype))
return true;
if (mAmrWbPayloadType.contains(ptype))
return true;
for (const auto& s: mOpusSpec)
if (s.mPayloadType == ptype)
return true;
for (const auto& s: mEvsSpec)
if (s.mPayloadType == ptype)
return true;
if (mIsac16KPayloadType == ptype || mIsac32KPayloadType == ptype)
return true;
if (mIlbc20PayloadType == ptype || mIlbc30PayloadType == ptype)
return true;
if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype)
return true;
return false;
}
std::string CodecList::Settings::toString() const
{
std::ostringstream oss;
// oss << "wrap IuUP: " << mWrapIuUP << std::endl
// << "skip decode: " << mSkipDecode << std::endl;
if (!mAmrWbPayloadType.empty())
{
oss << "AMR WB ptype: ";
for (int ptype: mAmrWbPayloadType)
oss << ptype << " ";
}
if (!mAmrWbOctetPayloadType.empty())
{
oss << "AMR WB octet ptype: ";
for (int ptype: mAmrWbOctetPayloadType)
oss << ptype << " ";
}
if (!mAmrNbPayloadType.empty())
{
oss << "AMR ptype: ";
for (int ptype: mAmrNbPayloadType)
oss << ptype << " ";
}
if (!mAmrNbOctetPayloadType.empty())
{
oss << "AMR octet ptype: ";
for (int ptype: mAmrNbOctetPayloadType)
oss << ptype << " ";
}
if (mIsac16KPayloadType != -1)
oss << "ISAC 16Khz ptype: " << mIsac16KPayloadType << " ";
if (mIsac32KPayloadType != -1)
oss << "ISAC 32Khz ptype: " << mIsac32KPayloadType << " ";
if (mIlbc20PayloadType != -1)
oss << "iLBC 20ms ptype: " << mIlbc20PayloadType << " ";
if (mIlbc30PayloadType != -1)
oss << "iLBC 30ms ptype: " << mIlbc30PayloadType << " ";
if (mGsmFrPayloadType != -1)
oss << "GSM FR ptype: " << mGsmFrPayloadType << ", GSM FR plength: " << mGsmFrPayloadLength << " ";
if (mGsmHrPayloadType != -1)
oss << "GSM HR ptype: " << mGsmHrPayloadType << " ";
if (mGsmEfrPayloadType != -1)
oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " ";
for (auto& spec: mEvsSpec)
{
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " ";
}
for (auto& spec: mOpusSpec)
{
oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl;
}
return oss.str();
}
void CodecList::Settings::clear()
{
// Remove all dynamic payload type assignments
mEvsSpec.clear();
mOpusSpec.clear();
mAmrNbOctetPayloadType.clear();
mAmrNbPayloadType.clear();
mAmrWbOctetPayloadType.clear();
mAmrWbPayloadType.clear();
mIsac16KPayloadType = -1;
mIsac32KPayloadType = -1;
mIlbc20PayloadType = -1;
mIlbc30PayloadType = -1;
mGsmEfrPayloadType = -1;
mGsmFrPayloadType = -1;
mGsmHrPayloadType = -1;
}
// ---------------- EvsSpec ---------------
bool CodecList::Settings::EvsSpec::isValid() const bool CodecList::Settings::EvsSpec::isValid() const
{ {
return mPayloadType >= 96 && mPayloadType <= 127; return mPayloadType >= 96 && mPayloadType <= 127;
@ -29,7 +151,7 @@ CodecList::Settings::EvsSpec CodecList::Settings::EvsSpec::parse(const std::stri
{ {
EvsSpec result; EvsSpec result;
auto parts = strx::split(spec, "-/"); auto parts = strx::split(spec, "-/ ,:");
if (parts.size() == 3) if (parts.size() == 3)
{ {
// Payload type number // Payload type number
@ -74,7 +196,7 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
{ {
OpusSpec result; OpusSpec result;
auto parts = strx::split(spec, "-"); auto parts = strx::split(spec, "-/ ,:");
if (parts.size() == 3) if (parts.size() == 3)
{ {
result.mPayloadType = strx::toInt(strx::trim(parts.front()).c_str(), -1); result.mPayloadType = strx::toInt(strx::trim(parts.front()).c_str(), -1);
@ -84,54 +206,163 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
return result; return result;
} }
static int findOctetMode(const char* line)
{
const char* param_name = "octet-align=";
auto p = strstr(line, param_name);
if (!p)
return 0;
p += strlen(param_name);
char int_buf[8] = {0};
size_t int_buf_offset = 0;
while (*p && isdigit(*p) && int_buf_offset < sizeof(int_buf))
int_buf[int_buf_offset++] = *p++;
return atoi(int_buf);
}
CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>& codeclist)
{
CodecList::Settings r{DefaultSettings};
for (auto& c: codeclist)
{
std::string codec_name = strx::uppercase(c.getName().c_str());
int samplerate = c.getRate();
int ptype = c.payloadType();
auto enc_params = c.encodingParameters(); // This must channels number for Opus codec
auto params = c.parameters();
// Dynamic payload type codecs only - ISAC / iLBC / Speex / etc.
if (codec_name == "OPUS")
{
// Check the parameters
int channels = strx::toInt(enc_params.c_str(), 1);
r.mOpusSpec.push_back({ptype, samplerate, channels});
}
else
if (codec_name == "AMR-WB")
{
int octet_mode = findOctetMode(params.c_str());
if (octet_mode != -1)
{
if (octet_mode == 0)
r.mAmrWbPayloadType.insert(ptype);
else
if (octet_mode == 1)
r.mAmrWbOctetPayloadType.insert(ptype);
}
}
else
if (codec_name == "AMR" || codec_name == "AMR-NB")
{
int octet_mode = findOctetMode(params.c_str());
if (octet_mode != -1)
{
if (octet_mode == 0)
r.mAmrNbPayloadType.insert(ptype);
else
if (octet_mode == 1)
r.mAmrNbOctetPayloadType.insert(ptype);
}
}
else
if (codec_name == "EVS")
{
r.mEvsSpec.push_back({ptype});
}
}
return r;
}
bool CodecList::Settings::operator == (const Settings& rhs) const
{
if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType) !=
std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType))
return false;
if (mAmrNbOctetPayloadType != rhs.mAmrNbOctetPayloadType)
return false;
if (mAmrNbPayloadType != rhs.mAmrNbPayloadType)
return false;
if (mAmrWbOctetPayloadType != rhs.mAmrWbOctetPayloadType)
return false;
if (mAmrWbPayloadType != rhs.mAmrWbPayloadType)
return false;
// ToDo: compare EVS and Opus specs
if (mEvsSpec.size() != rhs.mEvsSpec.size())
return false;
for (size_t i = 0; i < mEvsSpec.size(); i++)
if (mEvsSpec[i] != rhs.mEvsSpec[i])
return false;
if (mOpusSpec.size() != rhs.mOpusSpec.size())
return false;
for (size_t i = 0; i < mOpusSpec.size(); i++)
if (mOpusSpec[i] != rhs.mOpusSpec[i])
return false;
return true;
}
// ---------------------------------------- // ----------------------------------------
CodecList::Settings CodecList::Settings::DefaultSettings; CodecList::Settings CodecList::Settings::DefaultSettings;
CodecList::CodecList(const Settings& settings) CodecList::CodecList(const Settings& settings)
:mSettings(settings) :mSettings(settings)
{ {
//mFactoryList.push_back(new OpusCodec::OpusFactory(16000, 1)); init(mSettings);
#if !defined(TARGET_ANDROID)
if (settings.mOpusSpec.empty())
{
mFactoryList.push_back(new OpusCodec::OpusFactory(48000, 2, MT_OPUS_CODEC_PT));
} }
else
void CodecList::init(const Settings& settings)
{ {
mFactoryList.clear();
mSettings = settings;
#if defined(USE_OPUS_CODEC)
for (auto spec: settings.mOpusSpec) for (auto spec: settings.mOpusSpec)
{ {
mFactoryList.push_back(new OpusCodec::OpusFactory(spec.mRate, spec.mChannels, spec.mPayloadType)); mFactoryList.push_back(std::make_shared<OpusCodec::OpusFactory>(spec.mRate, spec.mChannels, spec.mPayloadType));
}
} }
#endif #endif
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI) #if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI)
#if defined(USE_AMR_CODEC)
for (int pt: mSettings.mAmrWbPayloadType) for (int pt: mSettings.mAmrWbPayloadType)
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt})); mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
for (int pt: mSettings.mAmrWbOctetPayloadType) for (int pt: mSettings.mAmrWbOctetPayloadType)
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt})); mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
for (int pt: mSettings.mAmrNbPayloadType) for (int pt: mSettings.mAmrNbPayloadType)
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt})); mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
for (int pt: mSettings.mAmrNbOctetPayloadType) for (int pt: mSettings.mAmrNbOctetPayloadType)
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt})); mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
mFactoryList.push_back(new GsmEfrCodec::GsmEfrFactory(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType)); if (mSettings.mGsmEfrPayloadType != -1)
mFactoryList.push_back(std::make_shared<GsmEfrCodec::GsmEfrFactory>(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
#endif #endif
#endif
mFactoryList.push_back(std::make_shared<G711Codec::AlawFactory>());
mFactoryList.push_back(std::make_shared<G711Codec::UlawFactory>());
//mFactoryList.push_back(new IsacCodec::IsacFactory16K(mSettings.mIsac16KPayloadType)); if (mSettings.mGsmFrPayloadType != -1)
//mFactoryList.push_back(new IlbcCodec::IlbcFactory(mSettings.mIlbc20PayloadType, mSettings.mIlbc30PayloadType)); mFactoryList.push_back(std::make_shared<GsmCodec::GsmFactory>(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
mFactoryList.push_back(new G711Codec::AlawFactory()); mFactoryList.push_back(std::make_shared<G722Codec::G722Factory>());
mFactoryList.push_back(new G711Codec::UlawFactory()); mFactoryList.push_back(std::make_shared<G729Codec::G729Factory>());
mFactoryList.push_back(new GsmCodec::GsmFactory(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
mFactoryList.push_back(new G722Codec::G722Factory());
mFactoryList.push_back(new G729Codec::G729Factory());
#ifndef TARGET_ANDROID #ifndef TARGET_ANDROID
mFactoryList.push_back(new GsmHrCodec::GsmHrFactory(mSettings.mGsmHrPayloadType)); if (mSettings.mGsmHrPayloadType != -1)
mFactoryList.push_back(std::make_shared<GsmHrCodec::GsmHrFactory>(mSettings.mGsmHrPayloadType));
#endif #endif
#if !defined(TARGET_ANDROID) #if !defined(TARGET_ANDROID) && defined(USE_EVS_CODEC)
for (auto& spec: settings.mEvsSpec) for (auto& spec: settings.mEvsSpec)
{ {
EVSCodec::StreamParameters evs_params; EVSCodec::StreamParameters evs_params;
@ -140,15 +371,14 @@ CodecList::CodecList(const Settings& settings)
evs_params.ptime = 20; evs_params.ptime = 20;
evs_params.ptype = spec.mPayloadType; evs_params.ptype = spec.mPayloadType;
mFactoryList.push_back(new EVSCodec::EVSFactory(evs_params)); mFactoryList.push_back(std::make_shared<EVSCodec::EVSFactory>(evs_params));
} }
#endif #endif
} }
CodecList::~CodecList() CodecList::~CodecList()
{ {
for (FactoryList::size_type i=0; i<mFactoryList.size(); i++) mFactoryList.clear();
delete mFactoryList[i];
} }
int CodecList::count() const int CodecList::count() const
@ -173,23 +403,30 @@ int CodecList::findCodec(const std::string &name) const
void CodecList::fillCodecMap(CodecMap& cm) void CodecList::fillCodecMap(CodecMap& cm)
{ {
cm.clear();
for (auto& factory: mFactoryList) for (auto& factory: mFactoryList)
{ {
// Create codec here. Although they are not needed right now - they can be needed to find codec's info. // Create codec here. Although they are not needed right now - they can be needed to find codec's info.
cm[factory->payloadType()] = factory->create(); // PCodec c = factory->create();
cm.insert({factory->payloadType(), PCodec()});
} }
} }
PCodec CodecList::createCodecByPayloadType(int payloadType)
{
for (auto& factory: mFactoryList)
{
if (factory->payloadType() == payloadType)
return factory->create();
}
return {};
}
CodecListPriority::CodecListPriority() CodecListPriority::CodecListPriority()
{ {}
}
CodecListPriority::~CodecListPriority() CodecListPriority::~CodecListPriority()
{ {}
}
bool CodecListPriority::isNegativePriority(const CodecListPriority::Item& item) bool CodecListPriority::isNegativePriority(const CodecListPriority::Item& item)
{ {
@ -224,7 +461,7 @@ void CodecListPriority::setupFrom(PVariantMap vmap)
{ {
Item item; Item item;
item.mCodecIndex = i; item.mCodecIndex = i;
item.mPriority = vmap->exists(i) ? vmap->at(i).asInt() : -1; item.mPriority = vmap->exists(i) ? vmap->at(i).asInt() : 1000; // Non listed codecs will get lower priority
mPriorityList.push_back(item); mPriorityList.push_back(item);
} }

View File

@ -6,13 +6,14 @@
#ifndef __MT_CODEC_LIST_H #ifndef __MT_CODEC_LIST_H
#define __MT_CODEC_LIST_H #define __MT_CODEC_LIST_H
#include "../config.h" #include "../engine_config.h"
#if defined(USE_RESIP_INTEGRATION)
#include "resiprocate/resip/stack/SdpContents.hxx" #include "resiprocate/resip/stack/SdpContents.hxx"
#endif
#include "MT_Codec.h" #include "MT_Codec.h"
#include <vector> #include <vector>
#include <set> #include <set>
#include <list>
#include "../helper/HL_VariantMap.h" #include "../helper/HL_VariantMap.h"
#define ALL_CODECS_STRING "OPUS,ISAC,ILBC,PCMU,PCMA,G722,GSM" #define ALL_CODECS_STRING "OPUS,ISAC,ILBC,PCMU,PCMA,G722,GSM"
@ -28,22 +29,22 @@ public:
bool mSkipDecode = false; bool mSkipDecode = false;
// AMR payload types // AMR payload types
std::set<int> mAmrWbPayloadType = { MT_AMRWB_PAYLOADTYPE }; std::set<int64_t> mAmrWbPayloadType = { };
std::set<int> mAmrNbPayloadType = { MT_AMRNB_PAYLOADTYPE }; std::set<int64_t> mAmrNbPayloadType = { };
std::set<int> mAmrWbOctetPayloadType = { MT_AMRWB_OCTET_PAYLOADTYPE }; std::set<int64_t> mAmrWbOctetPayloadType = { };
std::set<int> mAmrNbOctetPayloadType = { MT_AMRNB_OCTET_PAYLOADTYPE }; std::set<int64_t> mAmrNbOctetPayloadType = { };
bool isAmrWb(int ptype) const { return mAmrWbOctetPayloadType.count(ptype) > 0 || mAmrWbPayloadType.count(ptype) > 0; } bool isAmrWb(int ptype) const { return mAmrWbOctetPayloadType.count(ptype) > 0 || mAmrWbPayloadType.count(ptype) > 0; }
bool isAmrNb(int ptype) const { return mAmrNbOctetPayloadType.count(ptype) > 0 || mAmrNbPayloadType.count(ptype) > 0; } bool isAmrNb(int ptype) const { return mAmrNbOctetPayloadType.count(ptype) > 0 || mAmrNbPayloadType.count(ptype) > 0; }
int mIsac16KPayloadType = MT_ISAC16K_PAYLOADTYPE; int mIsac16KPayloadType = -1;
int mIsac32KPayloadType = MT_ISAC32K_PAYLOADTYPE; int mIsac32KPayloadType = -1;
int mIlbc20PayloadType = MT_ILBC20_PAYLOADTYPE; int mIlbc20PayloadType = -1;
int mIlbc30PayloadType = MT_ILBC30_PAYLOADTYPE; int mIlbc30PayloadType = -1;
int mGsmFrPayloadType = 3; // GSM is codec with fixed payload type. But sometimes it has to be overwritten. int mGsmFrPayloadType = -1; // GSM is codec with fixed payload type. But sometimes it has to be overwritten.
int mGsmFrPayloadLength = 33; // Expected GSM payload length int mGsmFrPayloadLength = 33; // Expected GSM payload length
int mGsmHrPayloadType = MT_GSMHR_PAYLOADTYPE; int mGsmHrPayloadType = -1;
int mGsmEfrPayloadType = MT_GSMEFR_PAYLOADTYPE; int mGsmEfrPayloadType = -1;
struct EvsSpec struct EvsSpec
{ {
@ -56,7 +57,7 @@ public:
Bandwidth_FB Bandwidth_FB
}; };
Bandwidth mBandwidth = Bandwidth_NB; Bandwidth mBandwidth = Bandwidth_FB;
enum Encoding enum Encoding
{ {
@ -67,6 +68,10 @@ public:
Encoding mEncodingType = Encoding_MIME; Encoding mEncodingType = Encoding_MIME;
bool isValid() const; bool isValid() const;
static EvsSpec parse(const std::string& spec); static EvsSpec parse(const std::string& spec);
bool operator == (const EvsSpec& rhs) const { return std::tie(mPayloadType, mBandwidth, mEncodingType) == std::tie(rhs.mPayloadType, rhs.mBandwidth, rhs.mEncodingType);}
bool operator != (const EvsSpec& rhs) const { return ! (operator ==) (rhs);}
}; };
std::vector<EvsSpec> mEvsSpec; std::vector<EvsSpec> mEvsSpec;
@ -77,26 +82,52 @@ public:
int mRate = -1; int mRate = -1;
int mChannels = -1; int mChannels = -1;
OpusSpec(int ptype = -1, int rate = -1, int channels = -1)
:mPayloadType(ptype), mRate(rate), mChannels(channels)
{}
bool isValid() const; bool isValid() const;
bool operator == (const OpusSpec& rhs) const { return std::tie(mPayloadType, mRate, mChannels) == std::tie(rhs.mPayloadType, rhs.mRate, rhs.mChannels);}
bool operator != (const OpusSpec& rhs) const { return ! (operator ==) (rhs);}
static OpusSpec parse(const std::string& spec); static OpusSpec parse(const std::string& spec);
}; };
std::vector<OpusSpec> mOpusSpec; std::vector<OpusSpec> mOpusSpec;
// Payload type
bool contains(int ptype) const;
// Textual representation - used in logging
std::string toString() const;
void clear();
static Settings DefaultSettings; static Settings DefaultSettings;
static Settings parseSdp(const std::list<resip::Codec>& codeclist);
bool operator == (const Settings& rhs) const;
}; };
CodecList(const Settings& settings); CodecList(const Settings& settings);
~CodecList(); ~CodecList();
void setSettings(const Settings& settings)
{
init(settings);
}
int count() const; int count() const;
Codec::Factory& codecAt(int index) const; Codec::Factory& codecAt(int index) const;
int findCodec(const std::string& name) const; int findCodec(const std::string& name) const;
void fillCodecMap(CodecMap& cm); void fillCodecMap(CodecMap& cm);
PCodec createCodecByPayloadType(int payloadType);
void clear();
protected: protected:
typedef std::vector<Codec::Factory*> FactoryList; typedef std::vector<std::shared_ptr<Codec::Factory>> FactoryList;
FactoryList mFactoryList; FactoryList mFactoryList;
Settings mSettings; Settings mSettings;
void init(const Settings& settings);
}; };
class CodecListPriority class CodecListPriority

View File

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h" #include "../engine_config.h"
#include "MT_Dtmf.h" #include "MT_Dtmf.h"

View File

@ -6,7 +6,7 @@
#ifndef MT_DTMF #ifndef MT_DTMF
#define MT_DTMF #define MT_DTMF
#include "../config.h" #include "../engine_config.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include "../helper/HL_ByteBuffer.h" #include "../helper/HL_ByteBuffer.h"

Some files were not shown because too many files have changed in this diff Show More