Compare commits

..

No commits in common. "master" and "ci" have entirely different histories.
master ... ci

5192 changed files with 898435 additions and 913632 deletions

8
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,8 @@
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
View File

@ -1,12 +0,0 @@
[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

View File

@ -1,373 +0,0 @@
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.

View File

@ -1,141 +0,0 @@
# 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.

View File

@ -1,44 +0,0 @@
#!/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()

View File

@ -1,15 +0,0 @@
#!/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

View File

@ -1,37 +0,0 @@
#!/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}')

View File

@ -1,23 +0,0 @@
#!/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,361 +1,198 @@
cmake_minimum_required(VERSION 3.20)
project(rtphone)
# Rely on C++ 20
set (CMAKE_CXX_STANDARD 20)
cmake_minimum_required(VERSION 3.0)
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 (L libs)
set (E engine)
set (rtphone_libs libs)
set (rtphone_engine engine)
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
option (USE_EVS_CODEC "Use EVS codec." ON)
option (USE_OPUS_CODEC "Use Opus codec." ON)
option (USE_MUSL "Build with MUSL library" OFF)
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." OFF)
option (USE_EVS_CODEC "Use EVS codec." OFF)
option (USE_SEVANA_LIB "Build with Sevana libraries" OFF)
# PIC code by default
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}")
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.1/include)
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.0/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*")
set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
set (TARGET_WIN ON)
add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
endif()
# Linux-specific definitions
if (CMAKE_SYSTEM MATCHES "Linux*")
set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H)
set (TARGET_LINUX ON)
set (LIBS_STATIC ${LIBS_STATIC} dl)
add_definitions (-DTARGET_LINUX)
endif()
# macOS-specific definitions
if (CMAKE_SYSTEM MATCHES "Darwin*")
set (DEFINES ${DEFINES} -DTARGET_OSX)
set (TARGET_OSX ON)
set (LIBS_STATIC ${LIBS_STATIC} dl)
add_definitions (-DTARGET_OSX)
endif()
#
if (CMAKE_SYSTEM MATCHES "Android")
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)
if (USE_SEVANA_LIB)
add_definitions( -DUSE_AQUA_LIBRARY -DUSE_PVQA_LIBRARY)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include)
endif()
set (RTPHONE_SOURCES
${E}/engine_config.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}/agent/Agent_Impl.cpp
${E}/agent/Agent_Impl.h
${E}/agent/Agent_AudioManager.cpp
${E}/agent/Agent_AudioManager.h
${E}/endpoint/EP_Account.cpp
${E}/endpoint/EP_Account.h
${E}/endpoint/EP_AudioProvider.cpp
${E}/endpoint/EP_AudioProvider.h
${E}/endpoint/EP_DataProvider.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
${rtphone_engine}/media/MT_Statistics.cpp
${rtphone_engine}/media/MT_WebRtc.cpp
${rtphone_engine}/media/MT_Stream.cpp
${rtphone_engine}/media/MT_SrtpHelper.cpp
${rtphone_engine}/media/MT_SingleAudioStream.cpp
${rtphone_engine}/media/MT_NativeRtpSender.cpp
${rtphone_engine}/media/MT_Dtmf.cpp
${rtphone_engine}/media/MT_CodecList.cpp
${rtphone_engine}/media/MT_Codec.cpp
${rtphone_engine}/media/MT_Box.cpp
${rtphone_engine}/media/MT_AudioStream.cpp
${rtphone_engine}/media/MT_AudioReceiver.cpp
${rtphone_engine}/media/MT_AudioCodec.cpp
${rtphone_engine}/media/MT_AmrCodec.cpp
${rtphone_engine}/media/MT_EvsCodec.cpp
${rtphone_engine}/media/MT_CngHelper.cpp
${rtphone_engine}/agent/Agent_Impl.cpp
${rtphone_engine}/agent/Agent_AudioManager.cpp
${rtphone_engine}/endpoint/EP_Account.cpp
${rtphone_engine}/endpoint/EP_AudioProvider.cpp
${rtphone_engine}/endpoint/EP_DataProvider.cpp
${rtphone_engine}/endpoint/EP_Engine.cpp
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
${rtphone_engine}/endpoint/EP_Observer.cpp
${rtphone_engine}/endpoint/EP_Session.cpp
)
if (USE_OPUS_CODEC)
set (DEFINES ${DEFINES} -DUSE_OPUS_CODEC)
endif()
set (RTPHONE_HEADERS
${rtphone_engine}/media/MT_Statistics.h
${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})
add_library (rtphone STATIC ${RTPHONE_SOURCES} ${RTPHONE_HEADERS})
add_subdirectory(${L}/resiprocate)
add_subdirectory(${L}/jrtplib/src)
add_subdirectory(${L}/libg729)
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)
if (USE_EVS_CODEC)
add_subdirectory(${L}/libevs)
endif()
set (LIBS ice_stack jrtplib g729_codec gsm_codec
gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp
uuid)
add_subdirectory(${L}/libgsm)
add_subdirectory(${L}/gsmhr)
add_subdirectory(${L}/g722)
add_subdirectory(${L}/speexdsp)
add_subdirectory(${L}/libsrtp)
add_subdirectory(${L}/webrtc)
add_subdirectory(${L}/opus)
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
if (CMAKE_SYSTEM MATCHES "Win*")
set (LIBS ${LIBS} )
else ()
set (LIBS ${LIBS} dl uuid)
endif ()
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()
set (LIBS ${LIBS})
endif (USE_AMR_CODEC)
if (USE_EVS_CODEC)
message("Media: EVS codec will be included.")
set (DEFINES ${DEFINES} -DUSE_EVS_CODEC)
set (LIBS_STATIC ${LIBS_STATIC} evs_codec)
add_definitions(-DUSE_EVS_CODEC)
endif()
target_compile_definitions(rtphone PUBLIC ${DEFINES})
target_link_libraries(rtphone
ice_stack jrtplib g729_codec gsm_codec
gsmhr_codec g722_codec srtp resiprocate
helper_lib
audio_lib
webrtc
speexdsp
# opus
uuid
${OPENSSL_SSL}
${OPENSSL_CRYPTO}
${LIBS})
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
PUBLIC
@ -363,18 +200,15 @@ target_include_directories(rtphone
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
${CMAKE_CURRENT_SOURCE_DIR}/libs
${LIB_PLATFORM}/opus/include
${E}/helper
${E}/audio
${E}/media
${L}
${L}/ice
PRIVATE
${L}/libevs/lib_com
${L}/libevs/lib_enc
${L}/libevs/lib_dec
${L}/speex/include
${L}/libs/json
${CMAKE_CURRENT_SOURCE_DIR}/libs/
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec
${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
)
# For MSVC static builds
# set_property(TARGET rtphone PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
configure_msvc_runtime()

View File

@ -1,12 +1,12 @@
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
/* 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/. */
#include "Agent_AudioManager.h"
#include "../engine/audio/Audio_WavFile.h"
#include "../engine/helper/HL_String.h"
#include "../engine/audio/Audio_Null.h"
#include "HL_String.h"
#if defined(TARGET_ANDROID)
# include "../engine/audio/Audio_Android.h"
@ -15,182 +15,168 @@
#define LOG_SUBSYSTEM "AudioManager"
// ---------------- AudioManager -------------
//static AudioManager GAudioManager;
AudioManager::AudioManager()
:mTerminal(nullptr), mAudioMonitoring(nullptr)
:mTerminal(nullptr)
{
mPlayer.setDelegate(this);
mPlayer.setDelegate(this);
}
AudioManager::~AudioManager()
{
// stop();
//stop();
}
AudioManager& AudioManager::instance()
{
static std::shared_ptr<AudioManager> GAudioManager;
if (!GAudioManager)
GAudioManager = std::make_shared<AudioManager>();
return *GAudioManager;
static std::shared_ptr<AudioManager> GAudioManager;
if (!GAudioManager)
GAudioManager = std::make_shared<AudioManager>();
return *GAudioManager;
}
void AudioManager::setTerminal(MT::Terminal* terminal)
{
mTerminal = terminal;
mTerminal = terminal;
}
MT::Terminal* AudioManager::terminal()
{
return mTerminal;
}
void AudioManager::setAudioMonitoring(Audio::DataConnection* monitoring)
{
mAudioMonitoring = monitoring;
}
Audio::DataConnection* AudioManager::audioMonitoring()
{
return mAudioMonitoring;
return mTerminal;
}
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
void AudioManager::start(int usageId)
{
assert(mTerminal);
LOCK_MANAGER;
assert(mTerminal);
LOCK_MANAGER;
ICELogInfo(<< "Start main audio with usage id " << usageId);
ICELogInfo(<< "Start main audio with usage id " << usageId);
if (mUsage.obtain(usageId) > 1)
return;
if (mUsage.obtain(usageId) > 1)
return;
if (Audio::OsEngine::instance())
Audio::OsEngine::instance()->open();
if (Audio::OsEngine::instance())
Audio::OsEngine::instance()->open();
if (!mAudioInput || !mAudioOutput)
{
// Disable AEC for now - because PVQA conflicts with speex AEC.
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
if (!mTerminal->audio())
mTerminal->setAudio(std::make_shared<Audio::DevicePair>(false, true));
if (!mAudioInput || !mAudioOutput)
{
// Disable AEC for now - because PVQA conflicts with speex AEC.
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
if (!mTerminal->audio())
{
auto audio = std::make_shared<Audio::DevicePair>();
audio->setAgc(true);
audio->setAec(false);
audio->setMonitoring(mAudioMonitoring);
if (!mAudioInput)
{
enumerator->open(Audio::myMicrophone);
int inputIndex = enumerator->indexOfDefaultDevice();
mTerminal->setAudio(audio);
}
// Construct and set to terminal's audio pair input device
if (usageId != atNull)
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
else
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
if (!mAudioInput)
{
enumerator->open(Audio::myMicrophone);
int inputIndex = enumerator->indexOfDefaultDevice();
mTerminal->audio()->setInput(mAudioInput);
}
// Construct and set to terminal's audio pair input device
if (usageId != atNull)
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
else
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
if (!mAudioOutput)
{
Audio::Enumerator *enumerator = Audio::Enumerator::make(usageId == atNull);
enumerator->open(Audio::mySpeaker);
int outputIndex = enumerator->indexOfDefaultDevice();
mTerminal->audio()->setInput(mAudioInput);
}
// Construct and set terminal's audio pair output device
if (usageId != atNull)
{
if (outputIndex >= enumerator->count())
outputIndex = 0;
if (!mAudioOutput)
{
Audio::Enumerator *enumerator = Audio::Enumerator::make(usageId == atNull);
enumerator->open(Audio::mySpeaker);
int outputIndex = enumerator->indexOfDefaultDevice();
mAudioOutput = Audio::POutputDevice(
Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
}
else
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
// Construct and set terminal's audio pair output device
if (usageId != atNull)
{
if (outputIndex >= enumerator->count())
outputIndex = 0;
mTerminal->audio()->setOutput(mAudioOutput);
}
}
mAudioOutput = Audio::POutputDevice(
Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
}
else
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
mTerminal->audio()->setOutput(mAudioOutput);
}
}
// Open audio
if (mAudioInput)
mAudioInput->open();
if (mAudioOutput)
mAudioOutput->open();
// Open audio
if (mAudioInput)
mAudioInput->open();
if (mAudioOutput)
mAudioOutput->open();
}
void AudioManager::close()
{
mUsage.clear();
if (mAudioInput)
{
mAudioInput->close();
mAudioInput.reset();
}
mUsage.clear();
if (mAudioInput)
{
mAudioInput->close();
mAudioInput.reset();
}
if (mAudioOutput)
{
mAudioOutput->close();
mAudioOutput.reset();
}
mPlayer.setOutput(Audio::POutputDevice());
if (mAudioOutput)
{
mAudioOutput->close();
mAudioOutput.reset();
}
mPlayer.setOutput(Audio::POutputDevice());
}
void AudioManager::stop(int usageId)
{
LOCK_MANAGER;
LOCK_MANAGER;
ICELogInfo( << "Stop main audio with usage id " << usageId);
if (mTerminal)
{
if (mTerminal->audio())
mTerminal->audio()->player().release(usageId);
}
ICELogInfo( << "Stop main audio with usage id " << usageId);
if (mTerminal)
{
if (mTerminal->audio())
mTerminal->audio()->player().release(usageId);
}
if (!mUsage.release(usageId))
{
close();
if (!mUsage.release(usageId))
{
close();
// Reset device pair on terminal side
mTerminal->setAudio(Audio::PDevicePair());
// Reset device pair on terminal side
mTerminal->setAudio(Audio::PDevicePair());
if (Audio::OsEngine::instance())
Audio::OsEngine::instance()->close();
}
if (Audio::OsEngine::instance())
Audio::OsEngine::instance()->close();
}
}
void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit)
{
// Check if file exists
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
// Check if file exists
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
#ifdef TARGET_WIN
r->open(strx::makeTstring(path));
r->open(StringHelper::makeTstring(path));
#else
r->open(path);
r->open(path);
#endif
if (!r->isOpened())
{
ICELogError(<< "Cannot open file to play");
return;
}
if (!r->isOpened())
{
ICELogError(<< "Cannot open file to play");
return;
}
// Delegate processing to existing audio device pair manager
mTerminal->audio()->player().add(usageId, r, lm == lmLoopAudio, timelimit);
start(usageId);
// Delegate processing to existing audio device pair manager
mTerminal->audio()->player().add(usageId, r, lm == lmLoopAudio, timelimit);
start(usageId);
}
void AudioManager::stopPlayFile(int usageId)
{
stop(usageId);
mPlayer.release(usageId);
stop(usageId);
mPlayer.release(usageId);
}
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item)
@ -199,9 +185,9 @@ void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item)
void AudioManager::process()
{
mPlayer.releasePlayed();
std::vector<int> ids;
mTerminal->audio()->player().retrieveUsageIds(ids);
for (unsigned i=0; i<ids.size(); i++)
stop(ids[i]);
mPlayer.releasePlayed();
std::vector<int> ids;
mTerminal->audio()->player().retrieveUsageIds(ids);
for (unsigned i=0; i<ids.size(); i++)
stop(ids[i]);
}

View File

@ -8,25 +8,28 @@
#include "../engine/audio/Audio_Interface.h"
#include "../engine/audio/Audio_Player.h"
#include "../engine/endpoint/EP_Engine.h"
#include "../engine/media/MT_Box.h"
#include "../engine/helper/HL_Log.h"
#include "../engine/helper/HL_Sync.h"
enum
{
AudioPrefix_Ring = 1,
AudioPrefix_Zero,
AudioPrefix_One,
AudioPrefix_Two,
AudioPrefix_Three,
AudioPrefix_Four,
AudioPrefix_Five,
AudioPrefix_Six,
AudioPrefix_Seven,
AudioPrefix_Eight,
AudioPrefix_Nine,
AudioPrefix_Asterisk,
AudioPrefix_Diez
AudioPrefix_Ring = 1,
AudioPrefix_Zero,
AudioPrefix_One,
AudioPrefix_Two,
AudioPrefix_Three,
AudioPrefix_Four,
AudioPrefix_Five,
AudioPrefix_Six,
AudioPrefix_Seven,
AudioPrefix_Eight,
AudioPrefix_Nine,
AudioPrefix_Asterisk,
AudioPrefix_Diez
};
#define AudioSessionCoeff 64
@ -34,56 +37,51 @@ enum
class AudioManager: public Audio::Player::EndOfAudioDelegate
{
public:
AudioManager();
virtual ~AudioManager();
AudioManager();
virtual ~AudioManager();
static AudioManager& instance();
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
void close();
static AudioManager& instance();
// Terminal and settings must be available for AudioManager
void setTerminal(MT::Terminal* terminal);
MT::Terminal* terminal();
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
void close();
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
void start(int usageId);
void stop(int usageId);
enum AudioTarget
{
atNull,
atReceiver,
atRinger
};
// Terminal and settings must be available for AudioManager
void setTerminal(MT::Terminal* terminal);
MT::Terminal* terminal();
enum LoopMode
{
lmLoopAudio,
lmNoloop
};
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
void start(int usageId);
void stop(int usageId);
enum AudioTarget
{
atNull,
atReceiver,
atRinger
};
enum LoopMode
{
lmLoopAudio,
lmNoloop
};
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
void stopPlayFile(int usageId);
void onFilePlayed(Audio::Player::PlaylistItem& item);
// Must be called from main loop to release used audio devices
void process();
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
void stopPlayFile(int usageId);
void onFilePlayed(Audio::Player::PlaylistItem& item);
// Must be called from main loop to release used audio devices
void process();
protected:
Audio::PInputDevice mAudioInput;
Audio::POutputDevice mAudioOutput;
Audio::Player mPlayer;
MT::Terminal* mTerminal;
Audio::DataConnection* mAudioMonitoring;
Audio::PInputDevice mAudioInput;
Audio::POutputDevice mAudioOutput;
Audio::Player mPlayer;
MT::Terminal* mTerminal;
std::map<int, int> UsageMap;
UsageCounter mUsage;
std::mutex mGuard;
std::map<int, int> UsageMap;
UsageCounter mUsage;
std::mutex mGuard;
};
#endif

View File

@ -4,9 +4,15 @@
#include "helper/HL_StreamState.h"
#include "helper/HL_VariantMap.h"
#include "helper/HL_CsvReader.h"
#include "helper/HL_Base64.h"
#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_SessionNotFound = "session not found";
@ -32,24 +38,6 @@ AgentImpl::~AgentImpl()
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()
{
while (!mShutdown)
@ -67,10 +55,10 @@ std::string AgentImpl::command(const std::string& command)
if (command.empty())
return "";
JsonCpp::Value d, answer;
Json::Value d, answer;
try
{
JsonCpp::Reader r;
Json::Reader r;
if (!r.parse(command, d))
return "";
@ -150,9 +138,7 @@ std::string AgentImpl::command(const std::string& command)
{
answer["status"] = e.what();
}
std::string result = answer.toStyledString();
return result;
return answer.toStyledString();
}
bool AgentImpl::waitForData(int /*milliseconds*/)
@ -165,18 +151,29 @@ std::string AgentImpl::read()
return "";
}
void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
void AgentImpl::processConfig(Json::Value &d, Json::Value &answer)
{
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();
config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
config()[CONFIG_TRANSPORT] = (transport == "any") ? 0 : (transport == "udp" ? 1 : (transport == "tcp" ? 2 : 3));
config()[CONFIG_IPV4] = d["ipv4"].asBool();
config()[CONFIG_IPV6] = d["ipv6"].asBool();
if (transport == "tls")
config()[CONFIG_SIPS] = true;
// Log file
std::string logfile = d["logfile"].asString();
ice::Logger& logger = ice::GLogger;
@ -188,13 +185,10 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
mUseNativeAudio = d["nativeaudio"].asBool();
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;
}
void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
void AgentImpl::processStart(Json::Value& /*request*/, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
if (mThread)
@ -203,9 +197,6 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
return; // Started already
}
// Process config (can be sent via start command as well)
// processConfig(request, answer);
// Start socket thread
SocketHeap::instance().start();
@ -217,23 +208,18 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
PVariantMap priorityConfig = std::make_shared<VariantMap>();
MT::CodecList& cl = mTerminal->codeclist();
for (int i=0; i<cl.count(); i++)
priorityConfig->at(i) = i;
// Disable dynamic payload codec types - commented for now
/*if (cl.codecAt(i).payloadType() < 96)
if (cl.codecAt(i).payloadType() < 96)
priorityConfig->at(i) = i;
else
priorityConfig->at(i) = -1;*/
priorityConfig->at(i) = -1;
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
// Enable audio
mAudioManager = std::make_shared<AudioManager>();
mAudioManager->setTerminal(mTerminal.get());
if (mAudioMonitoring)
mAudioManager->setAudioMonitoring(mAudioMonitoring);
// Do not start audio manager here. Start right before call.
// Do not start here. Start right before call.
// Initialize endpoint
start();
@ -244,13 +230,13 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
answer["status"] = Status_Ok;
}
void AgentImpl::processStop(JsonCpp::Value& /*request*/, JsonCpp::Value& answer)
void AgentImpl::processStop(Json::Value& /*request*/, Json::Value& answer)
{
stopAgentAndThread();
answer["status"] = Status_Ok;
}
void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
void AgentImpl::processCreateAccount(Json::Value &d, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
PVariantMap c = std::make_shared<VariantMap>();
@ -260,7 +246,7 @@ void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
auto nameAndPort = strx::parseHost(d["stun_server"].asString(), 3478);
auto nameAndPort = StringHelper::parseHost(d["stun_server"].asString(), 3478);
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
@ -270,7 +256,7 @@ void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
answer["status"] = Status_Ok;
}
void AgentImpl::processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer)
void AgentImpl::processStartAccount(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map
@ -284,7 +270,7 @@ void AgentImpl::processStartAccount(JsonCpp::Value& request, JsonCpp::Value& ans
answer["status"] = Status_AccountNotFound;
}
void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Value &answer)
void AgentImpl::processSetUserInfoToAccount(Json::Value &request, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map
@ -292,7 +278,7 @@ void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Va
if (accountIter != mAccountMap.end())
{
Account::UserInfo info;
JsonCpp::Value& arg = request["userinfo"];
Json::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys)
info[k] = arg[k].asString();
@ -304,7 +290,7 @@ void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Va
answer["status"] = Status_AccountNotFound;
}
void AgentImpl::processCreateSession(JsonCpp::Value &request, JsonCpp::Value &answer)
void AgentImpl::processCreateSession(Json::Value &request, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto accountIter = mAccountMap.find(request["account_id"].asInt());
@ -319,7 +305,7 @@ void AgentImpl::processCreateSession(JsonCpp::Value &request, JsonCpp::Value &an
answer["status"] = Status_AccountNotFound;
}
void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer)
void AgentImpl::processStartSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -342,32 +328,29 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
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();
sevana::aqua::config config = {
{"avlp", "off"},
{"smtnrm", "off"},
{"decor", "off"},
{"mprio", "off"},
{"npnt", "auto"},
{"voip", "on"},
{"enorm", "rms"},
{"g711", "off"},
{"spfrcor", "on"},
{"grad", "off"},
{"tmc", "on"},
{"miter", "1"},
{ "ratem", "%m" },
{ "trim", "a 10" },
{ "avlp", "off" },
{ "decor", "off" },
{ "mprio", "off" },
{ "miter", "1" },
{ "enorm", "off" },
{ "voip", "on" },
{ "g711", "on" },
{ "spfrcor", "on" },
{ "grad", "off" },
{ "ratem", "%%m" },
{ "trim", "a 2" },
{ "output", "json" },
{ "fau", path_faults},
{ "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";
// if (temp_path.size())
// config += " -fau " + temp_path;
/*if (temp_path.size())
config += " -fau " + temp_path; */
auto qc = std::make_shared<sevana::aqua>();
if (!qc->is_open())
@ -380,13 +363,12 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
mAquaMap[sessionIter->first] = qc;
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
#endif
*/
// TODO: support SRTP via StreamState::Srtp option in audio provider state
// Get user headers
Session::UserHeaders info;
JsonCpp::Value& arg = request["userinfo"];
Json::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys)
info[k] = arg[k].asString();
@ -402,7 +384,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
}
}
void AgentImpl::processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer)
void AgentImpl::processStopSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -417,7 +399,7 @@ void AgentImpl::processStopSession(JsonCpp::Value& request, JsonCpp::Value& answ
answer["status"] = Status_SessionNotFound;
}
void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer)
void AgentImpl::processAcceptSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
@ -431,7 +413,7 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
// Get user headers
Session::UserHeaders info;
JsonCpp::Value& arg = request["userinfo"];
Json::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys)
info[k] = arg[k].asString();
@ -447,7 +429,7 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
}
void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer)
void AgentImpl::processDestroySession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -455,13 +437,13 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
auto sessionIter = mSessionMap.find(sessionId);
if (sessionIter != mSessionMap.end())
mSessionMap.erase(sessionIter);
//#if defined(USE_AQUA_LIBRARY)
// closeAqua(sessionId);
//#endif
#if defined(USE_AQUA_LIBRARY)
closeAqua(sessionId);
#endif
answer["status"] = Status_Ok;
}
void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &answer)
void AgentImpl::processWaitForEvent(Json::Value &request, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
@ -481,8 +463,43 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &ans
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;
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
int rowIndex = 1;
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);
int sessionId = request["session_id"].asInt();
@ -491,18 +508,26 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
{
PSession session = sessionIter->second;
VariantMap result;
session->getSessionInfo(Session::InfoOptions::Detailed,
bool includePvqa = request["include_pvqa"].asBool();
#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);
if (result.exists(SessionInfo_AudioCodec))
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
if (result.exists(SessionInfo_NetworkMos))
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))
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
if (result.exists(SessionInfo_DroppedRtp))
answer["rtp_dropped"] = result[SessionInfo_DroppedRtp].asInt();
if (result.exists(SessionInfo_SentRtp))
answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt();
if (result.exists(SessionInfo_ReceivedRtp))
@ -520,13 +545,73 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
if (result.exists(SessionInfo_RemotePeer))
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;
}
else
answer["status"] = Status_SessionNotFound;
}
void AgentImpl::processNetworkChanged(JsonCpp::Value& /*request*/, JsonCpp::Value& /*answer*/)
void AgentImpl::processNetworkChanged(Json::Value& /*request*/, Json::Value& /*answer*/)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
}
@ -534,28 +619,30 @@ void AgentImpl::processNetworkChanged(JsonCpp::Value& /*request*/, JsonCpp::Valu
const std::string BeginCertificate = "-----BEGIN CERTIFICATE-----";
const std::string EndCertificate = "-----END CERTIFICATE-----";
void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer)
void AgentImpl::processAddRootCert(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
std::string pem = request["cert"].asString();
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);
if (pb != std::string::npos && pe != std::string::npos && pe > pb) {
std::string cert = pem.substr(pb, pe - pb + EndCertificate.size());
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe);
pb != std::string::npos && pe != std::string::npos;
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()));
pb = ++pe;
}
// Delete processed part
pem.erase(0, pe + EndCertificate.size());
}
answer["status"] = Status_Ok;
}
void AgentImpl::processLogMessage(JsonCpp::Value &request, JsonCpp::Value &answer)
void AgentImpl::processLogMessage(Json::Value &request, Json::Value &answer)
{
int level = request["level"].asInt();
std::string message = request["message"].asString();
@ -605,7 +692,7 @@ void AgentImpl::stopAgentAndThread()
SocketHeap::instance().stop();
}
void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer)
void AgentImpl::processUseStreamForSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
SessionMap::iterator sessionIter = mSessionMap.find(request["session_id"].asInt());
@ -637,7 +724,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
else
{
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
if (!reader->open(strx::makeTstring(path)))
if (!reader->open(StringHelper::makeTstring(path)))
answer["status"] = Status_FailedToOpenFile;
else
{
@ -647,52 +734,63 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
}
}
else
if (actionText == "write")
{
if (path.empty())
if (actionText == "write")
{
// Turn off recording from the stream
prov->writeFile(Audio::PWavFileWriter(), direction);
answer["status"] = Status_Ok;
}
else
{
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
answer["status"] = Status_FailedToOpenFile;
else
if (path.empty())
{
prov->writeFile(writer, direction);
// Turn off recording from the stream
prov->writeFile(Audio::PWavFileWriter(), direction);
answer["status"] = Status_Ok;
}
else
{
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
if (!writer->open(StringHelper::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
answer["status"] = Status_FailedToOpenFile;
else
{
prov->writeFile(writer, direction);
answer["status"] = Status_Ok;
}
}
}
}
else
if (actionText == "mirror")
{
prov->setupMirror(request["enable"].asBool());
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_AccountNotFound;
else
if (actionText == "mirror")
{
prov->setupMirror(request["enable"].asBool());
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_AccountNotFound;
}
else
answer["status"] = Status_NoMediaAction;
}
}
#if defined(USE_AQUA_LIBRARY)
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
{
/*switch (direction)
/* if (mIncomingAudioDump && direction == MT::Stream::MediaDirection::Incoming)
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::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
}*/
}
}
#endif
// Called on new incoming session; providers shoukld
#define EVENT_WITH_NAME(X) JsonCpp::Value v; v["event_name"] = X;
#define EVENT_WITH_NAME(X) Json::Value v; v["event_name"] = X;
PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
{
@ -837,7 +935,7 @@ void AgentImpl::onSipConnectionFailed()
addEvent(v);
}
void AgentImpl::addEvent(const JsonCpp::Value& v)
void AgentImpl::addEvent(const Json::Value& v)
{
std::unique_lock<std::mutex> lock(mEventListMutex);
mEventList.push_back(v);
@ -845,12 +943,12 @@ void AgentImpl::addEvent(const JsonCpp::Value& v)
}
#if defined(USE_AQUA_LIBRARY)
/*void AgentImpl::closeAqua(int sessionId)
void AgentImpl::closeAqua(int sessionId)
{
auto aquaIter = mAquaMap.find(sessionId);
if (aquaIter != mAquaMap.end()) {
aquaIter->second->close();
mAquaMap.erase(aquaIter);
}
}*/
#endif
}
#endif

View File

@ -13,15 +13,24 @@
#include "Agent_AudioManager.h"
#include <mutex>
#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, public MT::Stream::MediaObserver
class AgentImpl: public UserAgent
#if defined(USE_AQUA_LIBRARY)
, public MT::Stream::MediaObserver
#endif
{
protected:
std::recursive_mutex mAgentMutex;
std::mutex mEventListMutex;
std::condition_variable mEventListChangeCondVar;
std::vector<JsonCpp::Value> mEventList;
std::vector<Json::Value> mEventList;
bool mUseNativeAudio = false;
typedef std::map<int, PAccount> AccountMap;
@ -30,32 +39,39 @@ protected:
typedef std::map<int, PSession> SessionMap;
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;
volatile bool mShutdown;
std::shared_ptr<MT::Terminal> mTerminal;
std::shared_ptr<AudioManager> mAudioManager;
Audio::DataConnection* mAudioMonitoring = nullptr;
//Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump;
void run();
void addEvent(const JsonCpp::Value& v);
void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStart(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStop(JsonCpp::Value& request, JsonCpp::Value& answer);
void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer);
void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer);
void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer);
void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer);
void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer);
void addEvent(const Json::Value& v);
void processConfig(Json::Value& request, Json::Value& answer);
void processStart(Json::Value& request, Json::Value& answer);
void processStop(Json::Value& request, Json::Value& answer);
void processCreateAccount(Json::Value& request, Json::Value& answer);
void processStartAccount(Json::Value& request, Json::Value& answer);
void processSetUserInfoToAccount(Json::Value& request, Json::Value& answer);
void processCreateSession(Json::Value& request, Json::Value& answer);
void processStartSession(Json::Value& request, Json::Value& answer);
void processStopSession(Json::Value& request, Json::Value& answer);
void processAcceptSession(Json::Value& request, Json::Value& answer);
void processDestroySession(Json::Value& request, Json::Value& answer);
void processWaitForEvent(Json::Value& request, Json::Value& answer);
void processGetMediaStats(Json::Value& request, Json::Value& answer);
void processUseStreamForSession(Json::Value& request, Json::Value& answer);
void processNetworkChanged(Json::Value& request, Json::Value& answer);
void processAddRootCert(Json::Value& request, Json::Value& answer);
void processLogMessage(Json::Value& request, Json::Value& answer);
void stopAgentAndThread();
public:
@ -66,12 +82,6 @@ public:
bool waitForData(int milliseconds);
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
// Called on new incoming session; providers shoukld
PDataProvider onProviderNeeded(const std::string& name) override;
@ -121,8 +131,10 @@ public:
// Called when problem with SIP connection(s) detected
void onSipConnectionFailed() override;
#if defined(USE_AQUA_LIBRARY)
// Called on incoming & outgoing audio for voice sessions
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
#endif
};
#endif

View File

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

View File

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

View File

@ -1,245 +0,0 @@
#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

@ -1,109 +0,0 @@
/* 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

@ -10,171 +10,163 @@ using namespace Audio;
DataWindow::DataWindow()
{
mFilled = 0;
mData = NULL;
mCapacity = 0;
mFilled = 0;
mData = NULL;
mCapacity = 0;
}
DataWindow::~DataWindow()
{
if (mData)
free(mData);
if (mData)
free(mData);
}
void DataWindow::setCapacity(int capacity)
{
Lock l(mMutex);
int tail = capacity - mCapacity;
mData = (char*)realloc(mData, capacity);
if (tail > 0)
memset(mData + mCapacity, 0, tail);
mCapacity = capacity;
Lock l(mMutex);
int tail = capacity - mCapacity;
mData = (char*)realloc(mData, capacity);
if (tail > 0)
memset(mData + mCapacity, 0, tail);
mCapacity = capacity;
}
void DataWindow::addZero(int length)
{
Lock l(mMutex);
Lock l(mMutex);
if (length > mCapacity)
length = mCapacity;
if (length > mCapacity)
length = mCapacity;
int avail = mCapacity - mFilled;
int avail = mCapacity - mFilled;
if (avail < length)
{
memmove(mData, mData + length - avail, mFilled - (length - avail));
mFilled -= length - avail;
}
memset(mData + mFilled, 0, length);
mFilled += length;
if (avail < length)
{
memmove(mData, mData + length - avail, mFilled - (length - avail));
mFilled -= length - avail;
}
memset(mData + mFilled, 0, length);
mFilled += length;
}
void DataWindow::add(const void* data, int length)
{
Lock l(mMutex);
Lock l(mMutex);
if (length > mCapacity)
{
data = (char*)data + length - mCapacity;
length = mCapacity;
}
if (length > mCapacity)
{
// Use latest bytes from data buffer in this case.
data = (char*)data + length - mCapacity;
length = mCapacity;
}
// Check how much free space we have
int avail = mCapacity - mFilled;
if (avail < length)
{
// Find the portion of data to move & save
int delta = length - avail;
// Move the data
if (mFilled - delta > 0)
memmove(mData, mData + delta, mFilled - delta);
mFilled -= delta;
}
memcpy(mData + mFilled, data, length);
mFilled += length;
int avail = mCapacity - mFilled;
if (avail < length)
{
memmove(mData, mData + length - avail, mFilled - (length - avail));
mFilled -= length - avail;
}
memcpy(mData + mFilled, data, length);
mFilled += length;
}
void DataWindow::add(short sample)
{
add(&sample, sizeof sample);
add(&sample, sizeof sample);
}
void DataWindow::erase(int length)
{
Lock l(mMutex);
if (length > mFilled)
length = mFilled;
if (length != mFilled)
memmove(mData, mData + length, mFilled - length);
mFilled -= length;
Lock l(mMutex);
if (length > mFilled)
length = mFilled;
if (length != mFilled)
memmove(mData, mData + length, mFilled - length);
mFilled -= length;
}
const char* DataWindow::data() const
{
return mData;
return mData;
}
char* DataWindow::mutableData()
{
return mData;
return mData;
}
void DataWindow::clear()
{
Lock l(mMutex);
mFilled = 0;
Lock l(mMutex);
mFilled = 0;
}
short DataWindow::shortAt(int index) const
{
Lock l(mMutex);
assert(index < mFilled / 2);
return ((short*)mData)[index];
Lock l(mMutex);
assert(index < mFilled / 2);
return ((short*)mData)[index];
}
void DataWindow::setShortAt(short value, int index)
{
Lock l(mMutex);
assert(index < mFilled / 2);
((short*)mData)[index] = value;
Lock l(mMutex);
assert(index < mFilled / 2);
((short*)mData)[index] = value;
}
int DataWindow::read(void* buffer, int length)
{
Lock l(mMutex);
if (length > mFilled)
length = mFilled;
if (length)
{
if (buffer)
memcpy(buffer, mData, length);
if (length < mFilled)
memmove(mData, mData+length, mFilled - length);
mFilled -= length;
}
return length;
Lock l(mMutex);
if (length > mFilled)
length = mFilled;
if (length)
{
if (buffer)
memcpy(buffer, mData, length);
if (length < mFilled)
memmove(mData, mData+length, mFilled - length);
mFilled -= length;
}
return length;
}
int DataWindow::filled() const
{
Lock l(mMutex);
return mFilled;
Lock l(mMutex);
return mFilled;
}
void DataWindow::setFilled(int filled)
{
Lock l(mMutex);
mFilled = filled;
Lock l(mMutex);
mFilled = filled;
}
int DataWindow::capacity() const
{
Lock l(mMutex);
return mCapacity;
Lock l(mMutex);
return mCapacity;
}
void DataWindow::zero(int length)
{
Lock l(mMutex);
assert(length <= mCapacity);
mFilled = length;
memset(mData, 0, mFilled);
Lock l(mMutex);
assert(length <= mCapacity);
mFilled = length;
memset(mData, 0, mFilled);
}
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
{
Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
dst.setCapacity(src.filled()*2);
short* input = (short*)src.mutableData();
short* output = (short*)dst.mutableData();
dst.setCapacity(src.filled()*2);
short* input = (short*)src.mutableData();
short* output = (short*)dst.mutableData();
for (int i=0; i<src.filled()/2; i++)
output[i*2] = output[i*2+1] = input[i];
dst.mFilled = src.filled() * 2;
for (int i=0; i<src.filled()/2; i++)
output[i*2] = output[i*2+1] = input[i];
dst.mFilled = src.filled() * 2;
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
/* 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/. */
@ -6,7 +6,7 @@
#ifndef __AUDIO_DSOUND_H
#define __AUDIO_DSOUND_H
#include "../engine_config.h"
#include "../config.h"
#include <winsock2.h>
#include <windows.h>

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
#define __AUDIO_INTERFACE_H
#include <string>
#include "../engine_config.h"
#include "../config.h"
#include "../helper/HL_Types.h"
#include "../helper/HL_VariantMap.h"
#include "../helper/HL_Pointer.h"
@ -57,27 +57,6 @@ namespace Audio
sprintf(buffer, "%dHz %dch", mRate, mChannels);
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

View File

@ -3,7 +3,7 @@
* 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 "../engine_config.h"
#include "../config.h"
#include "../helper/HL_Exception.h"
#include "../helper/HL_Log.h"
@ -216,52 +216,48 @@ void Mixer::mix()
channelList[activeCounter++] = &mChannelList[i];
// No active channels - nothing to mix - exit
if (!activeCounter)
if (!activeCounter)
{
// ICELogDebug(<< "No active channel");
//ICELogDebug(<< "No active channel");
return;
}
// Optimized versions for 1& 2 active channels
if (activeCounter == 1)
{
// Copy much samples as we have
{
// Copy much samples as we have
Stream& audio = *channelList[0];
// Copy the decoded data
mOutput.add(audio.data().data(), audio.data().filled());
// Erase copied audio samples
audio.data().erase(audio.data().filled());
mOutput.add(audio.data().data(), audio.data().filled());
audio.data().erase(audio.data().filled());
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
}
}
else
if (activeCounter == 2)
{
if (activeCounter == 2)
{
Stream& audio1 = *channelList[0];
Stream& audio2 = *channelList[1];
int filled1 = audio1.data().filled() / 2, filled2 = audio2.data().filled() / 2;
Stream& audio2 = *channelList[1];
int filled1 = audio1.data().filled() / 2, filled2 = audio2.data().filled() / 2;
int available = filled1 > filled2 ? filled1 : filled2;
// Find how much samples can be mixed
int filled = mOutput.filled() / 2;
// Find how much samples can be mixed
int filled = mOutput.filled() / 2;
int maxsize = mOutput.capacity() / 2;
if (maxsize - filled < available)
available = maxsize - filled;
if (maxsize - filled < available)
available = maxsize - filled;
short sample = 0;
for (int i=0; i<available; i++)
{
short sample = 0;
for (int i=0; i<available; i++)
{
short sample1 = filled1 > i ? audio1.data().shortAt(i) : 0;
short sample2 = filled2 > i ? audio2.data().shortAt(i) : 0;
sample = (abs(sample1) > abs(sample2)) ? sample1 : sample2;
mOutput.add(sample);
}
audio1.data().erase(available*2);
audio2.data().erase(available*2);
}
audio1.data().erase(available*2);
audio2.data().erase(available*2);
}
else
{
do

View File

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

View File

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

View File

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

View File

@ -1,18 +1,14 @@
/* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
/* Copyright(C) 2007-2014 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/. */
#include "Audio_Player.h"
#include "../helper/HL_Log.h"
#define LOG_SUBSYSTEM "Player"
using namespace Audio;
// -------------- Player -----------
Player::Player()
:mDelegate(nullptr), mPlayedTime(0)
:mDelegate(NULL), mPlayedTime(0)
{
}
@ -51,7 +47,7 @@ void Player::onMicData(const Format& f, const void* buffer, int length)
void Player::onSpkData(const Format& f, void* buffer, int length)
{
Lock l(mGuard);
// Fill buffer by zero if player owns dedicated device
if (mOutput)
memset(buffer, 0, length);
@ -103,7 +99,7 @@ void Player::onFilePlayed()
void Player::obtain(int usage)
{
Lock l(mGuard);
auto usageIter = mUsage.find(usage);
UsageMap::iterator usageIter = mUsage.find(usage);
if (usageIter == mUsage.end())
mUsage[usage] = 1;
else
@ -136,7 +132,7 @@ int Player::releasePlayed()
{
Lock l(mGuard);
int result = mFinishedUsages.size();
while (!mFinishedUsages.empty())
while (mFinishedUsages.size())
{
release(mFinishedUsages.front());
mFinishedUsages.erase(mFinishedUsages.begin());

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
/* Copyright(C) 2007-2014 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/. */
@ -8,7 +8,6 @@
#include "../helper/HL_Log.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Statistics.h"
#include "Audio_Interface.h"
#include <deque>
#include <map>
@ -49,18 +48,15 @@ namespace Audio
void onMicData(const Format& f, const void* buffer, int length);
void onSpkData(const Format& f, void* buffer, int length);
void onFilePlayed();
void scheduleRelease();
void obtain(int usageId);
public:
Player();
~Player();
void setDelegate(EndOfAudioDelegate* d);
EndOfAudioDelegate* getDelegate() const;
void setOutput(POutputDevice output);
POutputDevice getOutput() const;
void add(int usageId, PWavFileReader file, bool loop, int timelength);
void release(int usageId);
void clear();

View File

@ -3,7 +3,7 @@
* 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 "../engine_config.h"
#include "../config.h"
#include "Audio_Quality.h"
#include "../helper/HL_Exception.h"
#include "../helper/HL_Types.h"
@ -17,10 +17,7 @@
using namespace Audio;
#ifndef SHRT_MAX
# define SHRT_MAX 32767 /* maximum (signed) short value */
#endif
#define SHRT_MAX 32767 /* maximum (signed) short value */
AgcFilter::AgcFilter(int channels)
{
static const float DefaultLevel = 0.8f;

View File

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

View File

@ -3,7 +3,7 @@
* 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 "../engine_config.h"
#include "../config.h"
#include "Audio_Resampler.h"
#include <stdlib.h>
#include <assert.h>
@ -18,7 +18,7 @@ namespace Audio
SpeexResampler::SpeexResampler()
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0), mChannels(0)
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0)
{
}
@ -125,13 +125,12 @@ int SpeexResampler::destRate()
size_t SpeexResampler::getDestLength(size_t sourceLen)
{
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f);
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f) / 2 * 2;
}
size_t SpeexResampler::getSourceLength(size_t destLen)
{
// Here we want to get 'destLen' number of samples
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f);
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f) / 2 * 2;
}
// Returns instance + speex resampler size in bytes
@ -155,7 +154,7 @@ int ChannelConverter::stereoToMono(const void *source, int sourceLength, void *d
int ChannelConverter::monoToStereo(const void *source, int sourceLength, void *dest, int destLength)
{
assert (destLength == sourceLength * 2);
assert(destLength == sourceLength * 2);
const short* input = (const short*)source;
short* output = (short*)dest;
// Convert starting from the end of buffer to allow inplace conversion
@ -274,7 +273,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
PResampler r;
if (resamplerIter == mResamplerMap.end())
{
r = std::make_shared<Resampler>();
r = PResampler(new Resampler());
r->start(AUDIO_CHANNELS, sourceRate, destRate);
mResamplerMap[RatePair(sourceRate, destRate)] = r;
}

View File

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

View File

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

View File

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

View File

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

113
src/engine/config.h Normal file
View File

@ -0,0 +1,113 @@
/* 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

File diff suppressed because it is too large Load Diff

View File

@ -24,118 +24,118 @@ class Session;
class Account: public resip::DnsResultSink
{
friend class UserAgent;
friend class NATDecorator;
friend class UserAgent;
friend class NATDecorator;
public:
Account(PVariantMap config, UserAgent& agent);
~Account();
Account(PVariantMap config, UserAgent& agent);
~Account();
void start();
void stop();
void refresh();
bool active();
int id() const;
void start();
void stop();
void refresh();
bool active();
int id() const;
enum class RegistrationState
{
None,
Registering,
Reregistering,
Registered,
Unregistering
};
RegistrationState registrationState();
enum class RegistrationState
{
None,
Registering,
Reregistering,
Registered,
Unregistering
};
RegistrationState registrationState();
/* Publishes new presence information */
void publishPresence(bool online, const std::string& content, int seconds = 600);
/* Publishes new presence information */
void publishPresence(bool online, const std::string& content, int seconds = 600);
/* Stops publishing of presence */
void stopPublish();
/* Stops publishing of presence */
void stopPublish();
/* Starts observing on specified target / package */
PClientObserver observe(const std::string& target, const std::string& package, void* tag);
/* Starts observing on specified target / package */
PClientObserver observe(const std::string& target, const std::string& package, void* tag);
/* Queues message to peer with specified mime type. Returns ID of message. */
int sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag);
/* Queues message to peer with specified mime type. Returns ID of message. */
int sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag);
/* Returns name of account - <sip:user@domain> */
std::string name();
/* Returns name of account - <sip:user@domain> */
std::string name();
/* Updates account with configuration */
void setup(VariantMap& config);
/* Updates account with configuration */
void setup(VariantMap& config);
/* Returns corresponding resiprocate profile */
std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; }
/* Returns corresponding resiprocate profile */
resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; }
typedef std::map<std::string, std::string> UserInfo;
void setUserInfo(const UserInfo& info);
UserInfo getUserInfo() const;
typedef std::map<std::string, std::string> UserInfo;
void setUserInfo(const UserInfo& info);
UserInfo getUserInfo() const;
protected:
PVariantMap mConfig;
PVariantMap mConfig;
// Registration
ResipSession* mRegistration;
resip::ClientRegistrationHandle mRegistrationHandle;
resip::ClientPublicationHandle mPublication;
resip::TransportType mUsedTransport;
// Registration
ResipSession* mRegistration;
resip::ClientRegistrationHandle mRegistrationHandle;
resip::ClientPublicationHandle mPublication;
resip::TransportType mUsedTransport;
RegistrationState mRegistrationState;
RegistrationState mRegistrationState;
ice::NetworkAddress mExternalAddress;
std::shared_ptr<resip::UserProfile> mProfile;
UserAgent& mAgent;
bool mPresenceOnline;
std::string mPresenceContent;
ice::NetworkAddress mExternalAddress;
resip::SharedPtr<resip::UserProfile> mProfile;
UserAgent& mAgent;
bool mPresenceOnline;
std::string mPresenceContent;
// Timer to refresh STUN server IP
ice::ICEScheduleTimer mRefreshStunServerIpTimer;
// Timer to refresh STUN server IP
ice::ICEScheduleTimer mRefreshStunServerIpTimer;
// Cached auth
resip::Auth mCachedAuth;
// Cached auth
resip::Auth mCachedAuth;
// Id of account
int mId;
// Id of account
int mId;
// User info about current state
UserInfo mUserInfo;
// User info about current state
UserInfo mUserInfo;
// List of client subscriptions sent from this account
typedef std::set<PClientObserver> ClientObserverSet;
ClientObserverSet mClientObserverSet;
// List of client subscriptions sent from this account
typedef std::set<PClientObserver> ClientObserverSet;
ClientObserverSet mClientObserverSet;
void process();
// Method queries new stun server ip from dns (if stun server is specified as dns name)
void queryStunServerIp();
void process();
// Method queries new stun server ip from dns (if stun server is specified as dns name)
void queryStunServerIp();
bool isResponsibleFor(const resip::NameAddr& addr);
enum class SecureScheme
{
SipsAndTls,
SipsOnly,
TlsOnly,
Nothing
};
bool isResponsibleFor(const resip::NameAddr& addr);
enum class SecureScheme
{
SipsAndTls,
SipsOnly,
TlsOnly,
Nothing
};
resip::NameAddr contact(SecureScheme ss = SecureScheme::SipsOnly);
resip::NameAddr contact(SecureScheme ss = SecureScheme::SipsOnly);
// This method prepares configuration, creates ice stack and sets ownership to session
void prepareIceStack(Session* session, ice::AgentRole role);
void onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
void onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response);
// This method prepares configuration, creates ice stack and sets ownership to session
void prepareIceStack(Session* session, ice::AgentRole role);
void onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
void onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response);
#pragma region DnsResultSink implementation
void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&);
#pragma endregion
static int generateId();
static std::atomic_int IdGenerator;
static int generateId();
static resip::AtomicCounter IdGenerator;
};
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))
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
mSrtpSuite = SRTP_NONE;
setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending);
setState((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending);
}
AudioProvider::~AudioProvider()
@ -64,7 +64,7 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer,
}
// Processes incoming data
void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source)
void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source)
{
if (!mActiveStream)
return;
@ -77,7 +77,7 @@ void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffe
}
// This method is called by user agent to send ICE packet from mediasocket
void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer, unsigned int size)
void AudioProvider::sendData(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size)
{
s->sendDatagram(destination, buffer, size);
}
@ -99,6 +99,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
else
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
}
#if defined(USE_RESIP_INTEGRATION)
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
if (mAvailableCodecs.empty())
@ -113,7 +114,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
if (mRemoteTelephoneCodec)
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
}
#endif
// Publish stream state
const char* attr = nullptr;
@ -124,8 +125,6 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
{
case msSendonly: attr = "recvonly"; break;
case msInactive: attr = "recvonly"; break;
case msRecvonly:
case msSendRecv: break; // Do nothing here
}
break;
@ -183,7 +182,6 @@ void AudioProvider::sessionEstablished(int conntype)
{
RemoteCodec& rc = mAvailableCodecs.front();
mActiveStream->setTransmittingCodec(*rc.mFactory, rc.mRemotePayloadType);
auto codec = dynamic_cast<MT::AudioStream*>(mActiveStream.get())->transmittingCodec();
dynamic_cast<MT::AudioStream*>(mActiveStream.get())->setTelephoneCodec(mRemoteTelephoneCodec);
}
}
@ -228,8 +226,10 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
for (int localIndex=0; localIndex<mCodecPriority.count(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)
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
#endif
}
if (!mAvailableCodecs.size())
@ -271,10 +271,11 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
return true;
}
void AudioProvider::setState(unsigned state)
{
setStateImpl(state);
mState = state;
if (mActiveStream)
mActiveStream->setState(state);
}
unsigned AudioProvider::state()
@ -300,12 +301,26 @@ std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
if (!mActiveStream)
return "";
// Use tag 1 - it is ok, as we use only single crypto attribute
int srtpTag = 1;
// Print key to base64 string
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
resip::Data d(keyBuffer->data(), keyBuffer->size());
resip::Data keyText = d.base64encode();
return std::format("{} {} inline:{}", 1, toString(suite), keyText.c_str());
// Create "crypto" attribute value
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)
@ -326,7 +341,15 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu
resip::Data rawkey = keyText.base64decode();
key = ByteBuffer(rawkey.c_str(), rawkey.size());
return toSrtpSuite(suite);
// Open srtp
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)
@ -358,10 +381,3 @@ void AudioProvider::setupMirror(bool enable)
if (mActiveStream)
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-2023 VoIP objects (voipobjects.com)
/* Copyright(C) 2007-2014 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/. */
@ -12,7 +12,9 @@
#include "../media/MT_Box.h"
#include "../media/MT_Stream.h"
#include "../media/MT_Codec.h"
#include "../audio/Audio_Interface.h"
#include "rutil/ThreadIf.hxx"
#include <vector>
#include <string>
@ -26,47 +28,46 @@ public:
virtual ~AudioProvider();
// Returns provider RTP name
std::string streamName() override;
std::string streamName();
// Returns provider RTP profile name
std::string streamProfile() override;
std::string streamProfile();
// Sets destination IP address
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
void setDestinationAddress(const RtpPair<InternetAddress>& addr);
// Processes incoming data
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source);
// This method is called by user agent to send ICE packet from mediasocket
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
// Updates SDP offer
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction);
// Called by user agent when session is deleted.
void sessionDeleted() override;
void sessionDeleted();
// Called by user agent when session is terminated.
void sessionTerminated() override;
void sessionTerminated();
// Called by user agent when session is started.
void sessionEstablished(int conntype) override;
void sessionEstablished(int conntype);
// Called by user agent to save media socket for this provider
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override;
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6);
// Called by user agent to get media socket for this provider
RtpPair<PDatagramSocket>& socket(int family) override;
RtpPair<PDatagramSocket>& socket(int family);
// Called by user agent to process media stream description from remote peer.
// Returns true if description is processed succesfully. Otherwise method returns false.
// myAnswer sets if the answer will be sent after.
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) override;
void setState(unsigned state) override;
unsigned state() override;
MT::Statistics getStatistics() override;
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection);
void setState(unsigned state);
unsigned state();
MT::Statistics getStatistics();
MT::PStream activeStream();
void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction);
@ -74,7 +75,6 @@ public:
void setupMirror(bool enable);
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
protected:
// SDP's stream name
@ -110,11 +110,8 @@ protected:
void* mMediaObserverTag = nullptr;
std::string createCryptoAttribute(SrtpSuite suite);
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
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

View File

@ -8,67 +8,67 @@
bool DataProvider::isSupported(const char* name)
{
return !strcmp(name, "audio");
return !strcmp(name, "audio");
//return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video"));
//return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video"));
}
void DataProvider::pause()
{
/*if (state() & STATE_SIPRECV)
/*if (state() & STATE_SIPRECV)
setState( state() & ~STATE_SIPRECV );*/
// Stop receive RTP stream
if (state() & (int)StreamState::Receiving)
setState( state() & ~(int)StreamState::Receiving );
mActive = mfPaused;
// Stop receive RTP stream
if (state() & (int)StreamState::Receiving)
setState( state() & ~(int)StreamState::Receiving );
mActive = mfPaused;
}
void DataProvider::resume()
{
// Tell remote peer about resumed receiving in SDP
//setState( state() | STATE_SIPRECV );
// Tell remote peer about resumed receiving in SDP
//setState( state() | STATE_SIPRECV );
// Start receive RTP stream
setState( state() | (int)StreamState::Receiving );
mActive = mfActive;
// Start receive RTP stream
setState( state() | (int)StreamState::Receiving );
mActive = mfActive;
}
bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection)
{
// Process paused and inactive calls
if (media.exists("sendonly"))
// Process paused and inactive calls
if (media.exists("sendonly"))
{
mRemoteState = msSendonly;
setState(state() & ~(int)StreamState::Sending);
}
else
if (media.exists("recvonly"))
{
mRemoteState = msRecvonly;
setState(state() & ~(int)StreamState::Receiving);
}
else
if (media.exists("inactive"))
{
mRemoteState = msInactive;
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving) );
}
else
{
mRemoteState = msSendRecv;
switch (mActive)
{
mRemoteState = msSendonly;
setState(state() & ~(int)StreamState::Sending);
}
else
if (media.exists("recvonly"))
{
mRemoteState = msRecvonly;
setState(state() & ~(int)StreamState::Receiving);
}
else
if (media.exists("inactive"))
{
mRemoteState = msInactive;
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving) );
}
else
{
mRemoteState = msSendRecv;
switch (mActive)
{
case mfActive:
setState(state() | (int)StreamState::Sending | (int)StreamState::Receiving);
break;
case mfActive:
setState(state() | (int)StreamState::Sending | (int)StreamState::Receiving);
break;
case mfPaused:
setState(state() | (int)StreamState::Sending );
break;
}
}
return true;
case mfPaused:
setState(state() | (int)StreamState::Sending );
break;
}
}
return true;
}

View File

@ -10,6 +10,7 @@
#include <vector>
#include "resip/stack/SdpContents.hxx"
#include "rutil/SharedPtr.hxx"
#include "../helper/HL_InternetAddress.h"
#include "../helper/HL_NetworkSocket.h"
@ -19,70 +20,70 @@
class DataProvider
{
public:
enum MediaFlow
{
mfActive,
mfPaused
};
enum MediaFlow
{
mfActive,
mfPaused
};
enum MediaState
{
msSendRecv,
msSendonly,
msRecvonly,
msInactive
};
enum MediaState
{
msSendRecv,
msSendonly,
msRecvonly,
msInactive
};
static bool isSupported(const char* name);
static bool isSupported(const char* name);
// Returns provider RTP name
virtual std::string streamName() = 0;
// Returns provider RTP profile name
virtual std::string streamProfile() = 0;
// Sets destination IP address
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
// Returns provider RTP name
virtual std::string streamName() = 0;
// Processes incoming data
virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
// Returns provider RTP profile name
virtual std::string streamProfile() = 0;
// 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;
// Updates SDP offer
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
// Sets destination IP address
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
// Called by user agent when session is deleted. Comes after sessionTerminated().
virtual void sessionDeleted() = 0;
// Processes incoming data
virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
// Called by user agent when session is terminated.
virtual void sessionTerminated() = 0;
// This method is called by user agent to send ICE packet from mediasocket
virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
// Called by user agent when session is started.
virtual void sessionEstablished(int conntype) = 0;
// Updates SDP offer
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
// Called by user agent to save media socket for this provider
virtual void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) = 0;
// Called by user agent to get media socket for this provider
virtual RtpPair<PDatagramSocket>& socket(int family) = 0;
// Called by user agent when session is deleted. Comes after sessionTerminated().
virtual void sessionDeleted() = 0;
// Called by user agent to process media stream description from remote peer.
// Returns true if description is processed succesfully. Otherwise method returns false.
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) = 0;
virtual unsigned state() = 0;
virtual void setState(unsigned state) = 0;
virtual void pause();
virtual void resume();
// Called by user agent when session is terminated.
virtual void sessionTerminated() = 0;
// Called by user agent when session is started.
virtual void sessionEstablished(int conntype) = 0;
// Called by user agent to save media socket for this provider
virtual void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) = 0;
// Called by user agent to get media socket for this provider
virtual RtpPair<PDatagramSocket>& socket(int family) = 0;
// Called by user agent to process media stream description from remote peer.
// Returns true if description is processed succesfully. Otherwise method returns false.
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) = 0;
virtual unsigned state() = 0;
virtual void setState(unsigned state) = 0;
virtual void pause();
virtual void resume();
virtual MT::Statistics getStatistics() = 0;
virtual MT::Statistics getStatistics() = 0;
protected:
MediaFlow mActive;
MediaState mRemoteState;
MediaFlow mActive;
MediaState mRemoteState;
};
typedef std::shared_ptr<DataProvider> PDataProvider;

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@
#include "../ice/ICETime.h"
#include <sstream>
#include <time.h>
#include "../engine_config.h"
#include "../config.h"
#include "EP_Session.h"
#include "EP_Observer.h"
#include "EP_DataProvider.h"
@ -62,60 +62,52 @@
using namespace std;
enum
{
TransportType_Any,
TransportType_Udp,
TransportType_Tcp,
TransportType_Tls
};
enum
{
CONFIG_IPV4 = 0, // Use IP4
CONFIG_IPV6, // Use IP6.
CONFIG_USERNAME, // Username. String value.
CONFIG_DOMAIN, // Domain. String value.
CONFIG_PASSWORD, // Password. String value.
CONFIG_RINSTANCE, // Determines if SIP rinstance field has to be used during registration. Boolean value.
CONFIG_INSTANCE_ID, // Instance id. It is alternative option to rinstance.
CONFIG_DISPLAYNAME, // Optional user display name. String value.
CONFIG_DOMAINPORT, // Optional domain port number. Integer value.
CONFIG_REGISTERDURATION, // Wanted duration for registration. Integer value. It is MANDATORY value.
CONFIG_RPORT, // Use SIP rport field. Recommended to set it to true. Boolean value.
CONFIG_KEEPALIVETIME, // Interval between UDP keep-alive messages. Boolean value.
CONFIG_RELAY, // Sets if TURN server must be used instead of STUN. Boolean value.
CONFIG_ICETIMEOUT, // Optional timeout for ICE connectivity checks and candidate gathering. Integer value.
CONFIG_ICEUSERNAME, // Optional username for TURN server. String value.
CONFIG_ICEPASSWORD, // Optional password for TURN server. String value.
CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value.
CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_NAME.
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
CONFIG_ICEREQUIRED, // ICE MUST be present in remote peer offers and answers. Boolean value.
CONFIG_TRANSPORT, // 0 - all transports, 1 - UDP, 2 - TCP, 3 - TLS,
CONFIG_SUBSCRIPTION_TIME, // Subscription time (in seconds)
CONFIG_SUBSCRIPTION_REFRESHTIME, // Refresh interval for subscriptions
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
CONFIG_ROOTCERT, // Additional root cert in PEM format; string.
CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to reduce number of steps of SIP transaction
CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
CONFIG_DEFERRELAYED, // Defer relayed media path
CONFIG_PROXY, // Proxy host name or IP address
CONFIG_PROXYPORT, // Proxy port number
CONFIG_CODEC_PRIORITY, // Another VariantMap with codec priorities,
CONFIG_ACCOUNT, // VariantMap with account configuration
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
CONFIG_OWN_DNS, // Use predefined DNS servers
CONFIG_REGID // reg-id value from RFC5626
CONFIG_IPV4 = 0, // Use IP4
CONFIG_IPV6, // Use IP6.
CONFIG_USERNAME, // Username. String value.
CONFIG_DOMAIN, // Domain. String value.
CONFIG_PASSWORD, // Password. String value.
CONFIG_RINSTANCE, // Determines if SIP rinstance field has to be used during registration. Boolean value.
CONFIG_INSTANCE_ID, // Instance id. It is alternative option to rinstance.
CONFIG_DISPLAYNAME, // Optional user display name. String value.
CONFIG_DOMAINPORT, // Optional domain port number. Integer value.
CONFIG_REGISTERDURATION, // Wanted duration for registration. Integer value. It is MANDATORY value.
CONFIG_RPORT, // Use SIP rport field. Recommended to set it to true. Boolean value.
CONFIG_KEEPALIVETIME, // Interval between UDP keep-alive messages. Boolean value.
CONFIG_RELAY, // Sets if TURN server must be used instead of STUN. Boolean value.
CONFIG_ICETIMEOUT, // Optional timeout for ICE connectivity checks and candidate gathering. Integer value.
CONFIG_ICEUSERNAME, // Optional username for TURN server. String value.
CONFIG_ICEPASSWORD, // Optional password for TURN server. String value.
CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value.
CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_NAME.
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
CONFIG_ICEREQUIRED, // ICE MUST be present in remote peer offers and answers. Boolean value.
CONFIG_TRANSPORT, // 0 - all transports, 1 - UDP, 2 - TCP, 3 - TLS,
CONFIG_SUBSCRIPTION_TIME, // Subscription time (in seconds)
CONFIG_SUBSCRIPTION_REFRESHTIME, // Refresh interval for subscriptions
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
CONFIG_ROOTCERT, // Additional root cert in PEM format; string.
CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to reduce number of steps of SIP transaction
CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
CONFIG_DEFERRELAYED, // Defer relayed media path
CONFIG_PROXY, // Proxy host name or IP address
CONFIG_PROXYPORT, // Proxy port number
CONFIG_CODEC_PRIORITY, // Another VariantMap with codec priorities,
CONFIG_ACCOUNT, // VariantMap with account configuration
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
CONFIG_OWN_DNS, // Use predefined DNS servers
CONFIG_REGID // reg-id value from RFC5626
};
// Conntype parameter for OnSessionEstablished event
enum
{
EV_SIP = 1,
EV_ICE = 2
EV_SIP = 1,
EV_ICE = 2
};
class UserAgent;
@ -124,7 +116,7 @@ class UserAgent;
class SIPAction
{
public:
virtual void Run(UserAgent& ua) = 0;
virtual void Run(UserAgent& ua) = 0;
};
typedef std::vector<SIPAction*> SIPActionVector;
@ -132,15 +124,15 @@ typedef std::vector<SIPAction*> SIPActionVector;
// Session termination reason
enum
{
Error,
Timeout,
Replaced,
LocalBye,
RemoteBye,
LocalCancel,
RemoteCancel,
Rejected, //Only as UAS, UAC has distinct onFailure callback
Referred
Error,
Timeout,
Replaced,
LocalBye,
RemoteBye,
LocalCancel,
RemoteCancel,
Rejected, //Only as UAS, UAC has distinct onFailure callback
Referred
};
class UserAgent: public resip::ClientRegistrationHandler,
@ -152,239 +144,238 @@ class UserAgent: public resip::ClientRegistrationHandler,
public resip::ServerSubscriptionHandler,
public resip::ClientPagerMessageHandler,
public resip::ServerPagerMessageHandler,
public resip::ClientPublicationHandler
//public resip::InternalTransport::TransportLogger
public resip::ClientPublicationHandler,
public resip::InternalTransport::TransportLogger
{
friend class Account;
friend class Session;
friend class ResipSession;
friend class NATDecorator;
friend class WatcherQueue;
friend class Account;
friend class Session;
friend class ResipSession;
friend class NATDecorator;
friend class WatcherQueue;
public:
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
static std::string formatSipAddress(const std::string& sip);
static bool isSipAddressValid(const std::string& sip);
struct SipAddress
{
bool mValid;
std::string mScheme;
std::string mUsername;
std::string mDomain;
std::string mDisplayname;
};
/* 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 std::string formatSipAddress(std::string sip);
static bool isSipAddressValid(std::string sip);
struct SipAddress
{
bool mValid;
std::string mScheme;
std::string mUsername;
std::string mDomain;
std::string mDisplayname;
};
static SipAddress parseSipAddress(const std::string& sip);
static SipAddress parseSipAddress(const std::string& sip);
UserAgent();
virtual ~UserAgent();
/* Brings user agent online. Basically it creates a signalling socket(s).
UserAgent();
virtual ~UserAgent();
/* Brings user agent online. Basically it creates a signalling socket(s).
This is asynchronous method. */
void start();
/* Shutdowns user agent. It closes all sessions, tries to unregister from server and disconnects from it.
void start();
/* Shutdowns user agent. It closes all sessions, tries to unregister from server and disconnects from it.
This is asynchronous method. onStop() event will be called later */
void shutdown();
void shutdown();
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything. onStop() is called in context of this method. */
void stop();
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything. onStop() is called in context of this method. */
void stop();
/* Checks if user agent is active (started). */
bool active();
/* Used to refresh existing registration(s), publication, subscriptions. */
void refresh();
/* Checks if user agent is active (started). */
bool active();
/* Runs sip & ice stacks. Event handlers are called in its context. */
void process();
/* Adds root cert in PEM format. Usable after start() call. */
void addRootCert(const ByteBuffer& data);
/* Used to refresh existing registration(s), publication, subscriptions. */
void refresh();
PAccount createAccount(PVariantMap config);
void deleteAccount(PAccount account);
/* Runs sip & ice stacks. Event handlers are called in its context. */
void process();
/* Creates session. Returns session ID. */
PSession createSession(PAccount account);
// Must be called when IP interface list is changed
void updateInterfaceList();
/* Adds root cert in PEM format. Usable after start() call. */
void addRootCert(const ByteBuffer& data);
// Called on new incoming session; providers shoukld
virtual PDataProvider onProviderNeeded(const std::string& name) = 0;
PAccount createAccount(PVariantMap config);
void deleteAccount(PAccount account);
// Called on new session offer
virtual void onNewSession(PSession s) = 0;
// Called when session is terminated
virtual void onSessionTerminated(PSession s, int responsecode, int reason) = 0;
// Called when session is established ok i.e. after all ICE signalling is finished
// Conntype is type of establish event - EV_SIP or EV_ICE
virtual void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) = 0;
/* Creates session. Returns session ID. */
PSession createSession(PAccount account);
// Called when client session gets
virtual void onSessionProvisional(PSession s, int code) = 0;
// Must be called when IP interface list is changed
void updateInterfaceList();
// Called when user agent started
virtual void onStart(int errorcode) = 0;
// Called when user agent stopped
virtual void onStop() = 0;
// Called on new incoming session; providers shoukld
virtual PDataProvider onProviderNeeded(const std::string& name) = 0;
// Called when account registered
virtual void onAccountStart(PAccount account) = 0;
// Called on new session offer
virtual void onNewSession(PSession s) = 0;
// Called when account removed or failed (non zero error code)
virtual void onAccountStop(PAccount account, int error) = 0;
// Called when session is terminated
virtual void onSessionTerminated(PSession s, int responsecode, int reason) = 0;
// Called when connectivity checks failed.
virtual void onConnectivityFailed(PSession s) = 0;
// Called when session is established ok i.e. after all ICE signalling is finished
// Conntype is type of establish event - EV_SIP or EV_ICE
virtual void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) = 0;
// Called when new candidate is gathered
virtual void onCandidateGathered(PSession s, const char* address);
// Called when network change detected
virtual void onNetworkChange(PSession s) = 0;
// Called when client session gets
virtual void onSessionProvisional(PSession s, int code) = 0;
// Called when all candidates are gathered
virtual void onGathered(PSession s);
// Called when user agent started
virtual void onStart(int errorcode) = 0;
// Called when new connectivity check is finished
virtual void onCheckFinished(PSession s, const char* description);
// Called when log message must be recorded
virtual void onLog(const char* msg);
// Called when problem with SIP connection(s) detected
virtual void onSipConnectionFailed() = 0;
// Called when user agent stopped
virtual void onStop() = 0;
// Subscribe/publish presence methods
virtual void onPublicationSuccess(PAccount acc);
virtual void onPublicationTerminated(PAccount acc, int code);
virtual void onClientObserverStart(PClientObserver observer);
virtual void onServerObserverStart(PServerObserver observer);
virtual void onClientObserverStop(PClientObserver observer, int code);
virtual void onServerObserverStop(PServerObserver observer, int code);
// Called when account registered
virtual void onAccountStart(PAccount account) = 0;
// Called when account removed or failed (non zero error code)
virtual void onAccountStop(PAccount account, int error) = 0;
// Called when connectivity checks failed.
virtual void onConnectivityFailed(PSession s) = 0;
// Called when new candidate is gathered
virtual void onCandidateGathered(PSession s, const char* address);
// Called when network change detected
virtual void onNetworkChange(PSession s) = 0;
// Called when all candidates are gathered
virtual void onGathered(PSession s);
// Called when new connectivity check is finished
virtual void onCheckFinished(PSession s, const char* description);
// Called when log message must be recorded
virtual void onLog(const char* msg);
// Called when problem with SIP connection(s) detected
virtual void onSipConnectionFailed() = 0;
// Subscribe/publish presence methods
virtual void onPublicationSuccess(PAccount acc);
virtual void onPublicationTerminated(PAccount acc, int code);
virtual void onClientObserverStart(PClientObserver observer);
virtual void onServerObserverStart(PServerObserver observer);
virtual void onClientObserverStop(PClientObserver observer, int code);
virtual void onServerObserverStop(PServerObserver observer, int code);
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, const std::string& content);
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
// Configuration methods
VariantMap& config();
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, const std::string& content);
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
// Configuration methods
VariantMap& config();
public:
// InviteSessionHandler implementation
// InviteSessionHandler implementation
#pragma region InviteSessionHandler implementation
/// called when an initial INVITE or the intial response to an outoing invite
virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
/// called when an initial INVITE or the intial response to an outoing invite
virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
/// Received a failure response from UAS
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// Received a failure response from UAS
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an in-dialog provisional response is received that contains an SDP body
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&, const resip::SdpContents&) override;
/// called when an in-dialog provisional response is received that contains an SDP body
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&, const resip::SdpContents&) override;
/// called when dialog enters the Early state - typically after getting 18x
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
/// called when dialog enters the Early state - typically after getting 18x
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
/// called when a dialog initiated as a UAC enters the connected state
virtual void onConnected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when a dialog initiated as a UAC enters the connected state
virtual void onConnected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when a dialog initiated as a UAS enters the connected state
virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when a dialog initiated as a UAS enters the connected state
virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related=0) override;
virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related=0) override;
/// called when a fork that was created through a 1xx never receives a 2xx
/// because another fork answered and this fork was canceled by a proxy.
virtual void onForkDestroyed(resip::ClientInviteSessionHandle) override;
/// called when a fork that was created through a 1xx never receives a 2xx
/// because another fork answered and this fork was canceled by a proxy.
virtual void onForkDestroyed(resip::ClientInviteSessionHandle) override;
/// called when a 3xx with valid targets is encountered in an early dialog
/// This is different then getting a 3xx in onTerminated, as another
/// request will be attempted, so the DialogSet will not be destroyed.
/// Basically an onTermintated that conveys more information.
/// checking for 3xx respones in onTerminated will not work as there may
/// be no valid targets.
virtual void onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when a 3xx with valid targets is encountered in an early dialog
/// This is different then getting a 3xx in onTerminated, as another
/// request will be attempted, so the DialogSet will not be destroyed.
/// Basically an onTermintated that conveys more information.
/// checking for 3xx respones in onTerminated will not work as there may
/// be no valid targets.
virtual void onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an SDP answer is received - has nothing to do with user
/// answering the call
virtual void onAnswer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
/// called when an SDP answer is received - has nothing to do with user
/// answering the call
virtual void onAnswer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
/// called when an SDP offer is received - must send an answer soon after this
virtual void onOffer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
/// called when an SDP offer is received - must send an answer soon after this
virtual void onOffer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
/// called when an Invite w/out SDP is sent, or any other context which
/// requires an SDP offer from the user
virtual void onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called if an offer in a UPDATE or re-INVITE was rejected - not real
/// useful. A SipMessage is provided if one is available
virtual void onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage* msg) override;
/// called when INFO message is received
virtual void onInfo(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an Invite w/out SDP is sent, or any other context which
/// requires an SDP offer from the user
virtual void onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when response to INFO message is received
virtual void onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called if an offer in a UPDATE or re-INVITE was rejected - not real
/// useful. A SipMessage is provided if one is available
virtual void onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage* msg) override;
/// called when MESSAGE message is received
virtual void onMessage(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when INFO message is received
virtual void onInfo(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when response to MESSAGE message is received
virtual void onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when response to INFO message is received
virtual void onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message is received. The refer is accepted or
/// rejected using the server subscription. If the offer is accepted,
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
/// InviteSession that will send notify messages using the ServerSubscription
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg) override;
/// called when MESSAGE message is received
virtual void onMessage(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when response to MESSAGE message is received
virtual void onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message receives a failure response
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message is received. The refer is accepted or
/// rejected using the server subscription. If the offer is accepted,
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
/// InviteSession that will send notify messages using the ServerSubscription
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg) override;
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message receives a failure response
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message receives an accepted response
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message receives an accepted response
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg) override;
#pragma endregion
// ClientRegistrationHandler implementation
// ClientRegistrationHandler implementation
#pragma region ClientRegistrationHandler implementation
/// Called when registraion succeeds or each time it is sucessfully
/// refreshed.
void onSuccess(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
/// Called when registraion succeeds or each time it is sucessfully
/// refreshed.
void onSuccess(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
// Called when all of my bindings have been removed
void onRemoved(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
/// call on Retry-After failure.
/// return values: -1 = fail, 0 = retry immediately, N = retry in N seconds
int onRequestRetry(resip::ClientRegistrationHandle, int retrySeconds, const resip::SipMessage& response) override;
/// Called if registration fails, usage will be destroyed (unless a
/// Registration retry interval is enabled in the Profile)
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
// Called when all of my bindings have been removed
void onRemoved(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
/// call on Retry-After failure.
/// return values: -1 = fail, 0 = retry immediately, N = retry in N seconds
int onRequestRetry(resip::ClientRegistrationHandle, int retrySeconds, const resip::SipMessage& response) override;
/// Called if registration fails, usage will be destroyed (unless a
/// Registration retry interval is enabled in the Profile)
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
#pragma endregion
#pragma region ExternalLogger implementation
/** return true to also do default logging, false to suppress default logging. */
virtual bool operator()(resip::Log::Level level,
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) override;
/** return true to also do default logging, false to suppress default logging. */
virtual bool operator()(resip::Log::Level level,
const resip::Subsystem& subsystem,
const resip::Data& appName,
const char* file,
int line,
const resip::Data& message,
const resip::Data& messageWithHeaders) override;
#pragma endregion
#pragma region DnsResultSink implementation
@ -398,96 +389,96 @@ public:
#pragma endregion
#pragma region TransportLogger implementation
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
#pragma endregion
#pragma region ClientPublicationHandler
void onSuccess(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
void onRemove(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
int onRequestRetry(resip::ClientPublicationHandle, int retrySeconds, const resip::SipMessage& status) override;
void onSuccess(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
void onRemove(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
int onRequestRetry(resip::ClientPublicationHandle, int retrySeconds, const resip::SipMessage& status) override;
#pragma endregion
#pragma region SubscriptionHandler
void onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMessage& notify);
void onUpdatePending(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
void onUpdateActive(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
void onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMessage& notify);
void onUpdatePending(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
void onUpdateActive(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
//unknown Subscription-State value
void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override;
//subscription can be ended through a notify or a failure response.
void onTerminated(resip::ClientSubscriptionHandle, const resip::SipMessage* msg) override;
//not sure if this has any value.
void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override;
//unknown Subscription-State value
void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override;
/// called to allow app to adorn a message.
void onReadyToSend(resip::ClientSubscriptionHandle, resip::SipMessage& msg) override;
void onNotifyNotReceived(resip::ClientSubscriptionHandle) override;
//subscription can be ended through a notify or a failure response.
void onTerminated(resip::ClientSubscriptionHandle, const resip::SipMessage* msg) override;
//not sure if this has any value.
void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override;
/// called to allow app to adorn a message.
void onReadyToSend(resip::ClientSubscriptionHandle, resip::SipMessage& msg) override;
void onNotifyNotReceived(resip::ClientSubscriptionHandle) override;
/// Called when a TCP or TLS flow to the server has terminated. This can be caused by socket
/// errors, or missing CRLF keep alives pong responses from the server.
// Called only if clientOutbound is enabled on the UserProfile and the first hop server
/// supports RFC5626 (outbound).
/// Default implementation is to re-form the subscription using a new flow
void onFlowTerminated(resip::ClientSubscriptionHandle) override;
void onNewSubscription(resip::ServerSubscriptionHandle, const resip::SipMessage& sub) override;
void onTerminated(resip::ServerSubscriptionHandle) override;
/// Called when a TCP or TLS flow to the server has terminated. This can be caused by socket
/// errors, or missing CRLF keep alives pong responses from the server.
// Called only if clientOutbound is enabled on the UserProfile and the first hop server
/// supports RFC5626 (outbound).
/// Default implementation is to re-form the subscription using a new flow
void onFlowTerminated(resip::ClientSubscriptionHandle) override;
void onNewSubscription(resip::ServerSubscriptionHandle, const resip::SipMessage& sub) override;
void onTerminated(resip::ServerSubscriptionHandle) override;
#pragma endregion
#pragma region PagerHandler
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, std::unique_ptr<resip::Contents> contents) override;
void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override;
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, std::unique_ptr<resip::Contents> contents) override;
void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override;
#pragma endregion
void onDumCanBeDeleted() override;
void onDumCanBeDeleted() override;
protected:
// Mutex to protect this instance
Mutex mGuard;
// Mutex to protect this instance
Mutex mGuard;
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
std::shared_ptr<resip::MasterProfile> mProfile;
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
resip::SharedPtr<resip::MasterProfile> mProfile;
// Resiprocate's SIP stack object pointer
resip::SipStack* mStack;
// Resiprocate's dialog usage manager object pointer
resip::DialogUsageManager* mDum;
// List of available transports. They are owned by SipStack - so there is no need to delete instances in UserAgent.
std::vector<resip::InternalTransport*> mTransportList;
// Resiprocate's SIP stack object pointer
resip::SipStack* mStack;
typedef std::map<int, PSession> SessionMap;
// Session's map
SessionMap mSessionMap;
// Resiprocate's dialog usage manager object pointer
resip::DialogUsageManager* mDum;
// Used configuration
VariantMap mConfig;
// List of available transports. They are owned by SipStack - so there is no need to delete instances in UserAgent.
std::vector<resip::InternalTransport*> mTransportList;
// Action vector
SIPActionVector mActionVector;
typedef std::map<int, PClientObserver> ClientObserverMap;
ClientObserverMap mClientObserverMap;
typedef std::map<int, PSession> SessionMap;
typedef std::map<int, PServerObserver> ServerObserverMap;
ServerObserverMap mServerObserverMap;
typedef std::set<PAccount> AccountSet;
AccountSet mAccountSet;
// Session's map
SessionMap mSessionMap;
// Constructs and sends INVITE to remote peer. Remote peer address is stored inside session object.
void sendOffer(Session* session);
void internalStopSession(Session& session);
void processWatchingList();
bool handleMultipartRelatedNotify(const resip::SipMessage& notify);
// Used configuration
VariantMap mConfig;
// Action vector
SIPActionVector mActionVector;
typedef std::map<int, PClientObserver> ClientObserverMap;
ClientObserverMap mClientObserverMap;
typedef std::map<int, PServerObserver> ServerObserverMap;
ServerObserverMap mServerObserverMap;
typedef std::set<PAccount> AccountSet;
AccountSet mAccountSet;
// Constructs and sends INVITE to remote peer. Remote peer address is stored inside session object.
void sendOffer(Session* session);
void internalStopSession(Session& session);
void processWatchingList();
bool handleMultipartRelatedNotify(const resip::SipMessage& notify);
PSession getUserSession(int sessionId);
PAccount getAccount(const resip::NameAddr& myAddr);
PAccount getAccount(Account* account);
PAccount getAccount(int sessionId);
PSession getUserSession(int sessionId);
PAccount getAccount(const resip::NameAddr& myAddr);
PAccount getAccount(Account* account);
PAccount getAccount(int sessionId);
};
#endif

View File

@ -2,7 +2,7 @@
#include "EP_Engine.h"
WatcherQueue::WatcherQueue(UserAgent& ua)
:mActiveId(0), mAgent(ua)
:mActiveId(0), mAgent(ua)
{}
WatcherQueue::~WatcherQueue()
@ -10,173 +10,173 @@ WatcherQueue::~WatcherQueue()
int WatcherQueue::add(std::string peer, std::string package, void* tag)
{
ice::Lock l(mGuard);
ice::Lock l(mGuard);
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
{
Item& item = mItemList[i];
if (item.mTarget == peer && item.mPackage == package &&
item.mState != Item::State_Deleting)
return item.mId;
}
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
{
Item& item = mItemList[i];
if (item.mTarget == peer && item.mPackage == package &&
item.mState != Item::State_Deleting)
return item.mId;
}
Item item;
item.mTarget = peer;
item.mPackage = package;
item.mTag = tag;
item.mState = Item::State_ScheduledToAdd;
item.mSession = new ResipSession(*mAgent.mDum);
item.mSession->setUa(&mAgent);
item.mSession->setType(ResipSession::Type_Subscription);
item.mSession->setTag(tag);
item.mId = item.mSession->sessionId();
item.mSession->setRemoteAddress(peer);
item.mTag = tag;
mItemList.push_back(item);
process();
Item item;
item.mTarget = peer;
item.mPackage = package;
item.mTag = tag;
item.mState = Item::State_ScheduledToAdd;
item.mSession = new ResipSession(*mAgent.mDum);
item.mSession->setUa(&mAgent);
item.mSession->setType(ResipSession::Type_Subscription);
item.mSession->setTag(tag);
item.mId = item.mSession->sessionId();
item.mSession->setRemoteAddress(peer);
item.mTag = tag;
mItemList.push_back(item);
process();
return item.mId;
return item.mId;
}
void WatcherQueue::remove(int id)
{
ice::Lock l(mGuard);
ice::Lock l(mGuard);
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
{
Item& item = mItemList[i];
if (item.mId == id && !id)
{
Item& item = mItemList[i];
if (item.mId == id && !id)
{
if (item.mState != Item::State_Deleting)
item.mState = Item::State_ScheduledToDelete;
}
if (item.mState != Item::State_Deleting)
item.mState = Item::State_ScheduledToDelete;
}
process();
}
process();
}
void WatcherQueue::refresh(int id)
{
ice::Lock l(mGuard);
ice::Lock l(mGuard);
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
{
Item& item = mItemList[i];
if (item.mId == id && !id)
{
Item& item = mItemList[i];
if (item.mId == id && !id)
{
if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active)
item.mState = Item::State_ScheduledToRefresh;
}
if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active)
item.mState = Item::State_ScheduledToRefresh;
}
process();
}
process();
}
void WatcherQueue::process()
{
while (!mActiveId)
while (!mActiveId)
{
// Find next item to process
ItemList::iterator i = mItemList.begin();
for (;i != mItemList.end() && !i->scheduled(); i++)
;
if (i == mItemList.end())
return;
resip::SharedPtr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
switch (i->mState)
{
// Find next item to process
ItemList::iterator i = mItemList.begin();
for (;i != mItemList.end() && !i->scheduled(); i++)
;
if (i == mItemList.end())
return;
case Item::State_ScheduledToAdd:
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_TIME))
expires = mAgent.mConfig[CONFIG_SUBSCRIPTION_TIME].asInt();
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
refresh = mAgent.mConfig[CONFIG_SUBSCRIPTION_REFRESHTIME].asInt();
std::shared_ptr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(i->mTarget)), resip::Data(i->mPackage),
expires, refresh, i->mSession);
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
mActiveId = i->mId;
i->mState = Item::State_Adding;
mAgent.mDum->send(msg);
break;
switch (i->mState)
{
case Item::State_ScheduledToAdd:
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_TIME))
expires = mAgent.mConfig[CONFIG_SUBSCRIPTION_TIME].asInt();
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
refresh = mAgent.mConfig[CONFIG_SUBSCRIPTION_REFRESHTIME].asInt();
case Item::State_ScheduledToDelete:
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, 0, 0);
if (i->mHandle.isValid())
{
mActiveId = i->mId;
i->mHandle->end();
i->mState = Item::State_Deleting;
break;
}
else
mItemList.erase(i);
break;
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(i->mTarget)), resip::Data(i->mPackage),
expires, refresh, i->mSession);
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
mActiveId = i->mId;
i->mState = Item::State_Adding;
mAgent.mDum->send(msg);
break;
case Item::State_ScheduledToRefresh:
if (i->mHandle.isValid())
{
mActiveId = i->mId;
i->mState = Item::State_Refreshing;
i->mHandle->requestRefresh();
}
else
mItemList.erase(i);
break;
case Item::State_ScheduledToDelete:
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, 0, 0);
if (i->mHandle.isValid())
{
mActiveId = i->mId;
i->mHandle->end();
i->mState = Item::State_Deleting;
break;
}
else
mItemList.erase(i);
break;
case Item::State_ScheduledToRefresh:
if (i->mHandle.isValid())
{
mActiveId = i->mId;
i->mState = Item::State_Refreshing;
i->mHandle->requestRefresh();
}
else
mItemList.erase(i);
break;
default:
break;
}
default:
break;
}
}
}
void WatcherQueue::onTerminated(int id, int code)
{
ice::Lock l(mGuard);
ItemList::iterator i = findById(id);
if (i != mItemList.end())
{
if (i->mSession)
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
mItemList.erase(i);
if (i->mId == mActiveId)
mActiveId = 0;
}
process();
ice::Lock l(mGuard);
ItemList::iterator i = findById(id);
if (i != mItemList.end())
{
if (i->mSession)
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
mItemList.erase(i);
if (i->mId == mActiveId)
mActiveId = 0;
}
process();
}
void WatcherQueue::onEstablished(int id, int code)
{
ice::Lock l(mGuard);
ItemList::iterator i = findById(id);
if (i != mItemList.end())
{
i->mState = Item::State_Active;
if (i->mId == mActiveId)
mActiveId = 0;
}
process();
ice::Lock l(mGuard);
ItemList::iterator i = findById(id);
if (i != mItemList.end())
{
i->mState = Item::State_Active;
if (i->mId == mActiveId)
mActiveId = 0;
}
process();
}
WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
{
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
if (i->mId == id)
return i;
return mItemList.end();
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
if (i->mId == id)
return i;
return mItemList.end();
}
void WatcherQueue::clear()
{
ice::Lock l(mGuard);
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
{
if (i->mHandle.isValid())
i->mHandle->end();
}
mItemList.clear();
ice::Lock l(mGuard);
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
{
if (i->mHandle.isValid())
i->mHandle->end();
}
mItemList.clear();
}

View File

@ -13,57 +13,57 @@ class UserAgent;
class WatcherQueue
{
public:
struct Item
struct Item
{
enum State
{
enum State
{
State_None,
State_Active,
State_ScheduledToAdd,
State_Adding,
State_ScheduledToRefresh,
State_Refreshing,
State_ScheduledToDelete,
State_Deleting
};
resip::ClientSubscriptionHandle mHandle; // Subscription handle
ResipSession* mSession;
State mState;
std::string mTarget; // Target's address
std::string mPackage; // Event package
void* mTag; // User tag
int mId;
Item()
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
{}
bool scheduled()
{
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || mState == State_ScheduledToRefresh;
}
State_None,
State_Active,
State_ScheduledToAdd,
State_Adding,
State_ScheduledToRefresh,
State_Refreshing,
State_ScheduledToDelete,
State_Deleting
};
WatcherQueue(UserAgent& agent);
~WatcherQueue();
int add(std::string peer, std::string package, void* tag);
void remove(int id);
void refresh(int id);
void clear();
resip::ClientSubscriptionHandle mHandle; // Subscription handle
ResipSession* mSession;
State mState;
std::string mTarget; // Target's address
std::string mPackage; // Event package
void* mTag; // User tag
int mId;
void onTerminated(int id, int code);
void onEstablished(int id, int code);
Item()
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
{}
bool scheduled()
{
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || mState == State_ScheduledToRefresh;
}
};
WatcherQueue(UserAgent& agent);
~WatcherQueue();
int add(std::string peer, std::string package, void* tag);
void remove(int id);
void refresh(int id);
void clear();
void onTerminated(int id, int code);
void onEstablished(int id, int code);
protected:
typedef std::vector<Item> ItemList;
ItemList mItemList;
ice::Mutex mGuard;
UserAgent& mAgent;
int mActiveId;
typedef std::vector<Item> ItemList;
ItemList mItemList;
ice::Mutex mGuard;
UserAgent& mAgent;
int mActiveId;
void process();
ItemList::iterator findById(int id);
void process();
ItemList::iterator findById(int id);
};
#endif

View File

@ -19,88 +19,88 @@ ClientObserver::~ClientObserver()
void ClientObserver::refresh()
{
if (mHandle.isValid())
mHandle->requestRefresh();
if (mHandle.isValid())
mHandle->requestRefresh();
}
void ClientObserver::stop()
{
if (mHandle.isValid())
mHandle->end();
else
if (mSession)
{
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
if (mSession)
mSession->end();
}
mSession = NULL;
if (mHandle.isValid())
mHandle->end();
else
if (mSession)
{
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
if (mSession)
mSession->end();
}
mSession = NULL;
}
std::string ClientObserver::peer()
{
return mPeer;
return mPeer;
}
ServerObserver::ServerObserver()
:mState(State_Incoming)
:mState(State_Incoming)
{
}
ServerObserver::~ServerObserver()
{
stop();
stop();
}
std::string ServerObserver::peer() const
{
return mPeer;
return mPeer;
}
std::string ServerObserver::package() const
{
return mPackage;
return mPackage;
}
void ServerObserver::update(std::string simpleId, bool online, std::string msg)
{
if (mState != State_Active)
return;
if (mState != State_Active)
return;
resip::Pidf p;
p.setEntity(mContact);
p.setSimpleId(resip::Data(simpleId));
p.setSimpleStatus(online, resip::Data(msg));
resip::Pidf p;
p.setEntity(mContact);
p.setSimpleId(resip::Data(simpleId));
p.setSimpleStatus(online, resip::Data(msg));
if (mHandle.isValid())
mHandle->send(mHandle->update(&p));
if (mHandle.isValid())
mHandle->send(mHandle->update(&p));
}
void ServerObserver::accept()
{
if (mHandle.isValid() && mState == State_Incoming)
{
mState = State_Active;
mHandle->accept();
}
if (mHandle.isValid() && mState == State_Incoming)
{
mState = State_Active;
mHandle->accept();
}
}
void ServerObserver::stop()
{
if (!mHandle.isValid())
return;
if (!mHandle.isValid())
return;
switch (mState)
{
case State_Incoming:
mHandle->reject(404);
break;
case State_Active:
mHandle->end();
break;
default:
break;
}
mState = State_Closed;
switch (mState)
{
case State_Incoming:
mHandle->reject(404);
break;
case State_Active:
mHandle->end();
break;
default:
break;
}
mState = State_Closed;
}

View File

@ -21,51 +21,51 @@ class ResipSession;
class ClientObserver
{
friend class Account;
friend class UserAgent;
friend class Account;
friend class UserAgent;
public:
ClientObserver();
~ClientObserver();
ClientObserver();
~ClientObserver();
void refresh();
void stop();
std::string peer();
void refresh();
void stop();
std::string peer();
protected:
resip::ClientSubscriptionHandle mHandle;
ResipSession* mSession;
int mSessionId;
std::string mPeer;
resip::ClientSubscriptionHandle mHandle;
ResipSession* mSession;
int mSessionId;
std::string mPeer;
};
typedef std::shared_ptr<ClientObserver> PClientObserver;
class ServerObserver
{
friend class UserAgent;
friend class UserAgent;
public:
ServerObserver();
~ServerObserver();
ServerObserver();
~ServerObserver();
std::string peer() const;
std::string package() const;
std::string peer() const;
std::string package() const;
void accept();
void update(std::string simpleId, bool online, std::string msg);
void stop();
void accept();
void update(std::string simpleId, bool online, std::string msg);
void stop();
protected:
enum State
{
State_Incoming,
State_Active,
State_Closed
};
State mState;
resip::ServerSubscriptionHandle mHandle;
std::string mPeer, mPackage;
resip::Uri mContact;
int mSessionId;
enum State
{
State_Incoming,
State_Active,
State_Closed
};
State mState;
resip::ServerSubscriptionHandle mHandle;
std::string mPeer, mPackage;
resip::Uri mContact;
int mSessionId;
};
typedef std::shared_ptr<ServerObserver> PServerObserver;

View File

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

View File

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

View File

@ -1,117 +0,0 @@
/* 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,20 +1,14 @@
cmake_minimum_required (VERSION 3.15)
project (helper_lib)
# Rely on C++ 11
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD 11)
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)
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
add_library(helper_lib ${HELPER_LIB_SOURCES})
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_include_directories(helper_lib PRIVATE ../../libs/ ../../engine ../)
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
bool isFloat = false;
strx::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
StringHelper::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
if (isFloat)
mCurrentLexem.mType = LexemType::Float;
else
@ -488,7 +488,7 @@ namespace Calc
case LexemType::Hex:
result->mType = Ast::Type::Number;
result->mValue = strx::fromHex2Int(l.mValue);
result->mValue = StringHelper::fromHex2Int(l.mValue);
break;
case LexemType::Float:

View File

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

View File

@ -1,7 +1,7 @@
#include "HL_File.h"
#include <fstream>
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
# include <unistd.h>
# include <sys/statvfs.h>
# include <memory.h>
@ -32,24 +32,23 @@ void FileHelper::remove(const char* s)
::remove(s);
}
// std::string FileHelper::gettempname()
// {
// #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
// int code = mkstemp(template_filename);
std::string FileHelper::gettempname()
{
#if defined(TARGET_LINUX)
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
mkstemp(template_filename);
return template_filename;
#elif defined(TARGET_WIN)
char buffer[L_tmpnam];
tmpnam(buffer);
// return template_filename;
// #elif defined(TARGET_WIN)
// char buffer[L_tmpnam];
// tmpnam(buffer);
// return buffer;
// #elif defined(TARGET_OSX)
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
// mktemp(template_filename);
// 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)
{
@ -65,7 +64,7 @@ std::string FileHelper::getCurrentDir()
return std::string();
#endif
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
char buf[512];
if (getcwd(buf, sizeof buf) != nullptr)
return buf;
@ -110,32 +109,3 @@ size_t FileHelper::getFreespace(const std::string& path)
#endif
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 char* s);
// static std::string gettempname();
static std::string gettempname();
static bool isAbsolute(const std::string& s);
static std::string getCurrentDir();
@ -23,7 +23,6 @@ public:
// Returns free space on volume for path
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
static size_t getFreespace(const std::string& path);
static std::string expandUserHome(const std::string& path);
};
#endif

View File

@ -103,8 +103,8 @@ bool Packet::parseV3(const ByteBuffer& packet)
if (!sourceAddr4.isEmpty())
mSourceAddress = sourceAddr4;
else
if (!sourceAddr6.isEmpty())
mSourceAddress = sourceAddr6;
if (!sourceAddr6.isEmpty())
mSourceAddress = sourceAddr6;
if (!mSourceAddress.isEmpty())
mSourceAddress.setPort(sourcePort);
@ -112,8 +112,8 @@ bool Packet::parseV3(const ByteBuffer& packet)
if (!destAddr4.isEmpty())
mDestinationAddress = destAddr4;
else
if (!destAddr6.isEmpty())
mDestinationAddress = destAddr6;
if (!destAddr6.isEmpty())
mDestinationAddress = destAddr6;
if (!mDestinationAddress.isEmpty())
mDestinationAddress.setPort(destPort);

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 */
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++) {
crc10_accum = ((crc10_accum << 8) & 0x3ff)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,10 +8,6 @@
# include <Windows.h>
#endif
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
# include <arpa/inet.h>
#endif
#include "HL_Rtp.h"
#include "HL_Exception.h"
#include "HL_String.h"
@ -30,25 +26,25 @@
struct RtpHeader
{
unsigned char cc:4; /* CSRC count */
unsigned char x:1; /* header extension flag */
unsigned char p:1; /* padding flag */
unsigned char version:2; /* protocol version */
unsigned char pt:7; /* payload type */
unsigned char m:1; /* marker bit */
unsigned short seq; /* sequence number */
unsigned int ts; /* timestamp */
unsigned int ssrc; /* synchronization source */
unsigned char cc:4; /* CSRC count */
unsigned char x:1; /* header extension flag */
unsigned char p:1; /* padding flag */
unsigned char version:2; /* protocol version */
unsigned char pt:7; /* payload type */
unsigned char m:1; /* marker bit */
unsigned short seq; /* sequence number */
unsigned int ts; /* timestamp */
unsigned int ssrc; /* synchronization source */
};
struct RtcpHeader
{
unsigned char rc:5; /* reception report count */
unsigned char p:1; /* padding flag */
unsigned char version:2; /* protocol version */
unsigned char pt:8; /* payload type */
uint16_t len; /* length */
uint32_t ssrc; /* synchronization source */
unsigned char rc:5; /* reception report count */
unsigned char p:1; /* padding flag */
unsigned char version:2; /* protocol version */
unsigned char pt:8; /* payload type */
uint16_t len; /* length */
uint32_t ssrc; /* synchronization source */
};
bool RtpHelper::isRtp(const void* buffer, size_t length)
@ -56,12 +52,8 @@ bool RtpHelper::isRtp(const void* buffer, size_t length)
if (length < 12)
return false;
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
if (h->version != 0b10)
return false;
unsigned char pt = h->pt;
bool rtp = ( (pt & 0x7F) >= 96 && (pt & 0x7F) <= 127) || ((pt & 0x7F) < 35);
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35);
return rtp;
}
@ -70,8 +62,9 @@ bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
{
if (length < 12)
return false;
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
return h->version == 0b10;
unsigned char b = ((const unsigned char*)buffer)[0];
return (b & 0xC0 ) == 128;
}
bool RtpHelper::isRtcp(const void* buffer, size_t length)
@ -82,17 +75,9 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
{
if (isRtp(buffer, length))
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
else
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);
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
}
int RtpHelper::findPtype(const void* buffer, size_t length)
@ -199,3 +184,64 @@ void RtpDump::flush()
}
#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-2025 VoIPobjects (voipobjects.com)
/* Copyright(C) 2007-2017 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/. */
@ -10,8 +10,11 @@
# include "jrtplib/src/rtppacket.h"
#endif
#include <cstdint>
#include <stdlib.h>
#include "HL_Uuid.h"
#include "HL_InternetAddress.h"
#include <vector>
#include <string>
// Class to carry rtp/rtcp socket pair
template<class T>
@ -33,14 +36,13 @@ struct RtpPair
class RtpHelper
{
public:
static bool isRtp(const void* buffer, size_t length);
static int findPtype(const void* buffer, size_t length);
static int findPacketNo(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 isRtp(const void* buffer, size_t length);
static int findPtype(const void* buffer, size_t length);
static int findPacketNo(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 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);
};
#if defined(USE_RTPDUMP)
@ -70,4 +72,19 @@ public:
};
#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

View File

@ -3,7 +3,7 @@
* 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 "../engine_config.h"
#include "../config.h"
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
@ -127,7 +127,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
if (sock == INVALID_SOCKET)
{
// Return null socket
auto result = std::make_shared<DatagramSocket>();
PDatagramSocket result(new DatagramSocket());
result->mLocalPort = port;
result->mFamily = family;
return result;
@ -170,7 +170,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
closesocket(sock);
throw Exception(ERR_NET_FAILED, WSAGetLastError());
}
auto resultObject = std::make_shared<DatagramSocket>();
PDatagramSocket resultObject(new DatagramSocket());
resultObject->mLocalPort = testport;
resultObject->mHandle = sock;
if (!resultObject->setBlocking(false))

View File

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

View File

@ -1,82 +0,0 @@
#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-2023 VoIP objects (voipobjects.com)
/* Copyright(C) 2007-2016 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/. */
@ -10,13 +10,13 @@
enum class StreamState
{
Sending = 1, // Transmitting RTP. Set this flag to allow outgoing media stream.
Sending = 1, // Transmitting RTP. Set this flag to allow outgoing media stream.
Receiving = 2, // Receiving RTP. Set this flag to allow receiving media stream.
Playing = 4, // Play to audio. Unmutes the audio from specified stream.
Grabbing = 8, // Capture audio. Unmutes the audio to specified stream.
Srtp = 16, // Use SRTP. Make attempt
SipSend = 32, // Declare send capability in SDP
SipRecv = 64 // Declare recv capability in SDP
Playing = 4, // Play to audio. Unmutes the audio from specified stream.
Grabbing = 8, // Capture audio. Unmutes the audio to specified stream.
Srtp = 16, // Use SRTP. Make attempt
SipSend = 32, // Declare send capability in SDP
SipRecv = 64 // Declare recv capability in SDP
};

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
/* Copyright(C) 2007-2017 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/. */
@ -8,7 +8,6 @@
#include <iomanip>
#include <memory.h>
#include <algorithm>
#include <inttypes.h>
#ifdef TARGET_WIN
# include <WinSock2.h>
@ -16,7 +15,7 @@
# include <cctype>
#endif
std::string strx::extractFilename(const std::string& path)
std::string StringHelper::extractFilename(const std::string& path)
{
if (path.empty())
return std::string();
@ -31,7 +30,7 @@ std::string strx::extractFilename(const std::string& path)
return path.substr(p_bs + 1);
}
std::string strx::appendPath(const std::string& s1, const std::string& s2)
std::string StringHelper::appendPath(const std::string& s1, const std::string& s2)
{
std::string result = s1;
if (!endsWith(result, "/") && !endsWith(result, "\\"))
@ -45,7 +44,7 @@ std::string strx::appendPath(const std::string& s1, const std::string& s2)
return result + s2;
}
std::string strx::makeUtf8(const std::tstring &arg)
std::string StringHelper::makeUtf8(const std::tstring &arg)
{
#if defined(TARGET_WIN)
size_t required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
@ -57,12 +56,12 @@ std::string strx::makeUtf8(const std::tstring &arg)
#endif
}
std::string strx::toUtf8(const std::tstring &arg)
std::string StringHelper::toUtf8(const std::tstring &arg)
{
return makeUtf8(arg);
}
std::tstring strx::makeTstring(const std::string& arg)
std::tstring StringHelper::makeTstring(const std::string& arg)
{
#if defined(TARGET_WIN)
size_t count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0);
@ -74,7 +73,7 @@ std::tstring strx::makeTstring(const std::string& arg)
#endif
}
int strx::toInt(const char *s, int defaultValue, bool* isOk)
int StringHelper::toInt(const char *s, int defaultValue, bool* isOk)
{
int result;
if (sscanf(s, "%d", &result) != 1)
@ -90,7 +89,7 @@ int strx::toInt(const char *s, int defaultValue, bool* isOk)
return result;
}
uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
uint64_t StringHelper::toUint64(const char* s, uint64_t def, bool *isOk)
{
uint64_t result = def;
#if defined(TARGET_WIN)
@ -110,24 +109,24 @@ uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
return result;
}
std::string strx::toHex(unsigned int value)
std::string StringHelper::toHex(unsigned int value)
{
char buffer[32];
sprintf(buffer, "%x", value);
return buffer;
}
std::string strx::toHex(const void *ptr)
std::string StringHelper::toHex(const void *ptr)
{
std::ostringstream oss;
oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr;
oss << std::hex << ptr;
return oss.str();
}
//must be lowercase for MD5
static const char hexmap[] = "0123456789abcdef";
std::string strx::toHex(const uint8_t* input, size_t inputLength)
std::string StringHelper::toHex(const uint8_t* input, size_t inputLength)
{
std::string result; result.resize(inputLength * 2);
@ -148,7 +147,7 @@ std::string strx::toHex(const uint8_t* input, size_t inputLength)
return result;
}
std::string strx::prefixLines(const std::string &source, const std::string &prefix)
std::string StringHelper::prefixLines(const std::string &source, const std::string &prefix)
{
// Read source line by line
std::istringstream iss(source);
@ -161,7 +160,7 @@ std::string strx::prefixLines(const std::string &source, const std::string &pref
return oss.str();
}
std::string strx::doubleToString(double value, int precision)
std::string StringHelper::doubleToString(double value, int precision)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(precision) << value;
@ -169,7 +168,7 @@ std::string strx::doubleToString(double value, int precision)
}
const char* strx::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
const char* StringHelper::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
{
#if defined(TARGET_WIN)
return (const char*)strstr(buffer, substring);
@ -179,7 +178,7 @@ const char* strx::findSubstring(const char* buffer, const char* substring, size_
}
void strx::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
void StringHelper::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
{
dst.clear();
std::string::size_type p = 0;
@ -203,21 +202,21 @@ void strx::split(const std::string& src, std::vector<std::string>& dst, const st
}
}
std::vector<std::string> strx::split(const std::string& src, const std::string& delims)
std::vector<std::string> StringHelper::split(const std::string& src, const std::string& delims)
{
std::vector<std::string> r;
split(src, r, delims);
return r;
}
std::pair<std::string, int> strx::parseHost(const std::string& host, int defaultPort)
std::pair<std::string, int> StringHelper::parseHost(const std::string& host, int defaultPort)
{
std::pair<std::string, int> result;
std::size_t p = host.find(':');
if (p != std::string::npos)
{
result.first = host.substr(0, p);
result.second = strx::toInt(host.c_str() + p + 1, defaultPort);
result.second = StringHelper::toInt(host.c_str() + p + 1, defaultPort);
}
else
{
@ -227,15 +226,15 @@ std::pair<std::string, int> strx::parseHost(const std::string& host, int default
return result;
}
std::pair<std::string, std::string> strx::parseAssignment(const std::string& s, bool trimQuotes)
std::pair<std::string, std::string> StringHelper::parseAssignment(const std::string& s, bool trimQuotes)
{
std::pair<std::string, std::string> result;
std::string::size_type p = s.find('=');
if (p != std::string::npos)
{
result.first = strx::trim(s.substr(0, p));
result.second = strx::trim(s.substr(p+1));
result.first = StringHelper::trim(s.substr(0, p));
result.second = StringHelper::trim(s.substr(p+1));
if (trimQuotes && result.second.size() >= 2)
{
if ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') ||
@ -244,19 +243,19 @@ std::pair<std::string, std::string> strx::parseAssignment(const std::string& s,
}
}
else
result.first = strx::trim(s);
result.first = StringHelper::trim(s);
return result;
}
std::string strx::intToString(int value)
std::string StringHelper::intToString(int value)
{
char buffer[32];
sprintf(buffer, "%d", value);
return buffer;
}
float strx::toFloat(const std::string &s, float v, bool* isOk)
float StringHelper::toFloat(const std::string &s, float v, bool* isOk)
{
float result = 0.0;
int code = sscanf(s.c_str(), "%f", &result);
@ -275,14 +274,14 @@ float strx::toFloat(const std::string &s, float v, bool* isOk)
return result;
}
std::string strx::trim(const std::string &s)
std::string StringHelper::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 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));
}
std::string strx::timeToString(time_t t)
std::string StringHelper::timeToString(time_t t)
{
char buffer[128] = "";
struct tm lt;
@ -296,12 +295,12 @@ std::string strx::timeToString(time_t t)
return buffer;
}
std::string strx::millisecondsToString(uint64_t t)
std::string StringHelper::millisecondsToString(uint64_t t)
{
return timeToString(t/1000);
}
int strx::fromHex2Int(const std::string &s)
int StringHelper::fromHex2Int(const std::string &s)
{
int result = 0;
sscanf(s.c_str(), "%x", &result);
@ -319,12 +318,12 @@ static int hex2code(char s)
return 0;
}
/*static int hex2code(const char* s)
static int hex2code(const char* s)
{
return (hex2code(s[0]) << 4) + hex2code(s[1]);
}*/
}
std::string strx::fromHex2String(const std::string& s)
std::string StringHelper::fromHex2String(const std::string& s)
{
std::string result; result.resize(s.size() / 2);
const char* t = s.c_str();
@ -334,7 +333,7 @@ std::string strx::fromHex2String(const std::string& s)
return result;
}
std::string strx::replace(const std::string& s, char f, char r)
std::string StringHelper::replace(const std::string& s, char f, char r)
{
std::string result(s);
for (std::string::size_type i = 0; i < result.size(); i++)
@ -344,7 +343,7 @@ std::string strx::replace(const std::string& s, char f, char r)
return result;
}
std::string strx::replace(const std::string& s, const std::string& tmpl, const std::string& n)
std::string StringHelper::replace(const std::string& s, const std::string& tmpl, const std::string& n)
{
std::string result(s);
std::string::size_type p = 0;
@ -357,7 +356,7 @@ std::string strx::replace(const std::string& s, const std::string& tmpl, const s
return result;
}
std::string strx::decodeUri(const std::string& s)
std::string StringHelper::decodeUri(const std::string& s)
{
std::string ret;
ret.reserve(s.size());
@ -380,19 +379,19 @@ std::string strx::decodeUri(const std::string& s)
return ret;
}
bool strx::startsWith(const std::string& s, const std::string& prefix)
bool StringHelper::startsWith(const std::string& s, const std::string& prefix)
{
std::string::size_type p = s.find(prefix);
return p == 0;
}
bool strx::endsWith(const std::string& s, const std::string& suffix)
bool StringHelper::endsWith(const std::string& s, const std::string& suffix)
{
std::string::size_type p = s.rfind(suffix);
return (p == s.size() - suffix.size());
}
int strx::stringToDuration(const std::string& s)
int StringHelper::stringToDuration(const std::string& s)
{
if (endsWith(s, "ms"))
return std::stoi(s.substr(0, s.size()-2));
@ -406,52 +405,92 @@ int strx::stringToDuration(const std::string& s)
return std::stoi(s) * 1000;
}
std::string strx::uppercase(const std::string& s)
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
// --------------------- XcapHelper -----------------
std::string XcapHelper::buildBuddyList(std::string listName, std::vector<std::string> buddies)
{
std::string r(s);
std::transform(r.begin(), r.end(), r.begin(), ::toupper);
return r;
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 strx::lowercase(const std::string& s)
std::string XcapHelper::buildRules(std::vector<std::string> buddies)
{
std::string r(s);
std::transform(r.begin(), r.end(), r.begin(), ::tolower);
return r;
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 strx::removeQuotes(const std::string& s)
std::string XcapHelper::buildServices(std::string serviceUri, std::string listRef)
{
std::string r(s);
if (s.empty())
return s;
std::ostringstream result;
if (r.front() == '"')
r = r.substr(1);
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>";
if (r.back() == '"')
r = r.substr(0, r.size()-1);
return r;
return result.str();
}
#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)
std::string XcapHelper::normalizeSipUri(std::string uri)
{
if (!haystack || !haystack_len || !needle || !needle_len)
return nullptr;
for (const char *h = (const char*)haystack;
haystack_len >= needle_len;
++h, --haystack_len) {
if (!memcmp(h, needle, needle_len)) {
return h;
if (uri.length())
{
if (uri[0] == '<')
uri.erase(0,1);
if (uri.length())
{
if (uri[uri.length()-1] == '>')
uri.erase(uri.length()-1, 1);
}
}
return nullptr;
return uri;
}
#endif

View File

@ -9,7 +9,6 @@
#include <vector>
#include <string>
#include <sstream>
#include <cstdint>
#include "HL_Types.h"
#ifdef TARGET_OSX
@ -17,7 +16,7 @@
#endif
class strx
class StringHelper
{
public:
static std::string extractFilename(const std::string& path);
@ -26,7 +25,6 @@ public:
static std::string makeUtf8(const std::tstring& arg);
static std::string toUtf8(const std::tstring& arg);
static std::tstring makeTstring(const std::string& arg);
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 std::string toHex(unsigned int value);
@ -35,14 +33,9 @@ public:
static std::string intToString(int value);
static std::string prefixLines(const std::string& source, const std::string& prefix);
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 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");
template <typename T>
@ -51,7 +44,7 @@ public:
std::ostringstream s;
for (const auto& i : v)
{
if (&i != &v.front())
if (&i != &v[0])
s << delimiter;
s << i;
}
@ -60,19 +53,18 @@ public:
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 float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
static std::string trim(const std::string& s);
static std::string timeToString(time_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, const std::string& tmpl, const std::string& n);
static std::string decodeUri(const std::string& s);
static bool startsWith(const std::string& s, const std::string& prefix);
static bool endsWith(const std::string& s, const std::string& suffix);
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
@ -85,11 +77,4 @@ 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

View File

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

View File

@ -14,11 +14,6 @@
#include <functional>
#include <assert.h>
#if defined(TARGET_WIN)
# include <WinSock2.h>
# include <Windows.h>
#endif
typedef std::recursive_mutex Mutex;
typedef std::unique_lock<std::recursive_mutex> Lock;
@ -51,7 +46,7 @@ public:
static uint64_t getCurrentId();
};
class chronox
class TimeHelper
{
public:
// Returns current timestamp in milliseconds
@ -64,14 +59,6 @@ public:
// Handles cases when clock is wrapped.
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
{
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();
for(size_t idx = 0; idx < num_of_threads; idx++)
this->workers.emplace_back(std::thread(&thread_pool::run_worker, this));
this->workers.push_back(std::thread(&thread_pool::run_worker, this));
}
// Add new work item to the pool
@ -20,22 +20,6 @@ void thread_pool::enqueue(const thread_pool::task& t)
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
thread_pool::~thread_pool()
@ -53,7 +37,7 @@ void thread_pool::run_worker()
{
ThreadHelper::setName(this->name);
task t;
while (!this->stop)
while(!this->stop)
{
{
std::unique_lock<std::mutex> lock(this->queue_mutex);

View File

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

View File

@ -1,53 +0,0 @@
#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

@ -1,17 +0,0 @@
#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

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

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
/* Copyright(C) 2007-2014 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/. */
@ -26,139 +26,4 @@ enum SdpDirection
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

View File

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

View File

@ -2,19 +2,39 @@
#define __HL_UUID_H
#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
{
public:
Uuid();
static Uuid generateOne();
static Uuid parse(const std::string& s);
std::string toString() const;
bool operator < (const Uuid& right) const;
Uuid();
static Uuid generateOne();
static Uuid parse(const std::string& s);
std::string toString() const;
bool operator < (const Uuid& right) const;
protected:
uint8_t mUuid[16];
#if defined(USE_NULL_UUID)
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

@ -1,109 +0,0 @@
/* 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

@ -1,23 +0,0 @@
/* 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,97 +1,31 @@
project (media_lib)
if (NOT DEFINED LIB_PLATFORM)
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)
# Rely on C++ 11
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
# Produce PIC code always
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
set (SOURCES
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
#set (USE_RESIP_INTEGRATION CACHE BOOL OFF "Integrate with resip structures")
file(GLOB MEDIA_LIB_SOURCES "*.cpp" "*.h")
MT_Statistics.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})
if(CMAKE_SYSTEM MATCHES "Linux*")
add_definitions(-DHAVE_NETINET_IN_H)
endif()
if (USE_EVS_CODEC)
message("Media: EVS codec will be included.")
target_compile_definitions (media_lib PUBLIC USE_EVS_CODEC)
list (APPEND LIBS_CODEC evs_codec)
if(CMAKE_SYSTEM MATCHES "Darwin*")
# OS X Specific flags
add_definitions(-DHAVE_NETINET_IN_H)
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*")
# Windows Specific flags - MSVC expected
target_compile_definitions(media_lib PRIVATE
_CRT_SECURE_NO_WARNINGS
HAVE_WINSOCK2_H
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
UNICODE
_UNICODE )
add_definitions(-D_CRT_SECURE_NO_WARNINGS -DHAVE_WINSOCK2_H
-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -DUNICODE -D_UNICODE )
endif()
# 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>")
add_library(media_lib ${MEDIA_LIB_SOURCES})
target_include_directories(media_lib
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/
@ -99,9 +33,9 @@ target_include_directories(media_lib
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/opus/include/
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs
${LIB_PLATFORM}/opus/include
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/
)
target_include_directories(media_lib
@ -111,5 +45,8 @@ target_include_directories(media_lib
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_op
${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,30 +5,85 @@
#include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_IuUP.h"
#include "../helper/HL_Exception.h"
#include <iostream>
#define LOG_SUBSYSTEM "AmrCodec"
using namespace MT;
// Constant of AMR-NB frame lengths in bytes.
const uint8_t amrnb_framelen[9] =
{12, 13, 15, 17, 19, 20, 26, 31, 5};
static const uint8_t amr_block_size[16]={ 13, 14, 16, 18, 20, 21, 27, 32,
6 , 0 , 0 , 0 , 0 , 0 , 0 , 1 };
/**
* 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] =
{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.
const uint8_t amrwb_framelen[10] =
{17, 23, 32, 37, 40, 46, 50, 58, 60, 5 /* SID packet */};
/**
* Constant of AMR-WB frame lengths in bytes.
*/
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] =
{132, 177, 253, 285, 317, 365, 397, 461, 477, 40 /* SID packet */};
{132, 177, 253, 285, 317, 365, 397, 461, 477, 40};
/**
* Constant of AMR-WB bitrates.
*/
const uint16_t amrwb_bitrates[9] =
{6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850};
// 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
{
const uint8_t* mPayload;
@ -64,13 +119,12 @@ struct AmrPayload
static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
{
AmrPayload result;
// Do not skip packet by default; I suppose packet is good enough by default.
result.mDiscardPacket = false;
// Wrap incoming data with ByteArray to make bit dequeuing easy
ByteBuffer byte_reader(input.mPayload, static_cast<size_t>(input.mPayloadLength));
BitReader bit_reader (input.mPayload, static_cast<size_t>(input.mPayloadLength));
ByteBuffer dataIn(input.mPayload, static_cast<size_t>(input.mPayloadLength));
BitReader br(input.mPayload, static_cast<size_t>(input.mPayloadLength));
// In bandwidth-efficient mode, the payload header simply consists of a
// 4-bit codec mode request:
@ -81,15 +135,16 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
// 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
// is present, and other values are for future use.
result.mCodeModeRequest = static_cast<uint8_t>(bit_reader.readBits(4));
result.mCodeModeRequest = static_cast<uint8_t>(br.readBits(4));
//ICELogMedia(<< "CMR: " << result.mCodeModeRequest);
// Consume extra 4 bits for octet aligned profile
if (input.mOctetAligned)
bit_reader.readBits(4);
br.readBits(4);
// Skip interleaving flags for now for octet aligned mode
if (input.mInterleaving && input.mOctetAligned)
bit_reader.readBits(8);
br.readBits(8);
// Silence codec mode constant (it differs for wideband and narrowband codecs)
uint8_t SID_FT = input.mWideband ? 9 : 8;
@ -103,121 +158,105 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
// 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
// this frame is the last frame in this payload.
F = bit_reader.readBit();
F = br.readBit();
// FT (4 bits): Frame type index, indicating either the AMR or AMR-WB
// speech coding mode or comfort noise (SID) mode of the
// corresponding frame carried in this payload.
FT = static_cast<uint8_t>(bit_reader.readBits(4));
FT = static_cast<uint8_t>(br.readBits(4));
// Q (1 bit): Frame quality indicator. If set to 0, indicates the
// corresponding frame is severely damaged, and the receiver should
// set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD
// depending on the frame type (FT).
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];
Q = br.readBit();
// 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
// avoid the loss of data synchronization in the depacketization
// process, which can result in a huge degradation in speech quality.
bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14);
// discard |= input.mWideband ? f.mFrameType >= 14 : f.mFrameType >= 15;
if (discard)
if ((input.mWideband && (FT >= 10 && FT <= 13)) ||
(!input.mWideband && (FT >= 9 && FT <= 14)))
{
ICELogMedia(<< "Discard corrupted packet");
// Discard bad packet
result.mDiscardPacket = true;
continue;
return result;
}
if (input.mWideband && f.mFrameType == 15)
{
// DTX, no sense to decode the data
continue;
}
// Handle padding for octet alignment
if (input.mOctetAligned)
br.readBits(2);
if (input.mWideband && f.mFrameType == 14)
{
// Speech lost code only
continue;
}
AmrFrame frame;
frame.mFrameType = FT;
if (!f.mGoodQuality)
{
// Bad quality, frame is damaged
continue;
}
frame.mMode = FT < SID_FT ? FT : 0xFF;
frame.mGoodQuality = Q == 1;
frame.mTimestamp = input.mCurrentTimestamp;
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];
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];
size_t bitsLength = input.mWideband ? amrwb_framelenbits[frame.mFrameType] : amrnb_framelenbits[frame.mFrameType];
size_t byteLength = input.mWideband ? amrwb_framelen[frame.mFrameType] : amrnb_framelen[frame.mFrameType];
ICELogMedia(<< "New AMR speech frame: frame type = " << FT <<
", mode = " << frame.mMode <<
", timestamp = " << static_cast<int>(frame.mTimestamp) <<
", bits length = " << static_cast<int>(bitsLength) <<
", byte length =" << static_cast<int>(byteLength) <<
", remaining packet length = " << static_cast<int>(dataIn.size()));
if (bitsLength > 0)
{
if (input.mOctetAligned)
{
if (byte_reader.size() < byteLength)
f.mGoodQuality = false;
if (dataIn.size() < byteLength)
frame.mGoodQuality = false;
else
{
// It is octet aligned scheme, so we are on byte boundary now
size_t byteOffset = bit_reader.position() / 8;
size_t byteOffset = br.count() / 8;
// Copy data of AMR frame
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;
}
frame.mData = std::make_shared<ByteBuffer>(input.mPayload + byteOffset, byteLength);
}
}
else
{
// Allocate place for copying
f.mData = std::make_shared<ByteBuffer>();
f.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
frame.mData = std::make_shared<ByteBuffer>();
frame.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
// Add header for decoder
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
frame.mData->mutableData()[0] = (frame.mFrameType << 3) | (1 << 2);
// Read bits
if (bit_reader.readBits(f.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
f.mGoodQuality = false;
if (br.readBits(frame.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
frame.mGoodQuality = false;
}
}
}
// 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;
}
/*static void predecodeAmrFrame(AmrFrame& frame, ByteBuffer& data)
{
// Data are already moved into
}*/
AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
:mConfig(config)
{
@ -239,9 +278,12 @@ int AmrNbCodec::CodecFactory::payloadType()
return mConfig.mPayloadType;
}
#ifdef USE_RESIP_INTEGRATION
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)
{
@ -253,6 +295,7 @@ void AmrNbCodec::CodecFactory::create(CodecMap& codecs)
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig));
}
#endif
PCodec AmrNbCodec::CodecFactory::create()
{
return PCodec(new AmrNbCodec(mConfig));
@ -335,9 +378,6 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
#define AMR_BITRATE_DTX 15
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
{
if (mConfig.mOctetAligned)
return 0;
if (mConfig.mIuUP)
{
// Try to parse IuUP frame
@ -445,7 +485,7 @@ int AmrNbCodec::plc(int lostFrames, void* output, int outputCapacity)
for (int i=0; i < lostFrames; i++)
{
uint8_t buffer[32];
unsigned char buffer[32];
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
dataOut += L_FRAME;
@ -479,8 +519,12 @@ int AmrWbCodec::CodecFactory::payloadType()
return mConfig.mPayloadType;
}
#ifdef USE_RESIP_INTEGRATION
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)
{
@ -492,17 +536,19 @@ void AmrWbCodec::CodecFactory::create(CodecMap& codecs)
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig));
}
#endif
PCodec AmrWbCodec::CodecFactory::create()
{
return PCodec(new AmrWbCodec(mConfig));
}
AmrWbStatistics MT::GAmrWbStatistics;
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config),
mSwitchCounter(0), mPreviousPacketLength(0)
{
//mEncoderCtx = E_IF_init();
mDecoderCtx = D_IF_init();
mCurrentDecoderTimestamp = 0;
}
@ -549,104 +595,110 @@ int AmrWbCodec::samplerate()
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{
throw Exception(ERR_NOT_IMPLEMENTED);
if (inputBytes % pcmLength())
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 AMR_BITRATE_DTX 15
int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output)
{
IuUP::Frame frame;
if (!IuUP::parse2(input.data(), input.size(), frame))
return 0;
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
{
ICELogInfo(<< "CRC check failed.");
return 0;
}
// Reserve space
ByteBuffer dataToDecode;
dataToDecode.resize(1 + frame.mPayloadSize);
// Copy AMR data
memmove(dataToDecode.mutableData() + 1, frame.mPayload, frame.mPayloadSize);
uint8_t frameType = 0xFF;
for (uint8_t ftIndex = 0; ftIndex <= 9 && frameType == 0xFF; ftIndex++)
if (amrwb_framelen[ftIndex] == frame.mPayloadSize)
frameType = ftIndex;
if (frameType == 0xFF)
return 0;
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0);
return pcmLength();
}
int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output)
{
AmrPayloadInfo info;
info.mCurrentTimestamp = mCurrentDecoderTimestamp;
info.mOctetAligned = mConfig.mOctetAligned;
info.mPayload = input.data();
info.mPayloadLength = input.size();
info.mWideband = true;
info.mInterleaving = false;
AmrPayload ap;
try
{
ap = parseAmrPayload(info);
}
catch(...)
{
GAmrWbStatistics.mNonParsed++;
ICELogDebug(<< "Failed to decode AMR payload");
return 0;
}
// Save current timestamp
mCurrentDecoderTimestamp = info.mCurrentTimestamp;
// Check if packet is corrupted
if (ap.mDiscardPacket)
{
GAmrWbStatistics.mDiscarded++;
return 0;
}
// Check for output buffer capacity
if (output.size() < (int)ap.mFrames.size() * pcmLength())
return 0;
short* dataOut = (short*)output.data();
size_t dataOutSizeInBytes = 0;
for (AmrFrame& frame: ap.mFrames)
{
memset(dataOut, 0, static_cast<size_t>(pcmLength()));
if (frame.mData)
{
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
dataOut += pcmLength() / 2;
dataOutSizeInBytes += pcmLength();
}
}
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);
{
IuUP::Frame frame;
if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame))
return 0;
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
{
ICELogInfo(<< "CRC check failed.");
return 0;
}
// Build first byte to help decoder
//ICELogDebug(<< "Decoding AMR frame length = " << frame.mPayloadSize);
// Reserve space
ByteBuffer dataToDecode;
dataToDecode.resize(1 + frame.mPayloadSize);
// Copy AMR data
memmove(dataToDecode.mutableData() + 1, frame.mPayload, frame.mPayloadSize);
uint8_t frameType = 0xFF;
for (uint8_t ftIndex = 0; ftIndex <= 9 && frameType == 0xFF; ftIndex++)
if (amrwb_framelen[ftIndex] == frame.mPayloadSize)
frameType = ftIndex;
if (frameType == 0xFF)
return 0;
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0);
return pcmLength();
}
else
return decodePlain(inputBuffer, outputBuffer);
{
AmrPayloadInfo info;
info.mCurrentTimestamp = mCurrentDecoderTimestamp;
info.mOctetAligned = mConfig.mOctetAligned;
info.mPayload = (const uint8_t*)input;
info.mPayloadLength = inputBytes;
info.mWideband = true;
info.mInterleaving = false;
AmrPayload ap;
try
{
ap = parseAmrPayload(info);
}
catch(...)
{
ICELogDebug(<< "Failed to decode AMR payload");
return 0;
}
// Save current timestamp
mCurrentDecoderTimestamp = info.mCurrentTimestamp;
// Check if packet is corrupted
if (ap.mDiscardPacket)
return 0;
// Check for output buffer capacity
if (outputCapacity < (int)ap.mFrames.size() * pcmLength())
return 0;
short* dataOut = (short*)output;
for (AmrFrame& frame: ap.mFrames)
{
memset(dataOut, 0, static_cast<size_t>(pcmLength()));
if (frame.mData)
{
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
dataOut += pcmLength() / 2;
}
}
return pcmLength() * ap.mFrames.size();
}
return 0;
}
@ -698,6 +750,8 @@ int GsmEfrCodec::GsmEfrFactory::payloadType()
return mPayloadType;
}
#ifdef USE_RESIP_INTEGRATION
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{
@ -713,6 +767,8 @@ void GsmEfrCodec::GsmEfrFactory::create(CodecMap& codecs)
codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP));
}
#endif
PCodec GsmEfrCodec::GsmEfrFactory::create()
{
return PCodec(new GsmEfrCodec(mIuUP));

View File

@ -1,10 +1,8 @@
#ifndef MT_AMRCODEC_H
#define MT_AMRCODEC_H
#include "../engine_config.h"
#include "../config.h"
#include <map>
#include <span>
#include "MT_Codec.h"
#include "../helper/HL_Pointer.h"
@ -16,16 +14,16 @@
namespace MT
{
struct AmrCodecConfig
{
bool mIuUP = false;
bool mOctetAligned = false;
int mPayloadType = -1;
};
struct AmrCodecConfig
{
bool mIuUP;
bool mOctetAligned;
int mPayloadType;
};
class AmrNbCodec : public Codec
{
protected:
class AmrNbCodec : public Codec
{
protected:
void* mEncoderCtx;
void* mDecoderCtx;
AmrCodecConfig mConfig;
@ -33,24 +31,25 @@ protected:
int mSwitchCounter;
int mPreviousPacketLength;
public:
public:
class CodecFactory: public Factory
{
public:
CodecFactory(const AmrCodecConfig& config);
CodecFactory(const AmrCodecConfig& config);
const char* name() override;
int samplerate() override;
int payloadType() override;
const char* name() override;
int samplerate() override;
int payloadType() override;
void updateSdp(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;
PCodec create() override;
#ifdef USE_RESIP_INTEGRATION
void updateSdp(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;
#endif
PCodec create() override;
protected:
AmrCodecConfig mConfig;
AmrCodecConfig mConfig;
};
AmrNbCodec(const AmrCodecConfig& config);
@ -65,18 +64,11 @@ public:
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
int getSwitchCounter() const;
};
};
struct AmrWbStatistics
{
int mDiscarded = 0;
int mNonParsed = 0;
};
extern AmrWbStatistics GAmrWbStatistics;
class AmrWbCodec : public Codec
{
protected:
class AmrWbCodec : public Codec
{
protected:
void* mEncoderCtx;
void* mDecoderCtx;
AmrCodecConfig mConfig;
@ -84,27 +76,25 @@ protected:
int mSwitchCounter;
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
{
public:
CodecFactory(const AmrCodecConfig& config);
CodecFactory(const AmrCodecConfig& config);
const char* name() override;
int samplerate() override;
int payloadType() override;
const char* name() override;
int samplerate() override;
int payloadType() override;
void updateSdp(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;
PCodec create() override;
#ifdef USE_RESIP_INTEGRATION
void updateSdp(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;
#endif
PCodec create() override;
protected:
AmrCodecConfig mConfig;
AmrCodecConfig mConfig;
};
AmrWbCodec(const AmrCodecConfig& config);
@ -119,34 +109,35 @@ public:
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
int getSwitchCounter() const;
};
};
class GsmEfrCodec : public Codec
{
protected:
class GsmEfrCodec : public Codec
{
protected:
void* mEncoderCtx;
void* mDecoderCtx;
bool mIuUP;
public:
public:
class GsmEfrFactory: public Factory
{
public:
GsmEfrFactory(bool iuup, int ptype);
GsmEfrFactory(bool iuup, int ptype);
const char* name() override;
int samplerate() override;
int payloadType() override;
const char* name() override;
int samplerate() override;
int payloadType() override;
void updateSdp(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;
PCodec create() override;
#ifdef USE_RESIP_INTEGRATION
void updateSdp(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;
#endif
PCodec create() override;
protected:
bool mIuUP;
int mPayloadType;
bool mIuUP;
int mPayloadType;
};
GsmEfrCodec(bool iuup = false);
@ -160,7 +151,7 @@ public:
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
};
};
} // End of MT namespace

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,24 +1,19 @@
/* Copyright(C) 2007-2021 VoIPobjects (voipobjects.com)
/* Copyright(C) 2007-2018 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/. */
#if defined(TARGET_WIN) && !defined(NOMINMAX)
# define NOMINMAX
#endif
#define NOMINMAX
#include "../engine_config.h"
#include "../config.h"
#include "MT_AudioReceiver.h"
#include "MT_AudioCodec.h"
#include "MT_CngHelper.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_Time.h"
#include "../audio/Audio_Interface.h"
#include "../audio/Audio_Resampler.h"
#include <cmath>
#include <iostream>
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC)
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI)
# include "MT_AmrCodec.h"
#endif
@ -31,7 +26,7 @@
using namespace MT;
// ----------------- RtpBuffer::Packet --------------
RtpBuffer::Packet::Packet(const std::shared_ptr<RTPPacket>& packet, int timelength, int rate)
RtpBuffer::Packet::Packet(std::shared_ptr<RTPPacket> packet, int timelength, int rate)
:mRtp(packet), mTimelength(timelength), mRate(rate)
{
}
@ -51,22 +46,12 @@ int RtpBuffer::Packet::rate() const
return mRate;
}
const std::vector<short>& RtpBuffer::Packet::pcm() const
{
return mPcm;
}
std::vector<short>& RtpBuffer::Packet::pcm()
{
return mPcm;
}
// ------------ RtpBuffer ----------------
RtpBuffer::RtpBuffer(Statistics& stat)
:mStat(stat)
:mStat(stat), mSsrc(0), mHigh(RTP_BUFFER_HIGH), mLow(RTP_BUFFER_LOW), mPrebuffer(RTP_BUFFER_PREBUFFER),
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()
@ -110,27 +95,19 @@ int RtpBuffer::getCount() const
return static_cast<int>(mPacketList.size());
}
bool SequenceSort(const std::shared_ptr<RtpBuffer::Packet>& p1, const std::shared_ptr<RtpBuffer::Packet>& p2)
bool SequenceSort(const RtpBuffer::Packet& p1, const RtpBuffer::Packet& p2)
{
return p1->rtp()->GetExtendedSequenceNumber() < p2->rtp()->GetExtendedSequenceNumber();
return p1.rtp()->GetExtendedSequenceNumber() < p2.rtp()->GetExtendedSequenceNumber();
}
std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate)
bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate)
{
if (!packet)
return std::shared_ptr<Packet>();
return false;
Lock l(mGuard);
// 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());
// Update jitter
@ -143,15 +120,16 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPa
// New sequence number
unsigned newSeqno = packet->GetExtendedSequenceNumber();
for (std::shared_ptr<Packet>& p: mPacketList)
for (PacketList::iterator iter = mPacketList.begin(); iter != mPacketList.end(); iter++)
{
unsigned seqno = p->rtp()->GetExtendedSequenceNumber();
Packet& p = *iter;
unsigned seqno = p.rtp()->GetExtendedSequenceNumber();
if (seqno == newSeqno)
{
mStat.mDuplicatedRtp++;
ICELogMedia(<< "Discovered duplicated packet, skipping");
return std::shared_ptr<Packet>();
return false;
}
if (seqno > maxno)
@ -160,16 +138,12 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPa
minno = seqno;
}
// Get amount of available audio (in milliseconds) in jitter buffer
int available = findTimelength();
if (newSeqno > minno || (available < mHigh))
{
// Insert into queue
auto p = std::make_shared<Packet>(packet, timelength, rate);
Packet p(packet, timelength, rate);
mPacketList.push_back(p);
// Sort again
std::sort(mPacketList.begin(), mPacketList.end(), SequenceSort);
// Limit by max timelength
@ -177,18 +151,21 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPa
if (available > mHigh)
ICELogMedia(<< "Available " << available << "ms with limit " << mHigh << "ms");
return p;
/*while (available > mHigh && mPacketList.size())
{
ICELogDebug( << "Dropping RTP packet from jitter buffer");
available -= mPacketList.front().timelength();
mPacketList.erase(mPacketList.begin());
}*/
}
else
{
ICELogMedia(<< "Too old packet, skipping");
mStat.mOldRtp++;
return std::shared_ptr<Packet>();
return false;
}
return std::shared_ptr<Packet>();
return true;
}
RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
@ -201,65 +178,78 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
// See if there is enough information in buffer
int total = findTimelength();
while (total > mHigh && mPacketList.size() && 0 != mHigh)
while (total > mHigh && mPacketList.size())
{
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
mFetchedPacket = mPacketList.front();
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Erase from packet list
mPacketList.erase(mPacketList.begin());
// Increase number in statistics
mStat.mPacketDropped++;
}
if (total < mLow)
{
// Still not prebuffered
result = FetchResult::NoPacket;
}
else
{
// 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 (mFetchedPacket.rtp())
{
if (mPacketList.empty())
{
result = FetchResult::NoPacket;
// Don't increase counter of lost packets here; maybe it is DTX
mStat.mPacketLoss++;
}
else
{
// Current sequence number ?
uint32_t seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
unsigned seqno = mPacketList.front().rtp()->GetExtendedSequenceNumber();
// Gap between new packet and previous on
int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
int gap = seqno - mFetchedPacket.rtp()->GetSequenceNumber() - 1;
gap = std::min(gap, 127);
if (gap > 0)
if (gap > 0 && mPacketList.empty())
{
// std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl;
mStat.mPacketLoss++;
//mStat.mLoss[gap]++;
mLastSeqno = *mLastSeqno + 1;
result = FetchResult::Gap;
mStat.mPacketLoss += gap;
mStat.mLoss[gap]++;
}
else
{
if (gap > 0)
{
mStat.mPacketLoss += gap;
mStat.mLoss[gap]++;
}
result = FetchResult::RegularPacket;
rl.push_back(mPacketList.front());
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
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);
rl.push_back(p.rtp());
}
}*/
// Save last returned normal packet
mFetchedPacket = mPacketList.front();
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Remove returned packet from the list
mPacketList.erase(mPacketList.begin());
@ -269,17 +259,16 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
else
{
// See if prebuffer limit is reached
if (findTimelength() >= mPrebuffer && !mPacketList.empty())
if (findTimelength() >= mPrebuffer)
{
// Normal packet will be returned
result = FetchResult::RegularPacket;
// Put it to output list
rl.push_back(mPacketList.front());
rl.push_back(mPacketList.front().rtp());
// Remember returned packet
mFetchedPacket = mPacketList.front();
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Remove returned packet from buffer list
mPacketList.erase(mPacketList.begin());
@ -302,7 +291,7 @@ int RtpBuffer::findTimelength()
{
int available = 0;
for (unsigned i = 0; i < mPacketList.size(); i++)
available += mPacketList[i]->timelength();
available += mPacketList[i].timelength();
return available;
}
@ -328,7 +317,7 @@ Receiver::~Receiver()
//-------------- AudioReceiver ----------------
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
:Receiver(stat), mBuffer(stat), mCodecSettings(settings),
:Receiver(stat), mBuffer(stat), mFrameCount(0), mFailedCount(0), mCodecSettings(settings),
mCodecList(settings)
{
// Init resamplers
@ -338,7 +327,6 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
// Init codecs
mCodecList.setSettings(settings);
mCodecList.fillCodecMap(mCodecMap);
#if defined(DUMP_DECODED)
@ -349,6 +337,12 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
AudioReceiver::~AudioReceiver()
{
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
if (mPVQA && mPvqaBuffer)
{
mStat.mPvqaMos = calculatePvqaMos(AUDIO_SAMPLERATE, mStat.mPvqaReport);
}
#endif
mResampler8.stop();
mResampler16.stop();
mResampler32.stop();
@ -356,145 +350,63 @@ AudioReceiver::~AudioReceiver()
mDecodedDump.reset();
}
// Update codec settings
void AudioReceiver::setCodecSettings(const CodecList::Settings& codecSettings)
bool AudioReceiver::add(std::shared_ptr<jrtplib::RTPPacket> p, Codec** codec)
{
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
mStat.mCodecCount[ptype]++;
mStat.mCodecCount[p->GetPayloadType()]++;
// Check if codec can be handled
Codec* codec = nullptr;
CodecMap::iterator codecIter = mCodecMap.find(ptype);
CodecMap::iterator codecIter = mCodecMap.find(p->GetPayloadType());
if (codecIter == mCodecMap.end())
{
time_length = 10;
ICELogMedia(<< "Cannot find codec in available codecs");
return false; // Reject packet with unknown payload type
}
else
// Check if codec is created actually
if (!codecIter->second)
{
// Check if codec is creating lazily
if (!codecIter->second)
{
codecIter->second = mCodecList.createCodecByPayloadType(ptype);
}
codec = codecIter->second.get();
// 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();
// Look for ptype
for (int codecIndex = 0; codecIndex < mCodecList.count(); codecIndex++)
if (mCodecList.codecAt(codecIndex).payloadType() == p->GetPayloadType())
codecIter->second = mCodecList.codecAt(codecIndex).create();
}
// 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
mJitterStats.process(p.get(), samplerate);
mJitterStats.process(p.get(), codecIter->second->samplerate());
mStat.mJitter = static_cast<float>(mJitterStats.get());
// Check if packet is CNG
if (payloadLength >= 1 && payloadLength <= 6 && (ptype == 0 || ptype == 8))
time_length = mLastPacketTimeLength ? mLastPacketTimeLength : 20;
timelen = mLastPacketTimeLength ? mLastPacketTimeLength : 20;
else
// Check if packet is too short from time length side
if (time_length < 2)
{
// It will cause statistics to report about bad RTP packet
// I have to replay last packet payload here to avoid report about lost packet
mBuffer.add(p, time_length, samplerate);
return false;
}
// Check if packet is too short from time length side
if (timelen < 2)
{
// It will cause statistics to report about bad RTP packet
// I have to replay last packet payload here to avoid report about lost packet
mBuffer.add(p, timelen, codecIter->second->samplerate());
return false;
}
// Queue packet to buffer
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;
return mBuffer.add(p, timelen, codecIter->second->samplerate());
}
void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
@ -508,14 +420,18 @@ void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
makeMonoAndResample(resample ? mCodec->samplerate() : 0,
mCodec->channels());
// Update PVQA with stats
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
updatePvqa(mResampledFrame, mResampledLength);
#endif
// Send to output
output.add(mResampledFrame, mResampledLength);
}
AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
{
DecodeResult result = DecodeResult_Skip;
bool had_decode = false;
bool result = false;
// Get next packet from buffer
RtpBuffer::ResultList rl;
@ -523,7 +439,7 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
switch (fr)
{
case RtpBuffer::FetchResult::Gap:
ICELogDebug(<< "Gap detected.");
ICELogInfo(<< "Gap detected.");
mDecodedLength = mResampledLength = 0;
if (mCngPacket && mCodec)
@ -534,47 +450,42 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
reinterpret_cast<short*>(mDecodedFrame), false);
}
else
if (mCodec && mFrameCount && !mCodecSettings.mSkipDecode)
{
// Do PLC to mDecodedFrame/mDecodedLength
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
if (mCodec && mFrameCount && !mCodecSettings.mSkipDecode)
{
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);
}
// Do PLC to mDecodedFrame/mDecodedLength
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame);
}
}
if (mDecodedLength)
{
processDecoded(output, options);
result = DecodeResult_Ok;
result = true;
}
break;
case RtpBuffer::FetchResult::NoPacket:
ICELogDebug(<< "No packet available in jitter buffer");
mFailedCount++;
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
if (mResampledLength > 0)
updatePvqa(nullptr, mResampledLength);
#endif
break;
case RtpBuffer::FetchResult::RegularPacket:
mFailedCount = 0;
for (std::shared_ptr<RtpBuffer::Packet>& p: rl)
for (std::shared_ptr<RTPPacket>& p: rl)
{
assert(p);
// Check if previously CNG packet was detected. Emit CNG audio here if needed.
if (options & DecodeOptions_FillCngGap && mCngPacket && mCodec)
{
// Fill CNG audio is server mode is present
int units = p->rtp()->GetTimestamp() - mCngPacket->GetTimestamp();
int units = p->GetTimestamp() - mCngPacket->GetTimestamp();
int milliseconds = units / (mCodec->samplerate() / 1000);
if (milliseconds > mLastPacketTimeLength)
{
@ -603,91 +514,73 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
if (mDecodedLength)
processDecoded(output, options);
}
result = DecodeResult_Ok;
result = true;
}
}
if (mEarlyDecode)
// Find codec
mCodec = mCodecMap[p->GetPayloadType()];
if (mCodec)
{
// ToDo - copy the decoded data to output buffer
if (rate)
*rate = mCodec->samplerate();
}
else
{
// Find codec by payload type
int ptype = p->rtp()->GetPayloadType();
mCodec = mCodecMap[ptype];
if (mCodec)
// Check if it is CNG packet
if ((p->GetPayloadType() == 0 || p->GetPayloadType() == 8) && p->GetPayloadLength() >= 1 && p->GetPayloadLength() <= 6)
{
if (rate)
*rate = mCodec->samplerate();
// Check if it is CNG packet
if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6)
{
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
{
mCngPacket = p->rtp();
mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength());
// Emit CNG mLastPacketLength milliseconds
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength,
(short*)mDecodedFrame, true);
if (mDecodedLength)
processDecoded(output, options);
}
result = DecodeResult_Ok;
}
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
{
// Reset CNG packet
mCngPacket.reset();
// Handle here regular RTP packets
// Check if payload length is ok
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)
{
// Find number of frames
mFrameCount = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() / mCodec->rtpLength() : 1;
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->rtp()->GetPayloadLength();
// Save last packet time length
mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
// Decode
for (int i=0; i<mFrameCount && !mCodecSettings.mSkipDecode; i++)
{
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
{
// Trigger the statistics
had_decode = true;
// Decode frame by frame
mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
frameLength, mDecodedFrame, sizeof mDecodedFrame);
if (mDecodedLength > 0)
processDecoded(output, options);
}
}
result = mFrameCount > 0 ? DecodeResult_Ok : DecodeResult_Skip;
// Check for bitrate counter
processStatisticsWithAmrCodec(mCodec.get());
}
else
{
result = DecodeResult_BadPacket;
// ICELogMedia(<< "RTP packet with tail.");
}
mCngPacket = p;
mCngDecoder.decode3389(p->GetPayloadData(), p->GetPayloadLength());
// Emit CNG mLastPacketLength milliseconds
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength,
(short*)mDecodedFrame, true);
if (mDecodedLength)
processDecoded(output, options);
}
result = true;
}
else
{
// Reset CNG packet
mCngPacket.reset();
// Handle here regular RTP packets
// Check if payload length is ok
int tail = mCodec->rtpLength() ? p->GetPayloadLength() % mCodec->rtpLength() : 0;
if (!tail)
{
// Find number of frames
mFrameCount = mCodec->rtpLength() ? p->GetPayloadLength() / mCodec->rtpLength() : 1;
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->GetPayloadLength();
// Save last packet time length
mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
// Decode
for (int i=0; i<mFrameCount && !mCodecSettings.mSkipDecode; i++)
{
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
{
// Decode frame by frame
mDecodedLength = mCodec->decode(p->GetPayloadData() + i*mCodec->rtpLength(),
frameLength, mDecodedFrame, sizeof mDecodedFrame);
if (mDecodedLength)
processDecoded(output, options);
}
}
result = mFrameCount > 0;
// Check for bitrate counter
processStatisticsWithAmrCodec(mCodec.get());
}
else
ICELogMedia(<< "RTP packet with tail.");
}
}
}
@ -697,18 +590,6 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
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;
}
@ -754,10 +635,66 @@ Codec* AudioReceiver::findCodec(int payloadType)
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)
{
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC)
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI)
AmrNbCodec* nb = dynamic_cast<AmrNbCodec*>(c);
AmrWbCodec* wb = dynamic_cast<AmrWbCodec*>(c);

View File

@ -1,4 +1,4 @@
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
/* Copyright(C) 2007-2017 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/. */
@ -8,16 +8,25 @@
#include "MT_Stream.h"
#include "MT_CodecList.h"
#include "MT_AudioCodec.h"
#include "MT_CngHelper.h"
#include "../helper/HL_Pointer.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Optional.hpp"
#include "jrtplib/src/rtppacket.h"
#include "jrtplib/src/rtcppacket.h"
#include "jrtplib/src/rtpsourcedata.h"
#include "../audio/Audio_DataWindow.h"
#include "../audio/Audio_Resampler.h"
#include <optional>
#if defined(USE_PVQA_LIBRARY)
# include "pvqa++.h"
#endif
#include <map>
// #define DUMP_DECODED
namespace MT
{
@ -36,19 +45,14 @@ namespace MT
class Packet
{
public:
Packet(const std::shared_ptr<RTPPacket>& packet, int timelen, int rate);
Packet(std::shared_ptr<RTPPacket> packet, int timelen, int rate);
std::shared_ptr<RTPPacket> rtp() const;
int timelength() const;
int rate() const;
const std::vector<short>& pcm() const;
std::vector<short>& pcm();
protected:
std::shared_ptr<RTPPacket> mRtp;
int mTimelength = 0, mRate = 0;
std::vector<short> mPcm;
};
RtpBuffer(Statistics& stat);
@ -56,49 +60,35 @@ namespace MT
unsigned ssrc();
void setSsrc(unsigned ssrc);
void setHigh(int milliseconds);
int high();
void setLow(int milliseconds);
int low();
void setPrebuffer(int milliseconds);
int prebuffer();
int getNumberOfReturnedPackets() const;
int getNumberOfAddPackets() const;
int findTimelength();
int getCount() const;
// 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);
bool add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
typedef std::vector<std::shared_ptr<Packet>> ResultList;
typedef std::vector<std::shared_ptr<RTPPacket>> ResultList;
typedef std::shared_ptr<ResultList> PResultList;
FetchResult fetch(ResultList& rl);
protected:
unsigned mSsrc = 0;
int mHigh = RTP_BUFFER_HIGH,
mLow = RTP_BUFFER_LOW,
mPrebuffer = RTP_BUFFER_PREBUFFER;
int mReturnedCounter = 0,
mAddCounter = 0;
unsigned mSsrc;
int mHigh, mLow, mPrebuffer;
int mReturnedCounter, mAddCounter;
mutable Mutex mGuard;
typedef std::vector<std::shared_ptr<Packet>> PacketList;
typedef std::vector<Packet> PacketList;
PacketList mPacketList;
Statistics& mStat;
bool mFirstPacketWillGo = true;
bool mFirstPacketWillGo;
jrtplib::RTPSourceStats mRtpStats;
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;
Packet mFetchedPacket;
};
class Receiver
@ -117,12 +107,9 @@ namespace MT
AudioReceiver(const CodecList::Settings& codecSettings, Statistics& stat);
~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.
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
bool add(std::shared_ptr<jrtplib::RTPPacket> p, Codec** codec = nullptr);
// Returns false when there is no rtp data from jitter
enum DecodeOptions
@ -133,14 +120,7 @@ namespace MT
DecodeOptions_SkipDecode = 4
};
enum DecodeResult
{
DecodeResult_Ok,
DecodeResult_Skip,
DecodeResult_BadPacket
};
DecodeResult getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr);
bool getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr);
// Looks for codec by payload type
Codec* findCodec(int payloadType);
@ -166,9 +146,6 @@ namespace MT
std::shared_ptr<jrtplib::RTPPacket> mCngPacket;
CngDecoder mCngDecoder;
// Decode RTP early, do not wait for speaker callback
bool mEarlyDecode = false;
// Buffer to hold decoded data
char mDecodedFrame[65536];
int mDecodedLength = 0;
@ -184,23 +161,26 @@ namespace MT
// Last packet time length
int mLastPacketTimeLength = 0;
int mFailedCount = 0;
int mFailedCount;
Audio::Resampler mResampler8, mResampler16,
mResampler32, mResampler48;
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
void makeMonoAndResample(int rate, int channels);
// Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame
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);
};

View File

@ -22,416 +22,409 @@
using namespace MT;
AudioStream::AudioStream(const CodecList::Settings& settings)
:mPacketTime(0), mEncodedTime(0), mCodecSettings(settings),
mRemoteTelephoneCodec(0), mRtpSession(), mTransmittingPayloadType(-1),
mRtpSender(mStat)
:mPacketTime(0), mEncodedTime(0), mCodecSettings(settings),
mRemoteTelephoneCodec(0), mRtpSession(), mTransmittingPayloadType(-1),
mRtpSender(mStat)
{
mOutputBuffer.setCapacity(16384);
mCapturedAudio.setCapacity(16384);
mCaptureResampler8.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 8000);
mCaptureResampler16.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 16000);
mCaptureResampler32.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 32000);
mCaptureResampler48.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 48000);
mOutputBuffer.setCapacity(16384);
mCapturedAudio.setCapacity(16384);
mCaptureResampler8.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 8000);
mCaptureResampler16.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 16000);
mCaptureResampler32.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 32000);
mCaptureResampler48.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 48000);
// Configure transmitter
jrtplib::RTPExternalTransmissionParams params(&mRtpSender, 0);
jrtplib::RTPSessionParams sessionParams;
sessionParams.SetAcceptOwnPackets(true);
sessionParams.SetMaximumPacketSize(MT_MAXRTPPACKET);
sessionParams.SetResolveLocalHostname(false);
sessionParams.SetUsePollThread(false);
sessionParams.SetOwnTimestampUnit(1/8000.0);
mRtpSession.Create(sessionParams, &params, jrtplib::RTPTransmitter::ExternalProto);
mRtpDtmfSession.Create(sessionParams, &params, jrtplib::RTPTransmitter::ExternalProto);
// Attach srtp session to sender
mRtpSender.setSrtpSession(&mSrtpSession);
//mRtpDump = new RtpDump("d:\\outgoing.rtp");
//mRtpSender.setDumpWriter(mRtpDump);
// Configure transmitter
jrtplib::RTPExternalTransmissionParams params(&mRtpSender, 0);
jrtplib::RTPSessionParams sessionParams;
sessionParams.SetAcceptOwnPackets(true);
sessionParams.SetMaximumPacketSize(MT_MAXRTPPACKET);
sessionParams.SetResolveLocalHostname(false);
sessionParams.SetUsePollThread(false);
sessionParams.SetOwnTimestampUnit(1/8000.0);
mRtpSession.Create(sessionParams, &params, jrtplib::RTPTransmitter::ExternalProto);
mRtpDtmfSession.Create(sessionParams, &params, jrtplib::RTPTransmitter::ExternalProto);
// Attach srtp session to sender
mRtpSender.setSrtpSession(&mSrtpSession);
//mRtpDump = new RtpDump("d:\\outgoing.rtp");
//mRtpSender.setDumpWriter(mRtpDump);
#if defined(DUMP_SENDING_AUDIO)
mSendingDump = std::make_shared<WavFileWriter>();
mSendingDump->open("sending_audio.wav", 8000, 1);
mSendingDump = std::make_shared<WavFileWriter>();
mSendingDump->open("sending_audio.wav", 8000, 1);
#endif
}
AudioStream::~AudioStream()
{
ICELogInfo(<< "Delete AudioStream instance");
if (mSendingDump)
{
mSendingDump->close();
mSendingDump.reset();
}
ICELogInfo(<< "Delete AudioStream instance");
if (mSendingDump)
{
mSendingDump->close();
mSendingDump.reset();
}
// Delete used rtp streams
for (AudioStreamMap::iterator streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
delete streamIter->second;
mStreamMap.clear();
// Delete used rtp streams
for (AudioStreamMap::iterator streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
delete streamIter->second;
mStreamMap.clear();
if (mRtpDtmfSession.IsActive())
mRtpDtmfSession.Destroy();
if (mRtpSession.IsActive())
mRtpSession.Destroy();
if (mRtpDtmfSession.IsActive())
mRtpDtmfSession.Destroy();
if (mRtpSession.IsActive())
mRtpSession.Destroy();
#if defined(USE_RTPDUMP)
if (mRtpDump)
{
mRtpDump->flush();
delete mRtpDump;
}
if (mRtpDump)
{
mRtpDump->flush();
delete mRtpDump;
}
#endif
mCaptureResampler8.stop();
mCaptureResampler16.stop();
mCaptureResampler32.stop();
mCaptureResampler48.stop();
ICELogInfo(<< "Encoded " << mEncodedTime << " milliseconds of audio");
mCaptureResampler8.stop();
mCaptureResampler16.stop();
mCaptureResampler32.stop();
mCaptureResampler48.stop();
ICELogInfo(<< "Encoded " << mEncodedTime << " milliseconds of audio");
if (mDumpStreams.mStreamForRecordingIncoming)
mDumpStreams.mStreamForRecordingIncoming->close();
if (mDumpStreams.mStreamForReadingOutgoing)
mDumpStreams.mStreamForReadingOutgoing->close();
if (mDumpStreams.mStreamForRecordingIncoming)
mDumpStreams.mStreamForRecordingIncoming->close();
if (mDumpStreams.mStreamForReadingOutgoing)
mDumpStreams.mStreamForReadingOutgoing->close();
if (mFinalStatistics)
*mFinalStatistics = mStat;
if (mFinalStatistics)
*mFinalStatistics = mStat;
ICELogInfo(<< mStat.toString());
ICELogInfo(<< mStat.toShortString());
}
void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)
{
Lock l(mMutex);
Stream::setDestination(dest);
mRtpSender.setDestination(dest);
Lock l(mMutex);
Stream::setDestination(dest);
mRtpSender.setDestination(dest);
}
void AudioStream::setTransmittingCodec(Codec::Factory& factory, int payloadType)
{
ICELogInfo(<< "Selected codec " << factory.name() << "/" << factory.samplerate() << " for transmitting");
Lock l(mMutex);
mTransmittingCodec = factory.create();
mTransmittingPayloadType = payloadType;
if (mRtpSession.IsActive())
mRtpSession.SetTimestampUnit(1.0 / mTransmittingCodec->samplerate());
ICELogInfo(<< "Selected codec " << factory.name() << "/" << factory.samplerate() << " for transmitting");
Lock l(mMutex);
mTransmittingCodec = factory.create();
mTransmittingPayloadType = payloadType;
if (mRtpSession.IsActive())
mRtpSession.SetTimestampUnit(1.0 / mTransmittingCodec->samplerate());
}
PCodec AudioStream::transmittingCodec()
{
Lock l(mMutex);
return mTransmittingCodec;
Lock l(mMutex);
return mTransmittingCodec;
}
void AudioStream::addData(const void* buffer, int bytes)
{
assert(bytes == AUDIO_MIC_BUFFER_SIZE);
assert(bytes == AUDIO_MIC_BUFFER_SIZE);
// Read predefined audio if configured
if (mDumpStreams.mStreamForReadingOutgoing)
{
if (mDumpStreams.mStreamForReadingOutgoing->isOpened())
mDumpStreams.mStreamForReadingOutgoing->read(const_cast<void*>(buffer), bytes);
}
// Read predefined audio if configured
if (mDumpStreams.mStreamForReadingOutgoing)
{
if (mDumpStreams.mStreamForReadingOutgoing->isOpened())
mDumpStreams.mStreamForReadingOutgoing->read(const_cast<void*>(buffer), bytes);
}
// Read mirrored audio if needed
if (mMirror && mMirrorPrebuffered)
mMirrorBuffer.read(const_cast<void*>(buffer), bytes);
// Read mirrored audio if needed
if (mMirror && mMirrorPrebuffered)
mMirrorBuffer.read(const_cast<void*>(buffer), bytes);
if (mMediaObserver)
mMediaObserver->onMedia(buffer, bytes, MT::Stream::MediaDirection::Outgoing, this, mMediaObserverTag);
if (mMediaObserver)
mMediaObserver->onMedia(buffer, bytes, MT::Stream::MediaDirection::Outgoing, this, mMediaObserverTag);
Codec* codec = nullptr;
{
Lock l(mMutex);
codec = mTransmittingCodec.get();
if (nullptr == codec) {
// ICELogDebug(<< "No transmitting codec selected.");
return;
}
}
Codec* codec = nullptr;
{
Lock l(mMutex);
codec = mTransmittingCodec.get();
if (!codec)
return;
}
// Resample
unsigned dstlen = unsigned(float(codec->samplerate() / float(AUDIO_SAMPLERATE)) * bytes);
Audio::Resampler* r = nullptr;
switch (codec->samplerate())
{
case 8000: r = &mCaptureResampler8; break;
case 16000: r = &mCaptureResampler16; break;
case 32000: r = &mCaptureResampler32; break;
case 48000: r = &mCaptureResampler48; break;
default:
assert(0);
}
// Resample
unsigned dstlen = unsigned(float(codec->samplerate() / float(AUDIO_SAMPLERATE)) * bytes);
Audio::Resampler* r = nullptr;
switch (codec->samplerate())
{
case 8000: r = &mCaptureResampler8; break;
case 16000: r = &mCaptureResampler16; break;
case 32000: r = &mCaptureResampler32; break;
case 48000: r = &mCaptureResampler48; break;
default:
assert(0);
}
size_t processedInput = 0;
dstlen = r->processBuffer(buffer, bytes, processedInput, mResampleBuffer, dstlen);
// ProcessedInput output value is ignored - because sample rate of input is always 8/16/32/48K - so all buffer is processed
size_t processedInput = 0;
dstlen = r->processBuffer(buffer, bytes, processedInput, mResampleBuffer, dstlen);
// ProcessedInput output value is ignored - because sample rate of input is always 8/16/32/48K - so all buffer is processed
// See if we need stereo <-> mono conversions
unsigned stereolen = 0;
if (codec->channels() != AUDIO_CHANNELS)
{
if (codec->channels() == 2)
stereolen = Audio::ChannelConverter::monoToStereo(mResampleBuffer, dstlen, mStereoBuffer, dstlen * 2);
else
dstlen = Audio::ChannelConverter::stereoToMono(mResampleBuffer, dstlen, mResampleBuffer, dstlen / 2);
}
// See if inband dtmf audio should be sent instead
ByteBuffer dtmf;
if (mDtmfContext.type() == DtmfContext::Dtmf_Inband && mDtmfContext.getInband(AUDIO_MIC_BUFFER_LENGTH, codec->samplerate(), dtmf))
mCapturedAudio.add(dtmf.data(), dtmf.size());
// See if we need stereo <-> mono conversions
unsigned stereolen = 0;
if (codec->channels() != AUDIO_CHANNELS)
{
if (codec->channels() == 2)
stereolen = Audio::ChannelConverter::monoToStereo(mResampleBuffer, dstlen, mStereoBuffer, dstlen * 2);
else
mCapturedAudio.add(stereolen ? mStereoBuffer : mResampleBuffer, stereolen ? stereolen : dstlen);
dstlen = Audio::ChannelConverter::stereoToMono(mResampleBuffer, dstlen, mResampleBuffer, dstlen / 2);
}
// See if it is time to send RFC2833 tone
ByteBuffer rfc2833, stopPacket;
if (mDtmfContext.type() == DtmfContext::Dtmf_Rfc2833 && mDtmfContext.getRfc2833(AUDIO_MIC_BUFFER_LENGTH, rfc2833, stopPacket))
// See if inband dtmf audio should be sent instead
ByteBuffer dtmf;
if (mDtmfContext.type() == DtmfContext::Dtmf_Inband && mDtmfContext.getInband(AUDIO_MIC_BUFFER_LENGTH, codec->samplerate(), dtmf))
mCapturedAudio.add(dtmf.data(), dtmf.size());
else
mCapturedAudio.add(stereolen ? mStereoBuffer : mResampleBuffer, stereolen ? stereolen : dstlen);
// See if it is time to send RFC2833 tone
ByteBuffer rfc2833, stopPacket;
if (mDtmfContext.type() == DtmfContext::Dtmf_Rfc2833 && mDtmfContext.getRfc2833(AUDIO_MIC_BUFFER_LENGTH, rfc2833, stopPacket))
{
if (rfc2833.size())
mRtpDtmfSession.SendPacket(rfc2833.data(), rfc2833.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
if (stopPacket.size())
{
if (rfc2833.size())
mRtpDtmfSession.SendPacket(rfc2833.data(), rfc2833.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
if (stopPacket.size())
{
for (int i=0; i<3; i++)
mRtpDtmfSession.SendPacket(stopPacket.data(), stopPacket.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
}
for (int i=0; i<3; i++)
mRtpDtmfSession.SendPacket(stopPacket.data(), stopPacket.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
}
}
int processed = 0;
int encodedTime = 0;
int packetTime = mPacketTime ? mPacketTime : codec->frameTime();
int processed = 0;
int encodedTime = 0;
int packetTime = mPacketTime ? mPacketTime : codec->frameTime();
// Make stereo version if required
for (int i=0; i<mCapturedAudio.filled() / mTransmittingCodec->pcmLength(); i++)
{
if (mSendingDump)
mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength());
// Make stereo version if required
for (int i=0; i<mCapturedAudio.filled() / codec->pcmLength(); i++)
{
if (mSendingDump)
mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength());
int produced;
produced = codec->encode((const char*)mCapturedAudio.data() + codec->pcmLength()*i,
codec->pcmLength(), mFrameBuffer, MT_MAXAUDIOFRAME);
int produced;
produced = codec->encode((const char*)mCapturedAudio.data() + codec->pcmLength()*i,
codec->pcmLength(), mFrameBuffer, MT_MAXAUDIOFRAME);
// Counter of processed input bytes of raw pcm data from microphone
processed += codec->pcmLength();
encodedTime += codec->frameTime();
mEncodedTime += codec->frameTime();
// Counter of processed input bytes of raw pcm data from microphone
processed += codec->pcmLength();
encodedTime += codec->frameTime();
mEncodedTime += codec->frameTime();
if (produced)
{
mEncodedAudio.appendBuffer(mFrameBuffer, produced);
if (packetTime <= encodedTime)
{
// Time to send packet
ICELogMedia(<< "Sending RTP packet pt = " << mTransmittingPayloadType << ", plength = " << (int)mEncodedAudio.size());
mRtpSession.SendPacketEx(mEncodedAudio.data(), mEncodedAudio.size(), mTransmittingPayloadType, false,
packetTime * codec->samplerate()/1000, 0, NULL, 0);
mEncodedAudio.clear();
encodedTime = 0;
}
}
if (produced)
{
mEncodedAudio.appendBuffer(mFrameBuffer, produced);
if (packetTime <= encodedTime)
{
// Time to send packet
ICELogMedia(<< "Sending RTP packet pt = " << mTransmittingPayloadType << ", plength = " << (int)mEncodedAudio.size());
mRtpSession.SendPacketEx(mEncodedAudio.data(), mEncodedAudio.size(), mTransmittingPayloadType, false,
packetTime * codec->samplerate()/1000, 0, NULL, 0);
mEncodedAudio.clear();
encodedTime = 0;
}
}
if (processed > 0)
mCapturedAudio.erase(processed);
}
mCapturedAudio.erase(processed);
}
void AudioStream::copyDataTo(Audio::Mixer& mixer, int needed)
{
// Local audio mixer - used to send audio to media observer
Audio::Mixer localMixer;
Audio::DataWindow forObserver;
// Local audio mixer - used to send audio to media observer
Audio::Mixer localMixer;
Audio::DataWindow forObserver;
// Iterate
for (auto& streamIter: mStreamMap)
// Iterate
for (auto& streamIter: mStreamMap)
{
Audio::DataWindow w;
w.setCapacity(32768);
SingleAudioStream* sas = streamIter.second;
if (sas)
{
Audio::DataWindow w;
w.setCapacity(32768);
sas->copyPcmTo(w, needed);
SingleAudioStream* sas = streamIter.second;
if (sas)
// Provide mirroring if needed
if (mMirror)
{
mMirrorBuffer.add(w.data(), w.filled());
if (!mMirrorPrebuffered)
mMirrorPrebuffered = mMirrorBuffer.filled() >= MT_MIRROR_PREBUFFER;
}
if (!(state() & (int)StreamState::Receiving))
w.zero(needed);
// Check if we do not need input from this stream
if (w.filled())
{
if (mDumpStreams.mStreamForRecordingIncoming)
{
sas->copyPcmTo(w, needed);
// Provide mirroring if needed
if (mMirror)
{
mMirrorBuffer.add(w.data(), w.filled());
if (!mMirrorPrebuffered)
mMirrorPrebuffered = mMirrorBuffer.filled() >= MT_MIRROR_PREBUFFER;
}
if (!(state() & (int)StreamState::Receiving))
w.zero(needed);
// Check if we do not need input from this stream
if (w.filled())
{
if (mDumpStreams.mStreamForRecordingIncoming)
{
if (mDumpStreams.mStreamForRecordingIncoming->isOpened())
mDumpStreams.mStreamForRecordingIncoming->write(w.data(), w.filled());
}
mixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
if (mMediaObserver)
localMixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
}
if (mDumpStreams.mStreamForRecordingIncoming->isOpened())
mDumpStreams.mStreamForRecordingIncoming->write(w.data(), w.filled());
}
}
mixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
if (mMediaObserver)
{
localMixer.mixAndGetPcm(forObserver);
mMediaObserver->onMedia(forObserver.data(), forObserver.capacity(), MT::Stream::MediaDirection::Incoming, this, mMediaObserverTag);
if (mMediaObserver)
localMixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
}
}
}
if (mMediaObserver)
{
localMixer.mixAndGetPcm(forObserver);
mMediaObserver->onMedia(forObserver.data(), forObserver.capacity(), MT::Stream::MediaDirection::Incoming, this, mMediaObserverTag);
}
}
void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length, InternetAddress& source)
{
jrtplib::RTPIPv6Address addr6;
jrtplib::RTPIPv4Address addr4;
jrtplib::RTPExternalTransmissionInfo* info = dynamic_cast<jrtplib::RTPExternalTransmissionInfo*>(mRtpSession.GetTransmissionInfo());
assert(info);
jrtplib::RTPIPv6Address addr6;
jrtplib::RTPIPv4Address addr4;
jrtplib::RTPExternalTransmissionInfo* info = dynamic_cast<jrtplib::RTPExternalTransmissionInfo*>(mRtpSession.GetTransmissionInfo());
assert(info);
// Drop RTP packets if stream is not receiving now; let RTCP go
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtp(buffer, length))
{
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the packet");
return;
}
// Drop RTP packets if stream is not receiving now; let RTCP go
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtpOrRtcp(buffer, length))
{
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the RT(C)P packet");
return;
}
// Copy incoming data to temp buffer to perform possible srtp unprotect
int receiveLength = length;
memcpy(mReceiveBuffer, buffer, length);
// Copy incoming data to temp buffer to perform possible srtp unprotect
int receiveLength = length;
memcpy(mReceiveBuffer, buffer, length);
if (mSrtpSession.active())
{
bool srtpResult;
size_t srcLength = length; size_t dstLength = sizeof mSrtpDecodeBuffer;
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
else
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
if (!srtpResult)
{
ICELogError(<<"Cannot decrypt SRTP packet.");
return;
}
memcpy(mReceiveBuffer, mSrtpDecodeBuffer, dstLength);
receiveLength = dstLength;
}
switch (source.family())
{
case AF_INET:
addr4.SetIP(source.sockaddr4()->sin_addr.s_addr);
addr4.SetPort(source.port());
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr4);
break;
case AF_INET6:
addr6.SetIP(source.sockaddr6()->sin6_addr);
addr6.SetPort(source.port());
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr6);
break;
default:
assert(0);
}
mStat.mReceived += length;
bool srtpResult;
if (mSrtpSession.active())
{
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
{
if (!mStat.mFirstRtpTime)
mStat.mFirstRtpTime = std::chrono::steady_clock::now();
mStat.mReceivedRtp++;
}
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength);
else
mStat.mReceivedRtcp++;
mRtpSession.Poll(); // maybe it is extra with external transmitter
bool hasData = mRtpSession.GotoFirstSourceWithData();
while (hasData)
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength);
if (!srtpResult)
{
std::shared_ptr<jrtplib::RTPPacket> packet(mRtpSession.GetNextPacket());
if (packet)
{
ICELogMedia(<< "jrtplib returned packet");
// Find right handler for rtp stream
SingleAudioStream* rtpStream = nullptr;
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());
if (streamIter == mStreamMap.end())
mStreamMap[packet->GetSSRC()] = rtpStream = new SingleAudioStream(mCodecSettings, mStat);
else
rtpStream = streamIter->second;
// Process incoming data packet
rtpStream->process(packet);
double rtt = mRtpSession.GetCurrentSourceInfo()->INF_GetRoundtripTime().GetDouble();
if (rtt > 0)
mStat.mRttDelay.process(rtt);
}
hasData = mRtpSession.GotoNextSourceWithData();
ICELogError(<<"Cannot decrypt SRTP packet.");
return;
}
}
//ICELogDebug(<< "Packet no: " << RtpHelper::findPacketNo(mReceiveBuffer, receiveLength));
switch (source.family())
{
case AF_INET:
addr4.SetIP(source.sockaddr4()->sin_addr.s_addr);
addr4.SetPort(source.port());
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr4);
break;
case AF_INET6:
addr6.SetIP(source.sockaddr6()->sin6_addr);
addr6.SetPort(source.port());
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr6);
break;
default:
assert(0);
}
mStat.mReceived += length;
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
{
if (!mStat.mFirstRtpTime.is_initialized())
mStat.mFirstRtpTime = std::chrono::system_clock::now();
mStat.mReceivedRtp++;
}
else
mStat.mReceivedRtcp++;
mRtpSession.Poll(); // maybe it is extra with external transmitter
bool hasData = mRtpSession.GotoFirstSourceWithData();
while (hasData)
{
std::shared_ptr<jrtplib::RTPPacket> packet(mRtpSession.GetNextPacket());
if (packet)
{
ICELogMedia(<< "jrtplib returned packet");
// Find right handler for rtp stream
SingleAudioStream* rtpStream = nullptr;
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());
if (streamIter == mStreamMap.end())
mStreamMap[packet->GetSSRC()] = rtpStream = new SingleAudioStream(mCodecSettings, mStat);
else
rtpStream = streamIter->second;
// Process incoming data packet
rtpStream->process(packet);
double rtt = mRtpSession.GetCurrentSourceInfo()->INF_GetRoundtripTime().GetDouble();
if (rtt > 0)
mStat.mRttDelay.process(rtt);
}
hasData = mRtpSession.GotoNextSourceWithData();
}
}
void AudioStream::setState(unsigned state)
{
Stream::setState(state);
Stream::setState(state);
}
void AudioStream::setTelephoneCodec(int payloadType)
{
mRemoteTelephoneCodec = payloadType;
mRemoteTelephoneCodec = payloadType;
}
void AudioStream::setSocket(const RtpPair<PDatagramSocket>& socket)
{
Stream::setSocket(socket);
mRtpSender.setSocket(socket);
Stream::setSocket(socket);
mRtpSender.setSocket(socket);
}
DtmfContext& AudioStream::queueOfDtmf()
{
return mDtmfContext;
return mDtmfContext;
}
void AudioStream::readFile(const Audio::PWavFileReader& stream, MediaDirection direction)
{
switch (direction)
{
case MediaDirection::Outgoing: mDumpStreams.mStreamForReadingOutgoing = stream; break;
case MediaDirection::Incoming: mDumpStreams.mStreamForReadingIncoming = stream; break;
}
switch (direction)
{
case MediaDirection::Outgoing: mDumpStreams.mStreamForReadingOutgoing = stream; break;
case MediaDirection::Incoming: mDumpStreams.mStreamForReadingIncoming = stream; break;
}
}
void AudioStream::writeFile(const Audio::PWavFileWriter& writer, MediaDirection direction)
{
switch (direction)
{
case MediaDirection::Outgoing: mDumpStreams.mStreamForRecordingOutgoing = writer; break;
case MediaDirection::Incoming: mDumpStreams.mStreamForRecordingIncoming = writer; break;
}
switch (direction)
{
case MediaDirection::Outgoing: mDumpStreams.mStreamForRecordingOutgoing = writer; break;
case MediaDirection::Incoming: mDumpStreams.mStreamForRecordingIncoming = writer; break;
}
}
void AudioStream::setupMirror(bool enable)
{
if (!mMirror && enable)
{
mMirrorBuffer.setCapacity(MT_MIRROR_CAPACITY);
mMirrorPrebuffered = false;
}
mMirror = enable;
if (!mMirror && enable)
{
mMirrorBuffer.setCapacity(MT_MIRROR_CAPACITY);
mMirrorPrebuffered = false;
}
mMirror = enable;
}
void AudioStream::setFinalStatisticsOutput(Statistics* stats)
{
mFinalStatistics = stats;
mFinalStatistics = stats;
}

View File

@ -6,7 +6,7 @@
#ifndef __MT_AUDIOSTREAM_H
#define __MT_AUDIOSTREAM_H
#include "../engine_config.h"
#include "../config.h"
#include "MT_Stream.h"
#include "MT_NativeRtpSender.h"
#include "MT_SingleAudioStream.h"
@ -25,10 +25,10 @@
namespace MT
{
class AudioStream: public Stream
{
public:
class AudioStream: public Stream
{
public:
AudioStream(const CodecList::Settings& codecSettings);
~AudioStream();
@ -38,7 +38,7 @@ public:
void setTransmittingCodec(Codec::Factory& factory, int payloadType) override;
PCodec transmittingCodec();
// Called to queue data captured from microphone.
// Called to queue data captured from microphone.
// Buffer holds 16bits PCM data with AUDIO_SAMPLERATE rate and AUDIO_CHANNELS channels.
void addData(const void* buffer, int length);
@ -51,7 +51,7 @@ public:
void setState(unsigned state) override;
void setTelephoneCodec(int payloadType);
DtmfContext& queueOfDtmf();
DtmfContext& queueOfDtmf();
void readFile(const Audio::PWavFileReader& stream, MediaDirection direction) override;
void writeFile(const Audio::PWavFileWriter& writer, MediaDirection direction) override;
@ -59,12 +59,12 @@ public:
void setFinalStatisticsOutput(Statistics* stats);
protected:
protected:
Audio::DataWindow mCapturedAudio; // Data from microphone
Audio::DataWindow mStereoCapturedAudio;
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 mStereoBuffer[AUDIO_MIC_BUFFER_SIZE*16]; // Temporary buffer to hold data converted to stereo
char mStereoBuffer[AUDIO_MIC_BUFFER_SIZE*8]; // Temporary buffer to hold data converted to stereo
PCodec mTransmittingCodec; // Current encoding codec
int mTransmittingPayloadType; // Payload type to mark outgoing packets
int mPacketTime; // Required packet time
@ -83,19 +83,18 @@ protected:
RtpDump* mRtpDump = nullptr;
#endif
Audio::Resampler mCaptureResampler8,
mCaptureResampler16,
mCaptureResampler32,
mCaptureResampler48;
DtmfContext mDtmfContext;
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE],
mSrtpDecodeBuffer[MAX_VALID_UDPPACKET_SIZE];
mCaptureResampler16,
mCaptureResampler32,
mCaptureResampler48;
DtmfContext mDtmfContext;
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE];
struct
{
Audio::PWavFileWriter mStreamForRecordingIncoming,
mStreamForRecordingOutgoing;
Audio::PWavFileReader mStreamForReadingIncoming,
mStreamForReadingOutgoing;
Audio::PWavFileWriter mStreamForRecordingIncoming,
mStreamForRecordingOutgoing;
Audio::PWavFileReader mStreamForReadingIncoming,
mStreamForReadingOutgoing;
} mDumpStreams;
Audio::PWavFileWriter mSendingDump;
@ -107,7 +106,7 @@ protected:
Statistics* mFinalStatistics = nullptr;
bool decryptSrtp(void* data, int* len);
};
};
};
#endif

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,9 @@
#ifndef __MT_CODEC_H
#define __MT_CODEC_H
#include "resiprocate/resip/stack/SdpContents.hxx"
#if defined(USE_RESIP_INTEGRATION)
# include "resiprocate/resip/stack/SdpContents.hxx"
#endif
#include "../helper/HL_Types.h"
#include <map>
#include "../helper/HL_Pointer.h"
@ -34,12 +36,14 @@ public:
virtual PCodec create() = 0;
virtual int channels();
#if defined(USE_RESIP_INTEGRATION)
typedef std::map<int, PCodec > CodecMap;
virtual void create(CodecMap& codecs);
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.
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
resip::Codec resipCodec();
#endif
};
virtual ~Codec() {}
virtual const char* name() = 0;
@ -59,13 +63,8 @@ public:
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;
// Returns size of decoded data (PCM signed short) in bytes
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;
// Returns size of codec in memory

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