Compare commits
No commits in common. "master" and "integration_with_pvqa_pcap" have entirely different histories.
master
...
integratio
|
|
@ -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
|
|
||||||
373
LICENSE_MPL.txt
373
LICENSE_MPL.txt
|
|
@ -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.
|
|
||||||
141
README.txt
141
README.txt
|
|
@ -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.
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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}')
|
|
||||||
|
|
@ -1,12 +1,61 @@
|
||||||
cmake_minimum_required(VERSION 3.20)
|
|
||||||
project(rtphone)
|
project(rtphone)
|
||||||
|
|
||||||
# Rely on C++ 20
|
cmake_minimum_required(VERSION 3.0)
|
||||||
set (CMAKE_CXX_STANDARD 20)
|
|
||||||
|
macro(configure_msvc_runtime)
|
||||||
|
if(MSVC)
|
||||||
|
# Default to statically-linked runtime.
|
||||||
|
if("${MSVC_RUNTIME}" STREQUAL "")
|
||||||
|
set(MSVC_RUNTIME "static")
|
||||||
|
endif()
|
||||||
|
# Set compiler options.
|
||||||
|
set(variables
|
||||||
|
CMAKE_C_FLAGS_DEBUG
|
||||||
|
CMAKE_C_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_C_FLAGS_RELEASE
|
||||||
|
CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_CXX_FLAGS_DEBUG
|
||||||
|
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_CXX_FLAGS_RELEASE
|
||||||
|
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||||
|
)
|
||||||
|
if(${MSVC_RUNTIME} STREQUAL "static")
|
||||||
|
message(STATUS
|
||||||
|
"rtphone: MSVC -> forcing use of statically-linked runtime."
|
||||||
|
)
|
||||||
|
foreach(variable ${variables})
|
||||||
|
if(${variable} MATCHES "/MD")
|
||||||
|
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
message(STATUS
|
||||||
|
"rtphone: MSVC -> forcing use of dynamically-linked runtime."
|
||||||
|
)
|
||||||
|
foreach(variable ${variables})
|
||||||
|
if(${variable} MATCHES "/MT")
|
||||||
|
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(variable ${variables})
|
||||||
|
string(REGEX REPLACE "/Z[iI7]" ""
|
||||||
|
${variable}
|
||||||
|
"${${variable}}")
|
||||||
|
|
||||||
|
set(${variable} "${${variable}} /Zi /Oy-")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
|
||||||
|
# Rely on C++ 11
|
||||||
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set (L libs)
|
set (rtphone_libs libs)
|
||||||
set (E engine)
|
set (rtphone_engine engine)
|
||||||
|
|
||||||
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
|
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
|
||||||
option (USE_EVS_CODEC "Use EVS codec." ON)
|
option (USE_EVS_CODEC "Use EVS codec." ON)
|
||||||
|
|
@ -15,347 +64,152 @@ option (USE_MUSL "Build with MUSL library" OFF)
|
||||||
|
|
||||||
# PIC code by default
|
# PIC code by default
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
set (RUNTIME_CPU_CAPABILITY_DETECTION ON)
|
|
||||||
|
|
||||||
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/libs/libraries)
|
|
||||||
include (${LIB_PLATFORM}/platform_libs.cmake)
|
|
||||||
|
|
||||||
|
if (NOT DEFINED LIB_PLATFORM)
|
||||||
|
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries)
|
||||||
|
endif()
|
||||||
|
|
||||||
message("Libraries: ${LIB_PLATFORM}")
|
message("Libraries: ${LIB_PLATFORM}")
|
||||||
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.1/include)
|
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.0/include)
|
||||||
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
||||||
message ("Using OpenSSL libs: ${OPENSSL_SSL} and ${OPENSSL_CRYPTO}")
|
|
||||||
include_directories(${OPENSSL_INCLUDE})
|
|
||||||
|
|
||||||
# Used defines for our project
|
|
||||||
set (DEFINES -DUSE_OPENSSL)
|
|
||||||
|
|
||||||
# Libraries for our project
|
|
||||||
set (LIBS_STATIC "")
|
|
||||||
set (LIBS_DYNAMIC "")
|
|
||||||
|
|
||||||
# Try to prefer static libraries anyway
|
|
||||||
set (CMAKE_FIND_LIBRARY_SUFFIXES .a .so .dylib)
|
|
||||||
|
|
||||||
# Windows-specific definitions
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||||
set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
||||||
set (TARGET_WIN ON)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Linux-specific definitions
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Linux*")
|
if (CMAKE_SYSTEM MATCHES "Linux*")
|
||||||
set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H)
|
add_definitions (-DTARGET_LINUX)
|
||||||
set (TARGET_LINUX ON)
|
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# macOS-specific definitions
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
||||||
set (DEFINES ${DEFINES} -DTARGET_OSX)
|
add_definitions (-DTARGET_OSX)
|
||||||
set (TARGET_OSX ON)
|
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Android")
|
if (CMAKE_SYSTEM MATCHES "Android")
|
||||||
message("Adding the Oboe library")
|
message("Adding the Oboe library")
|
||||||
set (OBOE_DIR libs/oboe)
|
set (OBOE_DIR libs/oboe)
|
||||||
add_subdirectory (${OBOE_DIR} ./oboe)
|
add_subdirectory (${OBOE_DIR} ./oboe)
|
||||||
include_directories (${OBOE_DIR}/include)
|
include_directories (${OBOE_DIR}/include)
|
||||||
set (DEFINES ${DEFINES} -DTARGET_ANDROID -DHAVE_NETINET_IN_H)
|
|
||||||
set (TARGET_ANDROID ON)
|
|
||||||
set (LIBS_STATIC ${LIBS} oboe)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_MUSL)
|
if (USE_MUSL)
|
||||||
set (DEFINES ${DEFINES} -DTARGET_MUSL)
|
add_definitions(-DTARGET_MUSL)
|
||||||
set (TARGET_MUSL ON)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set (RTPHONE_SOURCES
|
set (RTPHONE_SOURCES
|
||||||
${E}/engine_config.h
|
${rtphone_engine}/media/MT_Statistics.cpp
|
||||||
${E}/media/MT_Statistics.cpp
|
${rtphone_engine}/media/MT_WebRtc.cpp
|
||||||
${E}/media/MT_WebRtc.cpp
|
${rtphone_engine}/media/MT_Stream.cpp
|
||||||
${E}/media/MT_Stream.cpp
|
${rtphone_engine}/media/MT_SrtpHelper.cpp
|
||||||
${E}/media/MT_SrtpHelper.cpp
|
${rtphone_engine}/media/MT_SingleAudioStream.cpp
|
||||||
${E}/media/MT_SingleAudioStream.cpp
|
${rtphone_engine}/media/MT_NativeRtpSender.cpp
|
||||||
${E}/media/MT_NativeRtpSender.cpp
|
${rtphone_engine}/media/MT_Dtmf.cpp
|
||||||
${E}/media/MT_Dtmf.cpp
|
${rtphone_engine}/media/MT_CodecList.cpp
|
||||||
${E}/media/MT_CodecList.cpp
|
${rtphone_engine}/media/MT_Codec.cpp
|
||||||
${E}/media/MT_Codec.cpp
|
${rtphone_engine}/media/MT_Box.cpp
|
||||||
${E}/media/MT_Box.cpp
|
${rtphone_engine}/media/MT_AudioStream.cpp
|
||||||
${E}/media/MT_AudioStream.cpp
|
${rtphone_engine}/media/MT_AudioReceiver.cpp
|
||||||
${E}/media/MT_AudioReceiver.cpp
|
${rtphone_engine}/media/MT_AudioCodec.cpp
|
||||||
${E}/media/MT_AudioCodec.cpp
|
${rtphone_engine}/media/MT_CngHelper.cpp
|
||||||
${E}/media/MT_CngHelper.cpp
|
${rtphone_engine}/agent/Agent_Impl.cpp
|
||||||
${E}/agent/Agent_Impl.cpp
|
${rtphone_engine}/agent/Agent_AudioManager.cpp
|
||||||
${E}/agent/Agent_Impl.h
|
${rtphone_engine}/endpoint/EP_Account.cpp
|
||||||
${E}/agent/Agent_AudioManager.cpp
|
${rtphone_engine}/endpoint/EP_AudioProvider.cpp
|
||||||
${E}/agent/Agent_AudioManager.h
|
${rtphone_engine}/endpoint/EP_DataProvider.cpp
|
||||||
${E}/endpoint/EP_Account.cpp
|
${rtphone_engine}/endpoint/EP_Engine.cpp
|
||||||
${E}/endpoint/EP_Account.h
|
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
|
||||||
${E}/endpoint/EP_AudioProvider.cpp
|
${rtphone_engine}/endpoint/EP_Observer.cpp
|
||||||
${E}/endpoint/EP_AudioProvider.h
|
${rtphone_engine}/endpoint/EP_Session.cpp
|
||||||
${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
|
${rtphone_engine}/media/MT_Statistics.h
|
||||||
${E}/media/MT_WebRtc.h
|
${rtphone_engine}/media/MT_WebRtc.h
|
||||||
${E}/media/MT_Stream.h
|
${rtphone_engine}/media/MT_Stream.h
|
||||||
${E}/media/MT_SrtpHelper.h
|
${rtphone_engine}/media/MT_SrtpHelper.h
|
||||||
${E}/media/MT_SingleAudioStream.h
|
${rtphone_engine}/media/MT_SingleAudioStream.h
|
||||||
${E}/media/MT_NativeRtpSender.h
|
${rtphone_engine}/media/MT_NativeRtpSender.h
|
||||||
${E}/media/MT_Dtmf.h
|
${rtphone_engine}/media/MT_Dtmf.h
|
||||||
${E}/media/MT_CodecList.h
|
${rtphone_engine}/media/MT_CodecList.h
|
||||||
${E}/media/MT_Codec.h
|
${rtphone_engine}/media/MT_Codec.h
|
||||||
${E}/media/MT_Box.h
|
${rtphone_engine}/media/MT_Box.h
|
||||||
${E}/media/MT_AudioStream.h
|
${rtphone_engine}/media/MT_AudioStream.h
|
||||||
${E}/media/MT_AudioReceiver.h
|
${rtphone_engine}/media/MT_AudioReceiver.h
|
||||||
${E}/media/MT_AudioCodec.h
|
${rtphone_engine}/media/MT_AudioCodec.h
|
||||||
${E}/media/MT_CngHelper.h
|
|
||||||
|
|
||||||
${E}/media/MT_Statistics.cpp
|
${rtphone_engine}/media/MT_CngHelper.h
|
||||||
${E}/media/MT_WebRtc.cpp
|
${rtphone_engine}/agent/Agent_Impl.h
|
||||||
${E}/media/MT_Stream.cpp
|
${rtphone_engine}/agent/Agent_AudioManager.h
|
||||||
${E}/media/MT_SrtpHelper.cpp
|
${rtphone_engine}/endpoint/EP_Account.h
|
||||||
${E}/media/MT_SingleAudioStream.cpp
|
${rtphone_engine}/endpoint/EP_AudioProvider.h
|
||||||
${E}/media/MT_NativeRtpSender.cpp
|
${rtphone_engine}/endpoint/EP_DataProvider.h
|
||||||
${E}/media/MT_Dtmf.cpp
|
${rtphone_engine}/endpoint/EP_Engine.h
|
||||||
${E}/media/MT_CodecList.cpp
|
${rtphone_engine}/endpoint/EP_NetworkQueue.h
|
||||||
${E}/media/MT_Codec.cpp
|
${rtphone_engine}/endpoint/EP_Observer.h
|
||||||
${E}/media/MT_Box.cpp
|
${rtphone_engine}/endpoint/EP_Session.h
|
||||||
${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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (USE_AMR_CODEC)
|
||||||
|
add_definitions(-DUSE_AMR_CODEC)
|
||||||
|
set (RTPHONE_SOURCES ${RTPHONE_SOURCES} ${rtphone_engine}/media/MT_AmrCodec.cpp ${rtphone_engine}/media/MT_AmrCodec.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_EVS_CODEC)
|
||||||
|
add_definitions(-DUSE_EVS_CODEC)
|
||||||
|
set (RTPHONE_SOURCES ${RTPHONE_SOURCES} ${rtphone_engine}/media/MT_EvsCodec.cpp ${rtphone_engine}/media/MT_EvsCodec.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (USE_OPUS_CODEC)
|
if (USE_OPUS_CODEC)
|
||||||
set (DEFINES ${DEFINES} -DUSE_OPUS_CODEC)
|
add_definitions(-DUSE_OPUS_CODEC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library (rtphone STATIC ${RTPHONE_SOURCES})
|
add_library (rtphone STATIC ${RTPHONE_SOURCES})
|
||||||
|
|
||||||
add_subdirectory(${L}/resiprocate)
|
add_subdirectory(${rtphone_libs}/resiprocate)
|
||||||
add_subdirectory(${L}/jrtplib/src)
|
add_subdirectory(${rtphone_libs}/ice)
|
||||||
add_subdirectory(${L}/libg729)
|
add_subdirectory(${rtphone_libs}/jrtplib/src)
|
||||||
|
add_subdirectory(${rtphone_libs}/libg729)
|
||||||
|
|
||||||
if (USE_EVS_CODEC)
|
if (USE_EVS_CODEC)
|
||||||
add_subdirectory(${L}/libevs)
|
add_subdirectory(${rtphone_libs}/libevs)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(${L}/libgsm)
|
add_subdirectory(${rtphone_libs}/libgsm)
|
||||||
add_subdirectory(${L}/gsmhr)
|
add_subdirectory(${rtphone_libs}/gsmhr)
|
||||||
add_subdirectory(${L}/g722)
|
add_subdirectory(${rtphone_libs}/g722)
|
||||||
add_subdirectory(${L}/speexdsp)
|
add_subdirectory(${rtphone_libs}/speexdsp)
|
||||||
add_subdirectory(${L}/libsrtp)
|
add_subdirectory(${rtphone_libs}/srtp)
|
||||||
add_subdirectory(${L}/webrtc)
|
add_subdirectory(${rtphone_libs}/webrtc)
|
||||||
add_subdirectory(${L}/opus)
|
add_subdirectory(${rtphone_engine}/helper)
|
||||||
|
add_subdirectory(${rtphone_engine}/audio)
|
||||||
|
add_subdirectory(${rtphone_engine}/media)
|
||||||
|
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
|
set (LIBS ice_stack jrtplib g729_codec gsm_codec
|
||||||
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
|
gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp
|
||||||
|
uuid)
|
||||||
|
|
||||||
if (USE_AMR_CODEC)
|
if (CMAKE_SYSTEM MATCHES "Win*")
|
||||||
include (${LIB_PLATFORM}/platform_libs.cmake)
|
set (LIBS ${LIBS} )
|
||||||
message("Media: AMR NB and WB codecs will be included.")
|
else ()
|
||||||
set (DEFINES ${DEFINES} -DUSE_AMR_CODEC)
|
set (LIBS ${LIBS} dl uuid)
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (USE_EVS_CODEC)
|
if (CMAKE_SYSTEM MATCHES "Android")
|
||||||
message("Media: EVS codec will be included.")
|
set (LIBS ${LIBS} oboe)
|
||||||
set (DEFINES ${DEFINES} -DUSE_EVS_CODEC)
|
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} evs_codec)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(rtphone PUBLIC ${DEFINES})
|
|
||||||
|
|
||||||
if (TARGET_LINUX)
|
target_link_libraries(rtphone
|
||||||
target_link_options(rtphone PUBLIC -Wl,-Bstatic)
|
ice_stack jrtplib g729_codec gsm_codec
|
||||||
endif()
|
gsmhr_codec g722_codec srtp resiprocate
|
||||||
target_link_libraries(rtphone PUBLIC ${LIBS_STATIC} ${OPENSSL_SSL} ${OPENSSL_CRYPTO})
|
helper_lib
|
||||||
|
audio_lib
|
||||||
|
webrtc
|
||||||
|
speexdsp
|
||||||
|
uuid
|
||||||
|
${OPENSSL_SSL}
|
||||||
|
${OPENSSL_CRYPTO}
|
||||||
|
${LIBS} )
|
||||||
|
|
||||||
if (TARGET_LINUX)
|
|
||||||
target_link_options(rtphone PUBLIC -Wl,-Bdynamic)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_include_directories(rtphone
|
target_include_directories(rtphone
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
|
@ -363,18 +217,15 @@ target_include_directories(rtphone
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
||||||
${LIB_PLATFORM}/opus/include
|
${LIB_PLATFORM}/opus/include
|
||||||
${E}/helper
|
|
||||||
${E}/audio
|
|
||||||
${E}/media
|
|
||||||
${L}
|
|
||||||
${L}/ice
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${L}/libevs/lib_com
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/
|
||||||
${L}/libevs/lib_enc
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com
|
||||||
${L}/libevs/lib_dec
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc
|
||||||
${L}/speex/include
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec
|
||||||
${L}/libs/json
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
|
||||||
)
|
)
|
||||||
|
|
||||||
# For MSVC static builds
|
# For MSVC static builds
|
||||||
# set_property(TARGET rtphone PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
configure_msvc_runtime()
|
||||||
|
|
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "Agent_AudioManager.h"
|
#include "Agent_AudioManager.h"
|
||||||
#include "../engine/audio/Audio_WavFile.h"
|
#include "../engine/audio/Audio_WavFile.h"
|
||||||
|
#include "../engine/helper/HL_String.h"
|
||||||
#include "../engine/audio/Audio_Null.h"
|
#include "../engine/audio/Audio_Null.h"
|
||||||
#include "HL_String.h"
|
|
||||||
|
|
||||||
#if defined(TARGET_ANDROID)
|
#if defined(TARGET_ANDROID)
|
||||||
# include "../engine/audio/Audio_Android.h"
|
# include "../engine/audio/Audio_Android.h"
|
||||||
|
|
@ -15,8 +15,11 @@
|
||||||
#define LOG_SUBSYSTEM "AudioManager"
|
#define LOG_SUBSYSTEM "AudioManager"
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------- AudioManager -------------
|
||||||
|
//static AudioManager GAudioManager;
|
||||||
|
|
||||||
AudioManager::AudioManager()
|
AudioManager::AudioManager()
|
||||||
:mTerminal(nullptr), mAudioMonitoring(nullptr)
|
:mTerminal(nullptr)
|
||||||
{
|
{
|
||||||
mPlayer.setDelegate(this);
|
mPlayer.setDelegate(this);
|
||||||
}
|
}
|
||||||
|
|
@ -46,16 +49,6 @@ MT::Terminal* AudioManager::terminal()
|
||||||
return mTerminal;
|
return mTerminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::setAudioMonitoring(Audio::DataConnection* monitoring)
|
|
||||||
{
|
|
||||||
mAudioMonitoring = monitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
Audio::DataConnection* AudioManager::audioMonitoring()
|
|
||||||
{
|
|
||||||
return mAudioMonitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
|
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
|
||||||
void AudioManager::start(int usageId)
|
void AudioManager::start(int usageId)
|
||||||
{
|
{
|
||||||
|
|
@ -75,14 +68,7 @@ void AudioManager::start(int usageId)
|
||||||
// Disable AEC for now - because PVQA conflicts with speex AEC.
|
// Disable AEC for now - because PVQA conflicts with speex AEC.
|
||||||
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
|
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
|
||||||
if (!mTerminal->audio())
|
if (!mTerminal->audio())
|
||||||
{
|
mTerminal->setAudio(std::make_shared<Audio::DevicePair>(false, true));
|
||||||
auto audio = std::make_shared<Audio::DevicePair>();
|
|
||||||
audio->setAgc(true);
|
|
||||||
audio->setAec(false);
|
|
||||||
audio->setMonitoring(mAudioMonitoring);
|
|
||||||
|
|
||||||
mTerminal->setAudio(audio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mAudioInput)
|
if (!mAudioInput)
|
||||||
{
|
{
|
||||||
|
|
@ -172,7 +158,7 @@ void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarg
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
r->open(strx::makeTstring(path));
|
r->open(StringHelper::makeTstring(path));
|
||||||
#else
|
#else
|
||||||
r->open(path);
|
r->open(path);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@
|
||||||
|
|
||||||
#include "../engine/audio/Audio_Interface.h"
|
#include "../engine/audio/Audio_Interface.h"
|
||||||
#include "../engine/audio/Audio_Player.h"
|
#include "../engine/audio/Audio_Player.h"
|
||||||
|
#include "../engine/endpoint/EP_Engine.h"
|
||||||
#include "../engine/media/MT_Box.h"
|
#include "../engine/media/MT_Box.h"
|
||||||
|
#include "../engine/helper/HL_Log.h"
|
||||||
|
#include "../engine/helper/HL_Sync.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -46,9 +49,6 @@ public:
|
||||||
void setTerminal(MT::Terminal* terminal);
|
void setTerminal(MT::Terminal* terminal);
|
||||||
MT::Terminal* terminal();
|
MT::Terminal* terminal();
|
||||||
|
|
||||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
|
||||||
Audio::DataConnection* audioMonitoring();
|
|
||||||
|
|
||||||
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
|
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
|
||||||
void start(int usageId);
|
void start(int usageId);
|
||||||
void stop(int usageId);
|
void stop(int usageId);
|
||||||
|
|
@ -69,7 +69,6 @@ public:
|
||||||
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
|
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
|
||||||
void stopPlayFile(int usageId);
|
void stopPlayFile(int usageId);
|
||||||
|
|
||||||
|
|
||||||
void onFilePlayed(Audio::Player::PlaylistItem& item);
|
void onFilePlayed(Audio::Player::PlaylistItem& item);
|
||||||
|
|
||||||
// Must be called from main loop to release used audio devices
|
// Must be called from main loop to release used audio devices
|
||||||
|
|
@ -80,7 +79,6 @@ protected:
|
||||||
Audio::POutputDevice mAudioOutput;
|
Audio::POutputDevice mAudioOutput;
|
||||||
Audio::Player mPlayer;
|
Audio::Player mPlayer;
|
||||||
MT::Terminal* mTerminal;
|
MT::Terminal* mTerminal;
|
||||||
Audio::DataConnection* mAudioMonitoring;
|
|
||||||
|
|
||||||
std::map<int, int> UsageMap;
|
std::map<int, int> UsageMap;
|
||||||
UsageCounter mUsage;
|
UsageCounter mUsage;
|
||||||
|
|
|
||||||
|
|
@ -32,24 +32,6 @@ AgentImpl::~AgentImpl()
|
||||||
stopAgentAndThread();
|
stopAgentAndThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get access to internal audio manager. Value can be nullptr.
|
|
||||||
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
|
|
||||||
{
|
|
||||||
return mAudioManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
|
|
||||||
{
|
|
||||||
mAudioMonitoring = monitoring;
|
|
||||||
if (mAudioManager)
|
|
||||||
mAudioManager->setAudioMonitoring(monitoring);
|
|
||||||
}
|
|
||||||
|
|
||||||
Audio::DataConnection* AgentImpl::monitoring() const
|
|
||||||
{
|
|
||||||
return mAudioMonitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AgentImpl::run()
|
void AgentImpl::run()
|
||||||
{
|
{
|
||||||
while (!mShutdown)
|
while (!mShutdown)
|
||||||
|
|
@ -150,9 +132,7 @@ std::string AgentImpl::command(const std::string& command)
|
||||||
{
|
{
|
||||||
answer["status"] = e.what();
|
answer["status"] = e.what();
|
||||||
}
|
}
|
||||||
std::string result = answer.toStyledString();
|
return answer.toStyledString();
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AgentImpl::waitForData(int /*milliseconds*/)
|
bool AgentImpl::waitForData(int /*milliseconds*/)
|
||||||
|
|
@ -230,10 +210,8 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
|
||||||
// Enable audio
|
// Enable audio
|
||||||
mAudioManager = std::make_shared<AudioManager>();
|
mAudioManager = std::make_shared<AudioManager>();
|
||||||
mAudioManager->setTerminal(mTerminal.get());
|
mAudioManager->setTerminal(mTerminal.get());
|
||||||
if (mAudioMonitoring)
|
|
||||||
mAudioManager->setAudioMonitoring(mAudioMonitoring);
|
|
||||||
|
|
||||||
// Do not start audio manager here. Start right before call.
|
// Do not start here. Start right before call.
|
||||||
|
|
||||||
// Initialize endpoint
|
// Initialize endpoint
|
||||||
start();
|
start();
|
||||||
|
|
@ -260,7 +238,7 @@ void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
|
||||||
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
|
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
|
||||||
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
|
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
|
||||||
|
|
||||||
auto nameAndPort = 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_NAME] = nameAndPort.first;
|
||||||
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
|
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
|
||||||
|
|
||||||
|
|
@ -342,7 +320,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||||
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
||||||
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
||||||
|
|
||||||
/*#if defined(USE_AQUA_LIBRARY)
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
std::string path_faults = request["path_faults"].asString();
|
std::string path_faults = request["path_faults"].asString();
|
||||||
|
|
||||||
sevana::aqua::config config = {
|
sevana::aqua::config config = {
|
||||||
|
|
@ -366,8 +344,8 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||||
};
|
};
|
||||||
|
|
||||||
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
|
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
|
||||||
// if (temp_path.size())
|
/*if (temp_path.size())
|
||||||
// config += " -fau " + temp_path;
|
config += " -fau " + temp_path; */
|
||||||
|
|
||||||
auto qc = std::make_shared<sevana::aqua>();
|
auto qc = std::make_shared<sevana::aqua>();
|
||||||
if (!qc->is_open())
|
if (!qc->is_open())
|
||||||
|
|
@ -380,7 +358,6 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||||
mAquaMap[sessionIter->first] = qc;
|
mAquaMap[sessionIter->first] = qc;
|
||||||
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
|
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
|
||||||
#endif
|
#endif
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: support SRTP via StreamState::Srtp option in audio provider state
|
// TODO: support SRTP via StreamState::Srtp option in audio provider state
|
||||||
|
|
||||||
|
|
@ -455,9 +432,9 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
|
||||||
auto sessionIter = mSessionMap.find(sessionId);
|
auto sessionIter = mSessionMap.find(sessionId);
|
||||||
if (sessionIter != mSessionMap.end())
|
if (sessionIter != mSessionMap.end())
|
||||||
mSessionMap.erase(sessionIter);
|
mSessionMap.erase(sessionIter);
|
||||||
//#if defined(USE_AQUA_LIBRARY)
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
// closeAqua(sessionId);
|
closeAqua(sessionId);
|
||||||
//#endif
|
#endif
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,6 +458,41 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &ans
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_PVQA_LIBRARY)
|
||||||
|
static JsonCpp::Value CsvReportToJson(const std::string& report)
|
||||||
|
{
|
||||||
|
JsonCpp::Value detectorValues;
|
||||||
|
std::istringstream iss(report);
|
||||||
|
CsvReader reader(iss);
|
||||||
|
std::vector<std::string> cells;
|
||||||
|
if (reader.readLine(cells))
|
||||||
|
{
|
||||||
|
JsonCpp::Value detectorNames;
|
||||||
|
for (size_t nameIndex = 0; nameIndex < cells.size(); nameIndex++)
|
||||||
|
detectorNames[static_cast<int>(nameIndex)] = StringHelper::trim(cells[nameIndex]);
|
||||||
|
// Put first line name of columns
|
||||||
|
detectorValues[0] = detectorNames;
|
||||||
|
|
||||||
|
int rowIndex = 1;
|
||||||
|
while (reader.readLine(cells))
|
||||||
|
{
|
||||||
|
// Skip last column for now
|
||||||
|
JsonCpp::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(JsonCpp::Value& request, JsonCpp::Value& answer)
|
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
|
||||||
{
|
{
|
||||||
|
|
@ -491,13 +503,24 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
||||||
{
|
{
|
||||||
PSession session = sessionIter->second;
|
PSession session = sessionIter->second;
|
||||||
VariantMap result;
|
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);
|
result);
|
||||||
|
|
||||||
if (result.exists(SessionInfo_AudioCodec))
|
if (result.exists(SessionInfo_AudioCodec))
|
||||||
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
|
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
|
||||||
if (result.exists(SessionInfo_NetworkMos))
|
if (result.exists(SessionInfo_NetworkMos))
|
||||||
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
|
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
|
||||||
|
#if defined(USE_PVQA_LIBRARY)
|
||||||
|
if (result.exists(SessionInfo_PvqaMos))
|
||||||
|
answer["pvqa_mos"] = result[SessionInfo_PvqaMos].asFloat();
|
||||||
|
if (result.exists(SessionInfo_PvqaReport))
|
||||||
|
answer["pvqa_report"] = CsvReportToJson(result[SessionInfo_PvqaReport].asStdString());
|
||||||
|
#endif
|
||||||
if (result.exists(SessionInfo_PacketLoss))
|
if (result.exists(SessionInfo_PacketLoss))
|
||||||
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
|
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
|
||||||
if (result.exists(SessionInfo_DroppedRtp))
|
if (result.exists(SessionInfo_DroppedRtp))
|
||||||
|
|
@ -520,6 +543,74 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
||||||
if (result.exists(SessionInfo_RemotePeer))
|
if (result.exists(SessionInfo_RemotePeer))
|
||||||
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
|
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
if (includeAqua)
|
||||||
|
{
|
||||||
|
answer["incoming_audio"] = mAquaIncoming.hexstring();
|
||||||
|
answer["incoming_audio_samplerate"] = AUDIO_SAMPLERATE;
|
||||||
|
answer["incoming_audio_channels"] = AUDIO_CHANNELS;
|
||||||
|
|
||||||
|
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;
|
||||||
|
/*std::string aqua_audio_text;
|
||||||
|
if (Base64::Encode(std::string(reinterpret_cast<const char*>(mAquaIncoming.data()), mAquaIncoming.size()), &aqua_audio_text))
|
||||||
|
{
|
||||||
|
answer["aqua_audio"] = aqua_audio_text;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (r.mErrorCode) {
|
||||||
|
answer["aqua_error_code"] = r.mErrorCode;
|
||||||
|
answer["aqua_error_message"] = r.mErrorMessage;
|
||||||
|
}
|
||||||
|
closeAqua(sessionIter->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove test audio
|
||||||
|
mAquaIncoming.clear(); mAquaOutgoing.clear();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -540,16 +631,18 @@ void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answ
|
||||||
std::string pem = request["cert"].asString();
|
std::string pem = request["cert"].asString();
|
||||||
|
|
||||||
std::string::size_type pb = 0, pe = 0;
|
std::string::size_type pb = 0, pe = 0;
|
||||||
while (pb != std::string::npos && pe != std::string::npos) {
|
|
||||||
pb = pem.find(BeginCertificate, pb);
|
|
||||||
pe = pem.find(EndCertificate, pe);
|
|
||||||
|
|
||||||
if (pb != std::string::npos && pe != std::string::npos && pe > pb) {
|
for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe);
|
||||||
std::string cert = pem.substr(pb, pe - pb + EndCertificate.size());
|
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()));
|
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
|
||||||
|
|
||||||
pb = ++pe;
|
// Delete processed part
|
||||||
}
|
pem.erase(0, pe + EndCertificate.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
|
|
@ -637,7 +730,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
|
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
|
||||||
if (!reader->open(strx::makeTstring(path)))
|
if (!reader->open(StringHelper::makeTstring(path)))
|
||||||
answer["status"] = Status_FailedToOpenFile;
|
answer["status"] = Status_FailedToOpenFile;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -658,7 +751,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
|
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
|
||||||
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
|
if (!writer->open(StringHelper::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
|
||||||
answer["status"] = Status_FailedToOpenFile;
|
answer["status"] = Status_FailedToOpenFile;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -681,14 +774,25 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
|
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
|
||||||
{
|
{
|
||||||
/*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::Incoming: mAquaIncoming.appendBuffer(data, length); break;
|
||||||
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
|
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Called on new incoming session; providers shoukld
|
// Called on new incoming session; providers shoukld
|
||||||
|
|
@ -845,12 +949,12 @@ void AgentImpl::addEvent(const JsonCpp::Value& v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
/*void AgentImpl::closeAqua(int sessionId)
|
void AgentImpl::closeAqua(int sessionId)
|
||||||
{
|
{
|
||||||
auto aquaIter = mAquaMap.find(sessionId);
|
auto aquaIter = mAquaMap.find(sessionId);
|
||||||
if (aquaIter != mAquaMap.end()) {
|
if (aquaIter != mAquaMap.end()) {
|
||||||
aquaIter->second->close();
|
aquaIter->second->close();
|
||||||
mAquaMap.erase(aquaIter);
|
mAquaMap.erase(aquaIter);
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
|
class AgentImpl: public UserAgent
|
||||||
class AgentImpl: public UserAgent, public MT::Stream::MediaObserver
|
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::recursive_mutex mAgentMutex;
|
std::recursive_mutex mAgentMutex;
|
||||||
|
|
@ -30,12 +29,19 @@ protected:
|
||||||
typedef std::map<int, PSession> SessionMap;
|
typedef std::map<int, PSession> SessionMap;
|
||||||
SessionMap mSessionMap;
|
SessionMap mSessionMap;
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
// Keys are the same as used in mSessionMap
|
||||||
|
typedef std::map<int, std::shared_ptr<sevana::aqua>> AquaMap;
|
||||||
|
AquaMap mAquaMap;
|
||||||
|
ByteBuffer mAquaIncoming, mAquaOutgoing;
|
||||||
|
void closeAqua(int sessionId);
|
||||||
|
#endif
|
||||||
|
|
||||||
std::shared_ptr<std::thread> mThread;
|
std::shared_ptr<std::thread> mThread;
|
||||||
volatile bool mShutdown;
|
volatile bool mShutdown;
|
||||||
std::shared_ptr<MT::Terminal> mTerminal;
|
std::shared_ptr<MT::Terminal> mTerminal;
|
||||||
std::shared_ptr<AudioManager> mAudioManager;
|
std::shared_ptr<AudioManager> mAudioManager;
|
||||||
Audio::DataConnection* mAudioMonitoring = nullptr;
|
//Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void addEvent(const JsonCpp::Value& v);
|
void addEvent(const JsonCpp::Value& v);
|
||||||
|
|
@ -66,12 +72,6 @@ public:
|
||||||
bool waitForData(int milliseconds);
|
bool waitForData(int milliseconds);
|
||||||
std::string read();
|
std::string read();
|
||||||
|
|
||||||
// Get access to internal audio manager. Value can be nullptr.
|
|
||||||
const std::shared_ptr<AudioManager>& audioManager() const;
|
|
||||||
|
|
||||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
|
||||||
Audio::DataConnection* monitoring() const;
|
|
||||||
|
|
||||||
// UserAgent overrides
|
// UserAgent overrides
|
||||||
// Called on new incoming session; providers shoukld
|
// Called on new incoming session; providers shoukld
|
||||||
PDataProvider onProviderNeeded(const std::string& name) override;
|
PDataProvider onProviderNeeded(const std::string& name) override;
|
||||||
|
|
@ -121,8 +121,10 @@ public:
|
||||||
// Called when problem with SIP connection(s) detected
|
// Called when problem with SIP connection(s) detected
|
||||||
void onSipConnectionFailed() override;
|
void onSipConnectionFailed() override;
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
// Called on incoming & outgoing audio for voice sessions
|
// Called on incoming & outgoing audio for voice sessions
|
||||||
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -365,9 +365,8 @@ void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void *
|
||||||
// ------------ AndroidOutputDevice -----------------
|
// ------------ AndroidOutputDevice -----------------
|
||||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidOutputDevice::~AndroidOutputDevice()
|
AndroidOutputDevice::~AndroidOutputDevice()
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ int AndroidInputDevice::readBuffer(void* buffer)
|
||||||
// ------------ AndroidOutputDevice -----------------
|
// ------------ AndroidOutputDevice -----------------
|
||||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
|
||||||
}
|
}
|
||||||
AndroidOutputDevice::~AndroidOutputDevice()
|
AndroidOutputDevice::~AndroidOutputDevice()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,8 @@
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
// --- DevicePair ---
|
// --- DevicePair ---
|
||||||
DevicePair::DevicePair()
|
DevicePair::DevicePair(bool aec, bool agc)
|
||||||
:mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS),
|
:mConfig(NULL), mDelegate(NULL), mAec(aec), mAgc(agc), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS)
|
||||||
mMonitoring(nullptr)
|
|
||||||
{
|
{
|
||||||
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
||||||
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
||||||
|
|
@ -29,50 +28,26 @@ DevicePair::~DevicePair()
|
||||||
if (mInput)
|
if (mInput)
|
||||||
{
|
{
|
||||||
if (mInput->connection() == this)
|
if (mInput->connection() == this)
|
||||||
mInput->setConnection(nullptr);
|
mInput->setConnection(NULL);
|
||||||
mInput.reset();
|
mInput.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mOutput)
|
if (mOutput)
|
||||||
{
|
{
|
||||||
if (mOutput->connection() == this)
|
if (mOutput->connection() == this)
|
||||||
mOutput->setConnection(nullptr);
|
mOutput->setConnection(NULL);
|
||||||
mOutput.reset();
|
mOutput.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setAec(bool aec)
|
|
||||||
{
|
|
||||||
mAec = aec;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DevicePair::aec()
|
|
||||||
{
|
|
||||||
return mAec;
|
|
||||||
}
|
|
||||||
|
|
||||||
DevicePair& DevicePair::setAgc(bool agc)
|
|
||||||
{
|
|
||||||
mAgc = agc;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DevicePair::agc()
|
|
||||||
{
|
|
||||||
return mAgc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
VariantMap* DevicePair::config()
|
VariantMap* DevicePair::config()
|
||||||
{
|
{
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setConfig(VariantMap* config)
|
void DevicePair::setConfig(VariantMap* config)
|
||||||
{
|
{
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PInputDevice DevicePair::input()
|
PInputDevice DevicePair::input()
|
||||||
|
|
@ -80,17 +55,15 @@ PInputDevice DevicePair::input()
|
||||||
return mInput;
|
return mInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setInput(PInputDevice input)
|
void DevicePair::setInput(PInputDevice input)
|
||||||
{
|
{
|
||||||
if (mInput == input)
|
if (mInput == input)
|
||||||
return *this;
|
return;
|
||||||
|
|
||||||
mInput = input;
|
mInput = input;
|
||||||
mInput->setConnection(this);
|
mInput->setConnection(this);
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->deviceChanged(this);
|
mDelegate->deviceChanged(this);
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
POutputDevice DevicePair::output()
|
POutputDevice DevicePair::output()
|
||||||
|
|
@ -98,17 +71,14 @@ POutputDevice DevicePair::output()
|
||||||
return mOutput;
|
return mOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setOutput(POutputDevice output)
|
void DevicePair::setOutput(POutputDevice output)
|
||||||
{
|
{
|
||||||
if (output == mOutput)
|
if (output == mOutput)
|
||||||
return *this;
|
return;
|
||||||
|
|
||||||
mOutput = output;
|
mOutput = output;
|
||||||
mOutput->setConnection(this);
|
mOutput->setConnection(this);
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->deviceChanged(this);
|
mDelegate->deviceChanged(this);
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DevicePair::start()
|
bool DevicePair::start()
|
||||||
|
|
@ -118,7 +88,6 @@ bool DevicePair::start()
|
||||||
result = mInput->open();
|
result = mInput->open();
|
||||||
if (mOutput && result)
|
if (mOutput && result)
|
||||||
result &= mOutput->open();
|
result &= mOutput->open();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,10 +99,9 @@ void DevicePair::stop()
|
||||||
mOutput->close();
|
mOutput->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setDelegate(Delegate* dc)
|
void DevicePair::setDelegate(Delegate* dc)
|
||||||
{
|
{
|
||||||
mDelegate = dc;
|
mDelegate = dc;
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair::Delegate* DevicePair::delegate()
|
DevicePair::Delegate* DevicePair::delegate()
|
||||||
|
|
@ -141,17 +109,6 @@ DevicePair::Delegate* DevicePair::delegate()
|
||||||
return mDelegate;
|
return mDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
|
|
||||||
{
|
|
||||||
mMonitoring = monitoring;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataConnection* DevicePair::monitoring()
|
|
||||||
{
|
|
||||||
return mMonitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player& DevicePair::player()
|
Player& DevicePair::player()
|
||||||
{
|
{
|
||||||
return mPlayer;
|
return mPlayer;
|
||||||
|
|
@ -262,10 +219,6 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
|
|
||||||
mOutputNativeData.read(buffer, length);
|
mOutputNativeData.read(buffer, length);
|
||||||
|
|
||||||
// Send data to monitoring if needed
|
|
||||||
if (mMonitoring)
|
|
||||||
mMonitoring->onSpkData(f, buffer, length);
|
|
||||||
|
|
||||||
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
|
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
|
||||||
|
|
||||||
// AEC filter wants frames.
|
// AEC filter wants frames.
|
||||||
|
|
@ -276,6 +229,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
|
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
|
||||||
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
|
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
|
||||||
}
|
}
|
||||||
|
//ICELogMedia(<< "Audio::DevicePair::onSpkData() end")
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::processMicData(const Format& f, void* buffer, int length)
|
void DevicePair::processMicData(const Format& f, void* buffer, int length)
|
||||||
|
|
|
||||||
|
|
@ -26,32 +26,29 @@ namespace Audio
|
||||||
virtual void deviceChanged(DevicePair* dpair) = 0;
|
virtual void deviceChanged(DevicePair* dpair) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
DevicePair();
|
DevicePair(bool aec = true, bool agc = true);
|
||||||
virtual ~DevicePair();
|
virtual ~DevicePair();
|
||||||
|
|
||||||
DevicePair& setAec(bool aec);
|
void setAec(bool aec);
|
||||||
bool aec();
|
bool aec();
|
||||||
DevicePair& setAgc(bool agc);
|
void setAgc(bool agc);
|
||||||
bool agc();
|
bool agc();
|
||||||
|
|
||||||
VariantMap* config();
|
VariantMap* config();
|
||||||
DevicePair& setConfig(VariantMap* config);
|
void setConfig(VariantMap* config);
|
||||||
|
|
||||||
PInputDevice input();
|
PInputDevice input();
|
||||||
DevicePair& setInput(PInputDevice input);
|
void setInput(PInputDevice input);
|
||||||
|
|
||||||
POutputDevice output();
|
POutputDevice output();
|
||||||
DevicePair& setOutput(POutputDevice output);
|
void setOutput(POutputDevice output);
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
DevicePair& setDelegate(Delegate* dc);
|
void setDelegate(Delegate* dc);
|
||||||
Delegate* delegate();
|
Delegate* delegate();
|
||||||
|
|
||||||
DevicePair& setMonitoring(DataConnection* monitoring);
|
|
||||||
DataConnection* monitoring();
|
|
||||||
|
|
||||||
Player& player();
|
Player& player();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -66,7 +63,6 @@ namespace Audio
|
||||||
Player mPlayer;
|
Player mPlayer;
|
||||||
UniversalResampler mMicResampler, mSpkResampler;
|
UniversalResampler mMicResampler, mSpkResampler;
|
||||||
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
||||||
DataConnection* mMonitoring;
|
|
||||||
|
|
||||||
#ifdef DUMP_NATIVEOUTPUT
|
#ifdef DUMP_NATIVEOUTPUT
|
||||||
std::shared_ptr<WavFileWriter> mNativeOutputDump;
|
std::shared_ptr<WavFileWriter> mNativeOutputDump;
|
||||||
|
|
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __AUDIO_DSOUND_H
|
#ifndef __AUDIO_DSOUND_H
|
||||||
#define __AUDIO_DSOUND_H
|
#define __AUDIO_DSOUND_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ TimeSource::TimeSource(int quantTime, int nrOfQuants)
|
||||||
mTailTime = 0;
|
mTailTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeSource::~TimeSource()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void TimeSource::start()
|
void TimeSource::start()
|
||||||
{
|
{
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
|
|
@ -98,12 +102,11 @@ unsigned TimeSource::time()
|
||||||
#if defined(TARGET_ANDROID)
|
#if defined(TARGET_ANDROID)
|
||||||
assert(0);
|
assert(0);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- StubTimer ---
|
// --- StubTimer ---
|
||||||
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
||||||
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false), mCurrentTime(0)
|
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false)
|
||||||
{
|
{
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ namespace Audio
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TimeSource(int quantTime, int nrOfQuants);
|
TimeSource(int quantTime, int nrOfQuants);
|
||||||
~TimeSource() = default;
|
~TimeSource();
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#define __AUDIO_INTERFACE_H
|
#define __AUDIO_INTERFACE_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
#include "../helper/HL_VariantMap.h"
|
#include "../helper/HL_VariantMap.h"
|
||||||
#include "../helper/HL_Pointer.h"
|
#include "../helper/HL_Pointer.h"
|
||||||
|
|
@ -57,27 +57,6 @@ namespace Audio
|
||||||
sprintf(buffer, "%dHz %dch", mRate, mChannels);
|
sprintf(buffer, "%dHz %dch", mRate, mChannels);
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator == (const Format& rhs) const
|
|
||||||
{
|
|
||||||
return mRate == rhs.mRate && mChannels == rhs.mChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator != (const Format& rhs) const
|
|
||||||
{
|
|
||||||
return mRate != rhs.mRate || mChannels != rhs.mChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rate() const
|
|
||||||
{
|
|
||||||
return mRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int channels() const
|
|
||||||
{
|
|
||||||
return mChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataConnection
|
class DataConnection
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef _RX_MIXER_H
|
#ifndef _RX_MIXER_H
|
||||||
#define _RX_MIXER_H
|
#define _RX_MIXER_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "Audio_Resampler.h"
|
#include "Audio_Resampler.h"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include "Audio_Null.h"
|
#include "Audio_Null.h"
|
||||||
#include "helper/HL_Log.h"
|
#include "helper/HL_Log.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <chrono>
|
|
||||||
#define LOG_SUBSYSTEM "NULL audio"
|
#define LOG_SUBSYSTEM "NULL audio"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
@ -60,7 +59,7 @@ NullInputDevice::NullInputDevice()
|
||||||
|
|
||||||
NullInputDevice::~NullInputDevice()
|
NullInputDevice::~NullInputDevice()
|
||||||
{
|
{
|
||||||
internalClose();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullInputDevice::open()
|
bool NullInputDevice::open()
|
||||||
|
|
@ -73,7 +72,7 @@ bool NullInputDevice::open()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::internalClose()
|
void NullInputDevice::close()
|
||||||
{
|
{
|
||||||
mTimer.reset();
|
mTimer.reset();
|
||||||
if (mBuffer)
|
if (mBuffer)
|
||||||
|
|
@ -84,10 +83,6 @@ void NullInputDevice::internalClose()
|
||||||
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::close()
|
|
||||||
{
|
|
||||||
internalClose();
|
|
||||||
}
|
|
||||||
Format NullInputDevice::getFormat()
|
Format NullInputDevice::getFormat()
|
||||||
{
|
{
|
||||||
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
||||||
|
|
@ -110,7 +105,7 @@ NullOutputDevice::NullOutputDevice()
|
||||||
|
|
||||||
NullOutputDevice::~NullOutputDevice()
|
NullOutputDevice::~NullOutputDevice()
|
||||||
{
|
{
|
||||||
internalClose();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -123,18 +118,13 @@ bool NullOutputDevice::open()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullOutputDevice::internalClose()
|
void NullOutputDevice::close()
|
||||||
{
|
{
|
||||||
mTimer.reset();
|
mTimer.reset();
|
||||||
free(mBuffer); mBuffer = nullptr;
|
free(mBuffer); mBuffer = nullptr;
|
||||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullOutputDevice::close()
|
|
||||||
{
|
|
||||||
internalClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Format NullOutputDevice::getFormat()
|
Format NullOutputDevice::getFormat()
|
||||||
{
|
{
|
||||||
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
|
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,6 @@ namespace Audio
|
||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||||
void internalClose();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NullInputDevice();
|
NullInputDevice();
|
||||||
virtual ~NullInputDevice();
|
virtual ~NullInputDevice();
|
||||||
|
|
@ -57,8 +55,6 @@ namespace Audio
|
||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||||
|
|
||||||
void internalClose();
|
|
||||||
public:
|
public:
|
||||||
NullOutputDevice();
|
NullOutputDevice();
|
||||||
virtual ~NullOutputDevice();
|
virtual ~NullOutputDevice();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "Audio_Quality.h"
|
#include "Audio_Quality.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
|
|
@ -17,10 +17,7 @@
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
#ifndef SHRT_MAX
|
|
||||||
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||||
#endif
|
|
||||||
|
|
||||||
AgcFilter::AgcFilter(int channels)
|
AgcFilter::AgcFilter(int channels)
|
||||||
{
|
{
|
||||||
static const float DefaultLevel = 0.8f;
|
static const float DefaultLevel = 0.8f;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#ifndef __AUDIO_QUALITY_H
|
#ifndef __AUDIO_QUALITY_H
|
||||||
#define __AUDIO_QUALITY_H
|
#define __AUDIO_QUALITY_H
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "Audio_Resampler.h"
|
#include "Audio_Resampler.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Audio
|
||||||
|
|
||||||
|
|
||||||
SpeexResampler::SpeexResampler()
|
SpeexResampler::SpeexResampler()
|
||||||
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0), 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)
|
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)
|
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) / 2 * 2;
|
||||||
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns instance + speex resampler size in bytes
|
// Returns instance + speex resampler size in bytes
|
||||||
|
|
@ -274,7 +273,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
|
||||||
PResampler r;
|
PResampler r;
|
||||||
if (resamplerIter == mResamplerMap.end())
|
if (resamplerIter == mResamplerMap.end())
|
||||||
{
|
{
|
||||||
r = std::make_shared<Resampler>();
|
r = PResampler(new Resampler());
|
||||||
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
||||||
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,9 @@
|
||||||
#include "helper/HL_Exception.h"
|
#include "helper/HL_Exception.h"
|
||||||
#include "helper/HL_String.h"
|
#include "helper/HL_String.h"
|
||||||
#include "helper/HL_Log.h"
|
#include "helper/HL_Log.h"
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifndef WORD
|
#ifndef WORD
|
||||||
# define WORD unsigned short
|
# define WORD unsigned short
|
||||||
|
|
@ -40,7 +39,7 @@ using namespace Audio;
|
||||||
|
|
||||||
// ---------------------- WavFileReader -------------------------
|
// ---------------------- WavFileReader -------------------------
|
||||||
WavFileReader::WavFileReader()
|
WavFileReader::WavFileReader()
|
||||||
:mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
|
:mHandle(nullptr), mRate(0), mLastError(0)
|
||||||
{
|
{
|
||||||
mDataOffset = 0;
|
mDataOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -53,51 +52,38 @@ WavFileReader::~WavFileReader()
|
||||||
|
|
||||||
std::string WavFileReader::readChunk()
|
std::string WavFileReader::readChunk()
|
||||||
{
|
{
|
||||||
char name[5] = {0};
|
char name[5];
|
||||||
readBuffer(name, 4);
|
if (fread(name, 1, 4, mHandle) != 4)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
|
name[4] = 0;
|
||||||
std::string result = name;
|
std::string result = name;
|
||||||
uint32_t size = 0;
|
unsigned size;
|
||||||
readBuffer(&size, 4);
|
if (fread(&size, 4, 1, mHandle) != 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
if (result == "fact")
|
if (result == "fact")
|
||||||
{
|
fread(&mDataLength, 4, 1, mHandle);
|
||||||
uint32_t dataLength = 0;
|
|
||||||
readBuffer(&dataLength, sizeof dataLength);
|
|
||||||
mDataLength = dataLength;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
if (result != "data")
|
if (result != "data")
|
||||||
mInput->seekg(size, std::ios_base::beg);
|
fseek(mHandle, size, SEEK_CUR);
|
||||||
else
|
else
|
||||||
mDataLength = size;
|
mDataLength = size;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WavFileReader::readBuffer(void* buffer, size_t sz)
|
bool WavFileReader::open(const std::tstring& filename)
|
||||||
{
|
|
||||||
auto p = mInput->tellg();
|
|
||||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
|
||||||
if (mInput->tellg() - p != sz)
|
|
||||||
throw Exception(ERR_WAVFILE_FAILED);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
|
|
||||||
{
|
|
||||||
auto p = mInput->tellg();
|
|
||||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
|
||||||
return mInput->tellg() - p;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WavFileReader::open(const std::filesystem::path& p)
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mPath = p;
|
#ifdef WIN32
|
||||||
mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in);
|
mHandle = _wfopen(filename.c_str(), L"rb");
|
||||||
if (!mInput->is_open())
|
#else
|
||||||
|
mHandle = fopen(strx::makeUtf8(filename).c_str(), "rb");
|
||||||
|
#endif
|
||||||
|
if (NULL == mHandle)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
mLastError = errno;
|
mLastError = errno;
|
||||||
|
|
@ -111,62 +97,76 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
||||||
|
|
||||||
// Read the .WAV header
|
// Read the .WAV header
|
||||||
char riff[4];
|
char riff[4];
|
||||||
readBuffer(riff, sizeof riff);
|
if (fread(riff, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
|
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
// Read the file size
|
// Read the file size
|
||||||
uint32_t filesize = 0;
|
unsigned int filesize = 0;
|
||||||
readBuffer(&filesize, sizeof(filesize));
|
if (fread(&filesize, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
char wavefmt[9] = {0};
|
char wavefmt[9];
|
||||||
readBuffer(wavefmt, 8);
|
if (fread(wavefmt, 8, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
|
wavefmt[8] = 0;
|
||||||
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
uint32_t fmtSize = 0;
|
unsigned fmtSize = 0;
|
||||||
readBuffer(&fmtSize, sizeof(fmtSize));
|
if (fread(&fmtSize, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
auto fmtStart = mInput->tellg();
|
unsigned fmtStart = ftell(mHandle);
|
||||||
|
|
||||||
uint16_t formattag = 0;
|
unsigned short formattag = 0;
|
||||||
readBuffer(&formattag, sizeof(formattag));
|
if (fread(&formattag, 2, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
mChannels = 0;
|
mChannels = 0;
|
||||||
readBuffer(&mChannels, sizeof(mChannels));
|
if (fread(&mChannels, 2, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
mSamplerate = 0;
|
mRate = 0;
|
||||||
readBuffer(&mSamplerate, sizeof(mSamplerate));
|
if (fread(&mRate, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
uint32_t avgbytespersec = 0;
|
unsigned int avgbytespersec = 0;
|
||||||
readBuffer(&avgbytespersec, sizeof(avgbytespersec));
|
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
uint16_t blockalign = 0;
|
unsigned short blockalign = 0;
|
||||||
readBuffer(&blockalign, sizeof(blockalign));
|
if (fread(&blockalign, 2, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
mBits = 0;
|
mBits = 0;
|
||||||
readBuffer(&mBits, sizeof(mBits));
|
if (fread(&mBits, 2, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
if (mBits !=8 && mBits != 16)
|
if (mBits !=8 && mBits != 16)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
// Look for the chunk 'data'
|
// Read the "chunk"
|
||||||
mInput->seekg(fmtStart + std::streampos(fmtSize));
|
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
|
||||||
|
//unsigned pos = ftell(mHandle);
|
||||||
mDataLength = 0;
|
mDataLength = 0;
|
||||||
while (readChunk() != "data")
|
while (readChunk() != "data")
|
||||||
;
|
;
|
||||||
|
|
||||||
mDataOffset = mInput->tellg();
|
mFileName = filename;
|
||||||
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
|
mDataOffset = ftell(mHandle);
|
||||||
|
|
||||||
|
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
mInput.reset();
|
fclose(mHandle); mHandle = nullptr;
|
||||||
mLastError = static_cast<unsigned>(-1);
|
mLastError = static_cast<unsigned>(-1);
|
||||||
}
|
}
|
||||||
return isOpened();
|
return isOpened();
|
||||||
|
|
@ -175,117 +175,78 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
||||||
void WavFileReader::close()
|
void WavFileReader::close()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
mInput.reset();
|
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WavFileReader::read(void* buffer, size_t bytes)
|
|
||||||
{
|
{
|
||||||
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
|
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WavFileReader::readRaw(void* buffer, size_t bytes)
|
unsigned WavFileReader::read(short* buffer, unsigned samples)
|
||||||
{
|
|
||||||
return readRaw((short*)buffer, bytes / channels() / sizeof(short)) * channels() * sizeof(short);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WavFileReader::read(short* buffer, size_t samples)
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
if (!mInput)
|
if (!mHandle)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Get number of samples that must be read from source file
|
// Get number of samples that must be read from source file
|
||||||
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
||||||
bool useHeap = requiredBytes > sizeof mTempBuffer;
|
void* temp = alloca(requiredBytes);
|
||||||
void* temp;
|
|
||||||
if (useHeap)
|
|
||||||
temp = malloc(requiredBytes);
|
|
||||||
else
|
|
||||||
temp = mTempBuffer;
|
|
||||||
|
|
||||||
memset(temp, 0, requiredBytes);
|
memset(temp, 0, requiredBytes);
|
||||||
|
|
||||||
// Find required size of input buffer
|
// Find required size of input buffer
|
||||||
if (mDataLength)
|
if (mDataLength)
|
||||||
{
|
{
|
||||||
auto filePosition = mInput->tellg();
|
unsigned filePosition = ftell(mHandle);
|
||||||
|
|
||||||
// Check how much data we can read
|
// Check how much data we can read
|
||||||
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
|
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||||
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t readBytes = tryReadBuffer(temp, requiredBytes);
|
|
||||||
|
|
||||||
size_t processedBytes = 0;
|
|
||||||
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes,
|
|
||||||
buffer, samples * 2 * AUDIO_CHANNELS);
|
|
||||||
|
|
||||||
if (useHeap)
|
|
||||||
free(temp);
|
|
||||||
return result / 2 / AUDIO_CHANNELS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t WavFileReader::readRaw(short* buffer, size_t samples)
|
|
||||||
{
|
|
||||||
LOCK;
|
|
||||||
|
|
||||||
if (!mInput)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Get number of samples that must be read from source file
|
|
||||||
size_t requiredBytes = samples * channels() * sizeof(short);
|
|
||||||
|
|
||||||
// Find required size of input buffer
|
|
||||||
if (mDataLength)
|
|
||||||
{
|
|
||||||
auto filePosition = mInput->tellg();
|
|
||||||
|
|
||||||
// Check how much data we can read
|
|
||||||
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
|
|
||||||
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
|
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t readBytes = tryReadBuffer(buffer, requiredBytes);
|
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
|
||||||
return readBytes / channels() / sizeof(short);
|
size_t processedBytes = 0;
|
||||||
|
size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
|
||||||
|
buffer, samples * 2 * AUDIO_CHANNELS);
|
||||||
|
|
||||||
|
return result / 2 / AUDIO_CHANNELS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WavFileReader::isOpened()
|
bool WavFileReader::isOpened()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
if (!mInput)
|
|
||||||
return false;
|
return (mHandle != 0);
|
||||||
return mInput->is_open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WavFileReader::rewind()
|
void WavFileReader::rewind()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
if (mInput)
|
|
||||||
mInput->seekg(mDataOffset);
|
if (mHandle)
|
||||||
|
fseek(mHandle, mDataOffset, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path WavFileReader::path() const
|
std::tstring WavFileReader::filename() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mPath;
|
|
||||||
|
return mFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WavFileReader::size() const
|
unsigned WavFileReader::size() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
return mDataLength;
|
return mDataLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,8 +260,9 @@ unsigned WavFileReader::lastError() const
|
||||||
#define BITS_PER_CHANNEL 16
|
#define BITS_PER_CHANNEL 16
|
||||||
|
|
||||||
WavFileWriter::WavFileWriter()
|
WavFileWriter::WavFileWriter()
|
||||||
:mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
WavFileWriter::~WavFileWriter()
|
WavFileWriter::~WavFileWriter()
|
||||||
{
|
{
|
||||||
|
|
@ -313,74 +275,68 @@ void WavFileWriter::checkWriteResult(int result)
|
||||||
throw Exception(ERR_WAVFILE_FAILED, errno);
|
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;
|
|
||||||
|
|
||||||
auto p = mOutput->tellp();
|
|
||||||
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
|
|
||||||
if (mOutput->tellp() - p != sz)
|
|
||||||
throw Exception(ERR_WAVFILE_FAILED);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
close();
|
close();
|
||||||
mSamplerate = samplerate;
|
|
||||||
|
mRate = rate;
|
||||||
mChannels = channels;
|
mChannels = channels;
|
||||||
|
|
||||||
mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc);
|
#ifdef WIN32
|
||||||
if (!mOutput)
|
mHandle = _wfopen(filename.c_str(), L"wb");
|
||||||
|
#else
|
||||||
|
mHandle = fopen(strx::makeUtf8(filename).c_str(), "wb");
|
||||||
|
#endif
|
||||||
|
if (nullptr == mHandle)
|
||||||
{
|
{
|
||||||
int errorcode = errno;
|
ICELogError(<< "Failed to create .wav file: filename = " << strx::makeUtf8(filename) << " , error = " << errno);
|
||||||
ICELogError(<< "Failed to create .wav file: filename = " << p << " , error = " << errorcode);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the .WAV header
|
// Write the .WAV header
|
||||||
const char* riff = "RIFF";
|
const char* riff = "RIFF";
|
||||||
writeBuffer(riff, 4);
|
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
|
||||||
|
|
||||||
// Write the file size
|
// Write the file size
|
||||||
uint32_t filesize = 0;
|
unsigned int filesize = 0;
|
||||||
writeBuffer(&filesize, sizeof filesize);
|
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
|
||||||
|
|
||||||
const char* wavefmt = "WAVEfmt ";
|
const char* wavefmt = "WAVEfmt ";
|
||||||
writeBuffer(wavefmt, 8);
|
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
|
||||||
|
|
||||||
// Set the format description
|
// Set the format description
|
||||||
uint32_t dwFmtSize = 16; /*= 16L*/;
|
DWORD dwFmtSize = 16; /*= 16L*/;
|
||||||
writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
|
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
|
||||||
|
|
||||||
WaveFormatEx format;
|
WaveFormatEx format;
|
||||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag));
|
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
|
||||||
|
|
||||||
format.nChannels = mChannels;
|
format.nChannels = mChannels;
|
||||||
writeBuffer(&format.nChannels, sizeof(format.nChannels));
|
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
|
||||||
|
|
||||||
format.nSamplesPerSec = mSamplerate;
|
format.nSamplesPerSec = mRate;
|
||||||
writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec));
|
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
|
||||||
|
|
||||||
format.nAvgBytesPerSec = mSamplerate * 2 * mChannels;
|
format.nAvgBytesPerSec = mRate * 2 * mChannels;
|
||||||
writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec));
|
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
|
||||||
|
|
||||||
format.nBlockAlign = 2 * mChannels;
|
format.nBlockAlign = 2 * mChannels;
|
||||||
writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign));
|
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
|
||||||
|
|
||||||
format.wBitsPerSample = BITS_PER_CHANNEL;
|
format.wBitsPerSample = BITS_PER_CHANNEL;
|
||||||
writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample));
|
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
|
||||||
|
|
||||||
const char* data = "data";
|
const char* data = "data";
|
||||||
writeBuffer(data, 4);
|
checkWriteResult( fwrite(data, 4, 1, mHandle));
|
||||||
|
|
||||||
mPath = p;
|
mFileName = filename;
|
||||||
mWritten = 0;
|
mWritten = 0;
|
||||||
|
|
||||||
mLengthOffset = mOutput->tellp();
|
mLengthOffset = ftell(mHandle);
|
||||||
writeBuffer(&mWritten, sizeof mWritten);
|
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||||
|
|
||||||
return isOpened();
|
return isOpened();
|
||||||
}
|
}
|
||||||
|
|
@ -388,44 +344,51 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
|
||||||
void WavFileWriter::close()
|
void WavFileWriter::close()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
mOutput.reset();
|
|
||||||
|
if (mHandle)
|
||||||
|
{
|
||||||
|
fclose(mHandle);
|
||||||
|
mHandle = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
if (!mOutput)
|
if (!mHandle)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Seek the end of file - here new data will be written
|
// Seek the end of file
|
||||||
mOutput->seekp(0, std::ios_base::end);
|
fseek(mHandle, 0, SEEK_END);
|
||||||
mWritten += bytes;
|
mWritten += bytes;
|
||||||
|
|
||||||
// Write the data
|
// Write the data
|
||||||
writeBuffer(buffer, bytes);
|
fwrite(buffer, bytes, 1, mHandle);
|
||||||
|
|
||||||
// Write file length
|
// Write file length
|
||||||
mOutput->seekp(4, std::ios_base::beg);
|
fseek(mHandle, 4, SEEK_SET);
|
||||||
uint32_t fl = mWritten + 36;
|
int32_t fl = mWritten + 36;
|
||||||
writeBuffer(&fl, sizeof(fl));
|
fwrite(&fl, sizeof(fl), 1, mHandle);
|
||||||
|
|
||||||
// Write data length
|
// Write data length
|
||||||
mOutput->seekp(mLengthOffset, std::ios_base::beg);
|
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
|
||||||
writeBuffer(&mWritten, sizeof(mWritten));
|
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WavFileWriter::isOpened() const
|
bool WavFileWriter::isOpened()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mOutput.get();
|
|
||||||
|
return (mHandle != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path WavFileWriter::path() const
|
std::tstring WavFileWriter::filename()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mPath;
|
|
||||||
|
return mFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
|
|
@ -22,43 +20,35 @@ namespace Audio
|
||||||
class WavFileReader
|
class WavFileReader
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
uint16_t mChannels = 0;
|
FILE* mHandle;
|
||||||
uint16_t mBits = 0;
|
short mChannels;
|
||||||
int mSamplerate = 0;
|
short mBits;
|
||||||
std::filesystem::path mPath;
|
int mRate;
|
||||||
|
std::tstring mFileName;
|
||||||
mutable std::recursive_mutex mFileMtx;
|
mutable std::recursive_mutex mFileMtx;
|
||||||
size_t mDataOffset = 0;
|
unsigned mDataOffset;
|
||||||
size_t mDataLength = 0;
|
unsigned mDataLength;
|
||||||
Resampler mResampler;
|
Resampler mResampler;
|
||||||
unsigned mLastError = 0;
|
unsigned mLastError;
|
||||||
std::unique_ptr<std::ifstream> mInput;
|
|
||||||
uint8_t mTempBuffer[16384];
|
|
||||||
|
|
||||||
std::string readChunk();
|
std::string readChunk();
|
||||||
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
|
|
||||||
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WavFileReader();
|
WavFileReader();
|
||||||
~WavFileReader();
|
~WavFileReader();
|
||||||
|
|
||||||
bool open(const std::filesystem::path& p);
|
bool open(const std::tstring& filename);
|
||||||
void close();
|
void close();
|
||||||
bool isOpened();
|
bool isOpened();
|
||||||
void rewind();
|
void rewind();
|
||||||
int samplerate() const;
|
int rate() const;
|
||||||
int channels() const;
|
|
||||||
|
|
||||||
// This method returns number of read bytes
|
// This method returns number of read bytes
|
||||||
size_t read(void* buffer, size_t bytes);
|
unsigned read(void* buffer, unsigned bytes);
|
||||||
size_t readRaw(void* buffer, size_t bytes);
|
|
||||||
|
|
||||||
// This method returns number of read samples
|
// This method returns number of read samples
|
||||||
size_t read(short* buffer, size_t samples);
|
unsigned read(short* buffer, unsigned samples);
|
||||||
size_t readRaw(short* buffer, size_t samples);
|
std::tstring filename() const;
|
||||||
|
unsigned size() const;
|
||||||
std::filesystem::path path() const;
|
|
||||||
size_t size() const;
|
|
||||||
|
|
||||||
unsigned lastError() const;
|
unsigned lastError() const;
|
||||||
};
|
};
|
||||||
|
|
@ -68,25 +58,25 @@ typedef std::shared_ptr<WavFileReader> PWavFileReader;
|
||||||
class WavFileWriter
|
class WavFileWriter
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
|
FILE* mHandle; /// Handle of audio file.
|
||||||
std::filesystem::path mPath; /// Path to requested audio file.
|
std::tstring mFileName; /// Path to requested audio file.
|
||||||
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||||
size_t mWritten = 0; /// Amount of written data (in bytes)
|
int mWritten; /// Amount of written data (in bytes)
|
||||||
size_t mLengthOffset = 0; /// Position of length field.
|
int mLengthOffset; /// Position of length field.
|
||||||
int mSamplerate = 0,
|
int mRate,
|
||||||
mChannels = 0;
|
mChannels;
|
||||||
|
|
||||||
void checkWriteResult(int result);
|
void checkWriteResult(int result);
|
||||||
void writeBuffer(const void* buffer, size_t sz);
|
|
||||||
public:
|
public:
|
||||||
WavFileWriter();
|
WavFileWriter();
|
||||||
~WavFileWriter();
|
~WavFileWriter();
|
||||||
|
|
||||||
bool open(const std::filesystem::path& p, int samplerate, int channels);
|
bool open(const std::tstring& filename, int rate, int channels);
|
||||||
void close();
|
void close();
|
||||||
bool isOpened() const;
|
bool isOpened();
|
||||||
size_t write(const void* buffer, size_t bytes);
|
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;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,24 @@
|
||||||
project (audio_lib)
|
project (audio_lib)
|
||||||
|
|
||||||
# Rely on C++ 11
|
# Rely on C++ 11
|
||||||
set (CMAKE_CXX_STANDARD 20)
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
set (AUDIOLIB_SOURCES
|
set (AUDIOLIB_SOURCES
|
||||||
Audio_Resampler.cpp
|
Audio_Resampler.cpp
|
||||||
Audio_Resampler.h
|
|
||||||
Audio_Quality.cpp
|
Audio_Quality.cpp
|
||||||
Audio_Quality.h
|
|
||||||
Audio_Mixer.cpp
|
Audio_Mixer.cpp
|
||||||
Audio_Mixer.h
|
|
||||||
Audio_Interface.cpp
|
Audio_Interface.cpp
|
||||||
Audio_Interface.h
|
|
||||||
Audio_Helper.cpp
|
Audio_Helper.cpp
|
||||||
Audio_Helper.h
|
|
||||||
Audio_DataWindow.cpp
|
Audio_DataWindow.cpp
|
||||||
Audio_DataWindow.h
|
|
||||||
Audio_DevicePair.cpp
|
Audio_DevicePair.cpp
|
||||||
Audio_DevicePair.h
|
|
||||||
Audio_Player.cpp
|
Audio_Player.cpp
|
||||||
Audio_Player.h
|
|
||||||
Audio_Null.cpp
|
Audio_Null.cpp
|
||||||
Audio_Null.h
|
|
||||||
Audio_CoreAudio.cpp
|
Audio_CoreAudio.cpp
|
||||||
Audio_CoreAudio.h
|
|
||||||
Audio_DirectSound.cpp
|
Audio_DirectSound.cpp
|
||||||
Audio_DirectSound.h
|
|
||||||
Audio_AndroidOboe.cpp
|
|
||||||
Audio_AndroidOboe.h
|
|
||||||
Audio_WavFile.cpp
|
Audio_WavFile.cpp
|
||||||
Audio_WavFile.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(audio_lib ${AUDIOLIB_SOURCES})
|
add_library(audio_lib ${AUDIOLIB_SOURCES})
|
||||||
|
|
|
||||||
|
|
@ -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 24480
|
||||||
|
#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 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
|
||||||
|
|
@ -156,7 +156,7 @@ Account::Account(PVariantMap config, UserAgent& agent)
|
||||||
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
|
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
|
||||||
mRegistration(NULL)
|
mRegistration(NULL)
|
||||||
{
|
{
|
||||||
mProfile = std::make_shared<resip::UserProfile>(agent.mProfile);
|
mProfile = resip::SharedPtr<resip::UserProfile>(new resip::UserProfile(agent.mProfile));
|
||||||
mId = Account::generateId();
|
mId = Account::generateId();
|
||||||
setup(*config);
|
setup(*config);
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +201,7 @@ void Account::setup(VariantMap &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NAT decorator
|
// NAT decorator
|
||||||
mProfile->setOutboundDecorator(std::make_shared<NATDecorator>(mAgent));
|
mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent)));
|
||||||
|
|
||||||
// Rinstance
|
// Rinstance
|
||||||
if (config.exists(CONFIG_INSTANCE_ID))
|
if (config.exists(CONFIG_INSTANCE_ID))
|
||||||
|
|
@ -266,7 +266,7 @@ void Account::start()
|
||||||
|
|
||||||
// Create registration
|
// Create registration
|
||||||
mRegistration = new ResipSession(*mAgent.mDum);
|
mRegistration = new ResipSession(*mAgent.mDum);
|
||||||
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
||||||
|
|
||||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||||
|
|
@ -380,7 +380,7 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
|
||||||
observer->mSessionId = observer->mSession->sessionId();
|
observer->mSessionId = observer->mSession->sessionId();
|
||||||
observer->mPeer = target;
|
observer->mPeer = target;
|
||||||
|
|
||||||
std::shared_ptr<resip::SipMessage> msg;
|
resip::SharedPtr<resip::SipMessage> msg;
|
||||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
||||||
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
||||||
|
|
@ -498,7 +498,7 @@ void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
|
||||||
//config.mDetectNetworkChange = true;
|
//config.mDetectNetworkChange = true;
|
||||||
//config.mNetworkCheckInterval = 5000;
|
//config.mNetworkCheckInterval = 5000;
|
||||||
|
|
||||||
session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config));
|
session->mIceStack = resip::SharedPtr<ice::Stack>(ice::Stack::makeICEBox(config));
|
||||||
session->mIceStack->setEventHandler(session, this);
|
session->mIceStack->setEventHandler(session, this);
|
||||||
session->mIceStack->setRole(icerole);
|
session->mIceStack->setRole(icerole);
|
||||||
}
|
}
|
||||||
|
|
@ -552,8 +552,9 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||||
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
||||||
}
|
}
|
||||||
|
|
||||||
mUsedTransport = response.getReceivedTransportTuple().getType();
|
const resip::Transport* transport = response.getReceivedTransport();
|
||||||
//bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS;
|
mUsedTransport = transport->transport();
|
||||||
|
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
|
||||||
|
|
||||||
// Retry registration for stream based transport too
|
// Retry registration for stream based transport too
|
||||||
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
|
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
|
||||||
|
|
@ -595,7 +596,7 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||||
//mProfile->setDefaultFrom(from);
|
//mProfile->setDefaultFrom(from);
|
||||||
}
|
}
|
||||||
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
||||||
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
||||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||||
|
|
||||||
|
|
@ -739,8 +740,8 @@ Account::UserInfo Account::getUserInfo() const
|
||||||
return mUserInfo;
|
return mUserInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic_int Account::IdGenerator;
|
resip::AtomicCounter Account::IdGenerator;
|
||||||
int Account::generateId()
|
int Account::generateId()
|
||||||
{
|
{
|
||||||
return ++IdGenerator;
|
return IdGenerator.increment();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ public:
|
||||||
void setup(VariantMap& config);
|
void setup(VariantMap& config);
|
||||||
|
|
||||||
/* Returns corresponding resiprocate profile */
|
/* Returns corresponding resiprocate profile */
|
||||||
std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; }
|
resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; }
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> UserInfo;
|
typedef std::map<std::string, std::string> UserInfo;
|
||||||
void setUserInfo(const UserInfo& info);
|
void setUserInfo(const UserInfo& info);
|
||||||
|
|
@ -83,7 +83,7 @@ protected:
|
||||||
RegistrationState mRegistrationState;
|
RegistrationState mRegistrationState;
|
||||||
|
|
||||||
ice::NetworkAddress mExternalAddress;
|
ice::NetworkAddress mExternalAddress;
|
||||||
std::shared_ptr<resip::UserProfile> mProfile;
|
resip::SharedPtr<resip::UserProfile> mProfile;
|
||||||
UserAgent& mAgent;
|
UserAgent& mAgent;
|
||||||
bool mPresenceOnline;
|
bool mPresenceOnline;
|
||||||
std::string mPresenceContent;
|
std::string mPresenceContent;
|
||||||
|
|
@ -135,7 +135,7 @@ protected:
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
static int generateId();
|
static int generateId();
|
||||||
static std::atomic_int IdGenerator;
|
static resip::AtomicCounter IdGenerator;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Account> PAccount;
|
typedef std::shared_ptr<Account> PAccount;
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes incoming data
|
// 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)
|
if (!mActiveStream)
|
||||||
return;
|
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
|
// 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);
|
s->sendDatagram(destination, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +99,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||||
else
|
else
|
||||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
||||||
}
|
}
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
|
|
||||||
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
||||||
if (mAvailableCodecs.empty())
|
if (mAvailableCodecs.empty())
|
||||||
|
|
@ -113,7 +114,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||||
if (mRemoteTelephoneCodec)
|
if (mRemoteTelephoneCodec)
|
||||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Publish stream state
|
// Publish stream state
|
||||||
const char* attr = nullptr;
|
const char* attr = nullptr;
|
||||||
|
|
@ -124,8 +125,6 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||||
{
|
{
|
||||||
case msSendonly: attr = "recvonly"; break;
|
case msSendonly: attr = "recvonly"; break;
|
||||||
case msInactive: attr = "recvonly"; break;
|
case msInactive: attr = "recvonly"; break;
|
||||||
case msRecvonly:
|
|
||||||
case msSendRecv: break; // Do nothing here
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -228,8 +227,10 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
||||||
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
||||||
{
|
{
|
||||||
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
||||||
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mAvailableCodecs.size())
|
if (!mAvailableCodecs.size())
|
||||||
|
|
@ -300,12 +301,26 @@ std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
|
||||||
if (!mActiveStream)
|
if (!mActiveStream)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
|
// Use tag 1 - it is ok, as we use only single crypto attribute
|
||||||
|
int srtpTag = 1;
|
||||||
|
|
||||||
// Print key to base64 string
|
// Print key to base64 string
|
||||||
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
||||||
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
||||||
resip::Data keyText = d.base64encode();
|
resip::Data keyText = d.base64encode();
|
||||||
|
|
||||||
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)
|
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();
|
resip::Data rawkey = keyText.base64decode();
|
||||||
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
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)
|
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
||||||
|
|
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -12,7 +12,9 @@
|
||||||
#include "../media/MT_Box.h"
|
#include "../media/MT_Box.h"
|
||||||
#include "../media/MT_Stream.h"
|
#include "../media/MT_Stream.h"
|
||||||
#include "../media/MT_Codec.h"
|
#include "../media/MT_Codec.h"
|
||||||
|
#include "../audio/Audio_Interface.h"
|
||||||
|
|
||||||
|
#include "rutil/ThreadIf.hxx"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -35,10 +37,10 @@ public:
|
||||||
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
||||||
|
|
||||||
// Processes incoming data
|
// 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) override;
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
|
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
|
||||||
|
|
||||||
// Updates SDP offer
|
// Updates SDP offer
|
||||||
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
||||||
|
|
@ -74,7 +76,6 @@ public:
|
||||||
void setupMirror(bool enable);
|
void setupMirror(bool enable);
|
||||||
|
|
||||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||||
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// SDP's stream name
|
// SDP's stream name
|
||||||
|
|
@ -110,6 +111,7 @@ protected:
|
||||||
void* mMediaObserverTag = nullptr;
|
void* mMediaObserverTag = nullptr;
|
||||||
|
|
||||||
std::string createCryptoAttribute(SrtpSuite suite);
|
std::string createCryptoAttribute(SrtpSuite suite);
|
||||||
|
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
||||||
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
||||||
|
|
||||||
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "resip/stack/SdpContents.hxx"
|
#include "resip/stack/SdpContents.hxx"
|
||||||
|
#include "rutil/SharedPtr.hxx"
|
||||||
|
|
||||||
#include "../helper/HL_InternetAddress.h"
|
#include "../helper/HL_InternetAddress.h"
|
||||||
#include "../helper/HL_NetworkSocket.h"
|
#include "../helper/HL_NetworkSocket.h"
|
||||||
|
|
@ -45,10 +46,10 @@ public:
|
||||||
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
||||||
|
|
||||||
// Processes incoming data
|
// Processes incoming data
|
||||||
virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
virtual void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
||||||
|
|
||||||
// Updates SDP offer
|
// Updates SDP offer
|
||||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
||||||
|
|
|
||||||
|
|
@ -44,60 +44,6 @@
|
||||||
typedef resip::SdpContents::Session::Medium Medium;
|
typedef resip::SdpContents::Session::Medium Medium;
|
||||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||||
|
|
||||||
class TransportLogger: public resip::Transport::SipMessageLoggingHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void outboundMessage(const resip::Tuple &source, const resip::Tuple &destination, const resip::SipMessage &msg) override
|
|
||||||
{
|
|
||||||
std::ostringstream dest_buffer; dest_buffer << destination;
|
|
||||||
std::ostringstream msg_buffer; msg_buffer << msg;
|
|
||||||
std::string msg_text = msg_buffer.str();
|
|
||||||
#if defined(TARGET_ANDROID)
|
|
||||||
if (msg_text.size() > 512)
|
|
||||||
{
|
|
||||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << " :");
|
|
||||||
msg_text = strx::prefixLines(msg_text, "<---");
|
|
||||||
|
|
||||||
auto lines = strx::split(msg_text);
|
|
||||||
for (const auto& l: lines)
|
|
||||||
ICELogDebug(<< l);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
|
||||||
#else
|
|
||||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: retransmissions store already encoded messages, so callback doesn't send SipMessage it sends
|
|
||||||
// the encoded version of the SipMessage instead. If you need a SipMessage you will need to
|
|
||||||
// re-parse back into a SipMessage in the callback handler.
|
|
||||||
void outboundRetransmit(const resip::Tuple &source, const resip::Tuple &destination, const resip::SendData &data) override
|
|
||||||
{}
|
|
||||||
|
|
||||||
void inboundMessage(const resip::Tuple& source, const resip::Tuple& destination, const resip::SipMessage &msg) override
|
|
||||||
{
|
|
||||||
std::ostringstream source_buffer; source_buffer << source;
|
|
||||||
std::ostringstream msg_buffer; msg_buffer << msg;
|
|
||||||
std::string msg_text = msg_buffer.str();
|
|
||||||
#if defined(TARGET_ANDROID)
|
|
||||||
if (msg_text.size() > 512)
|
|
||||||
{
|
|
||||||
ICELogDebug(<< "Received from " << source_buffer.str() << " :");
|
|
||||||
msg_text = strx::prefixLines(msg_text, "--->");
|
|
||||||
auto lines = strx::split(msg_text);
|
|
||||||
for (const auto& l: lines)
|
|
||||||
ICELogDebug(<< l);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
|
||||||
#else
|
|
||||||
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_buffer.str(), "--->"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//-------------- UserAgent -----------------------
|
//-------------- UserAgent -----------------------
|
||||||
UserAgent::UserAgent()
|
UserAgent::UserAgent()
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +54,7 @@ UserAgent::UserAgent()
|
||||||
mConfig[CONFIG_RTCP_ATTR] = true;
|
mConfig[CONFIG_RTCP_ATTR] = true;
|
||||||
|
|
||||||
// Create master profile
|
// Create master profile
|
||||||
mProfile = std::make_shared<resip::MasterProfile>();
|
mProfile = resip::SharedPtr<resip::MasterProfile>(new resip::MasterProfile());
|
||||||
mProfile->clearSupportedMethods();
|
mProfile->clearSupportedMethods();
|
||||||
mProfile->addSupportedMethod(resip::INVITE);
|
mProfile->addSupportedMethod(resip::INVITE);
|
||||||
mProfile->addSupportedMethod(resip::BYE);
|
mProfile->addSupportedMethod(resip::BYE);
|
||||||
|
|
@ -147,7 +93,7 @@ void UserAgent::start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize resip loggег
|
// Initialize resip loggег
|
||||||
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Info, "Client", *this);
|
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Debug, "Client", *this);
|
||||||
|
|
||||||
// Build list of nameservers if specified
|
// Build list of nameservers if specified
|
||||||
resip::DnsStub::NameserverList nslist;
|
resip::DnsStub::NameserverList nslist;
|
||||||
|
|
@ -159,7 +105,7 @@ void UserAgent::start()
|
||||||
|
|
||||||
while (std::getline(ss, line))
|
while (std::getline(ss, line))
|
||||||
{
|
{
|
||||||
line = strx::trim(line);
|
line = StringHelper::trim(line);
|
||||||
ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized
|
ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized
|
||||||
switch (addr.family())
|
switch (addr.family())
|
||||||
{
|
{
|
||||||
|
|
@ -174,7 +120,7 @@ void UserAgent::start()
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
s = new resip::WinSecurity();
|
s = new resip::WinSecurity();
|
||||||
#elif defined(TARGET_OSX)
|
#elif defined(TARGET_OSX)
|
||||||
s = new resip::Security();
|
s = new resip::MacSecurity();
|
||||||
#elif defined(TARGET_LINUX)
|
#elif defined(TARGET_LINUX)
|
||||||
s = new resip::Security("/etc/ssl/certs");
|
s = new resip::Security("/etc/ssl/certs");
|
||||||
#elif defined(TARGET_ANDROID)
|
#elif defined(TARGET_ANDROID)
|
||||||
|
|
@ -196,14 +142,12 @@ void UserAgent::start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mStack->setTransportSipMessageLoggingHandler(std::make_shared<TransportLogger>());
|
|
||||||
|
|
||||||
// Add transports
|
// Add transports
|
||||||
mTransportList.clear();
|
mTransportList.clear();
|
||||||
resip::InternalTransport* t;
|
resip::InternalTransport* t;
|
||||||
|
|
||||||
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
|
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
||||||
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
|
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
||||||
|
|
||||||
switch (mConfig[CONFIG_TRANSPORT].asInt())
|
switch (mConfig[CONFIG_TRANSPORT].asInt())
|
||||||
{
|
{
|
||||||
|
|
@ -318,16 +262,16 @@ void UserAgent::shutdown()
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
for (auto& observerIter: mClientObserverMap)
|
for (auto observerIter: mClientObserverMap)
|
||||||
observerIter.second->stop();
|
observerIter.second->stop();
|
||||||
|
|
||||||
for (auto& observerIter: mServerObserverMap)
|
for (auto observerIter: mServerObserverMap)
|
||||||
observerIter.second->stop();
|
observerIter.second->stop();
|
||||||
|
|
||||||
for (auto& sessionIter: mSessionMap)
|
for (auto sessionIter: mSessionMap)
|
||||||
sessionIter.second->stop();
|
sessionIter.second->stop();
|
||||||
|
|
||||||
for (auto& accountIter: mAccountSet)
|
for (auto accountIter: mAccountSet)
|
||||||
accountIter->stop();
|
accountIter->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -335,24 +279,24 @@ void UserAgent::shutdown()
|
||||||
bool UserAgent::active()
|
bool UserAgent::active()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mStack != nullptr;
|
return mStack != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::refresh()
|
void UserAgent::refresh()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
for (auto& acc: mAccountSet)
|
for (auto acc: mAccountSet)
|
||||||
acc->refresh();
|
acc->refresh();
|
||||||
|
|
||||||
for (auto& observer: mClientObserverMap)
|
for (auto observer: mClientObserverMap)
|
||||||
observer.second->refresh();
|
observer.second->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onDumCanBeDeleted()
|
void UserAgent::onDumCanBeDeleted()
|
||||||
{
|
{
|
||||||
delete mDum; mDum = nullptr;
|
delete mDum; mDum = NULL;
|
||||||
delete mStack; mStack = nullptr;
|
delete mStack; mStack = NULL;
|
||||||
|
|
||||||
mClientObserverMap.clear();
|
mClientObserverMap.clear();
|
||||||
mServerObserverMap.clear();
|
mServerObserverMap.clear();
|
||||||
|
|
@ -371,10 +315,7 @@ void UserAgent::stop()
|
||||||
mTransportList.clear();
|
mTransportList.clear();
|
||||||
|
|
||||||
// Dump statistics here
|
// Dump statistics here
|
||||||
ICELogInfo(<< "Remaining "
|
ICELogInfo(<< "Remaining " << Session::InstanceCounter.value() << " session(s), " << ResipSession::InstanceCounter.value() << " resip DialogSet(s), " << resip::ClientRegistration::InstanceCounter.value() << " ClientRegistration(s)");
|
||||||
<< Session::InstanceCounter << " session(s), "
|
|
||||||
<< ResipSession::InstanceCounter << " resip DialogSet(s), "
|
|
||||||
<< resip::ClientRegistration::InstanceCounter << " ClientRegistration(s)");
|
|
||||||
|
|
||||||
mDum->shutdown(this);
|
mDum->shutdown(this);
|
||||||
onDumCanBeDeleted();
|
onDumCanBeDeleted();
|
||||||
|
|
@ -438,14 +379,15 @@ void UserAgent::process()
|
||||||
|
|
||||||
// Find all sessions
|
// Find all sessions
|
||||||
std::set<int> idSet;
|
std::set<int> idSet;
|
||||||
for (auto sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
SessionMap::iterator sessionIter;
|
||||||
|
for (sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
||||||
idSet.insert(sessionIter->first);
|
idSet.insert(sessionIter->first);
|
||||||
|
|
||||||
// Now process session one by one checking if current is available yet
|
// Now process session one by one checking if current is available yet
|
||||||
std::set<int>::iterator resipIter;
|
std::set<int>::iterator resipIter;
|
||||||
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
|
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
|
||||||
{
|
{
|
||||||
auto sessionIter = mSessionMap.find(*resipIter);
|
SessionMap::iterator sessionIter = mSessionMap.find(*resipIter);
|
||||||
if (sessionIter == mSessionMap.end())
|
if (sessionIter == mSessionMap.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -486,16 +428,9 @@ void UserAgent::process()
|
||||||
void UserAgent::addRootCert(const ByteBuffer& data)
|
void UserAgent::addRootCert(const ByteBuffer& data)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
if (!mStack)
|
|
||||||
return;
|
|
||||||
resip::Data b(data.data(), data.size());
|
resip::Data b(data.data(), data.size());
|
||||||
try {
|
|
||||||
mStack->getSecurity()->addRootCertPEM(b);
|
mStack->getSecurity()->addRootCertPEM(b);
|
||||||
}
|
}
|
||||||
catch(...) {
|
|
||||||
// Ignore silently
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PAccount UserAgent::createAccount(PVariantMap config)
|
PAccount UserAgent::createAccount(PVariantMap config)
|
||||||
{
|
{
|
||||||
|
|
@ -529,7 +464,7 @@ PSession UserAgent::createSession(PAccount account)
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UserAgent::formatSipAddress(const std::string& sip)
|
std::string UserAgent::formatSipAddress(std::string sip)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
if (sip.size())
|
if (sip.size())
|
||||||
|
|
@ -545,18 +480,17 @@ std::string UserAgent::formatSipAddress(const std::string& sip)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserAgent::isSipAddressValid(const std::string& sip)
|
bool UserAgent::isSipAddressValid(std::string sip)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string s = sip;
|
if (sip.find('<') == std::string::npos)
|
||||||
if (s.find('<') == std::string::npos)
|
sip = "<" + sip;
|
||||||
s = "<" + s;
|
if (sip.find('>') == std::string::npos)
|
||||||
if (s.find('>') == std::string::npos)
|
sip += ">";
|
||||||
s += ">";
|
|
||||||
|
|
||||||
resip::Data d(formatSipAddress(s));
|
resip::Data d(formatSipAddress(sip));
|
||||||
resip::Uri uri(d);
|
resip::Uri uri(d);
|
||||||
result = uri.isWellFormed();
|
result = uri.isWellFormed();
|
||||||
if (result)
|
if (result)
|
||||||
|
|
@ -606,7 +540,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserAgent::compareSipAddresses(const std::string& sip1, const std::string& sip2)
|
bool UserAgent::compareSipAddresses(std::string sip1, std::string sip2)
|
||||||
{
|
{
|
||||||
if (sip1.empty() || sip2.empty())
|
if (sip1.empty() || sip2.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -651,7 +585,7 @@ void UserAgent::sendOffer(Session* session)
|
||||||
if (session->mOriginVersion == 1)
|
if (session->mOriginVersion == 1)
|
||||||
{
|
{
|
||||||
// Construct INVITE session
|
// Construct INVITE session
|
||||||
auto msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
resip::SharedPtr<resip::SipMessage> msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
||||||
|
|
||||||
// Include user headers
|
// Include user headers
|
||||||
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++)
|
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++)
|
||||||
|
|
@ -721,19 +655,13 @@ void UserAgent::onFailure(resip::ClientRegistrationHandle h, const resip::SipMes
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
bool UserAgent::operator()(resip::Log::Level level,
|
bool UserAgent::operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName, const char* file,
|
||||||
const resip::Subsystem& subsystem,
|
int line, const resip::Data& message, const resip::Data& messageWithHeaders)
|
||||||
const resip::Data& appName,
|
|
||||||
const char* file,
|
|
||||||
int line,
|
|
||||||
const resip::Data& message,
|
|
||||||
const resip::Data& messageWithHeaders,
|
|
||||||
const resip::Data& instanceName)
|
|
||||||
{
|
{
|
||||||
std::string filename = file;
|
std::string filename = file;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
ss << "File " << strx::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
ss << "File " << StringHelper::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
||||||
if (level <= resip::Log::Crit)
|
if (level <= resip::Log::Crit)
|
||||||
ICELogCritical(<< ss.str())
|
ICELogCritical(<< ss.str())
|
||||||
else
|
else
|
||||||
|
|
@ -1020,7 +948,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
||||||
|
|
||||||
// See if remote stream has "rtcp" or "rtcp-mux" attributes
|
// See if remote stream has "rtcp" or "rtcp-mux" attributes
|
||||||
if (remoteStream.exists("rtcp"))
|
if (remoteStream.exists("rtcp"))
|
||||||
addr2.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
|
addr2.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
|
||||||
else
|
else
|
||||||
if (remoteStream.exists("rtcp-mux"))
|
if (remoteStream.exists("rtcp-mux"))
|
||||||
addr2.setPort( remoteDefaultPort );
|
addr2.setPort( remoteDefaultPort );
|
||||||
|
|
@ -1062,7 +990,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
||||||
s->mIceStack->clear();
|
s->mIceStack->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t version = sdp.session().origin().getVersion();
|
UInt64 version = sdp.session().origin().getVersion();
|
||||||
s->mRemoteOriginVersion = version;
|
s->mRemoteOriginVersion = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1087,12 +1015,12 @@ void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& m
|
||||||
if (sdp.session().exists("ice-ufrag"))
|
if (sdp.session().exists("ice-ufrag"))
|
||||||
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
|
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
|
||||||
|
|
||||||
//ice::Stack& ice = *s->mIceStack;
|
ice::Stack& ice = *s->mIceStack;
|
||||||
|
|
||||||
uint64_t version = sdp.session().origin().getVersion();
|
UInt64 version = sdp.session().origin().getVersion();
|
||||||
std::string remoteIp = sdp.session().connection().getAddress().c_str();
|
std::string remoteIp = sdp.session().connection().getAddress().c_str();
|
||||||
int code;
|
int code;
|
||||||
if ((uint64_t)-1 == s->mRemoteOriginVersion)
|
if ((UInt64)-1 == s->mRemoteOriginVersion)
|
||||||
{
|
{
|
||||||
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
|
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
|
||||||
}
|
}
|
||||||
|
|
@ -1593,7 +1521,7 @@ VariantMap& UserAgent::config()
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
||||||
{
|
{
|
||||||
resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
|
resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
|
||||||
while (endLine != resip::Data::npos)
|
while (endLine != resip::Data::npos)
|
||||||
|
|
@ -1605,9 +1533,9 @@ VariantMap& UserAgent::config()
|
||||||
}
|
}
|
||||||
if (0 == startLine)
|
if (0 == startLine)
|
||||||
output.push_back(content);
|
output.push_back(content);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/*static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
||||||
{
|
{
|
||||||
resip::Data::size_type p = input.find(":");
|
resip::Data::size_type p = input.find(":");
|
||||||
if (p == resip::Data::npos)
|
if (p == resip::Data::npos)
|
||||||
|
|
@ -1628,7 +1556,7 @@ VariantMap& UserAgent::config()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
|
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
|
||||||
{
|
{
|
||||||
|
|
@ -1636,17 +1564,17 @@ void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, con
|
||||||
ice::NetworkAddress address(*addr, addrlen);
|
ice::NetworkAddress address(*addr, addrlen);
|
||||||
std::string addressText = address.toStdString();
|
std::string addressText = address.toStdString();
|
||||||
|
|
||||||
/*switch (flow)
|
switch (flow)
|
||||||
{
|
{
|
||||||
case resip::InternalTransport::TransportLogger::Flow_Received:
|
case resip::InternalTransport::TransportLogger::Flow_Received:
|
||||||
ICELogDebug(<< "Received from " << addressText << ":" << "\n"
|
ICELogDebug(<< "Received from " << addressText << ":" << "\n"
|
||||||
<< strx::prefixLines(d, "--->"));
|
<< StringHelper::prefixLines(d, "--->"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case resip::InternalTransport::TransportLogger::Flow_Sent:
|
case resip::InternalTransport::TransportLogger::Flow_Sent:
|
||||||
ICELogDebug(<< "Sent to " << addressText << "\n" << strx::prefixLines(d, "<---"));
|
ICELogDebug(<< "Sent to " << addressText << "\n" << StringHelper::prefixLines(d, "<---"));
|
||||||
break;
|
break;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PSession UserAgent::getUserSession(int sessionId)
|
PSession UserAgent::getUserSession(int sessionId)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
#include "../ice/ICETime.h"
|
#include "../ice/ICETime.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "EP_Session.h"
|
#include "EP_Session.h"
|
||||||
#include "EP_Observer.h"
|
#include "EP_Observer.h"
|
||||||
#include "EP_DataProvider.h"
|
#include "EP_DataProvider.h"
|
||||||
|
|
@ -152,8 +152,8 @@ class UserAgent: public resip::ClientRegistrationHandler,
|
||||||
public resip::ServerSubscriptionHandler,
|
public resip::ServerSubscriptionHandler,
|
||||||
public resip::ClientPagerMessageHandler,
|
public resip::ClientPagerMessageHandler,
|
||||||
public resip::ServerPagerMessageHandler,
|
public resip::ServerPagerMessageHandler,
|
||||||
public resip::ClientPublicationHandler
|
public resip::ClientPublicationHandler,
|
||||||
//public resip::InternalTransport::TransportLogger
|
public resip::InternalTransport::TransportLogger
|
||||||
{
|
{
|
||||||
friend class Account;
|
friend class Account;
|
||||||
friend class Session;
|
friend class Session;
|
||||||
|
|
@ -162,9 +162,9 @@ class UserAgent: public resip::ClientRegistrationHandler,
|
||||||
friend class WatcherQueue;
|
friend class WatcherQueue;
|
||||||
public:
|
public:
|
||||||
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
||||||
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
|
static bool compareSipAddresses(std::string sip1, std::string sip2);
|
||||||
static std::string formatSipAddress(const std::string& sip);
|
static std::string formatSipAddress(std::string sip);
|
||||||
static bool isSipAddressValid(const std::string& sip);
|
static bool isSipAddressValid(std::string sip);
|
||||||
struct SipAddress
|
struct SipAddress
|
||||||
{
|
{
|
||||||
bool mValid;
|
bool mValid;
|
||||||
|
|
@ -383,8 +383,7 @@ public:
|
||||||
const char* file,
|
const char* file,
|
||||||
int line,
|
int line,
|
||||||
const resip::Data& message,
|
const resip::Data& message,
|
||||||
const resip::Data& messageWithHeaders,
|
const resip::Data& messageWithHeaders) override;
|
||||||
const resip::Data& instanceName) override;
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region DnsResultSink implementation
|
#pragma region DnsResultSink implementation
|
||||||
|
|
@ -398,7 +397,7 @@ public:
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region TransportLogger implementation
|
#pragma region TransportLogger implementation
|
||||||
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
|
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region ClientPublicationHandler
|
#pragma region ClientPublicationHandler
|
||||||
|
|
@ -448,7 +447,7 @@ protected:
|
||||||
Mutex mGuard;
|
Mutex mGuard;
|
||||||
|
|
||||||
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
|
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
|
||||||
std::shared_ptr<resip::MasterProfile> mProfile;
|
resip::SharedPtr<resip::MasterProfile> mProfile;
|
||||||
|
|
||||||
// Resiprocate's SIP stack object pointer
|
// Resiprocate's SIP stack object pointer
|
||||||
resip::SipStack* mStack;
|
resip::SipStack* mStack;
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ void WatcherQueue::process()
|
||||||
if (i == mItemList.end())
|
if (i == mItemList.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::shared_ptr<resip::SipMessage> msg;
|
resip::SharedPtr<resip::SipMessage> msg;
|
||||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||||
|
|
||||||
switch (i->mState)
|
switch (i->mState)
|
||||||
|
|
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -7,7 +7,11 @@
|
||||||
#include "EP_Engine.h"
|
#include "EP_Engine.h"
|
||||||
#include "EP_AudioProvider.h"
|
#include "EP_AudioProvider.h"
|
||||||
#include "../media/MT_Stream.h"
|
#include "../media/MT_Stream.h"
|
||||||
|
#include "../media/MT_AudioStream.h"
|
||||||
|
#include "../media/MT_Dtmf.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
|
#include "../helper/HL_Exception.h"
|
||||||
|
#include "../helper/HL_StreamState.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "../helper/HL_String.h"
|
#include "../helper/HL_String.h"
|
||||||
|
|
||||||
|
|
@ -16,7 +20,7 @@
|
||||||
typedef resip::SdpContents::Session::Medium Medium;
|
typedef resip::SdpContents::Session::Medium Medium;
|
||||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||||
|
|
||||||
#define IS_MULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
#define DOMULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
||||||
|
|
||||||
|
|
||||||
//------------ ResipSessionAppDialog ------------
|
//------------ ResipSessionAppDialog ------------
|
||||||
|
|
@ -33,12 +37,12 @@ ResipSessionAppDialog::~ResipSessionAppDialog()
|
||||||
|
|
||||||
#pragma region ResipSession
|
#pragma region ResipSession
|
||||||
|
|
||||||
std::atomic_int ResipSession::InstanceCounter;
|
resip::AtomicCounter ResipSession::InstanceCounter;
|
||||||
|
|
||||||
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
||||||
: resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
|
: resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
|
||||||
{
|
{
|
||||||
ResipSession::InstanceCounter++;
|
ResipSession::InstanceCounter.increment();
|
||||||
mTag = NULL;
|
mTag = NULL;
|
||||||
mTerminated = false;
|
mTerminated = false;
|
||||||
mOnWatchingStartSent = false;
|
mOnWatchingStartSent = false;
|
||||||
|
|
@ -58,7 +62,7 @@ ResipSession::~ResipSession()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ResipSession::InstanceCounter--;
|
ResipSession::InstanceCounter.decrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
|
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
|
||||||
|
|
@ -163,12 +167,12 @@ int ResipSession::sessionId()
|
||||||
return mSessionId;
|
return mSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResipSession::setUASProfile(const std::shared_ptr<resip::UserProfile>& profile)
|
void ResipSession::setUASProfile(std::shared_ptr<resip::UserProfile> profile)
|
||||||
{
|
{
|
||||||
mUASProfile = 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);
|
assert(mUserAgent != nullptr);
|
||||||
|
|
||||||
|
|
@ -180,7 +184,7 @@ std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const res
|
||||||
else
|
else
|
||||||
return mUserAgent->mProfile;
|
return mUserAgent->mProfile;
|
||||||
}
|
}
|
||||||
return std::shared_ptr<resip::UserProfile>();
|
return resip::SharedPtr<resip::UserProfile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
@ -258,11 +262,11 @@ void Session::Stream::setRtcpMuxAttr(bool value)
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Session
|
#pragma region Session
|
||||||
std::atomic_int Session::InstanceCounter;
|
resip::AtomicCounter Session::InstanceCounter;
|
||||||
|
|
||||||
Session::Session(PAccount account)
|
Session::Session(PAccount account)
|
||||||
{
|
{
|
||||||
InstanceCounter++;
|
InstanceCounter.increment();
|
||||||
mAccount = account;
|
mAccount = account;
|
||||||
mSessionId = Session::generateId();
|
mSessionId = Session::generateId();
|
||||||
mTag = NULL;
|
mTag = NULL;
|
||||||
|
|
@ -274,7 +278,7 @@ Session::Session(PAccount account)
|
||||||
mRole = Acceptor;
|
mRole = Acceptor;
|
||||||
mGatheredCandidates = false;
|
mGatheredCandidates = false;
|
||||||
mTerminated = false;
|
mTerminated = false;
|
||||||
mRemoteOriginVersion = (uint64_t)-1;
|
mRemoteOriginVersion = (UInt64)-1;
|
||||||
mResipSession = NULL;
|
mResipSession = NULL;
|
||||||
mRefCount = 1;
|
mRefCount = 1;
|
||||||
mOfferAnswerCounter = 0;
|
mOfferAnswerCounter = 0;
|
||||||
|
|
@ -292,7 +296,7 @@ Session::~Session()
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{}
|
{}
|
||||||
InstanceCounter--;
|
InstanceCounter.decrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::start(const std::string& peer)
|
void Session::start(const std::string& peer)
|
||||||
|
|
@ -445,19 +449,29 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||||
MT::Statistics stat;
|
MT::Statistics stat;
|
||||||
|
|
||||||
// Iterate all session providers
|
// Iterate all session providers
|
||||||
Stream* media = nullptr;
|
stat.reset();
|
||||||
for (Stream& stream: mStreamList)
|
Stream* media = NULL;
|
||||||
|
for (unsigned streamIndex = 0; streamIndex < mStreamList.size(); streamIndex++)
|
||||||
|
{
|
||||||
|
Stream& stream = mStreamList[streamIndex];
|
||||||
|
if (stream.provider())
|
||||||
{
|
{
|
||||||
if (!stream.provider())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
media = &stream;
|
media = &stream;
|
||||||
MT::Statistics s = stream.provider()->getStatistics();
|
MT::Statistics s = stream.provider()->getStatistics();
|
||||||
|
#if defined(USE_PVQA_LIBRARY) && !defined(TARGET_SERVER)
|
||||||
|
if (options != InfoOptions::Standard)
|
||||||
|
{
|
||||||
|
// This information is available AFTER audio stream is deleted
|
||||||
|
info[SessionInfo_PvqaMos] = s.mPvqaMos;
|
||||||
|
info[SessionInfo_PvqaReport] = s.mPvqaReport;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
|
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
|
||||||
info[SessionInfo_AudioCodec] = s.mCodecName;
|
info[SessionInfo_AudioCodec] = s.mCodecName;
|
||||||
|
|
||||||
stat += s;
|
stat += s;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
|
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
|
||||||
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
|
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
|
||||||
|
|
@ -468,7 +482,7 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||||
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
||||||
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
||||||
if (stat.mFirstRtpTime)
|
if (stat.mFirstRtpTime)
|
||||||
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::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
|
else
|
||||||
info[SessionInfo_Duration] = 0;
|
info[SessionInfo_Duration] = 0;
|
||||||
|
|
||||||
|
|
@ -535,11 +549,12 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
|
||||||
{
|
{
|
||||||
// Try to process incoming data by ICE stack
|
// Try to process incoming data by ICE stack
|
||||||
int component = -1, stream = -1;
|
int component = -1, stream = -1;
|
||||||
|
bool processed;
|
||||||
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
|
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
|
||||||
{
|
{
|
||||||
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
||||||
buffer.setRemoteAddress(src);
|
buffer.setRemoteAddress(src);
|
||||||
/*bool processed = */mIceStack->processIncomingData(stream, component, buffer);
|
processed = mIceStack->processIncomingData(stream, component, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -777,8 +792,8 @@ void Session::addProvider(PDataProvider provider)
|
||||||
s.setProvider( provider );
|
s.setProvider( provider );
|
||||||
|
|
||||||
// Allocate socket for provider
|
// Allocate socket for provider
|
||||||
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()) );
|
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()) );
|
||||||
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()) );
|
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()) );
|
||||||
s.provider()->setSocket(s.socket4(), s.socket6());
|
s.provider()->setSocket(s.socket4(), s.socket6());
|
||||||
|
|
||||||
// Create ICE stream/component
|
// Create ICE stream/component
|
||||||
|
|
@ -814,10 +829,10 @@ int Session::sessionId()
|
||||||
return mSessionId;
|
return mSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic_int Session::IdGenerator;
|
resip::AtomicCounter Session::IdGenerator;
|
||||||
int Session::generateId()
|
int Session::generateId()
|
||||||
{
|
{
|
||||||
return ++IdGenerator;
|
return (int)IdGenerator.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Session::remoteAddress() const
|
std::string Session::remoteAddress() const
|
||||||
|
|
@ -881,8 +896,8 @@ void Session::refreshMediaPath()
|
||||||
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
||||||
|
|
||||||
// Bring new socket to provider and stream
|
// Bring new socket to provider and stream
|
||||||
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ),
|
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX() ),
|
||||||
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX());
|
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX());
|
||||||
|
|
||||||
p->setSocket(s4, s6);
|
p->setSocket(s4, s6);
|
||||||
s.setSocket4(s4);
|
s.setSocket4(s4);
|
||||||
|
|
@ -899,7 +914,7 @@ void Session::refreshMediaPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Received offer with new SDP
|
// 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)
|
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
|
||||||
{
|
{
|
||||||
bool iceRestart = false;
|
bool iceRestart = false;
|
||||||
|
|
@ -944,7 +959,7 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
||||||
targetAddr.mRtcp.setPort( remoteStream.port() );
|
targetAddr.mRtcp.setPort( remoteStream.port() );
|
||||||
else
|
else
|
||||||
if (stream.rtcpAttr())
|
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
|
else
|
||||||
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
||||||
|
|
||||||
|
|
@ -961,8 +976,8 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
|
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()));
|
||||||
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
|
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()));
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,15 @@
|
||||||
#include "rutil/Logger.hxx"
|
#include "rutil/Logger.hxx"
|
||||||
#include "rutil/Random.hxx"
|
#include "rutil/Random.hxx"
|
||||||
#include "rutil/WinLeakCheck.hxx"
|
#include "rutil/WinLeakCheck.hxx"
|
||||||
|
#include "rutil/AtomicCounter.hxx"
|
||||||
|
|
||||||
#include "../ice/ICEBox.h"
|
#include "../ice/ICEBox.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <atomic>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
#include "EP_Session.h"
|
||||||
#include "EP_Account.h"
|
#include "EP_Account.h"
|
||||||
#include "EP_DataProvider.h"
|
#include "EP_DataProvider.h"
|
||||||
#include "EP_AudioProvider.h"
|
#include "EP_AudioProvider.h"
|
||||||
|
|
@ -213,8 +214,7 @@ public:
|
||||||
void refreshMediaPath();
|
void refreshMediaPath();
|
||||||
|
|
||||||
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
||||||
// There are passing string objects by value; this is correct; this values will modified on the stack.
|
int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||||
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
|
|
||||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
||||||
|
|
||||||
// Session ID
|
// Session ID
|
||||||
|
|
@ -224,7 +224,7 @@ public:
|
||||||
std::vector<Stream> mStreamList;
|
std::vector<Stream> mStreamList;
|
||||||
|
|
||||||
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
||||||
std::shared_ptr<ice::Stack> mIceStack;
|
resip::SharedPtr<ice::Stack> mIceStack;
|
||||||
|
|
||||||
// Pointer to owner user agent instance
|
// Pointer to owner user agent instance
|
||||||
UserAgent* mUserAgent;
|
UserAgent* mUserAgent;
|
||||||
|
|
@ -237,7 +237,7 @@ public:
|
||||||
|
|
||||||
// SDP's origin version for sending
|
// SDP's origin version for sending
|
||||||
int mOriginVersion;
|
int mOriginVersion;
|
||||||
uint64_t mRemoteOriginVersion;
|
UInt64 mRemoteOriginVersion;
|
||||||
|
|
||||||
// SDP's session version
|
// SDP's session version
|
||||||
int mSessionVersion;
|
int mSessionVersion;
|
||||||
|
|
@ -318,8 +318,8 @@ public:
|
||||||
void enqueueOffer();
|
void enqueueOffer();
|
||||||
void processQueuedOffer();
|
void processQueuedOffer();
|
||||||
static int generateId();
|
static int generateId();
|
||||||
static std::atomic_int IdGenerator;
|
static resip::AtomicCounter IdGenerator;
|
||||||
static std::atomic_int InstanceCounter;
|
static resip::AtomicCounter InstanceCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Session> PSession;
|
typedef std::shared_ptr<Session> PSession;
|
||||||
|
|
@ -354,13 +354,13 @@ public:
|
||||||
Type_Call,
|
Type_Call,
|
||||||
Type_Auto
|
Type_Auto
|
||||||
};
|
};
|
||||||
static std::atomic_int InstanceCounter;
|
static resip::AtomicCounter InstanceCounter;
|
||||||
|
|
||||||
|
|
||||||
ResipSession(resip::DialogUsageManager& dum);
|
ResipSession(resip::DialogUsageManager& dum);
|
||||||
virtual ~ResipSession();
|
virtual ~ResipSession();
|
||||||
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
||||||
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||||
|
|
||||||
void setType(Type type);
|
void setType(Type type);
|
||||||
Type type();
|
Type type();
|
||||||
|
|
@ -384,7 +384,7 @@ public:
|
||||||
|
|
||||||
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
||||||
|
|
||||||
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
|
void setUASProfile(std::shared_ptr<resip::UserProfile> profile);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool mTerminated;
|
bool mTerminated;
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -2,9 +2,11 @@ cmake_minimum_required (VERSION 3.15)
|
||||||
project (helper_lib)
|
project (helper_lib)
|
||||||
|
|
||||||
# Rely on C++ 11
|
# Rely on C++ 11
|
||||||
set (CMAKE_CXX_STANDARD 20)
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
option (USE_NULL_UUID "When enabled linking to libuuid is avoided" OFF)
|
||||||
|
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
||||||
|
|
@ -15,6 +17,3 @@ set_property(TARGET helper_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<C
|
||||||
# Private include directories
|
# Private include directories
|
||||||
target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .)
|
target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .)
|
||||||
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
||||||
if (TARGET_LINUX)
|
|
||||||
target_link_libraries (helper_lib PUBLIC uuid)
|
|
||||||
endif()
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include "HL_File.h"
|
#include "HL_File.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <sys/statvfs.h>
|
# include <sys/statvfs.h>
|
||||||
# include <memory.h>
|
# include <memory.h>
|
||||||
|
|
@ -32,24 +32,23 @@ void FileHelper::remove(const char* s)
|
||||||
::remove(s);
|
::remove(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::string FileHelper::gettempname()
|
std::string FileHelper::gettempname()
|
||||||
// {
|
{
|
||||||
// #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
#if defined(TARGET_LINUX)
|
||||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||||
// int code = mkstemp(template_filename);
|
mkstemp(template_filename);
|
||||||
|
return template_filename;
|
||||||
|
#elif defined(TARGET_WIN)
|
||||||
|
char buffer[L_tmpnam];
|
||||||
|
tmpnam(buffer);
|
||||||
|
|
||||||
// return template_filename;
|
return buffer;
|
||||||
// #elif defined(TARGET_WIN)
|
#elif defined(TARGET_OSX)
|
||||||
// char buffer[L_tmpnam];
|
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||||
// tmpnam(buffer);
|
mktemp(template_filename);
|
||||||
|
return template_filename;
|
||||||
// return buffer;
|
#endif
|
||||||
// #elif defined(TARGET_OSX)
|
}
|
||||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
|
||||||
// mktemp(template_filename);
|
|
||||||
// return template_filename;
|
|
||||||
// #endif
|
|
||||||
// }
|
|
||||||
|
|
||||||
bool FileHelper::isAbsolute(const std::string& s)
|
bool FileHelper::isAbsolute(const std::string& s)
|
||||||
{
|
{
|
||||||
|
|
@ -65,7 +64,7 @@ std::string FileHelper::getCurrentDir()
|
||||||
return std::string();
|
return std::string();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
char buf[512];
|
char buf[512];
|
||||||
if (getcwd(buf, sizeof buf) != nullptr)
|
if (getcwd(buf, sizeof buf) != nullptr)
|
||||||
return buf;
|
return buf;
|
||||||
|
|
@ -110,32 +109,3 @@ size_t FileHelper::getFreespace(const std::string& path)
|
||||||
#endif
|
#endif
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileHelper::expandUserHome(const std::string &path)
|
|
||||||
{
|
|
||||||
if (path.empty() || path[0] != '~')
|
|
||||||
return path; // No expansion needed
|
|
||||||
|
|
||||||
const char* home_dir = nullptr;
|
|
||||||
|
|
||||||
#ifdef TARGET_WIN
|
|
||||||
home_dir = std::getenv("USERPROFILE");
|
|
||||||
if (!home_dir)
|
|
||||||
{
|
|
||||||
home_dir = std::getenv("HOMEDRIVE");
|
|
||||||
const char* homepath = std::getenv("HOMEPATH");
|
|
||||||
if (home_dir && homepath) {
|
|
||||||
std::string fullpath(home_dir);
|
|
||||||
fullpath += homepath;
|
|
||||||
return fullpath + path.substr(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
home_dir = std::getenv("HOME");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!home_dir)
|
|
||||||
throw std::runtime_error("Unable to determine the home directory");
|
|
||||||
|
|
||||||
return std::string(home_dir) + path.substr(1);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public:
|
||||||
static void remove(const std::string& s);
|
static void remove(const std::string& s);
|
||||||
static void remove(const char* s);
|
static void remove(const char* s);
|
||||||
|
|
||||||
// static std::string gettempname();
|
static std::string gettempname();
|
||||||
static bool isAbsolute(const std::string& s);
|
static bool isAbsolute(const std::string& s);
|
||||||
|
|
||||||
static std::string getCurrentDir();
|
static std::string getCurrentDir();
|
||||||
|
|
@ -23,7 +23,6 @@ public:
|
||||||
// Returns free space on volume for path
|
// Returns free space on volume for path
|
||||||
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
||||||
static size_t getFreespace(const std::string& path);
|
static size_t getFreespace(const std::string& path);
|
||||||
static std::string expandUserHome(const std::string& path);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1208,7 +1208,7 @@ static const uint16_t byte_crc10_table[256] = {
|
||||||
/* Update the data block's CRC-10 remainder one byte at a time */
|
/* Update the data block's CRC-10 remainder one byte at a time */
|
||||||
uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size)
|
uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size)
|
||||||
{
|
{
|
||||||
/*register*/ int i;
|
register int i;
|
||||||
|
|
||||||
for (i = 0; i < data_blk_size; i++) {
|
for (i = 0; i < data_blk_size; i++) {
|
||||||
crc10_accum = ((crc10_accum << 8) & 0x3ff)
|
crc10_accum = ((crc10_accum << 8) & 0x3ff)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# include <asm/ioctls.h>
|
# include <asm/ioctls.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "HL_NetworkSocket.h"
|
#include "HL_NetworkSocket.h"
|
||||||
|
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||||
|
|
@ -27,7 +27,7 @@ DatagramSocket::DatagramSocket()
|
||||||
|
|
||||||
DatagramSocket::~DatagramSocket()
|
DatagramSocket::~DatagramSocket()
|
||||||
{
|
{
|
||||||
internalClose();
|
closeSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::open(int family)
|
void DatagramSocket::open(int family)
|
||||||
|
|
@ -61,7 +61,7 @@ void DatagramSocket::sendDatagram(InternetAddress &dest, const void *packetData,
|
||||||
if (mHandle == INVALID_SOCKET)
|
if (mHandle == INVALID_SOCKET)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*int sent = */::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
int sent = ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
||||||
|
|
@ -85,7 +85,7 @@ unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::internalClose()
|
void DatagramSocket::closeSocket()
|
||||||
{
|
{
|
||||||
if (mHandle != INVALID_SOCKET)
|
if (mHandle != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
|
|
@ -98,11 +98,6 @@ void DatagramSocket::internalClose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::closeSocket()
|
|
||||||
{
|
|
||||||
internalClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DatagramSocket::isValid() const
|
bool DatagramSocket::isValid() const
|
||||||
{
|
{
|
||||||
return mHandle != INVALID_SOCKET;
|
return mHandle != INVALID_SOCKET;
|
||||||
|
|
@ -168,7 +163,7 @@ unsigned DatagramAgreggator::count()
|
||||||
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
||||||
{
|
{
|
||||||
PDatagramSocket socket = mSocketVector[index];
|
PDatagramSocket socket = mSocketVector[index];
|
||||||
return FD_ISSET(socket->mHandle, &mReadSet);
|
return (FD_ISSET(socket->mHandle, &mReadSet) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,10 @@ public:
|
||||||
virtual SOCKET socket() const;
|
virtual SOCKET socket() const;
|
||||||
|
|
||||||
virtual void open(int family);
|
virtual void open(int family);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int mFamily;
|
int mFamily;
|
||||||
SOCKET mHandle;
|
SOCKET mHandle;
|
||||||
int mLocalPort;
|
int mLocalPort;
|
||||||
void internalClose();
|
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
||||||
PROCESS_INFORMATION pi = { 0 };
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
char* cmdline = (char*)_alloca(cmd.size()+1);
|
char* cmdline = (char*)_alloca(cmd.size()+1);
|
||||||
strcpy(cmdline, 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);
|
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
||||||
if (! fSuccess)
|
if (! fSuccess)
|
||||||
|
|
@ -114,7 +114,7 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
memset(&pi, 0, sizeof pi);
|
memset(&pi, 0, sizeof pi);
|
||||||
|
|
||||||
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
||||||
strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str());
|
strcpy(cmdbuffer, StringHelper::replace(cmdline, "/", "\\").c_str());
|
||||||
|
|
||||||
|
|
||||||
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
||||||
|
|
@ -160,14 +160,14 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
{
|
{
|
||||||
std::string line(buf, cr - buf -1);
|
std::string line(buf, cr - buf -1);
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(strx::trim(line));
|
callback(StringHelper::trim(line));
|
||||||
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} //for
|
} //for
|
||||||
|
|
||||||
if (buf[0])
|
if (buf[0])
|
||||||
callback(strx::trim(std::string(buf)));
|
callback(StringHelper::trim(std::string(buf)));
|
||||||
|
|
||||||
char ctrlc = 3;
|
char ctrlc = 3;
|
||||||
//if (finish_flag)
|
//if (finish_flag)
|
||||||
|
|
@ -278,12 +278,12 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
}
|
}
|
||||||
while (r == sizeof(buffer) - 1);
|
while (r == sizeof(buffer) - 1);
|
||||||
|
|
||||||
if (lines.find('\n') != std::string::npos && line_callback)
|
if (lines.find("\n") != std::string::npos && line_callback)
|
||||||
{
|
{
|
||||||
std::string::size_type p = 0;
|
std::string::size_type p = 0;
|
||||||
while (p < lines.size())
|
while (p < lines.size())
|
||||||
{
|
{
|
||||||
std::string::size_type d = lines.find('\n', p);
|
std::string::size_type d = lines.find("\n", p);
|
||||||
if (d != std::string::npos)
|
if (d != std::string::npos)
|
||||||
{
|
{
|
||||||
if (line_callback)
|
if (line_callback)
|
||||||
|
|
@ -311,8 +311,6 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
|
||||||
|
|
||||||
pid_t OsProcess::findPid(const std::string& cmdline)
|
pid_t OsProcess::findPid(const std::string& cmdline)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -334,6 +332,5 @@ void OsProcess::killByPid(pid_t pid)
|
||||||
return;
|
return;
|
||||||
execSystem("kill -9 " + std::to_string(pid) + " &");
|
execSystem("kill -9 " + std::to_string(pid) + " &");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@
|
||||||
# include <Windows.h>
|
# include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
|
||||||
# include <arpa/inet.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "HL_Rtp.h"
|
#include "HL_Rtp.h"
|
||||||
#include "HL_Exception.h"
|
#include "HL_Exception.h"
|
||||||
#include "HL_String.h"
|
#include "HL_String.h"
|
||||||
|
|
@ -56,12 +52,8 @@ bool RtpHelper::isRtp(const void* buffer, size_t length)
|
||||||
if (length < 12)
|
if (length < 12)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
|
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
|
||||||
if (h->version != 0b10)
|
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35);
|
||||||
return false;
|
|
||||||
|
|
||||||
unsigned char pt = h->pt;
|
|
||||||
bool rtp = ( (pt & 0x7F) >= 96 && (pt & 0x7F) <= 127) || ((pt & 0x7F) < 35);
|
|
||||||
return rtp;
|
return rtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,8 +62,9 @@ bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (length < 12)
|
if (length < 12)
|
||||||
return false;
|
return false;
|
||||||
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
|
unsigned char b = ((const unsigned char*)buffer)[0];
|
||||||
return h->version == 0b10;
|
|
||||||
|
return (b & 0xC0 ) == 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||||
|
|
@ -82,9 +75,9 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||||
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (isRtp(buffer, length))
|
if (isRtp(buffer, length))
|
||||||
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
|
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
|
||||||
else
|
else
|
||||||
return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc);
|
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
|
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
|
||||||
|
|
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -10,8 +10,11 @@
|
||||||
# include "jrtplib/src/rtppacket.h"
|
# include "jrtplib/src/rtppacket.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdint>
|
#include "HL_Uuid.h"
|
||||||
#include <stdlib.h>
|
#include "HL_InternetAddress.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// Class to carry rtp/rtcp socket pair
|
// Class to carry rtp/rtcp socket pair
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
@ -127,7 +127,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||||
if (sock == INVALID_SOCKET)
|
if (sock == INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
// Return null socket
|
// Return null socket
|
||||||
auto result = std::make_shared<DatagramSocket>();
|
PDatagramSocket result(new DatagramSocket());
|
||||||
result->mLocalPort = port;
|
result->mLocalPort = port;
|
||||||
result->mFamily = family;
|
result->mFamily = family;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -170,7 +170,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
||||||
}
|
}
|
||||||
auto resultObject = std::make_shared<DatagramSocket>();
|
PDatagramSocket resultObject(new DatagramSocket());
|
||||||
resultObject->mLocalPort = testport;
|
resultObject->mLocalPort = testport;
|
||||||
resultObject->mHandle = sock;
|
resultObject->mHandle = sock;
|
||||||
if (!resultObject->setBlocking(false))
|
if (!resultObject->setBlocking(false))
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __SOCKET_HEAP_H
|
#ifndef __SOCKET_HEAP_H
|
||||||
#define __SOCKET_HEAP_H
|
#define __SOCKET_HEAP_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
||||||
|
|
@ -93,11 +93,7 @@ int strx::toInt(const char *s, int defaultValue, bool* isOk)
|
||||||
uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||||
{
|
{
|
||||||
uint64_t result = def;
|
uint64_t result = def;
|
||||||
#if defined(TARGET_WIN)
|
if (sscanf(s, "%" SCNu64, &result) != 1)
|
||||||
if (sscanf(s, "%I64d", &result) != 1)
|
|
||||||
#else
|
|
||||||
if (sscanf(s, "%llu", &result) != 1)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
if (isOk)
|
if (isOk)
|
||||||
*isOk = false;
|
*isOk = false;
|
||||||
|
|
@ -319,10 +315,10 @@ static int hex2code(char s)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static int hex2code(const char* s)
|
static int hex2code(const char* s)
|
||||||
{
|
{
|
||||||
return (hex2code(s[0]) << 4) + hex2code(s[1]);
|
return (hex2code(s[0]) << 4) + hex2code(s[1]);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
std::string strx::fromHex2String(const std::string& s)
|
std::string strx::fromHex2String(const std::string& s)
|
||||||
{
|
{
|
||||||
|
|
@ -412,46 +408,3 @@ std::string strx::uppercase(const std::string& s)
|
||||||
std::transform(r.begin(), r.end(), r.begin(), ::toupper);
|
std::transform(r.begin(), r.end(), r.begin(), ::toupper);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string strx::lowercase(const std::string& s)
|
|
||||||
{
|
|
||||||
std::string r(s);
|
|
||||||
std::transform(r.begin(), r.end(), r.begin(), ::tolower);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string strx::removeQuotes(const std::string& s)
|
|
||||||
{
|
|
||||||
std::string r(s);
|
|
||||||
if (s.empty())
|
|
||||||
return s;
|
|
||||||
|
|
||||||
if (r.front() == '"')
|
|
||||||
r = r.substr(1);
|
|
||||||
|
|
||||||
if (r.back() == '"')
|
|
||||||
r = r.substr(0, r.size()-1);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(TARGET_WIN)
|
|
||||||
|
|
||||||
// MSVC++ lacks memmem support
|
|
||||||
const void *memmem(const void *haystack, size_t haystack_len,
|
|
||||||
const void * const needle, const size_t needle_len)
|
|
||||||
{
|
|
||||||
if (!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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstdint>
|
|
||||||
#include "HL_Types.h"
|
#include "HL_Types.h"
|
||||||
|
|
||||||
#ifdef TARGET_OSX
|
#ifdef TARGET_OSX
|
||||||
|
|
@ -20,9 +19,6 @@
|
||||||
class strx
|
class strx
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::string extractFilename(const std::string& path);
|
|
||||||
static std::string appendPath(const std::string& s1, const std::string& s2);
|
|
||||||
|
|
||||||
static std::string makeUtf8(const std::tstring& arg);
|
static std::string makeUtf8(const std::tstring& arg);
|
||||||
static std::string toUtf8(const std::tstring& arg);
|
static std::string toUtf8(const std::tstring& arg);
|
||||||
static std::tstring makeTstring(const std::string& arg);
|
static std::tstring makeTstring(const std::string& arg);
|
||||||
|
|
@ -33,13 +29,15 @@ public:
|
||||||
static std::string toHex(const void* ptr);
|
static std::string toHex(const void* ptr);
|
||||||
static std::string toHex(const uint8_t* input, size_t inputLength);
|
static std::string toHex(const uint8_t* input, size_t inputLength);
|
||||||
static std::string intToString(int value);
|
static std::string intToString(int value);
|
||||||
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
|
||||||
static std::string doubleToString(double value, int precision);
|
static std::string doubleToString(double value, int precision);
|
||||||
static int fromHex2Int(const std::string& s);
|
static int fromHex2Int(const std::string& s);
|
||||||
static std::string fromHex2String(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 float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
|
||||||
|
|
||||||
|
static std::string extractFilename(const std::string& path);
|
||||||
|
static std::string appendPath(const std::string& s1, const std::string& s2);
|
||||||
|
|
||||||
|
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
||||||
static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength);
|
static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength);
|
||||||
|
|
||||||
static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
|
static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
|
||||||
|
|
@ -51,7 +49,7 @@ public:
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
for (const auto& i : v)
|
for (const auto& i : v)
|
||||||
{
|
{
|
||||||
if (&i != &v.front())
|
if (&i != &v[0])
|
||||||
s << delimiter;
|
s << delimiter;
|
||||||
s << i;
|
s << i;
|
||||||
}
|
}
|
||||||
|
|
@ -69,10 +67,8 @@ public:
|
||||||
static bool startsWith(const std::string& s, const std::string& prefix);
|
static bool startsWith(const std::string& s, const std::string& prefix);
|
||||||
static bool endsWith(const std::string& s, const std::string& suffix);
|
static bool endsWith(const std::string& s, const std::string& suffix);
|
||||||
static int stringToDuration(const std::string& s);
|
static int stringToDuration(const std::string& s);
|
||||||
|
|
||||||
static std::string uppercase(const std::string& s);
|
static std::string uppercase(const std::string& s);
|
||||||
static std::string lowercase(const std::string& s);
|
|
||||||
static std::string removeQuotes(const std::string& s);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class XcapHelper
|
class XcapHelper
|
||||||
|
|
@ -85,11 +81,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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -77,21 +77,19 @@ uint64_t ThreadHelper::getCurrentId()
|
||||||
// ------------------- TimeHelper ---------------
|
// ------------------- TimeHelper ---------------
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
// Milliseconds starting from the epoch
|
|
||||||
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
// Seconds starting from the epoch
|
|
||||||
static time_t TimestampBase = time(nullptr);
|
static time_t TimestampBase = time(nullptr);
|
||||||
|
|
||||||
// Returns number of milliseconds starting from 01 Jan 1970 GMT
|
uint64_t TimeHelper::getTimestamp()
|
||||||
uint64_t chronox::getTimestamp()
|
|
||||||
{
|
{
|
||||||
time_point<steady_clock> t = steady_clock::now();
|
time_point<steady_clock> t = steady_clock::now();
|
||||||
|
|
||||||
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
||||||
|
|
||||||
return ms - TimestampStartPoint + TimestampBase * 1000;
|
return ms - TimestampStartPoint + TimestampBase * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t chronox::getUptime()
|
uint64_t TimeHelper::getUptime()
|
||||||
{
|
{
|
||||||
time_point<steady_clock> t = steady_clock::now();
|
time_point<steady_clock> t = steady_clock::now();
|
||||||
|
|
||||||
|
|
@ -100,7 +98,7 @@ uint64_t chronox::getUptime()
|
||||||
return ms - TimestampStartPoint;
|
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)
|
if (later > earlier)
|
||||||
return later - earlier;
|
return later - earlier;
|
||||||
|
|
@ -111,42 +109,14 @@ uint32_t chronox::getDelta(uint32_t later, uint32_t earlier)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec chronox::toTimespec(uint64_t milliseconds)
|
TimeHelper::ExecutionTime::ExecutionTime()
|
||||||
{
|
{
|
||||||
timespec r;
|
mStart = TimeHelper::getTimestamp();
|
||||||
r.tv_sec = milliseconds / 1000;
|
|
||||||
r.tv_nsec = milliseconds % 1000;
|
|
||||||
r.tv_nsec *= 1000 * 1000;
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t chronox::toTimestamp(const timeval& ts)
|
uint64_t TimeHelper::ExecutionTime::getSpentTime() const
|
||||||
{
|
{
|
||||||
return ts.tv_sec * 1000 + ts.tv_usec / 1000;
|
return TimeHelper::getTimestamp() - mStart;
|
||||||
}
|
|
||||||
|
|
||||||
int64_t chronox::getDelta(const timespec& a, const timespec& b)
|
|
||||||
{
|
|
||||||
uint64_t ms_a = a.tv_sec * 1000 + a.tv_nsec / 10000000;
|
|
||||||
uint64_t ms_b = b.tv_sec * 1000 + a.tv_nsec / 10000000;
|
|
||||||
return ms_a - ms_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t chronox::getDelta(const timeval& a, const timeval& b)
|
|
||||||
{
|
|
||||||
int64_t diff_seconds = a.tv_sec - b.tv_sec;
|
|
||||||
int64_t diff_microseconds = a.tv_usec - b.tv_usec;
|
|
||||||
return diff_seconds * 1000 + diff_microseconds / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
chronox::ExecutionTime::ExecutionTime()
|
|
||||||
{
|
|
||||||
mStart = chronox::getTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t chronox::ExecutionTime::getSpentTime() const
|
|
||||||
{
|
|
||||||
return chronox::getTimestamp() - mStart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------- BufferQueue -----------------
|
// --------------- BufferQueue -----------------
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,6 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#if defined(TARGET_WIN)
|
|
||||||
# include <WinSock2.h>
|
|
||||||
# include <Windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef std::recursive_mutex Mutex;
|
typedef std::recursive_mutex Mutex;
|
||||||
typedef std::unique_lock<std::recursive_mutex> Lock;
|
typedef std::unique_lock<std::recursive_mutex> Lock;
|
||||||
|
|
||||||
|
|
@ -51,7 +46,7 @@ public:
|
||||||
static uint64_t getCurrentId();
|
static uint64_t getCurrentId();
|
||||||
};
|
};
|
||||||
|
|
||||||
class chronox
|
class TimeHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Returns current timestamp in milliseconds
|
// Returns current timestamp in milliseconds
|
||||||
|
|
@ -64,14 +59,6 @@ public:
|
||||||
// Handles cases when clock is wrapped.
|
// Handles cases when clock is wrapped.
|
||||||
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
||||||
|
|
||||||
// Converts number of milliseconds starting from Epoch begin to timespec.
|
|
||||||
static timespec toTimespec(uint64_t milliseconds);
|
|
||||||
static uint64_t toTimestamp(const timeval& ts);
|
|
||||||
|
|
||||||
// Returns difference between timestamps in milliseconds
|
|
||||||
static int64_t getDelta(const timespec& a, const timespec& b);
|
|
||||||
static int64_t getDelta(const timeval& a, const timeval& b);
|
|
||||||
|
|
||||||
class ExecutionTime
|
class ExecutionTime
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ thread_pool::thread_pool(size_t num_of_threads, const std::string& name)
|
||||||
num_of_threads = std::thread::hardware_concurrency();
|
num_of_threads = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
for(size_t idx = 0; idx < num_of_threads; idx++)
|
for(size_t idx = 0; idx < num_of_threads; idx++)
|
||||||
this->workers.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
|
// Add new work item to the pool
|
||||||
|
|
@ -20,22 +20,6 @@ void thread_pool::enqueue(const thread_pool::task& t)
|
||||||
this->condition.notify_one();
|
this->condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_pool::wait(std::chrono::milliseconds interval)
|
|
||||||
{
|
|
||||||
while (size() != 0)
|
|
||||||
std::this_thread::sleep_for(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t thread_pool::size()
|
|
||||||
{
|
|
||||||
std::unique_lock l(this->queue_mutex);
|
|
||||||
return this->tasks.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t thread_pool::threads()
|
|
||||||
{
|
|
||||||
return this->workers.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// the destructor joins all threads
|
// the destructor joins all threads
|
||||||
thread_pool::~thread_pool()
|
thread_pool::~thread_pool()
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,10 @@ class thread_pool
|
||||||
public:
|
public:
|
||||||
typedef std::function<void()> task;
|
typedef std::function<void()> task;
|
||||||
|
|
||||||
thread_pool(size_t num_of_threads, const std::string& thread_name);
|
thread_pool(size_t num_of_threads, const std::string&);
|
||||||
~thread_pool();
|
~thread_pool();
|
||||||
|
|
||||||
void enqueue(const task& task);
|
void enqueue(const task& task);
|
||||||
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
|
|
||||||
size_t size();
|
|
||||||
size_t threads();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// need to keep track of threads so we can join them
|
// need to keep track of threads so we can join them
|
||||||
|
|
@ -36,7 +33,7 @@ private:
|
||||||
// synchronization
|
// synchronization
|
||||||
std::mutex queue_mutex;
|
std::mutex queue_mutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
std::atomic_bool stop = false;
|
bool stop = false;
|
||||||
|
|
||||||
// thread name prefix for worker threads
|
// thread name prefix for worker threads
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,10 @@
|
||||||
#include "HL_Time.h"
|
#include "HL_Time.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <chrono>
|
|
||||||
/* return current time in milliseconds */
|
/* return current time in milliseconds */
|
||||||
double now_ms(void)
|
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;
|
struct timespec res;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &res);
|
clock_gettime(CLOCK_MONOTONIC, &res);
|
||||||
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,6 @@
|
||||||
#ifndef __HELPER_TIME_H
|
#ifndef __HELPER_TIME_H
|
||||||
#define __HELPER_TIME_H
|
#define __HELPER_TIME_H
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
// Return current monotonic number of milliseconds starting from some point
|
|
||||||
extern double now_ms();
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "HL_Types.h"
|
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -26,139 +26,4 @@ enum SdpDirection
|
||||||
Sdp_Offer
|
Sdp_Offer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
template<
|
|
||||||
class K, class V,
|
|
||||||
class HashK = std::hash<K>, class EqK = std::equal_to<K>,
|
|
||||||
class HashV = std::hash<V>, class EqV = std::equal_to<V>
|
|
||||||
>
|
|
||||||
class BiMap {
|
|
||||||
public:
|
|
||||||
using key_type = K;
|
|
||||||
using mapped_type = V;
|
|
||||||
|
|
||||||
BiMap(const std::map<K,V>& initializers) {
|
|
||||||
for (const auto& item: initializers) {
|
|
||||||
insert(item.first, item.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert a new (key, value) pair. Returns false if either key or value already exists.
|
|
||||||
bool insert(const K& k, const V& v) {
|
|
||||||
if (contains_key(k) || contains_value(v)) return false;
|
|
||||||
auto ok = forward_.emplace(k, v);
|
|
||||||
try {
|
|
||||||
auto ov = reverse_.emplace(v, k);
|
|
||||||
if (!ov.second) { // shouldn't happen given the guard above
|
|
||||||
forward_.erase(k);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
forward_.erase(k);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return ok.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool insert(K&& k, V&& v) {
|
|
||||||
if (contains_key(k) || contains_value(v)) return false;
|
|
||||||
auto ok = forward_.emplace(std::move(k), std::move(v));
|
|
||||||
try {
|
|
||||||
auto ov = reverse_.emplace(ok.first->second, ok.first->first); // use stored refs
|
|
||||||
if (!ov.second) {
|
|
||||||
forward_.erase(ok.first);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
forward_.erase(ok.first);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return ok.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace value for existing key (and update reverse map). Returns false if value is already bound elsewhere.
|
|
||||||
bool replace_by_key(const K& k, const V& new_v) {
|
|
||||||
auto it = forward_.find(k);
|
|
||||||
if (it == forward_.end()) return false;
|
|
||||||
if (contains_value(new_v)) return false;
|
|
||||||
// remove old reverse, insert new reverse, then update forward
|
|
||||||
reverse_.erase(it->second);
|
|
||||||
reverse_.emplace(new_v, k);
|
|
||||||
it->second = new_v;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace key for existing value (and update forward map). Returns false if key is already bound elsewhere.
|
|
||||||
bool replace_by_value(const V& v, const K& new_k) {
|
|
||||||
auto it = reverse_.find(v);
|
|
||||||
if (it == reverse_.end()) return false;
|
|
||||||
if (contains_key(new_k)) return false;
|
|
||||||
forward_.erase(it->second);
|
|
||||||
forward_.emplace(new_k, v);
|
|
||||||
it->second = new_k;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Erase by key/value. Return number erased (0 or 1).
|
|
||||||
size_t erase_key(const K& k) {
|
|
||||||
auto it = forward_.find(k);
|
|
||||||
if (it == forward_.end()) return 0;
|
|
||||||
reverse_.erase(it->second);
|
|
||||||
forward_.erase(it);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t erase_value(const V& v) {
|
|
||||||
auto it = reverse_.find(v);
|
|
||||||
if (it == reverse_.end()) return 0;
|
|
||||||
forward_.erase(it->second);
|
|
||||||
reverse_.erase(it);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup
|
|
||||||
bool contains_key(const K& k) const { return forward_.find(k) != forward_.end(); }
|
|
||||||
bool contains_value(const V& v) const { return reverse_.find(v) != reverse_.end(); }
|
|
||||||
|
|
||||||
const V* find_by_key(const K& k) const {
|
|
||||||
auto it = forward_.find(k);
|
|
||||||
return (it == forward_.end()) ? nullptr : &it->second;
|
|
||||||
}
|
|
||||||
const K* find_by_value(const V& v) const {
|
|
||||||
auto it = reverse_.find(v);
|
|
||||||
return (it == reverse_.end()) ? nullptr : &it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// at() variants throw std::out_of_range on missing entries
|
|
||||||
const V& at_key(const K& k) const { return forward_.at(k); }
|
|
||||||
const K& at_value(const V& v) const { return reverse_.at(v); }
|
|
||||||
|
|
||||||
void clear() noexcept {
|
|
||||||
forward_.clear();
|
|
||||||
reverse_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const noexcept { return forward_.empty(); }
|
|
||||||
size_t size() const noexcept { return forward_.size(); }
|
|
||||||
|
|
||||||
// Reserve buckets for performance (optional)
|
|
||||||
void reserve(size_t n) {
|
|
||||||
forward_.reserve(n);
|
|
||||||
reverse_.reserve(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<K, V, HashK, EqK> forward_;
|
|
||||||
std::unordered_map<V, K, HashV, EqV> reverse_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
typedef std::chrono::steady_clock::time_point timepoint_t;
|
|
||||||
typedef std::chrono::steady_clock monoclock_t;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,77 @@
|
||||||
#include "HL_Uuid.h"
|
#include "HL_Uuid.h"
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <random>
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#if defined(TARGET_LINUX)
|
|
||||||
#define UUID_SYSTEM_GENERATOR
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "uuid.h"
|
|
||||||
|
|
||||||
Uuid::Uuid()
|
Uuid::Uuid()
|
||||||
{
|
{
|
||||||
|
#if defined(TARGET_WIN) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
memset(&mUuid, 0, sizeof mUuid);
|
memset(&mUuid, 0, sizeof mUuid);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Uuid Uuid::generateOne()
|
Uuid Uuid::generateOne()
|
||||||
{
|
{
|
||||||
Uuid result;
|
Uuid result;
|
||||||
// #if defined(TARGET_LINUX)
|
#if !defined(USE_NULL_UUID)
|
||||||
// auto id = uuids::uuid_system_generator{}();
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
// #else
|
uuid_generate(result.mUuid);
|
||||||
std::random_device rd;
|
#endif
|
||||||
auto seed_data = std::array<int, std::mt19937::state_size> {};
|
#if defined(TARGET_WIN)
|
||||||
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
|
UuidCreate(&result.mUuid);
|
||||||
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
|
#endif
|
||||||
std::mt19937 generator(seq);
|
#endif
|
||||||
uuids::uuid_random_generator gen{generator};
|
|
||||||
|
|
||||||
auto id = gen();
|
|
||||||
// #endif
|
|
||||||
memcpy(result.mUuid, id.as_bytes().data(), id.as_bytes().size_bytes());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uuid Uuid::parse(const std::string &s)
|
Uuid Uuid::parse(const std::string &s)
|
||||||
{
|
{
|
||||||
Uuid result;
|
Uuid result;
|
||||||
auto id = uuids::uuid::from_string(s);
|
#if !defined(USE_NULL_UUID)
|
||||||
if (id)
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
memcpy(result.mUuid, id->as_bytes().data(), id->as_bytes().size_bytes());
|
uuid_parse(s.c_str(), result.mUuid);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TARGET_WIN)
|
||||||
|
UuidFromStringA((RPC_CSTR)s.c_str(), &result.mUuid);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Uuid::toString() const
|
std::string Uuid::toString() const
|
||||||
{
|
{
|
||||||
auto s = std::span<uuids::uuid::value_type, 16>{(uuids::uuid::value_type*)mUuid, 16};
|
#if defined(USE_NULL_UUID)
|
||||||
uuids::uuid id(s);
|
return "UUID_disabled";
|
||||||
return uuids::to_string(id);
|
#else
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
|
uuid_unparse_lower(mUuid, buf);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TARGET_WIN)
|
||||||
|
RPC_CSTR s = nullptr;
|
||||||
|
UuidToStringA(&mUuid, &s);
|
||||||
|
if (s)
|
||||||
|
{
|
||||||
|
strcpy(buf, (const char*)s);
|
||||||
|
RpcStringFreeA(&s);
|
||||||
|
s = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Uuid::operator < (const Uuid& right) const
|
bool Uuid::operator < (const Uuid& right) const
|
||||||
{
|
{
|
||||||
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
|
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TARGET_WIN)
|
||||||
|
return memcmp(&mUuid, &right.mUuid, sizeof(mUuid)) < 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,14 @@
|
||||||
#define __HL_UUID_H
|
#define __HL_UUID_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdint.h>
|
|
||||||
|
#if (defined(TARGET_LINUX) || defined(TARGET_OSX)) && !defined(USE_NULL_UUID)
|
||||||
|
// Please do not forget "sudo apt install uuid-dev" on Ubuntu
|
||||||
|
# include <uuid/uuid.h>
|
||||||
|
#endif
|
||||||
|
#if defined(TARGET_WIN)
|
||||||
|
# include <rpc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
class Uuid
|
class Uuid
|
||||||
{
|
{
|
||||||
|
|
@ -14,7 +21,20 @@ public:
|
||||||
bool operator < (const Uuid& right) const;
|
bool operator < (const Uuid& right) const;
|
||||||
|
|
||||||
protected:
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
project (media_lib)
|
project (media_lib)
|
||||||
|
|
||||||
if (NOT DEFINED LIB_PLATFORM)
|
# Rely on C++ 11
|
||||||
message(FATAL_ERROR media_lib project requires LIB_PLATFORM to be set - it uses libraries from that directory)
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
endif()
|
|
||||||
|
|
||||||
# Rely on C++ 20
|
|
||||||
set (CMAKE_CXX_STANDARD 20)
|
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
# Produce PIC code always
|
# Produce PIC code always
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
option (USE_RESIP_INTEGRATION "Use some resiprocate specific routines" OFF)
|
||||||
|
|
||||||
set (SOURCES
|
set (SOURCES
|
||||||
MT_Statistics.cpp
|
MT_Statistics.cpp
|
||||||
|
|
@ -26,8 +23,6 @@ set (SOURCES
|
||||||
MT_AudioReceiver.cpp
|
MT_AudioReceiver.cpp
|
||||||
MT_AudioCodec.cpp
|
MT_AudioCodec.cpp
|
||||||
MT_CngHelper.cpp
|
MT_CngHelper.cpp
|
||||||
MT_AmrCodec.cpp
|
|
||||||
MT_EvsCodec.cpp
|
|
||||||
|
|
||||||
MT_Statistics.h
|
MT_Statistics.h
|
||||||
MT_WebRtc.h
|
MT_WebRtc.h
|
||||||
|
|
@ -43,48 +38,48 @@ set (SOURCES
|
||||||
MT_AudioReceiver.h
|
MT_AudioReceiver.h
|
||||||
MT_AudioCodec.h
|
MT_AudioCodec.h
|
||||||
MT_CngHelper.h
|
MT_CngHelper.h
|
||||||
MT_AmrCodec.h
|
|
||||||
MT_EvsCodec.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(media_lib ${SOURCES})
|
|
||||||
set (LIBS_CODEC)
|
|
||||||
|
|
||||||
if (USE_AMR_CODEC)
|
if (USE_AMR_CODEC)
|
||||||
include (${LIB_PLATFORM}/platform_libs.cmake)
|
|
||||||
message("Media: AMR NB and WB codecs will be included.")
|
message("Media: AMR NB and WB codecs will be included.")
|
||||||
target_compile_definitions(media_lib PUBLIC USE_AMR_CODEC)
|
add_definitions(-DUSE_AMR_CODEC)
|
||||||
list (APPEND LIBS_CODEC ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
set (SOURCES ${SOURCES} MT_AmrCodec.cpp MT_AmrCodec.h)
|
||||||
|
set (LIBS_CODEC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_EVS_CODEC)
|
if (USE_EVS_CODEC)
|
||||||
message("Media: EVS codec will be included.")
|
message("Media: EVS codec will be included.")
|
||||||
target_compile_definitions (media_lib PUBLIC USE_EVS_CODEC)
|
add_definitions (-DUSE_EVS_CODEC)
|
||||||
list (APPEND LIBS_CODEC evs_codec)
|
set (SOURCES ${SOURCES} MT_EvsCodec.cpp MT_EvsCodec.h)
|
||||||
|
set (LIBS_CODEC evs_codec)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_OPUS_CODEC)
|
if (USE_OPUS_CODEC)
|
||||||
message("Media: Opus codec will be included.")
|
message("Media: Opus codec will be included.")
|
||||||
target_compile_definitions(media_lib PUBLIC USE_OPUS_CODEC)
|
add_definitions(-DUSE_OPUS_CODEC)
|
||||||
list (APPEND LIBS_CODEC opus)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM MATCHES "Linux*" OR CMAKE_SYSTEM MATCHES "Darwin*")
|
|
||||||
target_compile_definitions(media_lib PUBLIC HAVE_NETINET_IN_H)
|
if(CMAKE_SYSTEM MATCHES "Linux*")
|
||||||
|
add_definitions(-DHAVE_NETINET_IN_H)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM MATCHES "Darwin*")
|
||||||
|
# OS X Specific flags
|
||||||
|
add_definitions(-DHAVE_NETINET_IN_H)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||||
# Windows Specific flags - MSVC expected
|
# Windows Specific flags - MSVC expected
|
||||||
target_compile_definitions(media_lib PRIVATE
|
add_definitions(
|
||||||
_CRT_SECURE_NO_WARNINGS
|
-D_CRT_SECURE_NO_WARNINGS
|
||||||
HAVE_WINSOCK2_H
|
-DHAVE_WINSOCK2_H
|
||||||
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
|
-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
|
||||||
UNICODE
|
-DUNICODE
|
||||||
_UNICODE )
|
-D_UNICODE )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_library(media_lib ${SOURCES})
|
||||||
|
|
||||||
# Dependency on ice_stack - Linux build requires it
|
# Dependency on ice_stack - Linux build requires it
|
||||||
# Codec libraries as well
|
# Codec libraries as well
|
||||||
|
|
@ -99,9 +94,9 @@ target_include_directories(media_lib
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/opus/include/
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/
|
||||||
${LIB_PLATFORM}/opus/include
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(media_lib
|
target_include_directories(media_lib
|
||||||
|
|
@ -112,4 +107,8 @@ target_include_directories(media_lib
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_math
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_math
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (USE_RESIP_INTEGRATION)
|
||||||
|
message("USE_RESIP_INTEGRATION is turned on!")
|
||||||
|
target_compile_definitions(media_lib PUBLIC -DUSE_RESIP_INTEGRATION)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,30 +5,85 @@
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_IuUP.h"
|
#include "../helper/HL_IuUP.h"
|
||||||
#include "../helper/HL_Exception.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "AmrCodec"
|
#define LOG_SUBSYSTEM "AmrCodec"
|
||||||
using namespace MT;
|
using namespace MT;
|
||||||
|
|
||||||
|
|
||||||
// Constant of AMR-NB frame lengths in bytes.
|
static const uint8_t amr_block_size[16]={ 13, 14, 16, 18, 20, 21, 27, 32,
|
||||||
const uint8_t amrnb_framelen[9] =
|
6 , 0 , 0 , 0 , 0 , 0 , 0 , 1 };
|
||||||
{12, 13, 15, 17, 19, 20, 26, 31, 5};
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant of AMR-NB frame lengths in bytes.
|
||||||
|
*/
|
||||||
|
const uint8_t amrnb_framelen[16] =
|
||||||
|
{12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant of AMR-NB frame lengths in bits.
|
||||||
|
*/
|
||||||
const uint16_t amrnb_framelenbits[9] =
|
const uint16_t amrnb_framelenbits[9] =
|
||||||
{95, 103, 118, 134, 148, 159, 204, 244, 39};
|
{95, 103, 118, 134, 148, 159, 204, 244, 39};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant of AMR-NB bitrates.
|
||||||
|
*/
|
||||||
|
const uint16_t amrnb_bitrates[8] =
|
||||||
|
{4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200};
|
||||||
|
|
||||||
// Constant of AMR-WB frame lengths in bytes.
|
/**
|
||||||
const uint8_t amrwb_framelen[10] =
|
* Constant of AMR-WB frame lengths in bytes.
|
||||||
{17, 23, 32, 37, 40, 46, 50, 58, 60, 5 /* SID packet */};
|
*/
|
||||||
|
const uint8_t amrwb_framelen[16] =
|
||||||
|
{17, 23, 32, 37, 40, 46, 50, 58, 60, 5, 0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant of AMR-WB frame lengths in bits.
|
||||||
|
*/
|
||||||
const uint16_t amrwb_framelenbits[10] =
|
const uint16_t amrwb_framelenbits[10] =
|
||||||
{132, 177, 253, 285, 317, 365, 397, 461, 477, 40 /* 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
|
// Helper routines
|
||||||
|
|
||||||
|
/*static int8_t bitrateToMode(uint16_t bitrate)
|
||||||
|
{
|
||||||
|
int8_t mode = -1;
|
||||||
|
|
||||||
|
switch (bitrate)
|
||||||
|
{
|
||||||
|
// AMR NB
|
||||||
|
case 4750: mode = 0; break;
|
||||||
|
case 5150: mode = 1; break;
|
||||||
|
case 5900: mode = 2; break;
|
||||||
|
case 6700: mode = 3; break;
|
||||||
|
case 7400: mode = 4; break;
|
||||||
|
case 7950: mode = 5; break;
|
||||||
|
case 10200: mode = 6; break;
|
||||||
|
case 12200: mode = 7; break;
|
||||||
|
|
||||||
|
// AMRWB
|
||||||
|
case 6600: mode = 0; break;
|
||||||
|
case 8850: mode = 1; break;
|
||||||
|
case 12650: mode = 2; break;
|
||||||
|
case 14250: mode = 3; break;
|
||||||
|
case 15850: mode = 4; break;
|
||||||
|
case 18250: mode = 5; break;
|
||||||
|
case 19850: mode = 6; break;
|
||||||
|
case 23050: mode = 7; break;
|
||||||
|
case 23850: mode = 8; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode;
|
||||||
|
}*/
|
||||||
|
|
||||||
struct AmrPayloadInfo
|
struct AmrPayloadInfo
|
||||||
{
|
{
|
||||||
const uint8_t* mPayload;
|
const uint8_t* mPayload;
|
||||||
|
|
@ -64,13 +119,12 @@ struct AmrPayload
|
||||||
static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
||||||
{
|
{
|
||||||
AmrPayload result;
|
AmrPayload result;
|
||||||
|
|
||||||
// Do not skip packet by default; I suppose packet is good enough by default.
|
// Do not skip packet by default; I suppose packet is good enough by default.
|
||||||
result.mDiscardPacket = false;
|
result.mDiscardPacket = false;
|
||||||
|
|
||||||
// Wrap incoming data with ByteArray to make bit dequeuing easy
|
// Wrap incoming data with ByteArray to make bit dequeuing easy
|
||||||
ByteBuffer byte_reader(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
ByteBuffer dataIn(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
||||||
BitReader bit_reader (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
|
// In bandwidth-efficient mode, the payload header simply consists of a
|
||||||
// 4-bit codec mode request:
|
// 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
|
// AMR, as defined in Table 1a in [2], or 0-8 for AMR-WB, as defined
|
||||||
// in Table 1a in [4]. CMR value 15 indicates that no mode request
|
// in Table 1a in [4]. CMR value 15 indicates that no mode request
|
||||||
// is present, and other values are for future use.
|
// is present, and other values are for future use.
|
||||||
result.mCodeModeRequest = static_cast<uint8_t>(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
|
// Consume extra 4 bits for octet aligned profile
|
||||||
if (input.mOctetAligned)
|
if (input.mOctetAligned)
|
||||||
bit_reader.readBits(4);
|
br.readBits(4);
|
||||||
|
|
||||||
// Skip interleaving flags for now for octet aligned mode
|
// Skip interleaving flags for now for octet aligned mode
|
||||||
if (input.mInterleaving && input.mOctetAligned)
|
if (input.mInterleaving && input.mOctetAligned)
|
||||||
bit_reader.readBits(8);
|
br.readBits(8);
|
||||||
|
|
||||||
// Silence codec mode constant (it differs for wideband and narrowband codecs)
|
// Silence codec mode constant (it differs for wideband and narrowband codecs)
|
||||||
uint8_t SID_FT = input.mWideband ? 9 : 8;
|
uint8_t SID_FT = input.mWideband ? 9 : 8;
|
||||||
|
|
@ -103,121 +158,105 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
||||||
// F (1 bit): If set to 1, indicates that this frame is followed by
|
// F (1 bit): If set to 1, indicates that this frame is followed by
|
||||||
// another speech frame in this payload; if set to 0, indicates that
|
// another speech frame in this payload; if set to 0, indicates that
|
||||||
// this frame is the last frame in this payload.
|
// this frame is the last frame in this payload.
|
||||||
F = bit_reader.readBit();
|
F = br.readBit();
|
||||||
|
|
||||||
// FT (4 bits): Frame type index, indicating either the AMR or AMR-WB
|
// FT (4 bits): Frame type index, indicating either the AMR or AMR-WB
|
||||||
// speech coding mode or comfort noise (SID) mode of the
|
// speech coding mode or comfort noise (SID) mode of the
|
||||||
// corresponding frame carried in this payload.
|
// corresponding frame carried in this payload.
|
||||||
FT = static_cast<uint8_t>(bit_reader.readBits(4));
|
FT = static_cast<uint8_t>(br.readBits(4));
|
||||||
|
|
||||||
// Q (1 bit): Frame quality indicator. If set to 0, indicates the
|
// Q (1 bit): Frame quality indicator. If set to 0, indicates the
|
||||||
// corresponding frame is severely damaged, and the receiver should
|
// corresponding frame is severely damaged, and the receiver should
|
||||||
// set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD
|
// set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD
|
||||||
// depending on the frame type (FT).
|
// depending on the frame type (FT).
|
||||||
Q = bit_reader.readBit();
|
Q = br.readBit();
|
||||||
|
|
||||||
// Handle padding for octet alignment
|
|
||||||
if (input.mOctetAligned)
|
|
||||||
bit_reader.readBits(2);
|
|
||||||
|
|
||||||
AmrFrame frame;
|
|
||||||
frame.mFrameType = FT;
|
|
||||||
frame.mSTI = 0;
|
|
||||||
frame.mMode = FT < SID_FT ? FT : 0xFF;
|
|
||||||
frame.mGoodQuality = Q == 1;
|
|
||||||
frame.mTimestamp = input.mCurrentTimestamp;
|
|
||||||
result.mFrames.push_back(frame);
|
|
||||||
input.mCurrentTimestamp += input.mWideband ? 320 : 160;
|
|
||||||
}
|
|
||||||
while (F != 0);
|
|
||||||
|
|
||||||
for (size_t frameIndex=0; frameIndex < result.mFrames.size() && !result.mDiscardPacket; frameIndex++)
|
|
||||||
{
|
|
||||||
AmrFrame& f = result.mFrames[frameIndex];
|
|
||||||
|
|
||||||
// If receiving a ToC entry with a FT value in the range 9-14 for AMR or
|
// If receiving a ToC entry with a FT value in the range 9-14 for AMR or
|
||||||
// 10-13 for AMR-WB, the whole packet SHOULD be discarded. This is to
|
// 10-13 for AMR-WB, the whole packet SHOULD be discarded. This is to
|
||||||
// avoid the loss of data synchronization in the depacketization
|
// avoid the loss of data synchronization in the depacketization
|
||||||
// process, which can result in a huge degradation in speech quality.
|
// process, which can result in a huge degradation in speech quality.
|
||||||
bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14);
|
if ((input.mWideband && (FT >= 10 && FT <= 13)) ||
|
||||||
// discard |= input.mWideband ? f.mFrameType >= 14 : f.mFrameType >= 15;
|
(!input.mWideband && (FT >= 9 && FT <= 14)))
|
||||||
if (discard)
|
|
||||||
{
|
{
|
||||||
|
ICELogMedia(<< "Discard corrupted packet");
|
||||||
|
// Discard bad packet
|
||||||
result.mDiscardPacket = true;
|
result.mDiscardPacket = true;
|
||||||
continue;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.mWideband && f.mFrameType == 15)
|
// Handle padding for octet alignment
|
||||||
|
if (input.mOctetAligned)
|
||||||
|
br.readBits(2);
|
||||||
|
|
||||||
|
AmrFrame frame;
|
||||||
|
frame.mFrameType = FT;
|
||||||
|
|
||||||
|
frame.mMode = FT < SID_FT ? FT : 0xFF;
|
||||||
|
frame.mGoodQuality = Q == 1;
|
||||||
|
frame.mTimestamp = input.mCurrentTimestamp;
|
||||||
|
|
||||||
|
result.mFrames.push_back(frame);
|
||||||
|
|
||||||
|
input.mCurrentTimestamp += input.mWideband ? 320 : 160;
|
||||||
|
}
|
||||||
|
while (F != 0);
|
||||||
|
|
||||||
|
for (size_t frameIndex=0; frameIndex < result.mFrames.size(); frameIndex++)
|
||||||
{
|
{
|
||||||
// DTX, no sense to decode the data
|
AmrFrame& frame = result.mFrames[frameIndex];
|
||||||
continue;
|
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 <<
|
||||||
if (input.mWideband && f.mFrameType == 14)
|
", mode = " << frame.mMode <<
|
||||||
{
|
", timestamp = " << static_cast<int>(frame.mTimestamp) <<
|
||||||
// Speech lost code only
|
", bits length = " << static_cast<int>(bitsLength) <<
|
||||||
continue;
|
", byte length =" << static_cast<int>(byteLength) <<
|
||||||
}
|
", remaining packet length = " << static_cast<int>(dataIn.size()));
|
||||||
|
|
||||||
if (!f.mGoodQuality)
|
|
||||||
{
|
|
||||||
// Bad quality, frame is damaged
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bitsLength = input.mWideband ? amrwb_framelenbits[f.mFrameType] : amrnb_framelenbits[f.mFrameType];
|
|
||||||
size_t byteLength = input.mWideband ? amrwb_framelen[f.mFrameType] : amrnb_framelen[f.mFrameType];
|
|
||||||
|
|
||||||
if (bitsLength > 0)
|
if (bitsLength > 0)
|
||||||
{
|
{
|
||||||
if (input.mOctetAligned)
|
if (input.mOctetAligned)
|
||||||
{
|
{
|
||||||
if (byte_reader.size() < byteLength)
|
if (dataIn.size() < byteLength)
|
||||||
f.mGoodQuality = false;
|
frame.mGoodQuality = false;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// It is octet aligned scheme, so we are on byte boundary now
|
// It is octet aligned scheme, so we are on byte boundary now
|
||||||
size_t byteOffset = bit_reader.position() / 8;
|
size_t byteOffset = br.count() / 8;
|
||||||
|
|
||||||
// Copy data of AMR frame
|
// Copy data of AMR frame
|
||||||
if (byteOffset + byteLength <= input.mPayloadLength)
|
frame.mData = std::make_shared<ByteBuffer>(input.mPayload + byteOffset, byteLength);
|
||||||
{
|
|
||||||
f.mData = std::make_shared<ByteBuffer>();
|
|
||||||
f.mData->resize(byteLength + 1); // payload + header
|
|
||||||
memcpy(f.mData->mutableData() + 1, input.mPayload + byteOffset, byteLength);
|
|
||||||
|
|
||||||
// Add header for decoder
|
|
||||||
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ICELogError(<< "Problem parsing AMR header: octet-aligned is set, available " << int(input.mPayloadLength - byteOffset)
|
|
||||||
<< " bytes but requested " << (int)byteLength);
|
|
||||||
result.mDiscardPacket = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Allocate place for copying
|
// Allocate place for copying
|
||||||
f.mData = std::make_shared<ByteBuffer>();
|
frame.mData = std::make_shared<ByteBuffer>();
|
||||||
f.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
|
frame.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
|
||||||
|
|
||||||
// Add header for decoder
|
// Add header for decoder
|
||||||
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
|
frame.mData->mutableData()[0] = (frame.mFrameType << 3) | (1 << 2);
|
||||||
|
|
||||||
// Read bits
|
// Read bits
|
||||||
if (bit_reader.readBits(f.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
|
if (br.readBits(frame.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
|
||||||
f.mGoodQuality = false;
|
frame.mGoodQuality = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Padding bits are skipped
|
// Padding bits are skipped
|
||||||
|
|
||||||
|
/*if (br.count() / 8 != br.position() / 8 &&
|
||||||
|
br.count() / 8 != br.position() / 8 + 1)
|
||||||
|
throw std::runtime_error("Failed to parse AMR frame");*/
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*static void predecodeAmrFrame(AmrFrame& frame, ByteBuffer& data)
|
||||||
|
{
|
||||||
|
// Data are already moved into
|
||||||
|
}*/
|
||||||
|
|
||||||
AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
|
AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
|
||||||
:mConfig(config)
|
:mConfig(config)
|
||||||
{
|
{
|
||||||
|
|
@ -239,9 +278,12 @@ int AmrNbCodec::CodecFactory::payloadType()
|
||||||
return mConfig.mPayloadType;
|
return mConfig.mPayloadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_RESIP_INTEGRATION
|
||||||
|
|
||||||
void AmrNbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void AmrNbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{}
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int AmrNbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
int AmrNbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
|
|
@ -253,6 +295,7 @@ void AmrNbCodec::CodecFactory::create(CodecMap& codecs)
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig));
|
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
PCodec AmrNbCodec::CodecFactory::create()
|
PCodec AmrNbCodec::CodecFactory::create()
|
||||||
{
|
{
|
||||||
return PCodec(new AmrNbCodec(mConfig));
|
return PCodec(new AmrNbCodec(mConfig));
|
||||||
|
|
@ -335,9 +378,6 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
|
||||||
#define AMR_BITRATE_DTX 15
|
#define AMR_BITRATE_DTX 15
|
||||||
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
if (mConfig.mOctetAligned)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (mConfig.mIuUP)
|
if (mConfig.mIuUP)
|
||||||
{
|
{
|
||||||
// Try to parse IuUP frame
|
// Try to parse IuUP frame
|
||||||
|
|
@ -445,7 +485,7 @@ int AmrNbCodec::plc(int lostFrames, void* output, int outputCapacity)
|
||||||
|
|
||||||
for (int i=0; i < lostFrames; i++)
|
for (int i=0; i < lostFrames; i++)
|
||||||
{
|
{
|
||||||
uint8_t buffer[32];
|
unsigned char buffer[32];
|
||||||
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
|
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
|
||||||
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
|
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
|
||||||
dataOut += L_FRAME;
|
dataOut += L_FRAME;
|
||||||
|
|
@ -479,8 +519,12 @@ int AmrWbCodec::CodecFactory::payloadType()
|
||||||
return mConfig.mPayloadType;
|
return mConfig.mPayloadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_RESIP_INTEGRATION
|
||||||
|
|
||||||
void AmrWbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void AmrWbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{}
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int AmrWbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
int AmrWbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
|
|
@ -492,17 +536,19 @@ void AmrWbCodec::CodecFactory::create(CodecMap& codecs)
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig));
|
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
PCodec AmrWbCodec::CodecFactory::create()
|
PCodec AmrWbCodec::CodecFactory::create()
|
||||||
{
|
{
|
||||||
return PCodec(new AmrWbCodec(mConfig));
|
return PCodec(new AmrWbCodec(mConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
AmrWbStatistics MT::GAmrWbStatistics;
|
|
||||||
|
|
||||||
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
|
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
|
||||||
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config),
|
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config),
|
||||||
mSwitchCounter(0), mPreviousPacketLength(0)
|
mSwitchCounter(0), mPreviousPacketLength(0)
|
||||||
{
|
{
|
||||||
|
//mEncoderCtx = E_IF_init();
|
||||||
mDecoderCtx = D_IF_init();
|
mDecoderCtx = D_IF_init();
|
||||||
mCurrentDecoderTimestamp = 0;
|
mCurrentDecoderTimestamp = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -549,16 +595,36 @@ int AmrWbCodec::samplerate()
|
||||||
|
|
||||||
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
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 L_FRAME 160
|
||||||
#define AMR_BITRATE_DTX 15
|
#define AMR_BITRATE_DTX 15
|
||||||
|
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output)
|
{
|
||||||
|
if (mConfig.mIuUP)
|
||||||
{
|
{
|
||||||
IuUP::Frame frame;
|
IuUP::Frame frame;
|
||||||
if (!IuUP::parse2(input.data(), input.size(), frame))
|
if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
|
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
|
||||||
|
|
@ -567,6 +633,9 @@ int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> ou
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build first byte to help decoder
|
||||||
|
//ICELogDebug(<< "Decoding AMR frame length = " << frame.mPayloadSize);
|
||||||
|
|
||||||
// Reserve space
|
// Reserve space
|
||||||
ByteBuffer dataToDecode;
|
ByteBuffer dataToDecode;
|
||||||
dataToDecode.resize(1 + frame.mPayloadSize);
|
dataToDecode.resize(1 + frame.mPayloadSize);
|
||||||
|
|
@ -583,17 +652,16 @@ int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> ou
|
||||||
|
|
||||||
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
|
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
|
||||||
|
|
||||||
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0);
|
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0);
|
||||||
return pcmLength();
|
return pcmLength();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output)
|
|
||||||
{
|
{
|
||||||
AmrPayloadInfo info;
|
AmrPayloadInfo info;
|
||||||
info.mCurrentTimestamp = mCurrentDecoderTimestamp;
|
info.mCurrentTimestamp = mCurrentDecoderTimestamp;
|
||||||
info.mOctetAligned = mConfig.mOctetAligned;
|
info.mOctetAligned = mConfig.mOctetAligned;
|
||||||
info.mPayload = input.data();
|
info.mPayload = (const uint8_t*)input;
|
||||||
info.mPayloadLength = input.size();
|
info.mPayloadLength = inputBytes;
|
||||||
info.mWideband = true;
|
info.mWideband = true;
|
||||||
info.mInterleaving = false;
|
info.mInterleaving = false;
|
||||||
|
|
||||||
|
|
@ -604,7 +672,6 @@ int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> o
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
GAmrWbStatistics.mNonParsed++;
|
|
||||||
ICELogDebug(<< "Failed to decode AMR payload");
|
ICELogDebug(<< "Failed to decode AMR payload");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -613,17 +680,13 @@ int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> o
|
||||||
|
|
||||||
// Check if packet is corrupted
|
// Check if packet is corrupted
|
||||||
if (ap.mDiscardPacket)
|
if (ap.mDiscardPacket)
|
||||||
{
|
|
||||||
GAmrWbStatistics.mDiscarded++;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
// Check for output buffer capacity
|
// Check for output buffer capacity
|
||||||
if (output.size() < (int)ap.mFrames.size() * pcmLength())
|
if (outputCapacity < (int)ap.mFrames.size() * pcmLength())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
short* dataOut = (short*)output.data();
|
short* dataOut = (short*)output;
|
||||||
size_t dataOutSizeInBytes = 0;
|
|
||||||
for (AmrFrame& frame: ap.mFrames)
|
for (AmrFrame& frame: ap.mFrames)
|
||||||
{
|
{
|
||||||
memset(dataOut, 0, static_cast<size_t>(pcmLength()));
|
memset(dataOut, 0, static_cast<size_t>(pcmLength()));
|
||||||
|
|
@ -632,22 +695,11 @@ int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> o
|
||||||
{
|
{
|
||||||
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
|
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
|
||||||
dataOut += pcmLength() / 2;
|
dataOut += pcmLength() / 2;
|
||||||
dataOutSizeInBytes += pcmLength();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dataOutSizeInBytes;
|
return pcmLength() * ap.mFrames.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
|
||||||
{
|
|
||||||
auto inputBuffer = std::span<const uint8_t>((uint8_t*)input, (size_t)inputBytes);
|
|
||||||
auto outputBuffer = std::span<uint8_t>((uint8_t*)output, (size_t)outputCapacity);
|
|
||||||
|
|
||||||
if (mConfig.mIuUP)
|
|
||||||
return decodeIuup(inputBuffer, outputBuffer);
|
|
||||||
else
|
|
||||||
return decodePlain(inputBuffer, outputBuffer);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -698,6 +750,8 @@ int GsmEfrCodec::GsmEfrFactory::payloadType()
|
||||||
return mPayloadType;
|
return mPayloadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_RESIP_INTEGRATION
|
||||||
|
|
||||||
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -713,6 +767,8 @@ void GsmEfrCodec::GsmEfrFactory::create(CodecMap& codecs)
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP));
|
codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
PCodec GsmEfrCodec::GsmEfrFactory::create()
|
PCodec GsmEfrCodec::GsmEfrFactory::create()
|
||||||
{
|
{
|
||||||
return PCodec(new GsmEfrCodec(mIuUP));
|
return PCodec(new GsmEfrCodec(mIuUP));
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
#ifndef MT_AMRCODEC_H
|
#ifndef MT_AMRCODEC_H
|
||||||
#define MT_AMRCODEC_H
|
#define MT_AMRCODEC_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include "MT_Codec.h"
|
#include "MT_Codec.h"
|
||||||
#include "../helper/HL_Pointer.h"
|
#include "../helper/HL_Pointer.h"
|
||||||
|
|
||||||
|
|
@ -18,9 +16,9 @@ namespace MT
|
||||||
{
|
{
|
||||||
struct AmrCodecConfig
|
struct AmrCodecConfig
|
||||||
{
|
{
|
||||||
bool mIuUP = false;
|
bool mIuUP;
|
||||||
bool mOctetAligned = false;
|
bool mOctetAligned;
|
||||||
int mPayloadType = -1;
|
int mPayloadType;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AmrNbCodec : public Codec
|
class AmrNbCodec : public Codec
|
||||||
|
|
@ -43,10 +41,11 @@ public:
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
|
#ifdef USE_RESIP_INTEGRATION
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
void create(CodecMap& codecs) override;
|
void create(CodecMap& codecs) override;
|
||||||
|
#endif
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -67,13 +66,6 @@ public:
|
||||||
int getSwitchCounter() const;
|
int getSwitchCounter() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AmrWbStatistics
|
|
||||||
{
|
|
||||||
int mDiscarded = 0;
|
|
||||||
int mNonParsed = 0;
|
|
||||||
};
|
|
||||||
extern AmrWbStatistics GAmrWbStatistics;
|
|
||||||
|
|
||||||
class AmrWbCodec : public Codec
|
class AmrWbCodec : public Codec
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -84,9 +76,6 @@ protected:
|
||||||
int mSwitchCounter;
|
int mSwitchCounter;
|
||||||
int mPreviousPacketLength;
|
int mPreviousPacketLength;
|
||||||
|
|
||||||
int decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output);
|
|
||||||
int decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class CodecFactory: public Factory
|
class CodecFactory: public Factory
|
||||||
{
|
{
|
||||||
|
|
@ -97,10 +86,11 @@ public:
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
|
#ifdef USE_RESIP_INTEGRATION
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
void create(CodecMap& codecs) override;
|
void create(CodecMap& codecs) override;
|
||||||
|
#endif
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -138,10 +128,11 @@ public:
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
|
#ifdef USE_RESIP_INTEGRATION
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
void create(CodecMap& codecs) override;
|
void create(CodecMap& codecs) override;
|
||||||
|
#endif
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "MT_AudioCodec.h"
|
#include "MT_AudioCodec.h"
|
||||||
#include "MT_CodecList.h"
|
#include "MT_CodecList.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
|
|
@ -58,14 +58,18 @@ int G729Codec::G729Factory::payloadType()
|
||||||
return 18;
|
return 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
void G729Codec::G729Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void G729Codec::G729Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
int G729Codec::G729Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
int G729Codec::G729Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
PCodec G729Codec::G729Factory::create()
|
PCodec G729Codec::G729Factory::create()
|
||||||
{
|
{
|
||||||
return std::make_shared<G729Codec>();
|
return std::make_shared<G729Codec>();
|
||||||
|
|
@ -121,7 +125,7 @@ int G729Codec::channels()
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static const int SamplesPerFrame = 80;
|
static const int SamplesPerFrame = 80;
|
||||||
int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
// Create encoder if it is not done yet
|
// Create encoder if it is not done yet
|
||||||
|
|
@ -237,6 +241,7 @@ OpusCodec::Params::Params()
|
||||||
mTargetBitrate = OPUS_TARGET_BITRATE;
|
mTargetBitrate = OPUS_TARGET_BITRATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
resip::Data OpusCodec::Params::toString() const
|
resip::Data OpusCodec::Params::toString() const
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|
@ -319,7 +324,7 @@ void OpusCodec::Params::parse(const resip::Data ¶ms)
|
||||||
mPtime = strx::toInt(paramIter->mValue.c_str(), 20);
|
mPtime = strx::toInt(paramIter->mValue.c_str(), 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
OpusCodec::OpusFactory::OpusFactory(int samplerate, int channels, int ptype)
|
OpusCodec::OpusFactory::OpusFactory(int samplerate, int channels, int ptype)
|
||||||
{
|
{
|
||||||
mSamplerate = samplerate;
|
mSamplerate = samplerate;
|
||||||
|
|
@ -347,6 +352,7 @@ int OpusCodec::OpusFactory::payloadType()
|
||||||
return mPType;
|
return mPType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
void OpusCodec::OpusFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void OpusCodec::OpusFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
// Put opus codec record
|
// Put opus codec record
|
||||||
|
|
@ -388,6 +394,7 @@ int OpusCodec::OpusFactory::processSdp(const resip::SdpContents::Session::Medium
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
PCodec OpusCodec::OpusFactory::create()
|
PCodec OpusCodec::OpusFactory::create()
|
||||||
{
|
{
|
||||||
|
|
@ -400,13 +407,15 @@ PCodec OpusCodec::OpusFactory::create()
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusCodec::OpusCodec(int samplerate, int channels, int ptime)
|
OpusCodec::OpusCodec(int samplerate, int channels, int ptime)
|
||||||
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(channels), mPTime(ptime), mSamplerate(samplerate), mDecoderChannels(0)
|
:mEncoderCtx(NULL), mDecoderCtx(NULL), mChannels(channels), mPTime(ptime), mSamplerate(samplerate)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
mEncoderCtx = opus_encoder_create(mSamplerate, mChannels, OPUS_APPLICATION_VOIP, &status);
|
mEncoderCtx = opus_encoder_create(48000, mChannels, OPUS_APPLICATION_VOIP, &status);
|
||||||
if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_COMPLEXITY(OPUS_CODEC_COMPLEXITY)))
|
if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_COMPLEXITY(OPUS_CODEC_COMPLEXITY)))
|
||||||
ICELogError(<< "Failed to set Opus encoder complexity");
|
ICELogError(<< "Failed to set Opus encoder complexity");
|
||||||
// Decoder creation is postponed until first packet arriving (because it may use different channel number
|
//if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_FORCE_CHANNELS(AUDIO_CHANNELS)))
|
||||||
|
// ICELogCritical(<<"Failed to set channel number in Opus encoder");
|
||||||
|
mDecoderCtx = opus_decoder_create(48000, mChannels, &status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusCodec::applyParams(const Params ¶ms)
|
void OpusCodec::applyParams(const Params ¶ms)
|
||||||
|
|
@ -424,7 +433,7 @@ void OpusCodec::applyParams(const Params ¶ms)
|
||||||
if (OPUS_OK != (error = opus_encoder_ctl(mEncoderCtx, OPUS_SET_PACKET_LOSS_PERC(params.mExpectedPacketLoss))))
|
if (OPUS_OK != (error = opus_encoder_ctl(mEncoderCtx, OPUS_SET_PACKET_LOSS_PERC(params.mExpectedPacketLoss))))
|
||||||
ICELogError(<< "Failed to (un)set expected packet loss. Error " << opus_strerror(error));
|
ICELogError(<< "Failed to (un)set expected packet loss. Error " << opus_strerror(error));
|
||||||
|
|
||||||
// mDecodeResampler.start(channels(), 48000, mSamplerate);
|
mDecodeResampler.start(channels(), 48000, mSamplerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusCodec::~OpusCodec()
|
OpusCodec::~OpusCodec()
|
||||||
|
|
@ -432,13 +441,13 @@ OpusCodec::~OpusCodec()
|
||||||
if (mDecoderCtx)
|
if (mDecoderCtx)
|
||||||
{
|
{
|
||||||
opus_decoder_destroy(mDecoderCtx);
|
opus_decoder_destroy(mDecoderCtx);
|
||||||
mDecoderCtx = nullptr;
|
mDecoderCtx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEncoderCtx)
|
if (mEncoderCtx)
|
||||||
{
|
{
|
||||||
opus_encoder_destroy(mEncoderCtx);
|
opus_encoder_destroy(mEncoderCtx);
|
||||||
mEncoderCtx = nullptr;
|
mEncoderCtx = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -484,113 +493,42 @@ int OpusCodec::encode(const void* input, int inputBytes, void* output, int outpu
|
||||||
|
|
||||||
int OpusCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
int OpusCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
// Examine the number of channels available in incoming packet
|
|
||||||
int nr_of_channels = opus_packet_get_nb_channels((const unsigned char*)input);
|
int nr_of_channels = opus_packet_get_nb_channels((const unsigned char*)input);
|
||||||
|
assert(nr_of_channels == channels());
|
||||||
|
|
||||||
// Recreate decoder if needed
|
int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char*)input, inputBytes);
|
||||||
if (mDecoderChannels != nr_of_channels)
|
if (nr_of_frames > 0)
|
||||||
{
|
{
|
||||||
if (mDecoderCtx)
|
// Send number of bytes for input and number of samples for output
|
||||||
{
|
int capacity = nr_of_frames * sizeof(opus_int16) * channels();
|
||||||
opus_decoder_destroy(mDecoderCtx);
|
|
||||||
mDecoderCtx = nullptr;
|
|
||||||
}
|
|
||||||
mDecoderChannels = nr_of_channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mDecoderCtx)
|
// Dangerous !
|
||||||
{
|
opus_int16* buffer = (opus_int16*)alloca(capacity);
|
||||||
int status = 0;
|
int decoded = opus_decode(mDecoderCtx, (const unsigned char*)input, inputBytes, (opus_int16*)buffer, capacity / (sizeof(short) * channels()), 0 /* FEC decoding is for lost packets */);
|
||||||
mDecoderCtx = opus_decoder_create(mSamplerate, mDecoderChannels, &status);
|
|
||||||
if (status)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char *) input,
|
|
||||||
inputBytes);
|
|
||||||
if (nr_of_frames <= 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// We support stereo and mono here.
|
|
||||||
int buffer_capacity = nr_of_frames * sizeof(opus_int16) * nr_of_channels;
|
|
||||||
opus_int16 *buffer_decode = (opus_int16 *)alloca(buffer_capacity);
|
|
||||||
int decoded = opus_decode(mDecoderCtx,
|
|
||||||
reinterpret_cast<const unsigned char *>(input), inputBytes,
|
|
||||||
buffer_decode, nr_of_frames, 0);
|
|
||||||
if (decoded < 0)
|
if (decoded < 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
ICELogCritical(<< "opus_decode() returned " << decoded);
|
// Resample 48000 to requested samplerate
|
||||||
|
size_t processed = 0;
|
||||||
|
return mDecodeResampler.processBuffer(buffer, decoded * sizeof(short) * channels(), processed, output, outputCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
opus_int16 *buffer_stereo = nullptr;
|
int OpusCodec::plc(int lostFrames, void* output, int outputCapacity)
|
||||||
int buffer_stereo_capacity = buffer_capacity * 2;
|
|
||||||
|
|
||||||
switch (nr_of_channels) {
|
|
||||||
case 1:
|
|
||||||
// Convert to stereo before
|
|
||||||
buffer_stereo = (opus_int16 *) alloca(buffer_stereo_capacity);
|
|
||||||
for (int i = 0; i < nr_of_frames; i++) {
|
|
||||||
buffer_stereo[i * 2 + 1] = buffer_decode[i];
|
|
||||||
buffer_stereo[i * 2] = buffer_decode[i];
|
|
||||||
}
|
|
||||||
assert(buffer_stereo_capacity <= outputCapacity);
|
|
||||||
memcpy(output, buffer_stereo, buffer_stereo_capacity);
|
|
||||||
result = buffer_stereo_capacity;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
assert(buffer_capacity <= outputCapacity);
|
|
||||||
memcpy(output, buffer_decode, buffer_capacity);
|
|
||||||
result = buffer_capacity;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int OpusCodec::plc(int lostPackets, void* output, int outputCapacity)
|
|
||||||
{
|
{
|
||||||
// Find how much frames do we need to produce and prefill it with silence
|
int nrOfSamplesInFrame = pcmLength() / (sizeof(short) * channels());
|
||||||
int frames_per_packet = (int)pcmLength() / (sizeof(opus_int16) * channels());
|
|
||||||
memset(output, 0, outputCapacity);
|
memset(output, 0, outputCapacity);
|
||||||
|
|
||||||
// Use this pointer as output
|
int nr_of_decoded_samples = 0;
|
||||||
opus_int16* data_output = reinterpret_cast<opus_int16*>(output);
|
for (int i=0; i<lostFrames; i++)
|
||||||
|
|
||||||
int nr_of_decoded_frames = 0;
|
|
||||||
|
|
||||||
// Buffer for single lost frame
|
|
||||||
opus_int16* buffer_plc = (opus_int16*)alloca(frames_per_packet * mDecoderChannels * sizeof(opus_int16));
|
|
||||||
for (int i=0; i<lostPackets; i++)
|
|
||||||
{
|
{
|
||||||
nr_of_decoded_frames = opus_decode(mDecoderCtx, nullptr, 0, buffer_plc, frames_per_packet, 0);
|
nr_of_decoded_samples += opus_decode(mDecoderCtx, nullptr, 0, (opus_int16*)output + nrOfSamplesInFrame * i, nrOfSamplesInFrame, 0);
|
||||||
assert(nr_of_decoded_frames == frames_per_packet);
|
|
||||||
switch (mDecoderChannels)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
// Convert mono to stereo
|
|
||||||
for (int i=0; i < nr_of_decoded_frames; i++)
|
|
||||||
{
|
|
||||||
data_output[i * 2] = buffer_plc[i];
|
|
||||||
data_output[i * 2 + 1] = buffer_plc[i+1];
|
|
||||||
}
|
}
|
||||||
data_output += frames_per_packet * mChannels;
|
return nr_of_decoded_samples ? lostFrames * pcmLength() : 0;
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
// Just copy data
|
|
||||||
memcpy(data_output, buffer_plc, frames_per_packet * sizeof(opus_int16) * mDecoderChannels);
|
|
||||||
data_output += frames_per_packet * mChannels;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ((char*)data_output - (char*)output) * sizeof(opus_int16);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -714,6 +652,7 @@ PCodec IlbcCodec::IlbcFactory::create()
|
||||||
return PCodec(new IlbcCodec(mPtime));
|
return PCodec(new IlbcCodec(mPtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
void IlbcCodec::IlbcFactory::create(CodecMap& codecs)
|
void IlbcCodec::IlbcFactory::create(CodecMap& codecs)
|
||||||
{
|
{
|
||||||
codecs[mPType20ms] = PCodec(create());
|
codecs[mPType20ms] = PCodec(create());
|
||||||
|
|
@ -775,6 +714,8 @@ int IlbcCodec::IlbcFactory::processSdp(const resip::SdpContents::Session::Medium
|
||||||
return pt;
|
return pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// --- IsacCodec(s) ---
|
// --- IsacCodec(s) ---
|
||||||
#define ISAC_CODEC_NAME "ISAC"
|
#define ISAC_CODEC_NAME "ISAC"
|
||||||
|
|
||||||
|
|
@ -1152,8 +1093,8 @@ int GsmCodec::plc(int lostFrames, void* output, int outputCapacity)
|
||||||
|
|
||||||
G722Codec::G722Codec()
|
G722Codec::G722Codec()
|
||||||
{
|
{
|
||||||
mEncoder = g722_encode_init(nullptr, 64000, 0);
|
mEncoder = g722_encode_init(NULL, 64000, 0);
|
||||||
mDecoder = g722_decode_init(nullptr, 64000, 0);
|
mDecoder = g722_decode_init(NULL, 64000, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __AUDIO_CODEC_H
|
#ifndef __AUDIO_CODEC_H
|
||||||
#define __AUDIO_CODEC_H
|
#define __AUDIO_CODEC_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "MT_Codec.h"
|
#include "MT_Codec.h"
|
||||||
#include "../audio/Audio_Resampler.h"
|
#include "../audio/Audio_Resampler.h"
|
||||||
|
|
@ -46,9 +46,10 @@ public:
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
|
#endif
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
};
|
};
|
||||||
G729Codec();
|
G729Codec();
|
||||||
|
|
@ -72,8 +73,7 @@ protected:
|
||||||
OpusEncoder *mEncoderCtx;
|
OpusEncoder *mEncoderCtx;
|
||||||
OpusDecoder *mDecoderCtx;
|
OpusDecoder *mDecoderCtx;
|
||||||
int mPTime, mSamplerate, mChannels;
|
int mPTime, mSamplerate, mChannels;
|
||||||
// Audio::SpeexResampler mDecodeResampler;
|
Audio::SpeexResampler mDecodeResampler;
|
||||||
int mDecoderChannels;
|
|
||||||
public:
|
public:
|
||||||
struct Params
|
struct Params
|
||||||
{
|
{
|
||||||
|
|
@ -81,8 +81,10 @@ public:
|
||||||
int mPtime, mTargetBitrate, mExpectedPacketLoss;
|
int mPtime, mTargetBitrate, mExpectedPacketLoss;
|
||||||
|
|
||||||
Params();
|
Params();
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
resip::Data toString() const;
|
resip::Data toString() const;
|
||||||
void parse(const resip::Data& params);
|
void parse(const resip::Data& params);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpusFactory: public Factory
|
class OpusFactory: public Factory
|
||||||
|
|
@ -100,8 +102,10 @@ public:
|
||||||
int channels() override;
|
int channels() override;
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
|
#endif
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -141,9 +145,11 @@ public:
|
||||||
const char* name();
|
const char* name();
|
||||||
int samplerate();
|
int samplerate();
|
||||||
int payloadType();
|
int payloadType();
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
void create(CodecMap& codecs);
|
void create(CodecMap& codecs);
|
||||||
|
#endif
|
||||||
PCodec create();
|
PCodec create();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,9 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#if defined(TARGET_WIN) && !defined(NOMINMAX)
|
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "MT_AudioReceiver.h"
|
#include "MT_AudioReceiver.h"
|
||||||
#include "MT_AudioCodec.h"
|
#include "MT_AudioCodec.h"
|
||||||
#include "MT_CngHelper.h"
|
#include "MT_CngHelper.h"
|
||||||
|
|
@ -16,7 +14,6 @@
|
||||||
#include "../audio/Audio_Interface.h"
|
#include "../audio/Audio_Interface.h"
|
||||||
#include "../audio/Audio_Resampler.h"
|
#include "../audio/Audio_Resampler.h"
|
||||||
#include <cmath>
|
#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) && defined(USE_AMR_CODEC)
|
||||||
# include "MT_AmrCodec.h"
|
# include "MT_AmrCodec.h"
|
||||||
|
|
@ -65,8 +62,6 @@ std::vector<short>& RtpBuffer::Packet::pcm()
|
||||||
RtpBuffer::RtpBuffer(Statistics& stat)
|
RtpBuffer::RtpBuffer(Statistics& stat)
|
||||||
:mStat(stat)
|
:mStat(stat)
|
||||||
{
|
{
|
||||||
if (mStat.mPacketLoss)
|
|
||||||
std::cout << "Warning: packet loss is not zero" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RtpBuffer::~RtpBuffer()
|
RtpBuffer::~RtpBuffer()
|
||||||
|
|
@ -122,6 +117,7 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPa
|
||||||
|
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
|
|
||||||
|
|
||||||
// Update statistics
|
// Update statistics
|
||||||
if (mLastAddTime == 0.0)
|
if (mLastAddTime == 0.0)
|
||||||
mLastAddTime = now_ms();
|
mLastAddTime = now_ms();
|
||||||
|
|
@ -201,14 +197,13 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
// See if there is enough information in buffer
|
// See if there is enough information in buffer
|
||||||
int total = findTimelength();
|
int total = findTimelength();
|
||||||
|
|
||||||
while (total > mHigh && mPacketList.size() && 0 != mHigh)
|
while (total > mHigh && mPacketList.size())
|
||||||
{
|
{
|
||||||
ICELogMedia( << "Dropping RTP packets from jitter buffer");
|
ICELogMedia( << "Dropping RTP packets from jitter buffer");
|
||||||
total -= mPacketList.front()->timelength();
|
total -= mPacketList.front()->timelength();
|
||||||
|
|
||||||
// Save it as last packet however - to not confuse loss packet counter
|
// Save it as last packet however - to not confuse loss packet counter
|
||||||
mFetchedPacket = mPacketList.front();
|
mFetchedPacket = mPacketList.front();
|
||||||
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
|
||||||
|
|
||||||
// Erase from packet list
|
// Erase from packet list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
|
|
@ -218,48 +213,47 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total < mLow)
|
if (total < mLow)
|
||||||
{
|
|
||||||
// Still not prebuffered
|
|
||||||
result = FetchResult::NoPacket;
|
result = FetchResult::NoPacket;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Did we fetch any packet before ?
|
|
||||||
bool is_fetched_packet = mFetchedPacket.get() != nullptr;
|
bool is_fetched_packet = mFetchedPacket.get() != nullptr;
|
||||||
if (is_fetched_packet)
|
if (is_fetched_packet)
|
||||||
is_fetched_packet &= mFetchedPacket->rtp().get() != nullptr;
|
is_fetched_packet &= mFetchedPacket->rtp().get() != nullptr;
|
||||||
|
|
||||||
if (mLastSeqno.has_value())
|
if (is_fetched_packet)
|
||||||
{
|
{
|
||||||
if (mPacketList.empty())
|
if (mPacketList.empty())
|
||||||
{
|
{
|
||||||
result = FetchResult::NoPacket;
|
result = FetchResult::NoPacket;
|
||||||
// Don't increase counter of lost packets here; maybe it is DTX
|
mStat.mPacketLoss++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Current sequence number ?
|
// Current sequence number ?
|
||||||
uint32_t seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
unsigned seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||||
|
|
||||||
// Gap between new packet and previous on
|
// Gap between new packet and previous on
|
||||||
int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
|
int gap = (int64_t)seqno - (int64_t)mFetchedPacket->rtp()->GetExtendedSequenceNumber() - 1;
|
||||||
gap = std::min(gap, 127);
|
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;
|
result = FetchResult::Gap;
|
||||||
|
mStat.mPacketLoss += gap;
|
||||||
|
mStat.mLoss[gap]++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (gap > 0)
|
||||||
|
{
|
||||||
|
mStat.mPacketLoss += gap;
|
||||||
|
mStat.mLoss[gap]++;
|
||||||
|
}
|
||||||
|
|
||||||
result = FetchResult::RegularPacket;
|
result = FetchResult::RegularPacket;
|
||||||
rl.push_back(mPacketList.front());
|
rl.push_back(mPacketList.front());
|
||||||
|
|
||||||
// Save last returned normal packet
|
// Save last returned normal packet
|
||||||
mFetchedPacket = mPacketList.front();
|
mFetchedPacket = mPacketList.front();
|
||||||
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
|
||||||
|
|
||||||
// Remove returned packet from the list
|
// Remove returned packet from the list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
|
|
@ -269,7 +263,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// See if prebuffer limit is reached
|
// See if prebuffer limit is reached
|
||||||
if (findTimelength() >= mPrebuffer && !mPacketList.empty())
|
if (findTimelength() >= mPrebuffer)
|
||||||
{
|
{
|
||||||
// Normal packet will be returned
|
// Normal packet will be returned
|
||||||
result = FetchResult::RegularPacket;
|
result = FetchResult::RegularPacket;
|
||||||
|
|
@ -279,7 +273,6 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
|
|
||||||
// Remember returned packet
|
// Remember returned packet
|
||||||
mFetchedPacket = mPacketList.front();
|
mFetchedPacket = mPacketList.front();
|
||||||
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
|
||||||
|
|
||||||
// Remove returned packet from buffer list
|
// Remove returned packet from buffer list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
|
|
@ -338,7 +331,6 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
|
||||||
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
|
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
|
||||||
|
|
||||||
// Init codecs
|
// Init codecs
|
||||||
mCodecList.setSettings(settings);
|
|
||||||
mCodecList.fillCodecMap(mCodecMap);
|
mCodecList.fillCodecMap(mCodecMap);
|
||||||
|
|
||||||
#if defined(DUMP_DECODED)
|
#if defined(DUMP_DECODED)
|
||||||
|
|
@ -349,6 +341,12 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
|
||||||
|
|
||||||
AudioReceiver::~AudioReceiver()
|
AudioReceiver::~AudioReceiver()
|
||||||
{
|
{
|
||||||
|
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
||||||
|
if (mPVQA && mPvqaBuffer)
|
||||||
|
{
|
||||||
|
mStat.mPvqaMos = calculatePvqaMos(AUDIO_SAMPLERATE, mStat.mPvqaReport);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
mResampler8.stop();
|
mResampler8.stop();
|
||||||
mResampler16.stop();
|
mResampler16.stop();
|
||||||
mResampler32.stop();
|
mResampler32.stop();
|
||||||
|
|
@ -363,17 +361,11 @@ void AudioReceiver::setCodecSettings(const CodecList::Settings& codecSettings)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mCodecSettings = codecSettings;
|
mCodecSettings = codecSettings;
|
||||||
mCodecList.setSettings(mCodecSettings); // This builds factory list with proper payload types according to payload types in settings
|
mCodecMap.clear();
|
||||||
|
mCodecList.setSettings(mCodecSettings);
|
||||||
// Rebuild codec map from factory list
|
|
||||||
mCodecList.fillCodecMap(mCodecMap);
|
mCodecList.fillCodecMap(mCodecMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
CodecList::Settings& AudioReceiver::getCodecSettings()
|
|
||||||
{
|
|
||||||
return mCodecSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t output_capacity)
|
size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t output_capacity)
|
||||||
{
|
{
|
||||||
// How much data was produced
|
// How much data was produced
|
||||||
|
|
@ -410,56 +402,46 @@ size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t out
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** detectedCodec)
|
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec)
|
||||||
{
|
{
|
||||||
// Estimate time length
|
|
||||||
int time_length = 0,
|
|
||||||
samplerate = 8000,
|
|
||||||
payloadLength = p->GetPayloadLength(),
|
|
||||||
ptype = p->GetPayloadType();
|
|
||||||
|
|
||||||
// ICELogInfo(<< "Adding packet No " << p->GetSequenceNumber());
|
// ICELogInfo(<< "Adding packet No " << p->GetSequenceNumber());
|
||||||
// Increase codec counter
|
// Increase codec counter
|
||||||
mStat.mCodecCount[ptype]++;
|
mStat.mCodecCount[p->GetPayloadType()]++;
|
||||||
|
|
||||||
// Check if codec can be handled
|
// Check if codec can be handled
|
||||||
Codec* codec = nullptr;
|
CodecMap::iterator codecIter = mCodecMap.find(p->GetPayloadType());
|
||||||
CodecMap::iterator codecIter = mCodecMap.find(ptype);
|
|
||||||
if (codecIter == mCodecMap.end())
|
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
|
||||||
// Check if codec is creating lazily
|
|
||||||
if (!codecIter->second)
|
if (!codecIter->second)
|
||||||
{
|
{
|
||||||
codecIter->second = mCodecList.createCodecByPayloadType(ptype);
|
// Look for ptype
|
||||||
|
for (int codecIndex = 0; codecIndex < mCodecList.count(); codecIndex++)
|
||||||
|
if (mCodecList.codecAt(codecIndex).payloadType() == p->GetPayloadType())
|
||||||
|
codecIter->second = mCodecList.codecAt(codecIndex).create();
|
||||||
}
|
}
|
||||||
codec = codecIter->second.get();
|
|
||||||
|
|
||||||
// Return pointer to codec if needed.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)
|
if (codec)
|
||||||
samplerate = codec->samplerate();
|
*codec = codecIter->second.get();
|
||||||
}
|
|
||||||
|
if (mStat.mCodecName.empty())
|
||||||
|
mStat.mCodecName = codecIter->second->name();
|
||||||
|
|
||||||
|
// Estimate time length
|
||||||
|
int time_length = 0, payloadLength = p->GetPayloadLength(), ptype = p->GetPayloadType();
|
||||||
|
|
||||||
|
if (!codecIter->second->rtpLength())
|
||||||
|
time_length = codecIter->second->frameTime();
|
||||||
|
else
|
||||||
|
time_length = lround(double(payloadLength) / codecIter->second->rtpLength() * codecIter->second->frameTime());
|
||||||
|
|
||||||
// Process jitter
|
// Process jitter
|
||||||
mJitterStats.process(p.get(), samplerate);
|
mJitterStats.process(p.get(), codecIter->second->samplerate());
|
||||||
mStat.mJitter = static_cast<float>(mJitterStats.get());
|
mStat.mJitter = static_cast<float>(mJitterStats.get());
|
||||||
|
|
||||||
// Check if packet is CNG
|
// Check if packet is CNG
|
||||||
|
|
@ -471,26 +453,23 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** de
|
||||||
{
|
{
|
||||||
// It will cause statistics to report about bad RTP packet
|
// It will cause statistics to report about bad RTP packet
|
||||||
// I have to replay last packet payload here to avoid report about lost packet
|
// I have to replay last packet payload here to avoid report about lost packet
|
||||||
mBuffer.add(p, time_length, samplerate);
|
mBuffer.add(p, time_length, codecIter->second->samplerate());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue packet to buffer
|
// Queue packet to buffer
|
||||||
auto packet = mBuffer.add(p, time_length, samplerate).get();
|
auto packet = mBuffer.add(p, time_length, codecIter->second->samplerate()).get();
|
||||||
|
|
||||||
if (packet)
|
if (packet)
|
||||||
{
|
{
|
||||||
// Check if early decoding configured
|
// Check if early decoding configured
|
||||||
if (mEarlyDecode && codec)
|
if (mEarlyDecode && *codec)
|
||||||
{
|
{
|
||||||
// Move data to packet buffer
|
// Move data to packet buffer
|
||||||
size_t available = decode_packet(*codec, *p, mDecodedFrame, sizeof mDecodedFrame);
|
size_t available = decode_packet(**codec, *p, mDecodedFrame, sizeof mDecodedFrame);
|
||||||
if (available > 0)
|
|
||||||
{
|
|
||||||
packet->pcm().resize(available / 2);
|
packet->pcm().resize(available / 2);
|
||||||
memcpy(packet->pcm().data(), mDecodedFrame, available / 2);
|
memcpy(packet->pcm().data(), mDecodedFrame, available / 2);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -508,6 +487,11 @@ void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
|
||||||
makeMonoAndResample(resample ? mCodec->samplerate() : 0,
|
makeMonoAndResample(resample ? mCodec->samplerate() : 0,
|
||||||
mCodec->channels());
|
mCodec->channels());
|
||||||
|
|
||||||
|
// Update PVQA with stats
|
||||||
|
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
||||||
|
updatePvqa(mResampledFrame, mResampledLength);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Send to output
|
// Send to output
|
||||||
output.add(mResampledFrame, mResampledLength);
|
output.add(mResampledFrame, mResampledLength);
|
||||||
}
|
}
|
||||||
|
|
@ -540,17 +524,7 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
|
||||||
if (options & DecodeOptions_SkipDecode)
|
if (options & DecodeOptions_SkipDecode)
|
||||||
mDecodedLength = 0;
|
mDecodedLength = 0;
|
||||||
else
|
else
|
||||||
{
|
|
||||||
mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame);
|
mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame);
|
||||||
if (!mDecodedLength)
|
|
||||||
{
|
|
||||||
// PLC is not support or failed
|
|
||||||
// So substitute the silence
|
|
||||||
size_t nr_of_samples = mCodec->frameTime() * mCodec->samplerate() / 1000 * sizeof(short);
|
|
||||||
mDecodedLength = nr_of_samples * sizeof(short);
|
|
||||||
memset(mDecodedFrame, 0, mDecodedLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDecodedLength)
|
if (mDecodedLength)
|
||||||
|
|
@ -563,10 +537,15 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
|
||||||
case RtpBuffer::FetchResult::NoPacket:
|
case RtpBuffer::FetchResult::NoPacket:
|
||||||
ICELogDebug(<< "No packet available in jitter buffer");
|
ICELogDebug(<< "No packet available in jitter buffer");
|
||||||
mFailedCount++;
|
mFailedCount++;
|
||||||
|
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
||||||
|
if (mResampledLength > 0)
|
||||||
|
updatePvqa(nullptr, mResampledLength);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RtpBuffer::FetchResult::RegularPacket:
|
case RtpBuffer::FetchResult::RegularPacket:
|
||||||
mFailedCount = 0;
|
mFailedCount = 0;
|
||||||
|
|
||||||
for (std::shared_ptr<RtpBuffer::Packet>& p: rl)
|
for (std::shared_ptr<RtpBuffer::Packet>& p: rl)
|
||||||
{
|
{
|
||||||
assert(p);
|
assert(p);
|
||||||
|
|
@ -673,7 +652,8 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
|
||||||
// Decode frame by frame
|
// Decode frame by frame
|
||||||
mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
|
mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
|
||||||
frameLength, mDecodedFrame, sizeof mDecodedFrame);
|
frameLength, mDecodedFrame, sizeof mDecodedFrame);
|
||||||
if (mDecodedLength > 0)
|
// mDecodedLength = 3840; // Opus 20 ms stereo
|
||||||
|
if (mDecodedLength)
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -754,6 +734,70 @@ Codec* AudioReceiver::findCodec(int payloadType)
|
||||||
return codecIter->second.get();
|
return codecIter->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
||||||
|
void AudioReceiver::initPvqa()
|
||||||
|
{
|
||||||
|
// Allocate space for 20 seconds audio
|
||||||
|
if (!mPvqaBuffer)
|
||||||
|
{
|
||||||
|
mPvqaBuffer = std::make_shared<Audio::DataWindow>();
|
||||||
|
mPvqaBuffer->setCapacity(Audio::Format().sizeFromTime(30000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate & open PVQA analyzer
|
||||||
|
if (!mPVQA)
|
||||||
|
{
|
||||||
|
mPVQA = std::make_shared<sevana::pvqa>();
|
||||||
|
bool is_opened = mPVQA->open(AUDIO_SAMPLERATE, 1, PVQA_INTERVAL);
|
||||||
|
if (!is_opened)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = static_cast<int>(fmt.timeFromSize(mPvqaBuffer->filled())) / (PVQA_INTERVAL * 1000);
|
||||||
|
if (frames > 0)
|
||||||
|
{
|
||||||
|
int time4pvqa = (int)(frames * PVQA_INTERVAL * 1000);
|
||||||
|
int size4pvqa = (int)fmt.sizeFromTime(time4pvqa);
|
||||||
|
ICELogDebug(<< "PVQA buffer has " << time4pvqa << " milliseconds of audio.");
|
||||||
|
bool update_result = mPVQA->update(mPvqaBuffer->data(), size4pvqa);
|
||||||
|
if (!update_result)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
mPvqaBuffer->erase(size4pvqa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float AudioReceiver::calculatePvqaMos(int rate, std::string& report)
|
||||||
|
{
|
||||||
|
if (mPVQA && mPvqaBuffer)
|
||||||
|
{
|
||||||
|
sevana::pvqa::result result;
|
||||||
|
if (mPVQA->get_result(result)) {
|
||||||
|
report = result.mReport;
|
||||||
|
return result.mMos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void AudioReceiver::processStatisticsWithAmrCodec(Codec* c)
|
void AudioReceiver::processStatisticsWithAmrCodec(Codec* c)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -8,16 +8,25 @@
|
||||||
|
|
||||||
#include "MT_Stream.h"
|
#include "MT_Stream.h"
|
||||||
#include "MT_CodecList.h"
|
#include "MT_CodecList.h"
|
||||||
|
#include "MT_AudioCodec.h"
|
||||||
#include "MT_CngHelper.h"
|
#include "MT_CngHelper.h"
|
||||||
|
#include "../helper/HL_Pointer.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
|
#include "../helper/HL_Optional.hpp"
|
||||||
|
|
||||||
#include "jrtplib/src/rtppacket.h"
|
#include "jrtplib/src/rtppacket.h"
|
||||||
|
#include "jrtplib/src/rtcppacket.h"
|
||||||
#include "jrtplib/src/rtpsourcedata.h"
|
#include "jrtplib/src/rtpsourcedata.h"
|
||||||
#include "../audio/Audio_DataWindow.h"
|
#include "../audio/Audio_DataWindow.h"
|
||||||
#include "../audio/Audio_Resampler.h"
|
#include "../audio/Audio_Resampler.h"
|
||||||
|
|
||||||
#include <optional>
|
/*#if defined(USE_PVQA_LIBRARY)
|
||||||
|
# include "pvqa++.h"
|
||||||
|
#endif*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
// #define DUMP_DECODED
|
||||||
|
|
||||||
namespace MT
|
namespace MT
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +104,6 @@ namespace MT
|
||||||
bool mFirstPacketWillGo = true;
|
bool mFirstPacketWillGo = true;
|
||||||
jrtplib::RTPSourceStats mRtpStats;
|
jrtplib::RTPSourceStats mRtpStats;
|
||||||
std::shared_ptr<Packet> mFetchedPacket;
|
std::shared_ptr<Packet> mFetchedPacket;
|
||||||
std::optional<uint32_t> mLastSeqno;
|
|
||||||
|
|
||||||
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
|
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
|
||||||
float mLastAddTime = 0.0;
|
float mLastAddTime = 0.0;
|
||||||
|
|
@ -119,7 +127,7 @@ namespace MT
|
||||||
|
|
||||||
// Update codec settings
|
// Update codec settings
|
||||||
void setCodecSettings(const CodecList::Settings& codecSettings);
|
void setCodecSettings(const CodecList::Settings& codecSettings);
|
||||||
CodecList::Settings& getCodecSettings();
|
|
||||||
// Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding.
|
// Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding.
|
||||||
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
|
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
|
||||||
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
|
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
|
||||||
|
|
@ -201,6 +209,16 @@ namespace MT
|
||||||
// Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame
|
// Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame
|
||||||
void processDecoded(Audio::DataWindow& output, int options);
|
void processDecoded(Audio::DataWindow& output, int options);
|
||||||
|
|
||||||
|
/*
|
||||||
|
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
||||||
|
std::shared_ptr<sevana::pvqa> mPVQA;
|
||||||
|
void initPvqa();
|
||||||
|
void updatePvqa(const void* data, int size);
|
||||||
|
float calculatePvqaMos(int rate, std::string& report);
|
||||||
|
|
||||||
|
std::shared_ptr<Audio::DataWindow> mPvqaBuffer;
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
void processStatisticsWithAmrCodec(Codec* c);
|
void processStatisticsWithAmrCodec(Codec* c);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -306,22 +306,18 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||||
int receiveLength = length;
|
int receiveLength = length;
|
||||||
memcpy(mReceiveBuffer, buffer, length);
|
memcpy(mReceiveBuffer, buffer, length);
|
||||||
|
|
||||||
|
bool srtpResult;
|
||||||
if (mSrtpSession.active())
|
if (mSrtpSession.active())
|
||||||
{
|
{
|
||||||
bool srtpResult;
|
|
||||||
size_t srcLength = length; size_t dstLength = sizeof mSrtpDecodeBuffer;
|
|
||||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength);
|
||||||
else
|
else
|
||||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength);
|
||||||
if (!srtpResult)
|
if (!srtpResult)
|
||||||
{
|
{
|
||||||
ICELogError(<<"Cannot decrypt SRTP packet.");
|
ICELogError(<<"Cannot decrypt SRTP packet.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(mReceiveBuffer, mSrtpDecodeBuffer, dstLength);
|
|
||||||
receiveLength = dstLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (source.family())
|
switch (source.family())
|
||||||
|
|
@ -347,8 +343,8 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||||
mStat.mReceived += length;
|
mStat.mReceived += length;
|
||||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||||
{
|
{
|
||||||
if (!mStat.mFirstRtpTime)
|
if (!mStat.mFirstRtpTime.is_initialized())
|
||||||
mStat.mFirstRtpTime = std::chrono::steady_clock::now();
|
mStat.mFirstRtpTime = std::chrono::system_clock::now();
|
||||||
mStat.mReceivedRtp++;
|
mStat.mReceivedRtp++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __MT_AUDIOSTREAM_H
|
#ifndef __MT_AUDIOSTREAM_H
|
||||||
#define __MT_AUDIOSTREAM_H
|
#define __MT_AUDIOSTREAM_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "MT_Stream.h"
|
#include "MT_Stream.h"
|
||||||
#include "MT_NativeRtpSender.h"
|
#include "MT_NativeRtpSender.h"
|
||||||
#include "MT_SingleAudioStream.h"
|
#include "MT_SingleAudioStream.h"
|
||||||
|
|
@ -87,8 +87,7 @@ protected:
|
||||||
mCaptureResampler32,
|
mCaptureResampler32,
|
||||||
mCaptureResampler48;
|
mCaptureResampler48;
|
||||||
DtmfContext mDtmfContext;
|
DtmfContext mDtmfContext;
|
||||||
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE],
|
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE];
|
||||||
mSrtpDecodeBuffer[MAX_VALID_UDPPACKET_SIZE];
|
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#include "MT_CngHelper.h"
|
#include "MT_CngHelper.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ int Codec::Factory::channels()
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
void Codec::Factory::create(CodecMap& codecs)
|
void Codec::Factory::create(CodecMap& codecs)
|
||||||
{
|
{
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(create());
|
codecs[payloadType()] = std::shared_ptr<Codec>(create());
|
||||||
|
|
@ -36,4 +38,4 @@ int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecC
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
#ifndef __MT_CODEC_H
|
#ifndef __MT_CODEC_H
|
||||||
#define __MT_CODEC_H
|
#define __MT_CODEC_H
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
# include "resiprocate/resip/stack/SdpContents.hxx"
|
# include "resiprocate/resip/stack/SdpContents.hxx"
|
||||||
|
#endif
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "../helper/HL_Pointer.h"
|
#include "../helper/HL_Pointer.h"
|
||||||
|
|
@ -34,12 +36,14 @@ public:
|
||||||
virtual PCodec create() = 0;
|
virtual PCodec create() = 0;
|
||||||
|
|
||||||
virtual int channels();
|
virtual int channels();
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
typedef std::map<int, PCodec > CodecMap;
|
typedef std::map<int, PCodec > CodecMap;
|
||||||
virtual void create(CodecMap& codecs);
|
virtual void create(CodecMap& codecs);
|
||||||
virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
// Returns payload type from chosen codec if success. -1 is returned for negative result.
|
// Returns payload type from chosen codec if success. -1 is returned for negative result.
|
||||||
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
resip::Codec resipCodec();
|
resip::Codec resipCodec();
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
virtual ~Codec() {}
|
virtual ~Codec() {}
|
||||||
virtual const char* name() = 0;
|
virtual const char* name() = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2022 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "MT_CodecList.h"
|
#include "MT_CodecList.h"
|
||||||
#include "MT_AudioCodec.h"
|
#include "MT_AudioCodec.h"
|
||||||
|
|
||||||
|
|
@ -25,95 +24,33 @@ using namespace MT;
|
||||||
|
|
||||||
using strx = strx;
|
using strx = strx;
|
||||||
|
|
||||||
|
// ---------------- EvsSpec ---------------
|
||||||
bool CodecList::Settings::contains(int ptype) const
|
|
||||||
{
|
|
||||||
if (ptype >= 0 && ptype < 96)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (mAmrNbOctetPayloadType.contains(ptype))
|
|
||||||
return true;
|
|
||||||
if (mAmrNbPayloadType.contains(ptype))
|
|
||||||
return true;
|
|
||||||
if (mAmrWbOctetPayloadType.contains(ptype))
|
|
||||||
return true;
|
|
||||||
if (mAmrWbPayloadType.contains(ptype))
|
|
||||||
return true;
|
|
||||||
for (const auto& s: mOpusSpec)
|
|
||||||
if (s.mPayloadType == ptype)
|
|
||||||
return true;
|
|
||||||
for (const auto& s: mEvsSpec)
|
|
||||||
if (s.mPayloadType == ptype)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (mIsac16KPayloadType == ptype || mIsac32KPayloadType == ptype)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (mIlbc20PayloadType == ptype || mIlbc30PayloadType == ptype)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CodecList::Settings::toString() const
|
std::string CodecList::Settings::toString() const
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
// oss << "wrap IuUP: " << mWrapIuUP << std::endl
|
oss << "wrap IuUP: " << mWrapIuUP << std::endl
|
||||||
// << "skip decode: " << mSkipDecode << std::endl;
|
<< "skip decode: " << mSkipDecode << std::endl;
|
||||||
|
|
||||||
if (!mAmrWbPayloadType.empty())
|
|
||||||
{
|
|
||||||
oss << "AMR WB ptype: ";
|
|
||||||
for (int ptype: mAmrWbPayloadType)
|
for (int ptype: mAmrWbPayloadType)
|
||||||
oss << ptype << " ";
|
oss << "AMR WB ptype: " << ptype << std::endl;
|
||||||
}
|
|
||||||
if (!mAmrWbOctetPayloadType.empty())
|
|
||||||
{
|
|
||||||
oss << "AMR WB octet ptype: ";
|
|
||||||
for (int ptype: mAmrWbOctetPayloadType)
|
for (int ptype: mAmrWbOctetPayloadType)
|
||||||
oss << ptype << " ";
|
oss << "AMR WB octet-aligned ptype: " << ptype << std::endl;
|
||||||
}
|
|
||||||
|
|
||||||
if (!mAmrNbPayloadType.empty())
|
|
||||||
{
|
|
||||||
oss << "AMR ptype: ";
|
|
||||||
for (int ptype: mAmrNbPayloadType)
|
for (int ptype: mAmrNbPayloadType)
|
||||||
oss << ptype << " ";
|
oss << "AMR NB ptype: " << ptype << std::endl;
|
||||||
}
|
|
||||||
|
|
||||||
if (!mAmrNbOctetPayloadType.empty())
|
|
||||||
{
|
|
||||||
oss << "AMR octet ptype: ";
|
|
||||||
for (int ptype: mAmrNbOctetPayloadType)
|
for (int ptype: mAmrNbOctetPayloadType)
|
||||||
oss << ptype << " ";
|
oss << "AMR NB octet-aligned ptype:" << ptype << std::endl;
|
||||||
}
|
|
||||||
if (mIsac16KPayloadType != -1)
|
|
||||||
oss << "ISAC 16Khz ptype: " << mIsac16KPayloadType << " ";
|
|
||||||
|
|
||||||
if (mIsac32KPayloadType != -1)
|
oss << "ISAC 16Khz ptype: " << mIsac16KPayloadType << std::endl
|
||||||
oss << "ISAC 32Khz ptype: " << mIsac32KPayloadType << " ";
|
<< "ISAC 32Khz ptype: " << mIsac32KPayloadType << std::endl
|
||||||
|
<< "iLBC 20ms ptype: " << mIlbc20PayloadType << std::endl
|
||||||
if (mIlbc20PayloadType != -1)
|
<< "iLBC 30ms ptype: " << mIlbc30PayloadType << std::endl
|
||||||
oss << "iLBC 20ms ptype: " << mIlbc20PayloadType << " ";
|
<< "GSM FR ptype: " << mGsmFrPayloadType << ", GSM FR plength: " << mGsmFrPayloadLength << std::endl
|
||||||
|
<< "GSM HR ptype: " << mGsmHrPayloadType << std::endl
|
||||||
if (mIlbc30PayloadType != -1)
|
<< "GSM EFR ptype: " << mGsmEfrPayloadType << std::endl;
|
||||||
oss << "iLBC 30ms ptype: " << mIlbc30PayloadType << " ";
|
|
||||||
|
|
||||||
if (mGsmFrPayloadType != -1)
|
|
||||||
oss << "GSM FR ptype: " << mGsmFrPayloadType << ", GSM FR plength: " << mGsmFrPayloadLength << " ";
|
|
||||||
|
|
||||||
if (mGsmHrPayloadType != -1)
|
|
||||||
oss << "GSM HR ptype: " << mGsmHrPayloadType << " ";
|
|
||||||
|
|
||||||
if (mGsmEfrPayloadType != -1)
|
|
||||||
oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " ";
|
|
||||||
|
|
||||||
for (auto& spec: mEvsSpec)
|
for (auto& spec: mEvsSpec)
|
||||||
{
|
{
|
||||||
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " ";
|
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& spec: mOpusSpec)
|
for (auto& spec: mOpusSpec)
|
||||||
|
|
@ -124,24 +61,6 @@ std::string CodecList::Settings::toString() const
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodecList::Settings::clear()
|
|
||||||
{
|
|
||||||
// Remove all dynamic payload type assignments
|
|
||||||
mEvsSpec.clear();
|
|
||||||
mOpusSpec.clear();
|
|
||||||
mAmrNbOctetPayloadType.clear();
|
|
||||||
mAmrNbPayloadType.clear();
|
|
||||||
mAmrWbOctetPayloadType.clear();
|
|
||||||
mAmrWbPayloadType.clear();
|
|
||||||
mIsac16KPayloadType = -1;
|
|
||||||
mIsac32KPayloadType = -1;
|
|
||||||
mIlbc20PayloadType = -1;
|
|
||||||
mIlbc30PayloadType = -1;
|
|
||||||
mGsmEfrPayloadType = -1;
|
|
||||||
mGsmFrPayloadType = -1;
|
|
||||||
mGsmHrPayloadType = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CodecList::Settings::EvsSpec::isValid() const
|
bool CodecList::Settings::EvsSpec::isValid() const
|
||||||
{
|
{
|
||||||
return mPayloadType >= 96 && mPayloadType <= 127;
|
return mPayloadType >= 96 && mPayloadType <= 127;
|
||||||
|
|
@ -151,7 +70,7 @@ CodecList::Settings::EvsSpec CodecList::Settings::EvsSpec::parse(const std::stri
|
||||||
{
|
{
|
||||||
EvsSpec result;
|
EvsSpec result;
|
||||||
|
|
||||||
auto parts = strx::split(spec, "-/ ,:");
|
auto parts = strx::split(spec, "-/");
|
||||||
if (parts.size() == 3)
|
if (parts.size() == 3)
|
||||||
{
|
{
|
||||||
// Payload type number
|
// Payload type number
|
||||||
|
|
@ -196,7 +115,7 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
|
||||||
{
|
{
|
||||||
OpusSpec result;
|
OpusSpec result;
|
||||||
|
|
||||||
auto parts = strx::split(spec, "-/ ,:");
|
auto parts = strx::split(spec, "-");
|
||||||
if (parts.size() == 3)
|
if (parts.size() == 3)
|
||||||
{
|
{
|
||||||
result.mPayloadType = strx::toInt(strx::trim(parts.front()).c_str(), -1);
|
result.mPayloadType = strx::toInt(strx::trim(parts.front()).c_str(), -1);
|
||||||
|
|
@ -206,21 +125,6 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int findOctetMode(const char* line)
|
|
||||||
{
|
|
||||||
const char* param_name = "octet-align=";
|
|
||||||
auto p = strstr(line, param_name);
|
|
||||||
if (!p)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
p += strlen(param_name);
|
|
||||||
char int_buf[8] = {0};
|
|
||||||
size_t int_buf_offset = 0;
|
|
||||||
while (*p && isdigit(*p) && int_buf_offset < sizeof(int_buf))
|
|
||||||
int_buf[int_buf_offset++] = *p++;
|
|
||||||
return atoi(int_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>& codeclist)
|
CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>& codeclist)
|
||||||
{
|
{
|
||||||
CodecList::Settings r{DefaultSettings};
|
CodecList::Settings r{DefaultSettings};
|
||||||
|
|
@ -231,47 +135,15 @@ CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>&
|
||||||
int samplerate = c.getRate();
|
int samplerate = c.getRate();
|
||||||
int ptype = c.payloadType();
|
int ptype = c.payloadType();
|
||||||
|
|
||||||
auto enc_params = c.encodingParameters(); // This must channels number for Opus codec
|
|
||||||
auto params = c.parameters();
|
|
||||||
|
|
||||||
// Dynamic payload type codecs only - ISAC / iLBC / Speex / etc.
|
// Dynamic payload type codecs only - ISAC / iLBC / Speex / etc.
|
||||||
if (codec_name == "OPUS")
|
if (codec_name == "OPUS")
|
||||||
{
|
{
|
||||||
// Check the parameters
|
// Check the parameters
|
||||||
|
auto enc_params = c.encodingParameters(); // This must channels number for Opus codec
|
||||||
|
auto params = c.parameters();
|
||||||
int channels = strx::toInt(enc_params.c_str(), 1);
|
int channels = strx::toInt(enc_params.c_str(), 1);
|
||||||
r.mOpusSpec.push_back({ptype, samplerate, channels});
|
r.mOpusSpec.push_back({ptype, samplerate, channels});
|
||||||
}
|
}
|
||||||
else
|
|
||||||
if (codec_name == "AMR-WB")
|
|
||||||
{
|
|
||||||
int octet_mode = findOctetMode(params.c_str());
|
|
||||||
if (octet_mode != -1)
|
|
||||||
{
|
|
||||||
if (octet_mode == 0)
|
|
||||||
r.mAmrWbPayloadType.insert(ptype);
|
|
||||||
else
|
|
||||||
if (octet_mode == 1)
|
|
||||||
r.mAmrWbOctetPayloadType.insert(ptype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (codec_name == "AMR" || codec_name == "AMR-NB")
|
|
||||||
{
|
|
||||||
int octet_mode = findOctetMode(params.c_str());
|
|
||||||
if (octet_mode != -1)
|
|
||||||
{
|
|
||||||
if (octet_mode == 0)
|
|
||||||
r.mAmrNbPayloadType.insert(ptype);
|
|
||||||
else
|
|
||||||
if (octet_mode == 1)
|
|
||||||
r.mAmrNbOctetPayloadType.insert(ptype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (codec_name == "EVS")
|
|
||||||
{
|
|
||||||
r.mEvsSpec.push_back({ptype});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
@ -324,12 +196,21 @@ CodecList::CodecList(const Settings& settings)
|
||||||
|
|
||||||
void CodecList::init(const Settings& settings)
|
void CodecList::init(const Settings& settings)
|
||||||
{
|
{
|
||||||
|
for (auto f: mFactoryList)
|
||||||
|
delete f;
|
||||||
mFactoryList.clear();
|
mFactoryList.clear();
|
||||||
mSettings = settings;
|
|
||||||
#if defined(USE_OPUS_CODEC)
|
#if defined(USE_OPUS_CODEC)
|
||||||
|
if (settings.mOpusSpec.empty())
|
||||||
|
{
|
||||||
|
mFactoryList.push_back(new OpusCodec::OpusFactory(48000, 2, MT_OPUS_CODEC_PT));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
for (auto spec: settings.mOpusSpec)
|
for (auto spec: settings.mOpusSpec)
|
||||||
{
|
{
|
||||||
mFactoryList.push_back(std::make_shared<OpusCodec::OpusFactory>(spec.mRate, spec.mChannels, spec.mPayloadType));
|
mFactoryList.push_back(new OpusCodec::OpusFactory(spec.mRate, spec.mChannels, spec.mPayloadType));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -337,29 +218,28 @@ void CodecList::init(const Settings& settings)
|
||||||
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI)
|
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI)
|
||||||
#if defined(USE_AMR_CODEC)
|
#if defined(USE_AMR_CODEC)
|
||||||
for (int pt: mSettings.mAmrWbPayloadType)
|
for (int pt: mSettings.mAmrWbPayloadType)
|
||||||
mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
|
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt}));
|
||||||
for (int pt: mSettings.mAmrWbOctetPayloadType)
|
for (int pt: mSettings.mAmrWbOctetPayloadType)
|
||||||
mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
|
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt}));
|
||||||
|
|
||||||
for (int pt: mSettings.mAmrNbPayloadType)
|
for (int pt: mSettings.mAmrNbPayloadType)
|
||||||
mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
|
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt}));
|
||||||
for (int pt: mSettings.mAmrNbOctetPayloadType)
|
for (int pt: mSettings.mAmrNbOctetPayloadType)
|
||||||
mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
|
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt}));
|
||||||
|
|
||||||
if (mSettings.mGsmEfrPayloadType != -1)
|
mFactoryList.push_back(new GsmEfrCodec::GsmEfrFactory(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
|
||||||
mFactoryList.push_back(std::make_shared<GsmEfrCodec::GsmEfrFactory>(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
mFactoryList.push_back(std::make_shared<G711Codec::AlawFactory>());
|
// mFactoryList.push_back(new IsacCodec::IsacFactory16K(mSettings.mIsac16KPayloadType));
|
||||||
mFactoryList.push_back(std::make_shared<G711Codec::UlawFactory>());
|
// mFactoryList.push_back(new IlbcCodec::IlbcFactory(mSettings.mIlbc20PayloadType, mSettings.mIlbc30PayloadType));
|
||||||
|
mFactoryList.push_back(new G711Codec::AlawFactory());
|
||||||
|
mFactoryList.push_back(new G711Codec::UlawFactory());
|
||||||
|
|
||||||
if (mSettings.mGsmFrPayloadType != -1)
|
mFactoryList.push_back(new GsmCodec::GsmFactory(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
|
||||||
mFactoryList.push_back(std::make_shared<GsmCodec::GsmFactory>(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
|
// mFactoryList.push_back(new G722Codec::G722Factory());
|
||||||
mFactoryList.push_back(std::make_shared<G722Codec::G722Factory>());
|
// mFactoryList.push_back(new G729Codec::G729Factory());
|
||||||
mFactoryList.push_back(std::make_shared<G729Codec::G729Factory>());
|
|
||||||
#ifndef TARGET_ANDROID
|
#ifndef TARGET_ANDROID
|
||||||
if (mSettings.mGsmHrPayloadType != -1)
|
mFactoryList.push_back(new GsmHrCodec::GsmHrFactory(mSettings.mGsmHrPayloadType));
|
||||||
mFactoryList.push_back(std::make_shared<GsmHrCodec::GsmHrFactory>(mSettings.mGsmHrPayloadType));
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(TARGET_ANDROID) && defined(USE_EVS_CODEC)
|
#if !defined(TARGET_ANDROID) && defined(USE_EVS_CODEC)
|
||||||
|
|
@ -371,14 +251,15 @@ void CodecList::init(const Settings& settings)
|
||||||
evs_params.ptime = 20;
|
evs_params.ptime = 20;
|
||||||
evs_params.ptype = spec.mPayloadType;
|
evs_params.ptype = spec.mPayloadType;
|
||||||
|
|
||||||
mFactoryList.push_back(std::make_shared<EVSCodec::EVSFactory>(evs_params));
|
mFactoryList.push_back(new EVSCodec::EVSFactory(evs_params));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CodecList::~CodecList()
|
CodecList::~CodecList()
|
||||||
{
|
{
|
||||||
mFactoryList.clear();
|
for (auto f: mFactoryList)
|
||||||
|
delete f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CodecList::count() const
|
int CodecList::count() const
|
||||||
|
|
@ -407,26 +288,21 @@ void CodecList::fillCodecMap(CodecMap& cm)
|
||||||
for (auto& factory: mFactoryList)
|
for (auto& factory: mFactoryList)
|
||||||
{
|
{
|
||||||
// Create codec here. Although they are not needed right now - they can be needed to find codec's info.
|
// Create codec here. Although they are not needed right now - they can be needed to find codec's info.
|
||||||
// PCodec c = factory->create();
|
PCodec c = factory->create();
|
||||||
cm.insert({factory->payloadType(), PCodec()});
|
cm.insert({factory->payloadType(), c});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PCodec CodecList::createCodecByPayloadType(int payloadType)
|
|
||||||
{
|
|
||||||
for (auto& factory: mFactoryList)
|
|
||||||
{
|
|
||||||
if (factory->payloadType() == payloadType)
|
|
||||||
return factory->create();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
CodecListPriority::CodecListPriority()
|
CodecListPriority::CodecListPriority()
|
||||||
{}
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
CodecListPriority::~CodecListPriority()
|
CodecListPriority::~CodecListPriority()
|
||||||
{}
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool CodecListPriority::isNegativePriority(const CodecListPriority::Item& item)
|
bool CodecListPriority::isNegativePriority(const CodecListPriority::Item& item)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@
|
||||||
#ifndef __MT_CODEC_LIST_H
|
#ifndef __MT_CODEC_LIST_H
|
||||||
#define __MT_CODEC_LIST_H
|
#define __MT_CODEC_LIST_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
# include "resiprocate/resip/stack/SdpContents.hxx"
|
# include "resiprocate/resip/stack/SdpContents.hxx"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "MT_Codec.h"
|
#include "MT_Codec.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -29,22 +31,22 @@ public:
|
||||||
bool mSkipDecode = false;
|
bool mSkipDecode = false;
|
||||||
|
|
||||||
// AMR payload types
|
// AMR payload types
|
||||||
std::set<int64_t> mAmrWbPayloadType = { };
|
std::set<int> mAmrWbPayloadType = { MT_AMRWB_PAYLOADTYPE };
|
||||||
std::set<int64_t> mAmrNbPayloadType = { };
|
std::set<int> mAmrNbPayloadType = { MT_AMRNB_PAYLOADTYPE };
|
||||||
std::set<int64_t> mAmrWbOctetPayloadType = { };
|
std::set<int> mAmrWbOctetPayloadType = { MT_AMRWB_OCTET_PAYLOADTYPE };
|
||||||
std::set<int64_t> mAmrNbOctetPayloadType = { };
|
std::set<int> mAmrNbOctetPayloadType = { MT_AMRNB_OCTET_PAYLOADTYPE };
|
||||||
|
|
||||||
bool isAmrWb(int ptype) const { return mAmrWbOctetPayloadType.count(ptype) > 0 || mAmrWbPayloadType.count(ptype) > 0; }
|
bool isAmrWb(int ptype) const { return mAmrWbOctetPayloadType.count(ptype) > 0 || mAmrWbPayloadType.count(ptype) > 0; }
|
||||||
bool isAmrNb(int ptype) const { return mAmrNbOctetPayloadType.count(ptype) > 0 || mAmrNbPayloadType.count(ptype) > 0; }
|
bool isAmrNb(int ptype) const { return mAmrNbOctetPayloadType.count(ptype) > 0 || mAmrNbPayloadType.count(ptype) > 0; }
|
||||||
|
|
||||||
int mIsac16KPayloadType = -1;
|
int mIsac16KPayloadType = MT_ISAC16K_PAYLOADTYPE;
|
||||||
int mIsac32KPayloadType = -1;
|
int mIsac32KPayloadType = MT_ISAC32K_PAYLOADTYPE;
|
||||||
int mIlbc20PayloadType = -1;
|
int mIlbc20PayloadType = MT_ILBC20_PAYLOADTYPE;
|
||||||
int mIlbc30PayloadType = -1;
|
int mIlbc30PayloadType = MT_ILBC30_PAYLOADTYPE;
|
||||||
int mGsmFrPayloadType = -1; // GSM is codec with fixed payload type. But sometimes it has to be overwritten.
|
int mGsmFrPayloadType = 3; // GSM is codec with fixed payload type. But sometimes it has to be overwritten.
|
||||||
int mGsmFrPayloadLength = 33; // Expected GSM payload length
|
int mGsmFrPayloadLength = 33; // Expected GSM payload length
|
||||||
int mGsmHrPayloadType = -1;
|
int mGsmHrPayloadType = MT_GSMHR_PAYLOADTYPE;
|
||||||
int mGsmEfrPayloadType = -1;
|
int mGsmEfrPayloadType = MT_GSMEFR_PAYLOADTYPE;
|
||||||
|
|
||||||
struct EvsSpec
|
struct EvsSpec
|
||||||
{
|
{
|
||||||
|
|
@ -57,7 +59,7 @@ public:
|
||||||
Bandwidth_FB
|
Bandwidth_FB
|
||||||
};
|
};
|
||||||
|
|
||||||
Bandwidth mBandwidth = Bandwidth_FB;
|
Bandwidth mBandwidth = Bandwidth_NB;
|
||||||
|
|
||||||
enum Encoding
|
enum Encoding
|
||||||
{
|
{
|
||||||
|
|
@ -70,7 +72,7 @@ public:
|
||||||
static EvsSpec parse(const std::string& spec);
|
static EvsSpec parse(const std::string& spec);
|
||||||
|
|
||||||
bool operator == (const EvsSpec& rhs) const { return std::tie(mPayloadType, mBandwidth, mEncodingType) == std::tie(rhs.mPayloadType, rhs.mBandwidth, rhs.mEncodingType);}
|
bool operator == (const EvsSpec& rhs) const { return std::tie(mPayloadType, mBandwidth, mEncodingType) == std::tie(rhs.mPayloadType, rhs.mBandwidth, rhs.mEncodingType);}
|
||||||
bool operator != (const EvsSpec& rhs) const { return ! (operator ==) (rhs);}
|
bool operator != (const EvsSpec& rhs) const { return ! (operator ==) (rhs);};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -88,42 +90,35 @@ public:
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
bool operator == (const OpusSpec& rhs) const { return std::tie(mPayloadType, mRate, mChannels) == std::tie(rhs.mPayloadType, rhs.mRate, rhs.mChannels);}
|
bool operator == (const OpusSpec& rhs) const { return std::tie(mPayloadType, mRate, mChannels) == std::tie(rhs.mPayloadType, rhs.mRate, rhs.mChannels);}
|
||||||
bool operator != (const OpusSpec& rhs) const { return ! (operator ==) (rhs);}
|
bool operator != (const OpusSpec& rhs) const { return ! (operator ==) (rhs);};
|
||||||
|
|
||||||
static OpusSpec parse(const std::string& spec);
|
static OpusSpec parse(const std::string& spec);
|
||||||
};
|
};
|
||||||
std::vector<OpusSpec> mOpusSpec;
|
std::vector<OpusSpec> mOpusSpec;
|
||||||
|
|
||||||
// Payload type
|
|
||||||
bool contains(int ptype) const;
|
|
||||||
|
|
||||||
// Textual representation - used in logging
|
// Textual representation - used in logging
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
void clear();
|
|
||||||
|
|
||||||
static Settings DefaultSettings;
|
static Settings DefaultSettings;
|
||||||
|
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
static Settings parseSdp(const std::list<resip::Codec>& codeclist);
|
static Settings parseSdp(const std::list<resip::Codec>& codeclist);
|
||||||
|
#endif
|
||||||
bool operator == (const Settings& rhs) const;
|
bool operator == (const Settings& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
CodecList(const Settings& settings);
|
CodecList(const Settings& settings);
|
||||||
~CodecList();
|
~CodecList();
|
||||||
void setSettings(const Settings& settings)
|
void setSettings(const Settings& settings) { init(settings); }
|
||||||
{
|
|
||||||
init(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
int count() const;
|
int count() const;
|
||||||
Codec::Factory& codecAt(int index) const;
|
Codec::Factory& codecAt(int index) const;
|
||||||
int findCodec(const std::string& name) const;
|
int findCodec(const std::string& name) const;
|
||||||
void fillCodecMap(CodecMap& cm);
|
void fillCodecMap(CodecMap& cm);
|
||||||
PCodec createCodecByPayloadType(int payloadType);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::vector<std::shared_ptr<Codec::Factory>> FactoryList;
|
typedef std::vector<Codec::Factory*> FactoryList;
|
||||||
FactoryList mFactoryList;
|
FactoryList mFactoryList;
|
||||||
Settings mSettings;
|
Settings mSettings;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "MT_Dtmf.h"
|
#include "MT_Dtmf.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef MT_DTMF
|
#ifndef MT_DTMF
|
||||||
#define MT_DTMF
|
#define MT_DTMF
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,8 @@ PCodec EVSCodec::EVSFactory::create()
|
||||||
}
|
}
|
||||||
|
|
||||||
EVSCodec::EVSCodec(): EVSCodec(StreamParameters())
|
EVSCodec::EVSCodec(): EVSCodec(StreamParameters())
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
EVSCodec::EVSCodec(const StreamParameters &sp)
|
EVSCodec::EVSCodec(const StreamParameters &sp)
|
||||||
{
|
{
|
||||||
|
|
@ -226,7 +227,8 @@ int EVSCodec::decode(const void* input, int input_length, void* output, int outp
|
||||||
else
|
else
|
||||||
buffer = std::string(reinterpret_cast<const char*>(input), input_length);
|
buffer = std::string(reinterpret_cast<const char*>(input), input_length);
|
||||||
}
|
}
|
||||||
else // Skip CMR byte
|
else
|
||||||
|
// Skip CMR byte
|
||||||
buffer = std::string(reinterpret_cast<const char*>(input) + 1, input_length-1);
|
buffer = std::string(reinterpret_cast<const char*>(input) + 1, input_length-1);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
#ifndef __MT_EVS_CODEC_H
|
#pragma once
|
||||||
#define __MT_EVS_CODEC_H
|
|
||||||
|
|
||||||
#include "../engine_config.h"
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -12,6 +10,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include "MT_Codec.h"
|
#include "MT_Codec.h"
|
||||||
|
|
||||||
#include "libevs/lib_com/prot.h"
|
#include "libevs/lib_com/prot.h"
|
||||||
|
|
@ -41,7 +40,7 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EVSFactory(StreamParameters& sp);
|
EVSFactory(StreamParameters& sp);
|
||||||
const char* name() { return MT_EVS_CODECNAME; }
|
const char* name() { return MT_EVS_CODECNAME; };
|
||||||
int samplerate();
|
int samplerate();
|
||||||
int payloadType();
|
int payloadType();
|
||||||
PCodec create();
|
PCodec create();
|
||||||
|
|
@ -52,7 +51,7 @@ public:
|
||||||
EVSCodec(const StreamParameters& sp);
|
EVSCodec(const StreamParameters& sp);
|
||||||
~EVSCodec() override;
|
~EVSCodec() override;
|
||||||
|
|
||||||
const char* name() override { return MT_EVS_CODECNAME; }
|
const char* name() override { return MT_EVS_CODECNAME; } ;
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int pcmLength() override;
|
int pcmLength() override;
|
||||||
int frameTime() override;
|
int frameTime() override;
|
||||||
|
|
@ -69,5 +68,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace
|
} // End of namespace
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@
|
||||||
#ifndef __MT_NATIVE_RTP_SENDER_H
|
#ifndef __MT_NATIVE_RTP_SENDER_H
|
||||||
#define __MT_NATIVE_RTP_SENDER_H
|
#define __MT_NATIVE_RTP_SENDER_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "jrtplib/src/rtpexternaltransmitter.h"
|
#include "jrtplib/src/rtpexternaltransmitter.h"
|
||||||
#include "libsrtp/include/srtp.h"
|
#include "srtp/include/srtp.h"
|
||||||
#include "../helper/HL_NetworkSocket.h"
|
#include "../helper/HL_NetworkSocket.h"
|
||||||
#include "../helper/HL_InternetAddress.h"
|
#include "../helper/HL_InternetAddress.h"
|
||||||
#include "../helper/HL_Rtp.h"
|
#include "../helper/HL_Rtp.h"
|
||||||
|
|
|
||||||
|
|
@ -1,90 +1,42 @@
|
||||||
/* Copyright(C) 2007-2025 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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
|
||||||
#include "MT_SrtpHelper.h"
|
#include "MT_SrtpHelper.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
#include "../helper/HL_Rtp.h"
|
#include "../helper/HL_Rtp.h"
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <format>
|
|
||||||
|
|
||||||
struct SrtpSuiteAndName
|
|
||||||
{
|
|
||||||
SrtpSuite mSuite;
|
|
||||||
std::string mName, mAltName;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<SrtpSuiteAndName> SrtpSuiteList {
|
|
||||||
{ SRTP_AES_256_AUTH_80, "AES_CM_256_HMAC_SHA1_80", "AES_256_CM_HMAC_SHA1_80" },
|
|
||||||
{ SRTP_AES_128_AUTH_80, "AES_CM_128_HMAC_SHA1_80", "AES_128_CM_HMAC_SHA1_80" },
|
|
||||||
{ SRTP_AES_192_AUTH_80, "AES_CM_192_HMAC_SHA1_80", "AES_192_CM_HMAC_SHA1_80" },
|
|
||||||
{ SRTP_AES_256_AUTH_32, "AES_CM_256_HMAC_SHA1_32", "AES_256_CM_HMAC_SHA1_32" },
|
|
||||||
{ SRTP_AES_192_AUTH_32, "AES_CM_192_HMAC_SHA1_32", "AES_192_CM_HMAC_SHA1_32" },
|
|
||||||
{ SRTP_AES_128_AUTH_32, "AES_CM_128_HMAC_SHA1_32", "AES_128_CM_HMAC_SHA1_32" },
|
|
||||||
{ SRTP_AES_128_AUTH_NULL, "AES_CM_128_NULL_AUTH", "AES_128_CM_NULL_AUTH"},
|
|
||||||
{ SRTP_AED_AES_256_GCM, "AEAD_AES_256_GCM"},
|
|
||||||
{ SRTP_AED_AES_128_GCM, "AEAD_AES_128_GCM"}
|
|
||||||
};
|
|
||||||
|
|
||||||
extern SrtpSuite toSrtpSuite(const std::string_view& s)
|
|
||||||
{
|
|
||||||
for (const auto& suite: SrtpSuiteList)
|
|
||||||
if (s == suite.mName || s == suite.mAltName)
|
|
||||||
return suite.mSuite;
|
|
||||||
|
|
||||||
return SRTP_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern std::string_view toString(SrtpSuite suite)
|
|
||||||
{
|
|
||||||
for (const auto& item: SrtpSuiteList)
|
|
||||||
if (item.mSuite == suite)
|
|
||||||
return item.mName;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void (*set_srtp_policy_function) (srtp_crypto_policy_t*);
|
|
||||||
|
|
||||||
set_srtp_policy_function findPolicyFunction(SrtpSuite suite)
|
|
||||||
{
|
|
||||||
switch (suite)
|
|
||||||
{
|
|
||||||
case SRTP_AES_128_AUTH_80: return &srtp_crypto_policy_set_rtp_default; break;
|
|
||||||
case SRTP_AES_192_AUTH_80: return &srtp_crypto_policy_set_aes_cm_192_hmac_sha1_80; break;
|
|
||||||
case SRTP_AES_256_AUTH_80: return &srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80; break;
|
|
||||||
case SRTP_AES_128_AUTH_32: return &srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32; break;
|
|
||||||
case SRTP_AES_192_AUTH_32: return &srtp_crypto_policy_set_aes_cm_192_hmac_sha1_32; break;
|
|
||||||
case SRTP_AES_256_AUTH_32: return &srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32; break;
|
|
||||||
case SRTP_AES_128_AUTH_NULL: return &srtp_crypto_policy_set_aes_cm_128_null_auth; break;
|
|
||||||
case SRTP_AED_AES_256_GCM: return &srtp_crypto_policy_set_aes_gcm_256_16_auth; break;
|
|
||||||
case SRTP_AED_AES_128_GCM: return &srtp_crypto_policy_set_aes_gcm_128_16_auth; break;
|
|
||||||
case SRTP_NONE: return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- SrtpStream ---
|
// --- SrtpStream ---
|
||||||
static void configureSrtpStream(SrtpStream& s, uint16_t ssrc, SrtpSuite suite)
|
void initSrtpStream(SrtpStream& s, unsigned ssrc, SrtpSuite suite)
|
||||||
{
|
{
|
||||||
s.second.ssrc.type = ssrc_specific;
|
s.second.ssrc.type = ssrc_specific;
|
||||||
s.second.ssrc.value = ntohl(ssrc);
|
s.second.ssrc.value = ntohl(ssrc);
|
||||||
s.second.next = nullptr;
|
s.second.next = NULL;
|
||||||
set_srtp_policy_function func = findPolicyFunction(suite);
|
switch (suite)
|
||||||
if (!func)
|
{
|
||||||
throw std::runtime_error(std::format("SRTP suite {} is not supported", toString(suite)));
|
case SRTP_AES_128_AUTH_80:
|
||||||
func(&s.second.rtp);
|
crypto_policy_set_aes_cm_128_hmac_sha1_80(&s.second.rtp);
|
||||||
func(&s.second.rtcp);
|
crypto_policy_set_aes_cm_128_hmac_sha1_80(&s.second.rtcp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SRTP_AES_256_AUTH_80:
|
||||||
|
crypto_policy_set_aes_cm_256_hmac_sha1_80(&s.second.rtp);
|
||||||
|
crypto_policy_set_aes_cm_256_hmac_sha1_80(&s.second.rtcp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SrtpSession::SrtpSession()
|
SrtpSession::SrtpSession()
|
||||||
:mInboundSession(nullptr), mOutboundSession(nullptr)
|
:mInboundSession(NULL), mOutboundSession(NULL)
|
||||||
{
|
{
|
||||||
|
mInboundSession = NULL;
|
||||||
|
mOutboundSession = NULL;
|
||||||
mSuite = SRTP_NONE;
|
mSuite = SRTP_NONE;
|
||||||
|
|
||||||
memset(&mInboundPolicy, 0, sizeof mInboundPolicy);
|
memset(&mInboundPolicy, 0, sizeof mInboundPolicy);
|
||||||
|
|
@ -93,18 +45,15 @@ SrtpSession::SrtpSession()
|
||||||
memset(&mOutboundPolicy, 0, sizeof mOutboundPolicy);
|
memset(&mOutboundPolicy, 0, sizeof mOutboundPolicy);
|
||||||
mOutboundPolicy.ssrc.type = ssrc_specific;
|
mOutboundPolicy.ssrc.type = ssrc_specific;
|
||||||
|
|
||||||
// Generate outgoing keys for all ciphers
|
// Generate outgoing keys
|
||||||
auto putKey = [this](SrtpSuite suite, size_t length){
|
|
||||||
auto key = std::make_shared<ByteBuffer>();
|
mOutgoingKey[SRTP_AES_128_AUTH_80-1].first = PByteBuffer(new ByteBuffer());
|
||||||
key->resize(length);
|
mOutgoingKey[SRTP_AES_128_AUTH_80-1].first->resize(30);
|
||||||
RAND_bytes(key->mutableData(), key->size());
|
crypto_get_random((unsigned char*)mOutgoingKey[SRTP_AES_128_AUTH_80-1].first->mutableData(), 30);
|
||||||
mOutgoingKey[suite].first = key;
|
|
||||||
};
|
mOutgoingKey[SRTP_AES_256_AUTH_80-1].first = PByteBuffer(new ByteBuffer());
|
||||||
putKey(SRTP_AES_128_AUTH_80, 30); putKey(SRTP_AES_128_AUTH_32, 30);
|
mOutgoingKey[SRTP_AES_256_AUTH_80-1].first->resize(46);
|
||||||
putKey(SRTP_AES_192_AUTH_80, 38); putKey(SRTP_AES_192_AUTH_32, 38);
|
crypto_get_random((unsigned char*)mOutgoingKey[SRTP_AES_256_AUTH_80-1].first->mutableData(), 46);
|
||||||
putKey(SRTP_AES_256_AUTH_80, 46); putKey(SRTP_AES_256_AUTH_32, 46);
|
|
||||||
putKey(SRTP_AED_AES_128_GCM, 28);
|
|
||||||
putKey(SRTP_AED_AES_256_GCM, 44);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +61,7 @@ SrtpSession::~SrtpSession()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void SrtpSession::addSsrc(unsigned ssrc, SsrcDirection d)
|
void SrtpSession::addSsrc(unsigned ssrc, SsrcDirection d)
|
||||||
{
|
{
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
assert(mSuite != SRTP_NONE);
|
assert(mSuite != SRTP_NONE);
|
||||||
|
|
@ -127,7 +76,7 @@ SrtpSession::~SrtpSession()
|
||||||
if (streamIter != mIncomingMap.end())
|
if (streamIter != mIncomingMap.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
configureSrtpStream(s, ssrc, mSuite);
|
initSrtpStream(s, ssrc, mSuite);
|
||||||
s.second.key = (unsigned char*)mIncomingKey.first->mutableData();
|
s.second.key = (unsigned char*)mIncomingKey.first->mutableData();
|
||||||
mIncomingMap[ssrc] = s;
|
mIncomingMap[ssrc] = s;
|
||||||
srtp_add_stream(mInboundSession, &s.second);
|
srtp_add_stream(mInboundSession, &s.second);
|
||||||
|
|
@ -137,13 +86,13 @@ SrtpSession::~SrtpSession()
|
||||||
streamIter = mOutgoingMap.find(ssrc);
|
streamIter = mOutgoingMap.find(ssrc);
|
||||||
if (streamIter != mOutgoingMap.end())
|
if (streamIter != mOutgoingMap.end())
|
||||||
return;
|
return;
|
||||||
configureSrtpStream(s, ssrc, mSuite);
|
initSrtpStream(s, ssrc, mSuite);
|
||||||
s.second.key = (unsigned char*)mOutgoingKey[int(mSuite)-1].first->mutableData();
|
s.second.key = (unsigned char*)mOutgoingKey[int(mSuite)-1].first->mutableData();
|
||||||
mOutgoingMap[ssrc] = s;
|
mOutgoingMap[ssrc] = s;
|
||||||
srtp_add_stream(mOutboundSession, &s.second);
|
srtp_add_stream(mOutboundSession, &s.second);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
void SrtpSession::open(ByteBuffer& incomingKey, SrtpSuite suite)
|
void SrtpSession::open(ByteBuffer& incomingKey, SrtpSuite suite)
|
||||||
{
|
{
|
||||||
|
|
@ -157,26 +106,33 @@ void SrtpSession::open(ByteBuffer& incomingKey, SrtpSuite suite)
|
||||||
mSuite = suite;
|
mSuite = suite;
|
||||||
|
|
||||||
// Save key
|
// Save key
|
||||||
mIncomingKey.first = std::make_shared<ByteBuffer>(incomingKey);
|
mIncomingKey.first = PByteBuffer(new ByteBuffer(incomingKey));
|
||||||
|
|
||||||
auto policyFunction = findPolicyFunction(suite);
|
// Update policy
|
||||||
if (!policyFunction)
|
switch (suite)
|
||||||
throw std::runtime_error(std::format("SRTP suite {} not found", toString(suite)));
|
{
|
||||||
|
case SRTP_AES_128_AUTH_80:
|
||||||
|
crypto_policy_set_aes_cm_128_hmac_sha1_80(&mInboundPolicy.rtp);
|
||||||
|
crypto_policy_set_aes_cm_128_hmac_sha1_80(&mInboundPolicy.rtcp);
|
||||||
|
crypto_policy_set_aes_cm_128_hmac_sha1_80(&mOutboundPolicy.rtp);
|
||||||
|
crypto_policy_set_aes_cm_128_hmac_sha1_80(&mOutboundPolicy.rtcp);
|
||||||
|
break;
|
||||||
|
|
||||||
// Configure policies
|
case SRTP_AES_256_AUTH_80:
|
||||||
policyFunction(&mInboundPolicy.rtp);
|
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mInboundPolicy.rtp);
|
||||||
policyFunction(&mInboundPolicy.rtcp);
|
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mInboundPolicy.rtcp);
|
||||||
policyFunction(&mOutboundPolicy.rtp);
|
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mOutboundPolicy.rtp);
|
||||||
policyFunction(&mOutboundPolicy.rtcp);
|
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mOutboundPolicy.rtcp);
|
||||||
|
break;
|
||||||
mOutboundPolicy.key = (unsigned char*)mOutgoingKey[int(suite)].first->mutableData();
|
|
||||||
mOutboundPolicy.ssrc.type = ssrc_any_outbound;
|
|
||||||
|
|
||||||
|
case SRTP_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mOutboundPolicy.key = (unsigned char*)mOutgoingKey[int(suite)-1].first->mutableData();
|
||||||
mInboundPolicy.key = (unsigned char*)mIncomingKey.first->mutableData();
|
mInboundPolicy.key = (unsigned char*)mIncomingKey.first->mutableData();
|
||||||
mInboundPolicy.ssrc.type = ssrc_any_inbound;
|
|
||||||
|
|
||||||
// Create SRTP session
|
// Create SRTP session
|
||||||
srtp_err_status_t err;
|
err_status_t err;
|
||||||
|
|
||||||
err = srtp_create(&mOutboundSession, &mOutboundPolicy);
|
err = srtp_create(&mOutboundSession, &mOutboundPolicy);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -200,13 +156,13 @@ void SrtpSession::close()
|
||||||
if (mOutboundSession)
|
if (mOutboundSession)
|
||||||
{
|
{
|
||||||
srtp_dealloc(mOutboundSession);
|
srtp_dealloc(mOutboundSession);
|
||||||
mOutboundSession = nullptr;
|
mOutboundSession = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mInboundSession)
|
if (mInboundSession)
|
||||||
{
|
{
|
||||||
srtp_dealloc(mInboundSession);
|
srtp_dealloc(mInboundSession);
|
||||||
mInboundSession = nullptr;
|
mInboundSession = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,70 +175,44 @@ SrtpKeySalt& SrtpSession::outgoingKey(SrtpSuite suite)
|
||||||
|
|
||||||
bool SrtpSession::protectRtp(void* buffer, int* length)
|
bool SrtpSession::protectRtp(void* buffer, int* length)
|
||||||
{
|
{
|
||||||
// addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
||||||
|
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
if (mOutboundSession)
|
if (mOutboundSession)
|
||||||
{
|
return srtp_protect(mOutboundSession, buffer, length) == 0;
|
||||||
size_t srtp_len = MAX_VALID_UDPPACKET_SIZE;
|
|
||||||
auto code = srtp_protect(mOutboundSession,
|
|
||||||
(const uint8_t*)buffer, (size_t)*length,
|
|
||||||
(uint8_t*)buffer, &srtp_len,
|
|
||||||
0 /* mki_index, non-used */);
|
|
||||||
*length = srtp_len;
|
|
||||||
return code == 0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrtpSession::protectRtcp(void* buffer, int* length)
|
bool SrtpSession::protectRtcp(void* buffer, int* length)
|
||||||
{
|
{
|
||||||
//addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
||||||
|
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
if (mOutboundSession)
|
if (mOutboundSession)
|
||||||
{
|
return srtp_protect_rtcp(mOutboundSession, buffer, length) == 0;
|
||||||
size_t srtp_len = MAX_VALID_UDPPACKET_SIZE;
|
|
||||||
auto code = srtp_protect_rtcp(mOutboundSession,
|
|
||||||
(const uint8_t*)buffer, (size_t)*length,
|
|
||||||
(uint8_t*)buffer, &srtp_len,
|
|
||||||
0 /* mki_index, non-used */);
|
|
||||||
*length = srtp_len;
|
|
||||||
return code == 0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrtpSession::unprotectRtp(const void* src, size_t srcLength, void* dst, size_t* dstLength)
|
bool SrtpSession::unprotectRtp(void* buffer, int* length)
|
||||||
{
|
{
|
||||||
//addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
||||||
|
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
if (mInboundSession)
|
if (mInboundSession)
|
||||||
{
|
return srtp_unprotect(mInboundSession, buffer, length) == 0;
|
||||||
auto code = srtp_unprotect(mInboundSession,
|
|
||||||
(const uint8_t*)src, srcLength,
|
|
||||||
(uint8_t*)dst, dstLength);
|
|
||||||
return code == 0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrtpSession::unprotectRtcp(const void* src, size_t srcLength, void* dst, size_t* dstLength)
|
bool SrtpSession::unprotectRtcp(void* buffer, int* length)
|
||||||
{
|
{
|
||||||
//addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
||||||
|
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
if (mInboundSession)
|
if (mInboundSession)
|
||||||
{
|
return srtp_unprotect_rtcp(mInboundSession, buffer, (int*)length) == 0;
|
||||||
auto code = srtp_unprotect_rtcp(mInboundSession,
|
|
||||||
(const uint8_t*)src, srcLength,
|
|
||||||
(uint8_t*)dst, dstLength);
|
|
||||||
return code == 0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -293,9 +223,9 @@ void SrtpSession::initSrtp()
|
||||||
if (GSrtpInitialized)
|
if (GSrtpInitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto err = srtp_init();
|
err_status_t err = srtp_init();
|
||||||
|
|
||||||
if (err != srtp_err_status_ok)
|
if (err != err_status_ok)
|
||||||
throw Exception(ERR_SRTP, err);
|
throw Exception(ERR_SRTP, err);
|
||||||
GSrtpInitialized = true;
|
GSrtpInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,35 +10,21 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "libsrtp/include/srtp.h"
|
#include "srtp/include/srtp.h"
|
||||||
#include "HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
#include "HL_Types.h"
|
|
||||||
|
|
||||||
#define NAME_SRTP_AES_256_AUTH_80 "AES_CM_256_HMAC_SHA1_80"
|
#define SRTP_SUITE_NAME_2 "AES_CM_256_HMAC_SHA1_80"
|
||||||
#define NAME_SRTP_AES_128_AUTH_80 "AES_CM_128_HMAC_SHA1_80"
|
#define SRTP_SUITE_NAME_1 "AES_CM_128_HMAC_SHA1_80"
|
||||||
|
|
||||||
enum SrtpSuite
|
enum SrtpSuite
|
||||||
{
|
{
|
||||||
SRTP_NONE,
|
SRTP_NONE,
|
||||||
SRTP_AES_128_AUTH_80,
|
SRTP_AES_128_AUTH_80,
|
||||||
SRTP_AES_256_AUTH_80,
|
SRTP_AES_256_AUTH_80,
|
||||||
SRTP_AES_192_AUTH_80,
|
SRTP_LAST = SRTP_AES_256_AUTH_80
|
||||||
SRTP_AES_128_AUTH_32,
|
|
||||||
SRTP_AES_256_AUTH_32,
|
|
||||||
SRTP_AES_192_AUTH_32,
|
|
||||||
SRTP_AES_128_AUTH_NULL,
|
|
||||||
SRTP_AED_AES_256_GCM,
|
|
||||||
SRTP_AED_AES_128_GCM,
|
|
||||||
SRTP_LAST = SRTP_AED_AES_128_GCM
|
|
||||||
// ToDo:
|
|
||||||
// a=crypto:1 AEAD_AES_256_GCM_8 inline:tN2A0vRjFBimpQsW2GasuJuPe7hKE26gki30APC8DVuySqCOYTs8lYBPR5I=
|
|
||||||
// a=crypto:3 AEAD_AES_128_GCM_8 inline:Ok7VL8SmBHSbZLw4dK6iQgpliYKGdY9BHLJcRw==
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SrtpSuite toSrtpSuite(const std::string_view& s);
|
|
||||||
extern std::string_view toString(SrtpSuite suite);
|
|
||||||
|
|
||||||
typedef std::pair<PByteBuffer, PByteBuffer> SrtpKeySalt;
|
typedef std::pair<PByteBuffer, PByteBuffer> SrtpKeySalt;
|
||||||
typedef std::pair<unsigned, srtp_policy_t> SrtpStream;
|
typedef std::pair<unsigned, srtp_policy_t> SrtpStream;
|
||||||
|
|
||||||
|
|
@ -62,8 +48,8 @@ public:
|
||||||
/* bufferPtr is RTP packet data i.e. header + payload. Buffer must be big enough to hold encrypted data. */
|
/* bufferPtr is RTP packet data i.e. header + payload. Buffer must be big enough to hold encrypted data. */
|
||||||
bool protectRtp(void* buffer, int* length);
|
bool protectRtp(void* buffer, int* length);
|
||||||
bool protectRtcp(void* buffer, int* length);
|
bool protectRtcp(void* buffer, int* length);
|
||||||
bool unprotectRtp(const void* src, size_t srcLength, void* dst, size_t* dstLength);
|
bool unprotectRtp(void* buffer, int* length);
|
||||||
bool unprotectRtcp(const void* src, size_t srcLength, void* dst, size_t* dstLength);
|
bool unprotectRtcp(void* buffer, int* length);
|
||||||
|
|
||||||
|
|
||||||
static void initSrtp();
|
static void initSrtp();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#include <cmath>
|
#include <math.h>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "MT_Statistics.h"
|
#include "MT_Statistics.h"
|
||||||
|
#include "audio/Audio_Interface.h"
|
||||||
|
#include "helper/HL_Log.h"
|
||||||
#define LOG_SUBSYSTEM "Statistics"
|
#define LOG_SUBSYSTEM "Statistics"
|
||||||
|
|
||||||
using namespace MT;
|
using namespace MT;
|
||||||
|
|
@ -12,7 +13,7 @@ void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
|
||||||
uint32_t timestamp = packet->GetTimestamp();
|
uint32_t timestamp = packet->GetTimestamp();
|
||||||
jrtplib::RTPTime receiveTime = packet->GetReceiveTime();
|
jrtplib::RTPTime receiveTime = packet->GetReceiveTime();
|
||||||
|
|
||||||
if (!mLastJitter)
|
if (!mLastJitter.is_initialized())
|
||||||
{
|
{
|
||||||
// First packet
|
// First packet
|
||||||
mReceiveTime = receiveTime;
|
mReceiveTime = receiveTime;
|
||||||
|
|
@ -54,11 +55,8 @@ void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
|
||||||
mReceiveTime = receiveTime;
|
mReceiveTime = receiveTime;
|
||||||
mReceiveTimestamp = timestamp;
|
mReceiveTimestamp = timestamp;
|
||||||
|
|
||||||
// And mJitter are in milliseconds again
|
// And mJitter are in seconds again
|
||||||
float jitter_s = mLastJitter.value() / (float(rate));
|
mJitter.process(mLastJitter.value() / float(rate));
|
||||||
// std::cout << "Jitter (in seconds): " << std::dec << jitter_s << std::endl;
|
|
||||||
|
|
||||||
mJitter.process(jitter_s);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,10 +65,40 @@ void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
|
||||||
|
|
||||||
|
|
||||||
Statistics::Statistics()
|
Statistics::Statistics()
|
||||||
{}
|
:mReceived(0), mSent(0), mReceivedRtp(0), mSentRtp(0),
|
||||||
|
mReceivedRtcp(0), mSentRtcp(0), mDuplicatedRtp(0), mOldRtp(0), mIllegalRtp(0),
|
||||||
|
mPacketLoss(0), mJitter(0.0), mAudioTime(0), mDecodedSize(0), mSsrc(0), mPacketDropped(0)
|
||||||
|
{
|
||||||
|
mBitrateSwitchCounter = 0;
|
||||||
|
memset(mLoss, 0, sizeof mLoss);
|
||||||
|
|
||||||
|
// It is to keep track of statistics instance via grep | wc -l
|
||||||
|
//ICELogDebug(<< "Create statistics instance.");
|
||||||
|
}
|
||||||
|
|
||||||
Statistics::~Statistics()
|
Statistics::~Statistics()
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statistics::reset()
|
||||||
|
{
|
||||||
|
mReceived = 0;
|
||||||
|
mSent = 0;
|
||||||
|
mReceivedRtp = 0;
|
||||||
|
mSentRtp = 0;
|
||||||
|
mReceivedRtcp = 0;
|
||||||
|
mSentRtcp = 0;
|
||||||
|
mDuplicatedRtp = 0;
|
||||||
|
mOldRtp = 0;
|
||||||
|
mPacketLoss = 0;
|
||||||
|
mIllegalRtp = 0;
|
||||||
|
mJitter = 0.0;
|
||||||
|
mAudioTime = 0;
|
||||||
|
mPacketDropped = 0;
|
||||||
|
mDecodedSize = 0;
|
||||||
|
|
||||||
|
memset(mLoss, 0, sizeof mLoss);
|
||||||
|
}
|
||||||
|
|
||||||
void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
||||||
{
|
{
|
||||||
|
|
@ -99,7 +127,7 @@ void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
*burstr = 0;
|
*burstr = 0;
|
||||||
|
//printf("total loss: %d\n", lost);
|
||||||
if (mReceivedRtp > 0)
|
if (mReceivedRtp > 0)
|
||||||
*lossr = (double)((double)lost / (double)mReceivedRtp);
|
*lossr = (double)((double)lost / (double)mReceivedRtp);
|
||||||
else
|
else
|
||||||
|
|
@ -171,16 +199,16 @@ Statistics& Statistics::operator += (const Statistics& src)
|
||||||
mCodecName = src.mCodecName;
|
mCodecName = src.mCodecName;
|
||||||
|
|
||||||
// Find minimal
|
// Find minimal
|
||||||
if (mFirstRtpTime)
|
if (mFirstRtpTime.is_initialized())
|
||||||
{
|
{
|
||||||
if (src.mFirstRtpTime)
|
if (src.mFirstRtpTime.is_initialized())
|
||||||
{
|
{
|
||||||
if (mFirstRtpTime.value() > src.mFirstRtpTime.value())
|
if (mFirstRtpTime.value() > src.mFirstRtpTime.value())
|
||||||
mFirstRtpTime = src.mFirstRtpTime;
|
mFirstRtpTime = src.mFirstRtpTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (src.mFirstRtpTime)
|
if (src.mFirstRtpTime.is_initialized())
|
||||||
mFirstRtpTime = src.mFirstRtpTime;
|
mFirstRtpTime = src.mFirstRtpTime;
|
||||||
|
|
||||||
mBitrateSwitchCounter += src.mBitrateSwitchCounter;
|
mBitrateSwitchCounter += src.mBitrateSwitchCounter;
|
||||||
|
|
@ -203,8 +231,8 @@ Statistics& Statistics::operator -= (const Statistics& src)
|
||||||
mOldRtp -= src.mOldRtp;
|
mOldRtp -= src.mOldRtp;
|
||||||
mPacketLoss -= src.mPacketLoss;
|
mPacketLoss -= src.mPacketLoss;
|
||||||
mPacketDropped -= src.mPacketDropped;
|
mPacketDropped -= src.mPacketDropped;
|
||||||
mAudioTime -= src.mAudioTime;
|
|
||||||
|
|
||||||
|
mAudioTime -= src.mAudioTime;
|
||||||
for (auto codecStat: src.mCodecCount)
|
for (auto codecStat: src.mCodecCount)
|
||||||
{
|
{
|
||||||
if (mCodecCount.find(codecStat.first) != mCodecCount.end())
|
if (mCodecCount.find(codecStat.first) != mCodecCount.end())
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,15 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "audio/Audio_DataWindow.h"
|
#include "audio/Audio_DataWindow.h"
|
||||||
#include "helper/HL_Optional.hpp"
|
#include "helper/HL_Optional.hpp"
|
||||||
#include "helper/HL_Statistics.h"
|
#include "helper/HL_Statistics.h"
|
||||||
#include "helper/HL_Types.h"
|
|
||||||
|
|
||||||
#include "jrtplib/src/rtptimeutilities.h"
|
#include "jrtplib/src/rtptimeutilities.h"
|
||||||
#include "jrtplib/src/rtppacket.h"
|
#include "jrtplib/src/rtppacket.h"
|
||||||
|
|
||||||
|
using std::experimental::optional;
|
||||||
|
|
||||||
namespace MT
|
namespace MT
|
||||||
{
|
{
|
||||||
|
|
@ -41,7 +39,7 @@ protected:
|
||||||
uint32_t mReceiveTimestamp = 0;
|
uint32_t mReceiveTimestamp = 0;
|
||||||
|
|
||||||
// It is classic jitter value in units
|
// It is classic jitter value in units
|
||||||
std::optional<float> mLastJitter;
|
optional<float> mLastJitter;
|
||||||
|
|
||||||
// Some statistics for jitter value in seconds
|
// Some statistics for jitter value in seconds
|
||||||
TestResult<float> mJitter;
|
TestResult<float> mJitter;
|
||||||
|
|
@ -53,36 +51,39 @@ protected:
|
||||||
class Statistics
|
class Statistics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
size_t mReceived = 0, // Received traffic in bytes
|
size_t mReceived, // Received traffic in bytes
|
||||||
mSent = 0, // Sent traffic in bytes
|
mSent, // Sent traffic in bytes
|
||||||
mReceivedRtp = 0, // Number of received rtp packets
|
mReceivedRtp, // Number of received rtp packets
|
||||||
mSentRtp = 0, // Number of sent rtp packets
|
mSentRtp, // Number of sent rtp packets
|
||||||
mReceivedRtcp = 0, // Number of received rtcp packets
|
mReceivedRtcp, // Number of received rtcp packets
|
||||||
mSentRtcp = 0, // Number of sent rtcp packets
|
mSentRtcp, // Number of sent rtcp packets
|
||||||
mDuplicatedRtp = 0, // Number of received duplicated rtp packets
|
mDuplicatedRtp, // Number of received duplicated rtp packets
|
||||||
mOldRtp = 0, // Number of late rtp packets
|
mOldRtp, // Number of late rtp packets
|
||||||
mPacketLoss = 0, // Number of lost packets
|
mPacketLoss, // Number of lost packets
|
||||||
mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б
|
mPacketDropped, // Number of dropped packets (due to time unsync when playing)б
|
||||||
mIllegalRtp = 0; // Number of rtp packets with bad payload type
|
mIllegalRtp; // Number of rtp packets with bad payload type
|
||||||
|
|
||||||
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
|
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
|
||||||
mDecodeRequested, // Average amount of requested audio frames to play
|
mDecodeRequested, // Average amount of requested audio frames to play
|
||||||
mPacketInterval; // Average interval between packet adding to jitter buffer
|
mPacketInterval; // Average interval between packet adding to jitter buffer
|
||||||
|
|
||||||
std::array<float, 128> mLoss = {0}; // Every item is number of loss of corresping length
|
int mLoss[128]; // Every item is number of loss of corresping length
|
||||||
size_t mAudioTime = 0; // Decoded/found time in milliseconds
|
size_t mAudioTime; // Decoded/found time in milliseconds
|
||||||
size_t mDecodedSize = 0; // Number of decoded bytes
|
size_t mDecodedSize; // Number of decoded bytes
|
||||||
uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream
|
uint16_t mSsrc; // Last known SSRC ID in a RTP stream
|
||||||
ice::NetworkAddress mRemotePeer; // Last known remote RTP address
|
ice::NetworkAddress mRemotePeer; // Last known remote RTP address
|
||||||
|
|
||||||
// AMR codec bitrate switch counter
|
// AMR codec bitrate switch counter
|
||||||
int mBitrateSwitchCounter = 0;
|
int mBitrateSwitchCounter;
|
||||||
|
|
||||||
std::string mCodecName;
|
std::string mCodecName;
|
||||||
float mJitter = 0.0f; // Jitter
|
|
||||||
|
float mJitter; // Jitter
|
||||||
|
|
||||||
TestResult<float> mRttDelay; // RTT delay
|
TestResult<float> mRttDelay; // RTT delay
|
||||||
|
|
||||||
// Timestamp when first RTP packet has arrived
|
// Timestamp when first RTP packet has arrived
|
||||||
std::optional<timepoint_t> mFirstRtpTime;
|
optional<std::chrono::system_clock::time_point> mFirstRtpTime;
|
||||||
|
|
||||||
std::map<int, int> mCodecCount; // Stats on used codecs
|
std::map<int, int> mCodecCount; // Stats on used codecs
|
||||||
|
|
||||||
|
|
@ -97,7 +98,11 @@ public:
|
||||||
Statistics& operator += (const Statistics& src);
|
Statistics& operator += (const Statistics& src);
|
||||||
Statistics& operator -= (const Statistics& src);
|
Statistics& operator -= (const Statistics& src);
|
||||||
|
|
||||||
float mNetworkMos = 0.0f;
|
float mNetworkMos = 0.0;
|
||||||
|
#if defined(USE_PVQA_LIBRARY) && !defined(PVQA_SERVER)
|
||||||
|
float mPvqaMos = 0.0;
|
||||||
|
std::string mPvqaReport;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "MT_WebRtc.h"
|
#include "MT_WebRtc.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# Run manually to reformat a file:
|
|
||||||
# clang-format -i --style=file <file>
|
|
||||||
Language: Cpp
|
|
||||||
BasedOnStyle: Google
|
|
||||||
IndentPPDirectives: AfterHash
|
|
||||||
IndentCaseLabels: false
|
|
||||||
AlwaysBreakTemplateDeclarations: false
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<!--
|
|
||||||
Please make sure that the problem reproduces on the current master before
|
|
||||||
submitting an issue.
|
|
||||||
If possible please provide a repro on Compiler Explorer:
|
|
||||||
https://godbolt.org/z/fxccbh53W.
|
|
||||||
-->
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<!--
|
|
||||||
Please read the contribution guidelines before submitting a pull request:
|
|
||||||
https://github.com/fmtlib/fmt/blob/master/CONTRIBUTING.md.
|
|
||||||
By submitting this pull request, you agree that your contributions are licensed
|
|
||||||
under the {fmt} license, and agree to future changes to the licensing.
|
|
||||||
-->
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
name: doc
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
# Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken.
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Create Build Environment
|
|
||||||
run: |
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install doxygen python3-virtualenv
|
|
||||||
sudo npm install -g less clean-css
|
|
||||||
cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
env:
|
|
||||||
KEY: ${{secrets.KEY}}
|
|
||||||
run: $GITHUB_WORKSPACE/support/build-docs.py
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
name: linux
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
cxx: [g++-4.8, g++-10, clang++-9]
|
|
||||||
build_type: [Debug, Release]
|
|
||||||
std: [11]
|
|
||||||
os: [ubuntu-18.04]
|
|
||||||
include:
|
|
||||||
- cxx: g++-4.8
|
|
||||||
install: sudo apt install g++-4.8
|
|
||||||
os: ubuntu-18.04
|
|
||||||
- cxx: g++-8
|
|
||||||
build_type: Debug
|
|
||||||
std: 14
|
|
||||||
install: sudo apt install g++-8
|
|
||||||
os: ubuntu-18.04
|
|
||||||
- cxx: g++-8
|
|
||||||
build_type: Debug
|
|
||||||
std: 17
|
|
||||||
install: sudo apt install g++-8
|
|
||||||
os: ubuntu-18.04
|
|
||||||
- cxx: g++-10
|
|
||||||
build_type: Debug
|
|
||||||
std: 17
|
|
||||||
os: ubuntu-18.04
|
|
||||||
- cxx: g++-11
|
|
||||||
build_type: Debug
|
|
||||||
std: 20
|
|
||||||
os: ubuntu-20.04
|
|
||||||
install: sudo apt install g++-11
|
|
||||||
- cxx: clang++-8
|
|
||||||
build_type: Debug
|
|
||||||
std: 17
|
|
||||||
cxxflags: -stdlib=libc++
|
|
||||||
os: ubuntu-18.04
|
|
||||||
install: sudo apt install clang-8 libc++-8-dev libc++abi-8-dev
|
|
||||||
- cxx: clang++-9
|
|
||||||
build_type: Debug
|
|
||||||
fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
|
|
||||||
std: 17
|
|
||||||
os: ubuntu-18.04
|
|
||||||
- cxx: clang++-11
|
|
||||||
build_type: Debug
|
|
||||||
std: 20
|
|
||||||
os: ubuntu-20.04
|
|
||||||
- cxx: clang++-11
|
|
||||||
build_type: Debug
|
|
||||||
std: 20
|
|
||||||
cxxflags: -stdlib=libc++
|
|
||||||
os: ubuntu-20.04
|
|
||||||
install: sudo apt install libc++-11-dev libc++abi-11-dev
|
|
||||||
- shared: -DBUILD_SHARED_LIBS=ON
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Create Build Environment
|
|
||||||
run: |
|
|
||||||
${{matrix.install}}
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install locales-all
|
|
||||||
cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
env:
|
|
||||||
CXX: ${{matrix.cxx}}
|
|
||||||
CXXFLAGS: ${{matrix.cxxflags}}
|
|
||||||
run: |
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.fuzz}} ${{matrix.shared}} \
|
|
||||||
-DCMAKE_CXX_STANDARD=${{matrix.std}} -DFMT_DOC=OFF \
|
|
||||||
-DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \
|
|
||||||
-DFMT_PEDANTIC=ON -DFMT_WERROR=ON $GITHUB_WORKSPACE
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
threads=`nproc`
|
|
||||||
cmake --build . --config ${{matrix.build_type}} --parallel $threads
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: ctest -C ${{matrix.build_type}}
|
|
||||||
env:
|
|
||||||
CTEST_OUTPUT_ON_FAILURE: True
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
name: macos
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: macos-10.15
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
build_type: [Debug, Release]
|
|
||||||
include:
|
|
||||||
- shared: -DBUILD_SHARED_LIBS=ON
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Create Build Environment
|
|
||||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.shared}} \
|
|
||||||
-DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON \
|
|
||||||
-DFMT_DOC=OFF -DFMT_PEDANTIC=ON -DFMT_WERROR=ON $GITHUB_WORKSPACE
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
threads=`sysctl -n hw.logicalcpu`
|
|
||||||
cmake --build . --config ${{matrix.build_type}} --parallel $threads
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: ctest -C ${{matrix.build_type}}
|
|
||||||
env:
|
|
||||||
CTEST_OUTPUT_ON_FAILURE: True
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
name: windows
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ${{matrix.os}}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
# windows-2019 has MSVC 2019 installed;
|
|
||||||
# windows-2022 has MSVC 2022 installed:
|
|
||||||
# https://github.com/actions/virtual-environments.
|
|
||||||
os: [windows-2019]
|
|
||||||
platform: [Win32, x64]
|
|
||||||
toolset: [v140, v141, v142]
|
|
||||||
standard: [14, 17, 20]
|
|
||||||
shared: ["", -DBUILD_SHARED_LIBS=ON]
|
|
||||||
build_type: [Debug, Release]
|
|
||||||
exclude:
|
|
||||||
- { toolset: v140, standard: 17 }
|
|
||||||
- { toolset: v140, standard: 20 }
|
|
||||||
- { toolset: v141, standard: 14 }
|
|
||||||
- { toolset: v141, standard: 20 }
|
|
||||||
- { toolset: v142, standard: 14 }
|
|
||||||
- { platform: Win32, toolset: v140 }
|
|
||||||
- { platform: Win32, toolset: v141 }
|
|
||||||
- { platform: Win32, standard: 14 }
|
|
||||||
- { platform: Win32, standard: 20 }
|
|
||||||
- { platform: x64, toolset: v140, shared: -DBUILD_SHARED_LIBS=ON }
|
|
||||||
- { platform: x64, toolset: v141, shared: -DBUILD_SHARED_LIBS=ON }
|
|
||||||
- { platform: x64, standard: 14, shared: -DBUILD_SHARED_LIBS=ON }
|
|
||||||
- { platform: x64, standard: 20, shared: -DBUILD_SHARED_LIBS=ON }
|
|
||||||
include:
|
|
||||||
- os: windows-2022
|
|
||||||
platform: x64
|
|
||||||
toolset: v143
|
|
||||||
build_type: Debug
|
|
||||||
standard: 20
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Create Build Environment
|
|
||||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
# Use a bash shell for $GITHUB_WORKSPACE.
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
cmake -A ${{matrix.platform}} -T ${{matrix.toolset}} \
|
|
||||||
-DCMAKE_CXX_STANDARD=${{matrix.standard}} \
|
|
||||||
${{matrix.shared}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
|
||||||
$GITHUB_WORKSPACE
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
$threads = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
|
|
||||||
cmake --build . --config ${{matrix.build_type}} --parallel $threads
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: ctest -C ${{matrix.build_type}} -V
|
|
||||||
env:
|
|
||||||
CTEST_OUTPUT_ON_FAILURE: True
|
|
||||||
|
|
||||||
mingw:
|
|
||||||
runs-on: windows-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: msys2 {0}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
sys: [ mingw64, mingw32, ucrt64 ]
|
|
||||||
steps:
|
|
||||||
- uses: msys2/setup-msys2@v2
|
|
||||||
with:
|
|
||||||
release: false
|
|
||||||
msystem: ${{matrix.sys}}
|
|
||||||
pacboy: cc:p cmake:p ninja:p lld:p
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Configure
|
|
||||||
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
env: { LDFLAGS: -fuse-ld=lld }
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build ../build
|
|
||||||
- name: Test
|
|
||||||
run: ctest -j `nproc` --test-dir ../build
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
.vscode/
|
|
||||||
.vs/
|
|
||||||
|
|
||||||
*.iml
|
|
||||||
.idea/
|
|
||||||
.externalNativeBuild/
|
|
||||||
.gradle/
|
|
||||||
gradle/
|
|
||||||
gradlew*
|
|
||||||
local.properties
|
|
||||||
build/
|
|
||||||
support/.cxx
|
|
||||||
|
|
||||||
bin/
|
|
||||||
/_CPack_Packages
|
|
||||||
/CMakeScripts
|
|
||||||
/doc/doxyxml
|
|
||||||
/doc/html
|
|
||||||
/doc/node_modules
|
|
||||||
virtualenv
|
|
||||||
/Testing
|
|
||||||
/install_manifest.txt
|
|
||||||
*~
|
|
||||||
*.a
|
|
||||||
*.so*
|
|
||||||
*.xcodeproj
|
|
||||||
*.zip
|
|
||||||
cmake_install.cmake
|
|
||||||
CPack*.cmake
|
|
||||||
fmt-*.cmake
|
|
||||||
CTestTestfile.cmake
|
|
||||||
CMakeCache.txt
|
|
||||||
CMakeFiles
|
|
||||||
FMT.build
|
|
||||||
Makefile
|
|
||||||
run-msbuild.bat
|
|
||||||
fmt.pc
|
|
||||||
|
|
@ -1,387 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.1...3.18)
|
|
||||||
|
|
||||||
# Fallback for using newer policies on CMake <3.12.
|
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Determine if fmt is built as a subproject (using add_subdirectory)
|
|
||||||
# or if it is the master project.
|
|
||||||
if (NOT DEFINED FMT_MASTER_PROJECT)
|
|
||||||
set(FMT_MASTER_PROJECT OFF)
|
|
||||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
|
||||||
set(FMT_MASTER_PROJECT ON)
|
|
||||||
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Joins arguments and places the results in ${result_var}.
|
|
||||||
function(join result_var)
|
|
||||||
set(result "")
|
|
||||||
foreach (arg ${ARGN})
|
|
||||||
set(result "${result}${arg}")
|
|
||||||
endforeach ()
|
|
||||||
set(${result_var} "${result}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
function(enable_module target)
|
|
||||||
if (MSVC)
|
|
||||||
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
|
|
||||||
target_compile_options(${target}
|
|
||||||
PRIVATE /interface /ifcOutput ${BMI}
|
|
||||||
INTERFACE /reference fmt=${BMI})
|
|
||||||
endif ()
|
|
||||||
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
|
|
||||||
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
include(CMakeParseArguments)
|
|
||||||
|
|
||||||
# Sets a cache variable with a docstring joined from multiple arguments:
|
|
||||||
# set(<variable> <value>... CACHE <type> <docstring>...)
|
|
||||||
# This allows splitting a long docstring for readability.
|
|
||||||
function(set_verbose)
|
|
||||||
# cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
|
|
||||||
# list instead.
|
|
||||||
list(GET ARGN 0 var)
|
|
||||||
list(REMOVE_AT ARGN 0)
|
|
||||||
list(GET ARGN 0 val)
|
|
||||||
list(REMOVE_AT ARGN 0)
|
|
||||||
list(REMOVE_AT ARGN 0)
|
|
||||||
list(GET ARGN 0 type)
|
|
||||||
list(REMOVE_AT ARGN 0)
|
|
||||||
join(doc ${ARGN})
|
|
||||||
set(${var} ${val} CACHE ${type} ${doc})
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Set the default CMAKE_BUILD_TYPE to Release.
|
|
||||||
# This should be done before the project command since the latter can set
|
|
||||||
# CMAKE_BUILD_TYPE itself (it does so for nmake).
|
|
||||||
if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
|
|
||||||
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
|
|
||||||
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
|
|
||||||
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
project(FMT CXX)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
|
|
||||||
"Installation directory for include files, a relative path that "
|
|
||||||
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
|
|
||||||
|
|
||||||
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
|
||||||
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
|
|
||||||
OFF)
|
|
||||||
|
|
||||||
# Options that control generation of various targets.
|
|
||||||
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
|
|
||||||
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT})
|
|
||||||
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
|
|
||||||
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
|
||||||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
|
||||||
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
|
||||||
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
|
|
||||||
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
|
|
||||||
|
|
||||||
set(FMT_CAN_MODULE OFF)
|
|
||||||
if (CMAKE_CXX_STANDARD GREATER 17 AND
|
|
||||||
# msvc 16.10-pre4
|
|
||||||
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
|
|
||||||
set(FMT_CAN_MODULE OFF)
|
|
||||||
endif ()
|
|
||||||
if (NOT FMT_CAN_MODULE)
|
|
||||||
set(FMT_MODULE OFF)
|
|
||||||
message(STATUS "Module support is disabled.")
|
|
||||||
endif ()
|
|
||||||
if (FMT_TEST AND FMT_MODULE)
|
|
||||||
# The tests require {fmt} to be compiled as traditional library
|
|
||||||
message(STATUS "Testing is incompatible with build mode 'module'.")
|
|
||||||
endif ()
|
|
||||||
set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
|
|
||||||
if (FMT_SYSTEM_HEADERS)
|
|
||||||
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Get version from core.h
|
|
||||||
file(READ include/fmt/core.h core_h)
|
|
||||||
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
|
|
||||||
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.")
|
|
||||||
endif ()
|
|
||||||
# Use math to skip leading zeros if any.
|
|
||||||
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
|
||||||
math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
|
|
||||||
math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
|
||||||
join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.
|
|
||||||
${CPACK_PACKAGE_VERSION_PATCH})
|
|
||||||
message(STATUS "Version: ${FMT_VERSION}")
|
|
||||||
|
|
||||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
|
||||||
|
|
||||||
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
|
|
||||||
|
|
||||||
include(cxx14)
|
|
||||||
include(JoinPaths)
|
|
||||||
|
|
||||||
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
|
|
||||||
if (${index} GREATER -1)
|
|
||||||
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
|
|
||||||
# compatibility with older CMake versions.
|
|
||||||
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
|
|
||||||
endif ()
|
|
||||||
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
|
|
||||||
|
|
||||||
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET)
|
|
||||||
set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING
|
|
||||||
"Preset for the export of private symbols")
|
|
||||||
set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS
|
|
||||||
hidden default)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN)
|
|
||||||
set_verbose(CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL
|
|
||||||
"Whether to add a compile flag to hide symbols of inline functions")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|
||||||
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
|
||||||
-Wold-style-cast -Wundef
|
|
||||||
-Wredundant-decls -Wwrite-strings -Wpointer-arith
|
|
||||||
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
|
||||||
-Wcast-align
|
|
||||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
|
||||||
-Winvalid-pch -Woverloaded-virtual
|
|
||||||
-Wconversion -Wundef
|
|
||||||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
|
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
|
||||||
-Wno-dangling-else -Wno-unused-local-typedefs)
|
|
||||||
endif ()
|
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
|
|
||||||
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
|
|
||||||
-Wvector-operation-performance -Wsized-deallocation -Wshadow)
|
|
||||||
endif ()
|
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
|
|
||||||
-Wnull-dereference -Wduplicated-cond)
|
|
||||||
endif ()
|
|
||||||
set(WERROR_FLAG -Werror)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
|
|
||||||
-Wdeprecated -Wweak-vtables -Wshadow
|
|
||||||
-Wno-gnu-zero-variadic-macro-arguments)
|
|
||||||
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
|
|
||||||
if (HAS_NULLPTR_WARNING)
|
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
|
||||||
-Wzero-as-null-pointer-constant)
|
|
||||||
endif ()
|
|
||||||
set(WERROR_FLAG -Werror)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
set(PEDANTIC_COMPILE_FLAGS /W3)
|
|
||||||
set(WERROR_FLAG /WX)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
|
||||||
# If Microsoft SDK is installed create script run-msbuild.bat that
|
|
||||||
# calls SetEnv.cmd to set up build environment and runs msbuild.
|
|
||||||
# It is useful when building Visual Studio projects with the SDK
|
|
||||||
# toolchain rather than Visual Studio.
|
|
||||||
include(FindSetEnv)
|
|
||||||
if (WINSDK_SETENV)
|
|
||||||
set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
|
|
||||||
endif ()
|
|
||||||
# Set FrameworkPathOverride to get rid of MSB3644 warnings.
|
|
||||||
join(netfxpath
|
|
||||||
"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
|
|
||||||
".NETFramework\\v4.0")
|
|
||||||
file(WRITE run-msbuild.bat "
|
|
||||||
${MSBUILD_SETUP}
|
|
||||||
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
function(add_headers VAR)
|
|
||||||
set(headers ${${VAR}})
|
|
||||||
foreach (header ${ARGN})
|
|
||||||
set(headers ${headers} include/fmt/${header})
|
|
||||||
endforeach()
|
|
||||||
set(${VAR} ${headers} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Define the fmt library, its includes and the needed defines.
|
|
||||||
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
|
|
||||||
format-inl.h os.h ostream.h printf.h ranges.h std.h
|
|
||||||
xchar.h)
|
|
||||||
if (FMT_MODULE)
|
|
||||||
set(FMT_SOURCES src/fmt.cc)
|
|
||||||
elseif (FMT_OS)
|
|
||||||
set(FMT_SOURCES src/format.cc src/os.cc)
|
|
||||||
else()
|
|
||||||
set(FMT_SOURCES src/format.cc)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
|
||||||
add_library(fmt::fmt ALIAS fmt)
|
|
||||||
|
|
||||||
if (FMT_WERROR)
|
|
||||||
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
|
|
||||||
endif ()
|
|
||||||
if (FMT_PEDANTIC)
|
|
||||||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
|
||||||
endif ()
|
|
||||||
if (FMT_MODULE)
|
|
||||||
enable_module(fmt)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
|
||||||
|
|
||||||
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
|
||||||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
|
||||||
|
|
||||||
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
|
|
||||||
|
|
||||||
set_target_properties(fmt PROPERTIES
|
|
||||||
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
|
|
||||||
PUBLIC_HEADER "${FMT_HEADERS}"
|
|
||||||
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
|
|
||||||
|
|
||||||
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
|
|
||||||
# property because it's not set by default.
|
|
||||||
set(FMT_LIB_NAME fmt)
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (BUILD_SHARED_LIBS)
|
|
||||||
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
|
|
||||||
endif ()
|
|
||||||
if (FMT_SAFE_DURATION_CAST)
|
|
||||||
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(fmt-header-only INTERFACE)
|
|
||||||
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
|
|
||||||
|
|
||||||
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
|
||||||
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
|
|
||||||
|
|
||||||
target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
|
||||||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
|
||||||
|
|
||||||
# Install targets.
|
|
||||||
if (FMT_INSTALL)
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
|
||||||
"Installation directory for cmake files, a relative path that "
|
|
||||||
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute "
|
|
||||||
"path.")
|
|
||||||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
|
||||||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
|
||||||
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
|
||||||
set(targets_export_name fmt-targets)
|
|
||||||
|
|
||||||
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
|
||||||
"Installation directory for libraries, a relative path that "
|
|
||||||
"will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.")
|
|
||||||
|
|
||||||
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
|
|
||||||
"Installation directory for pkgconfig (.pc) files, a relative "
|
|
||||||
"path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
|
|
||||||
"absolute path.")
|
|
||||||
|
|
||||||
# Generate the version, config and target files into the build directory.
|
|
||||||
write_basic_package_version_file(
|
|
||||||
${version_config}
|
|
||||||
VERSION ${FMT_VERSION}
|
|
||||||
COMPATIBILITY AnyNewerVersion)
|
|
||||||
|
|
||||||
join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}")
|
|
||||||
join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}")
|
|
||||||
|
|
||||||
configure_file(
|
|
||||||
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
|
|
||||||
"${pkgconfig}"
|
|
||||||
@ONLY)
|
|
||||||
configure_package_config_file(
|
|
||||||
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
|
|
||||||
${project_config}
|
|
||||||
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
|
||||||
|
|
||||||
set(INSTALL_TARGETS fmt fmt-header-only)
|
|
||||||
|
|
||||||
# Install the library and headers.
|
|
||||||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
|
||||||
LIBRARY DESTINATION ${FMT_LIB_DIR}
|
|
||||||
ARCHIVE DESTINATION ${FMT_LIB_DIR}
|
|
||||||
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
|
|
||||||
FRAMEWORK DESTINATION "."
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
||||||
|
|
||||||
# Use a namespace because CMake provides better diagnostics for namespaced
|
|
||||||
# imported targets.
|
|
||||||
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
|
||||||
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
|
|
||||||
|
|
||||||
# Install version, config and target files.
|
|
||||||
install(
|
|
||||||
FILES ${project_config} ${version_config}
|
|
||||||
DESTINATION ${FMT_CMAKE_DIR})
|
|
||||||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
|
||||||
NAMESPACE fmt::)
|
|
||||||
|
|
||||||
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
|
|
||||||
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
|
||||||
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (FMT_DOC)
|
|
||||||
add_subdirectory(doc)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (FMT_TEST)
|
|
||||||
enable_testing()
|
|
||||||
add_subdirectory(test)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Control fuzzing independent of the unit tests.
|
|
||||||
if (FMT_FUZZ)
|
|
||||||
add_subdirectory(test/fuzzing)
|
|
||||||
|
|
||||||
# The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing
|
|
||||||
# mode and make fuzzing practically possible. It is similar to
|
|
||||||
# FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to
|
|
||||||
# avoid interfering with fuzzing of projects that use {fmt}.
|
|
||||||
# See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode.
|
|
||||||
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
|
||||||
if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
|
|
||||||
# Get the list of ignored files from .gitignore.
|
|
||||||
file (STRINGS ${gitignore} lines)
|
|
||||||
list(REMOVE_ITEM lines /doc/html)
|
|
||||||
foreach (line ${lines})
|
|
||||||
string(REPLACE "." "[.]" line "${line}")
|
|
||||||
string(REPLACE "*" ".*" line "${line}")
|
|
||||||
set(ignored_files ${ignored_files} "${line}$" "${line}/")
|
|
||||||
endforeach ()
|
|
||||||
set(ignored_files ${ignored_files}
|
|
||||||
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
|
|
||||||
|
|
||||||
set(CPACK_SOURCE_GENERATOR ZIP)
|
|
||||||
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})
|
|
||||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION})
|
|
||||||
set(CPACK_PACKAGE_NAME fmt)
|
|
||||||
set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst)
|
|
||||||
include(CPack)
|
|
||||||
endif ()
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
Contributing to {fmt}
|
|
||||||
=====================
|
|
||||||
|
|
||||||
By submitting a pull request or a patch, you represent that you have the right
|
|
||||||
to license your contribution to the {fmt} project owners and the community,
|
|
||||||
agree that your contributions are licensed under the {fmt} license, and agree
|
|
||||||
to future changes to the licensing.
|
|
||||||
|
|
||||||
All C++ code must adhere to [Google C++ Style Guide](
|
|
||||||
https://google.github.io/styleguide/cppguide.html) with the following
|
|
||||||
exceptions:
|
|
||||||
|
|
||||||
* Exceptions are permitted
|
|
||||||
* snake_case should be used instead of UpperCamelCase for function and type
|
|
||||||
names
|
|
||||||
|
|
||||||
All documentation must adhere to the [Google Developer Documentation Style
|
|
||||||
Guide](https://developers.google.com/style).
|
|
||||||
|
|
||||||
Thanks for contributing!
|
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue