Compare commits
131 Commits
resiprocat
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
9a4823e6a9 | |
|
|
c3deb8378d | |
|
|
e0f07b6f7a | |
|
|
a7993c8d20 | |
|
|
1559190bcc | |
|
|
786cefed45 | |
|
|
186ec4ccb4 | |
|
|
ffd371d6e7 | |
|
|
f90d64b279 | |
|
|
cf27012d27 | |
|
|
e267415700 | |
|
|
3e87b9e7ec | |
|
|
3f9dbda40a | |
|
|
eec5aabc42 | |
|
|
4e00f659e3 | |
|
|
1599bfc9fc | |
|
|
6df835bb95 | |
|
|
fd878f0d0e | |
|
|
706339ec97 | |
|
|
a35db35b48 | |
|
|
46de58ccf4 | |
|
|
f891caedcc | |
|
|
cba8e24622 | |
|
|
84cc39e669 | |
|
|
916de57660 | |
|
|
e8bebc14c2 | |
|
|
a74f8a6cdf | |
|
|
dd242dfa48 | |
|
|
190d4fde54 | |
|
|
d105e582f4 | |
|
|
d4a47807d8 | |
|
|
e6cb2a22f7 | |
|
|
6b7b086bf7 | |
|
|
5a05d9d40b | |
|
|
48f6bf21d9 | |
|
|
6f55c91c7a | |
|
|
906a422865 | |
|
|
375bf64275 | |
|
|
b8c107fb5b | |
|
|
178ebac88a | |
|
|
6b6f4147db | |
|
|
011919f846 | |
|
|
94fea9d5a9 | |
|
|
028ce8b982 | |
|
|
5ddd7bbeed | |
|
|
e90ae17212 | |
|
|
1ada2a9159 | |
|
|
64d2209b02 | |
|
|
f9c009c40e | |
|
|
c67319838f | |
|
|
ebc9c3220e | |
|
|
9dc59d5ed6 | |
|
|
8b33a3a7e6 | |
|
|
d852fdde3a | |
|
|
f63fba6c6a | |
|
|
80451a1a32 | |
|
|
474256bc88 | |
|
|
fadaf3541f | |
|
|
efa31427ca | |
|
|
83373cb586 | |
|
|
8d0c8ba4de | |
|
|
a0b7cdcd99 | |
|
|
933d52b297 | |
|
|
efbf9c5bf8 | |
|
|
f30e111ebe | |
|
|
a7176fefeb | |
|
|
caa2239da1 | |
|
|
93bf339f00 | |
|
|
7510ff5697 | |
|
|
5ce85e3a09 | |
|
|
34e43bf301 | |
|
|
84d11d3b04 | |
|
|
cedc44e24c | |
|
|
6fef519a4b | |
|
|
f8dd94dfa4 | |
|
|
c45c686582 | |
|
|
b5d0242c74 | |
|
|
4ba1ea7fa2 | |
|
|
bf2d513080 | |
|
|
fe52356799 | |
|
|
0e044faf16 | |
|
|
4d7ca3e4f3 | |
|
|
9afbd9e0fd | |
|
|
d895385d19 | |
|
|
cde5cca940 | |
|
|
34c28e35e1 | |
|
|
e0b02db39d | |
|
|
8cb34fad83 | |
|
|
f0c37b1bb1 | |
|
|
5bf8d665e2 | |
|
|
f3ebe11a42 | |
|
|
3a81626b8e | |
|
|
10ec751e43 | |
|
|
7c346fbe9b | |
|
|
caf1283f0a | |
|
|
2c2167c840 | |
|
|
d9b8f4d2d0 | |
|
|
62e4828241 | |
|
|
81bc17fbe2 | |
|
|
5a0a7e1070 | |
|
|
20c2ac3e63 | |
|
|
d08b2e27a5 | |
|
|
4cff4a0988 | |
|
|
5e6b4bb929 | |
|
|
7aadde3a09 | |
|
|
62f2d996c6 | |
|
|
05e3cc8cb6 | |
|
|
5030064925 | |
|
|
9f875b5f15 | |
|
|
4ca4e84547 | |
|
|
cf9be1c3f2 | |
|
|
8a851e5e86 | |
|
|
62d72fda5c | |
|
|
37471d56ff | |
|
|
69374d2973 | |
|
|
5eb4bf2690 | |
|
|
677faa0615 | |
|
|
8ec6245496 | |
|
|
4ce7f50964 | |
|
|
3f33c86949 | |
|
|
c924329203 | |
|
|
75c4888e43 | |
|
|
2eb0d17fba | |
|
|
8dd494157f | |
|
|
22653e6241 | |
|
|
fa053324fa | |
|
|
978079fd55 | |
|
|
ace77323d2 | |
|
|
6ff23247ec | |
|
|
9ef74113b3 | |
|
|
3beeac2d0c |
|
|
@ -0,0 +1,12 @@
|
|||
[submodule "src/libs/resiprocate"]
|
||||
path = src/libs/resiprocate
|
||||
url = git@git.sevana.biz:public/resiprocate.git
|
||||
branch = sevana
|
||||
|
||||
[submodule "src/libs/libsrtp"]
|
||||
path = src/libs/libsrtp
|
||||
url = git@git.sevana.biz:public/libsrtp.git
|
||||
branch = master
|
||||
[submodule "src/libs/libraries"]
|
||||
path = src/libs/libraries
|
||||
url = git@git.sevana.biz:public/libraries.git
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
# RTPhone Platform
|
||||
|
||||
RTPhone is a comprehensive real-time communication (RTC) platform that provides a complete software stack for building VoIP/SIP-based communication applications. Developed by VoIP Objects (Sevana), RTPhone delivers production-ready voice communication capabilities with extensive codec support and cross-platform compatibility.
|
||||
|
||||
## Overview
|
||||
|
||||
RTPhone serves as a static library (`librtphone.a`) that can be integrated into larger telephony and communication systems. It provides a JSON-based command interface for easy integration and control, making it suitable for building softphones, PBX systems, WebRTC gateways, and carrier-grade voice solutions.
|
||||
|
||||
## Key Features
|
||||
|
||||
### Audio Codec Support
|
||||
RTPhone supports an extensive range of audio codecs:
|
||||
|
||||
**Standard Codecs:**
|
||||
- G.711 (A-law/¼-law)
|
||||
- G.722 (16kHz wideband)
|
||||
- G.729
|
||||
- GSM (Full Rate, Half Rate, Enhanced Full Rate)
|
||||
- iLBC (20ms/30ms)
|
||||
- ISAC (16kHz/32kHz)
|
||||
|
||||
**Advanced Codecs:**
|
||||
- AMR-NB/AMR-WB (Adaptive Multi-Rate Narrowband/Wideband) - please be aware - there is no patents for AMR codecs usage included ! You should acquire them on your own.
|
||||
- EVS (Enhanced Voice Services) - 3GPP's latest codec. Again - please be aware - there is no patents for EVS codec usage included ! You should acquire them on your own.
|
||||
- Opus - Modern low-latency codec
|
||||
- Speex (with acoustic echo cancellation)
|
||||
|
||||
**Codec Features:**
|
||||
- Bandwidth-efficient and octet-aligned modes
|
||||
- IuUP (Iu User Plane) protocol support for 3G networks
|
||||
- Dynamic codec switching
|
||||
- Packet loss concealment (PLC)
|
||||
- Comfort noise generation (CNG)
|
||||
|
||||
### Network & Protocol Support
|
||||
|
||||
**SIP Features:**
|
||||
- Full SIP 2.0 implementation via reSIProcate
|
||||
- Multiple transport protocols (UDP, TCP, TLS)
|
||||
- Registration, authentication, and session management
|
||||
- SIP MESSAGE, presence, and REFER support
|
||||
|
||||
**Media Transport:**
|
||||
- RTP/RTCP for media streaming
|
||||
- SRTP for secure media
|
||||
- ICE for NAT traversal with STUN/TURN support
|
||||
- WebRTC integration components
|
||||
- IPv4 and IPv6 support
|
||||
|
||||
### Cross-Platform Audio Support
|
||||
- DirectSound/WMME (Windows)
|
||||
- Core Audio (macOS/iOS)
|
||||
- ALSA/PulseAudio (Linux)
|
||||
- Oboe (Android) for low-latency audio
|
||||
- PortAudio fallback support
|
||||
|
||||
### Audio Quality Features
|
||||
- 48kHz sample rate support
|
||||
- Acoustic Echo Cancellation (AEC)
|
||||
- Audio resampling and format conversion
|
||||
- Multi-channel audio mixing
|
||||
- Perceptual Voice Quality Assessment (PVQA)
|
||||
|
||||
## Architecture
|
||||
|
||||
The platform is organized into several core modules:
|
||||
|
||||
- **Engine/Agent**: JSON-based command interface
|
||||
- **Engine/Endpoint**: SIP user agent implementation
|
||||
- **Engine/Media**: Audio codec management and processing
|
||||
- **Engine/Audio**: Cross-platform audio I/O handling
|
||||
- **Engine/Helper**: Utility functions (networking, logging, threading)
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Linux (x64, ARM/Raspberry Pi)
|
||||
- Windows (32/64-bit)
|
||||
- macOS
|
||||
- Android (with Oboe integration)
|
||||
- iOS
|
||||
|
||||
## Building
|
||||
|
||||
RTPhone uses a CMake-based build system with cross-compilation support:
|
||||
|
||||
### Linux
|
||||
```bash
|
||||
python3 build_linux.py
|
||||
```
|
||||
|
||||
### Android
|
||||
```bash
|
||||
python3 build_android.py
|
||||
# or
|
||||
./build_android.sh
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
- CMake 3.10+
|
||||
- OpenSSL 1.1+
|
||||
- Boost libraries
|
||||
- Platform-specific audio libraries
|
||||
|
||||
## Recent Updates
|
||||
|
||||
Recent development has focused on:
|
||||
- AMR codec parsing and decoding improvements
|
||||
- Octet-aligned mode fixes for AMR-WB
|
||||
- RTP SSRC handling enhancements
|
||||
- Build system optimizations
|
||||
- Code modernization to C++20
|
||||
|
||||
## Use Cases
|
||||
|
||||
RTPhone is good for building:
|
||||
- VoIP softphones and mobile applications
|
||||
- PBX and telephony server systems
|
||||
- RTP proxies
|
||||
- Carrier-grade voice communication platforms
|
||||
- 3GPP/IMS-compliant systems
|
||||
|
||||
## Security
|
||||
|
||||
RTPhone includes comprehensive security features:
|
||||
- OpenSSL 1.1 integration for encryption
|
||||
- TLS transport layer security
|
||||
- SRTP media encryption
|
||||
- Certificate management support
|
||||
|
||||
## Integration
|
||||
|
||||
The platform provides a developer-friendly interface with:
|
||||
- Event-driven architecture
|
||||
- Comprehensive logging system
|
||||
- Modern C++20 codebase
|
||||
|
||||
For detailed integration instructions and API documentation, please refer to the source code and header files in the `/src/engine/` directory.
|
||||
|
||||
## License
|
||||
|
||||
Our source code is licensed under the MPL license. Naturally, any third-party components we use are subject to their respective licenses.
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/python3
|
||||
from pathlib import Path
|
||||
import os
|
||||
import multiprocessing
|
||||
import shutil
|
||||
|
||||
# Temporary build directory
|
||||
DIR_BUILD = 'build_android'
|
||||
|
||||
# Android NDK home directory
|
||||
NDK_HOME = os.environ['ANDROID_NDK_HOME']
|
||||
|
||||
# CMake toolchain file
|
||||
TOOLCHAIN_FILE = f'{NDK_HOME}/build/cmake/android.toolchain.cmake'
|
||||
|
||||
# This directory
|
||||
DIR_THIS = Path(__file__).parent.resolve()
|
||||
|
||||
# Path to app
|
||||
DIR_SOURCE = (DIR_THIS / '../src').resolve()
|
||||
|
||||
def make_build() -> Path:
|
||||
if Path(DIR_BUILD).exists():
|
||||
shutil.rmtree(DIR_BUILD)
|
||||
os.mkdir(DIR_BUILD)
|
||||
os.chdir(DIR_BUILD)
|
||||
|
||||
cmd = f'cmake -DCMAKE_TOOLCHAIN_FILE={TOOLCHAIN_FILE} '
|
||||
cmd += f'-DANDROID_NDK=$NDK_HOME '
|
||||
cmd += f'-DANDROID_PLATFORM=24 '
|
||||
cmd += f'-DCMAKE_BUILD=Release '
|
||||
cmd += f'-DANDROID_ABI="arm64-v8a" '
|
||||
cmd += '../src'
|
||||
retcode = os.system(cmd)
|
||||
if retcode != 0:
|
||||
raise RuntimeError('Problem when configuring the project')
|
||||
|
||||
cmd = f'cmake --build . -j {multiprocessing.cpu_count()}'
|
||||
retcode = os.system(cmd)
|
||||
if retcode != 0:
|
||||
raise RuntimeError('Problem when building the project')
|
||||
|
||||
if __name__ == '__main__':
|
||||
make_build()
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
import os
|
||||
import multiprocessing
|
||||
import shutil
|
||||
|
||||
# Temporary build directory
|
||||
DIR_BUILD = 'build_linux'
|
||||
|
||||
# This directory
|
||||
DIR_THIS = Path(__file__).parent.resolve()
|
||||
|
||||
# Path to app
|
||||
DIR_SOURCE = (DIR_THIS / '../src').resolve()
|
||||
|
||||
def make_build() -> Path:
|
||||
if Path(DIR_BUILD).exists():
|
||||
shutil.rmtree(DIR_BUILD)
|
||||
os.mkdir(DIR_BUILD)
|
||||
os.chdir(DIR_BUILD)
|
||||
|
||||
cmd = f'cmake ../src -G Ninja'
|
||||
retcode = os.system(cmd)
|
||||
if retcode != 0:
|
||||
raise RuntimeError('Problem when configuring the project')
|
||||
|
||||
cmd = f'cmake --build . -j {multiprocessing.cpu_count()}'
|
||||
retcode = os.system(cmd)
|
||||
if retcode != 0:
|
||||
raise RuntimeError('Problem when building the project')
|
||||
|
||||
os.chdir('..')
|
||||
return Path(DIR_BUILD) / 'librtphone.a'
|
||||
|
||||
if __name__ == '__main__':
|
||||
p = make_build()
|
||||
print (f'Built: {p}')
|
||||
|
|
@ -1,61 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
project(rtphone)
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
macro(configure_msvc_runtime)
|
||||
if(MSVC)
|
||||
# Default to statically-linked runtime.
|
||||
if("${MSVC_RUNTIME}" STREQUAL "")
|
||||
set(MSVC_RUNTIME "static")
|
||||
endif()
|
||||
# Set compiler options.
|
||||
set(variables
|
||||
CMAKE_C_FLAGS_DEBUG
|
||||
CMAKE_C_FLAGS_MINSIZEREL
|
||||
CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS_DEBUG
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
)
|
||||
if(${MSVC_RUNTIME} STREQUAL "static")
|
||||
message(STATUS
|
||||
"rtphone: MSVC -> forcing use of statically-linked runtime."
|
||||
)
|
||||
foreach(variable ${variables})
|
||||
if(${variable} MATCHES "/MD")
|
||||
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS
|
||||
"rtphone: MSVC -> forcing use of dynamically-linked runtime."
|
||||
)
|
||||
foreach(variable ${variables})
|
||||
if(${variable} MATCHES "/MT")
|
||||
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(variable ${variables})
|
||||
string(REGEX REPLACE "/Z[iI7]" ""
|
||||
${variable}
|
||||
"${${variable}}")
|
||||
|
||||
set(${variable} "${${variable}} /Zi /Oy-")
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
# Rely on C++ 17
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
# Rely on C++ 20
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set (rtphone_libs libs)
|
||||
set (rtphone_engine engine)
|
||||
set (L libs)
|
||||
set (E engine)
|
||||
|
||||
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
|
||||
option (USE_EVS_CODEC "Use EVS codec." ON)
|
||||
|
|
@ -64,149 +15,347 @@ option (USE_MUSL "Build with MUSL library" OFF)
|
|||
|
||||
# PIC code by default
|
||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set (RUNTIME_CPU_CAPABILITY_DETECTION ON)
|
||||
|
||||
if (NOT DEFINED LIB_PLATFORM)
|
||||
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries)
|
||||
endif()
|
||||
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/libs/libraries)
|
||||
include (${LIB_PLATFORM}/platform_libs.cmake)
|
||||
|
||||
|
||||
message("Libraries: ${LIB_PLATFORM}")
|
||||
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.1/include)
|
||||
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
||||
message ("Using OpenSSL libs: ${OPENSSL_SSL} and ${OPENSSL_CRYPTO}")
|
||||
add_definitions(-DUSE_OPENSSL)
|
||||
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*")
|
||||
add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
||||
set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
||||
set (TARGET_WIN ON)
|
||||
endif()
|
||||
|
||||
# Linux-specific definitions
|
||||
if (CMAKE_SYSTEM MATCHES "Linux*")
|
||||
add_definitions (-DTARGET_LINUX -DHAVE_NETINET_IN_H)
|
||||
set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H)
|
||||
set (TARGET_LINUX ON)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
||||
endif()
|
||||
|
||||
|
||||
# macOS-specific definitions
|
||||
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
||||
add_definitions (-DTARGET_OSX)
|
||||
set (DEFINES ${DEFINES} -DTARGET_OSX)
|
||||
set (TARGET_OSX ON)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
||||
endif()
|
||||
|
||||
#
|
||||
if (CMAKE_SYSTEM MATCHES "Android")
|
||||
message("Adding the Oboe library")
|
||||
set (OBOE_DIR libs/oboe)
|
||||
add_subdirectory (${OBOE_DIR} ./oboe)
|
||||
include_directories (${OBOE_DIR}/include)
|
||||
add_definitions(-DTARGET_ANDROID -DHAVE_NETINET_IN_H)
|
||||
set (DEFINES ${DEFINES} -DTARGET_ANDROID -DHAVE_NETINET_IN_H)
|
||||
set (TARGET_ANDROID ON)
|
||||
set (LIBS_STATIC ${LIBS} oboe)
|
||||
endif()
|
||||
|
||||
if (USE_MUSL)
|
||||
add_definitions(-DTARGET_MUSL)
|
||||
set (DEFINES ${DEFINES} -DTARGET_MUSL)
|
||||
set (TARGET_MUSL ON)
|
||||
endif()
|
||||
|
||||
set (RTPHONE_SOURCES
|
||||
${rtphone_engine}/media/MT_Statistics.cpp
|
||||
${rtphone_engine}/media/MT_WebRtc.cpp
|
||||
${rtphone_engine}/media/MT_Stream.cpp
|
||||
${rtphone_engine}/media/MT_SrtpHelper.cpp
|
||||
${rtphone_engine}/media/MT_SingleAudioStream.cpp
|
||||
${rtphone_engine}/media/MT_NativeRtpSender.cpp
|
||||
${rtphone_engine}/media/MT_Dtmf.cpp
|
||||
${rtphone_engine}/media/MT_CodecList.cpp
|
||||
${rtphone_engine}/media/MT_Codec.cpp
|
||||
${rtphone_engine}/media/MT_Box.cpp
|
||||
${rtphone_engine}/media/MT_AudioStream.cpp
|
||||
${rtphone_engine}/media/MT_AudioReceiver.cpp
|
||||
${rtphone_engine}/media/MT_AudioCodec.cpp
|
||||
${rtphone_engine}/media/MT_CngHelper.cpp
|
||||
${rtphone_engine}/agent/Agent_Impl.cpp
|
||||
${rtphone_engine}/agent/Agent_Impl.h
|
||||
${rtphone_engine}/agent/Agent_AudioManager.cpp
|
||||
${rtphone_engine}/agent/Agent_AudioManager.h
|
||||
${rtphone_engine}/endpoint/EP_Account.cpp
|
||||
${rtphone_engine}/endpoint/EP_Account.h
|
||||
${rtphone_engine}/endpoint/EP_AudioProvider.cpp
|
||||
${rtphone_engine}/endpoint/EP_AudioProvider.h
|
||||
${rtphone_engine}/endpoint/EP_DataProvider.cpp
|
||||
${rtphone_engine}/endpoint/EP_DataProvider.h
|
||||
${rtphone_engine}/endpoint/EP_Engine.cpp
|
||||
${rtphone_engine}/endpoint/EP_Engine.h
|
||||
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
|
||||
${rtphone_engine}/endpoint/EP_NetworkQueue.h
|
||||
${rtphone_engine}/endpoint/EP_Observer.cpp
|
||||
${rtphone_engine}/endpoint/EP_Observer.h
|
||||
${rtphone_engine}/endpoint/EP_Session.cpp
|
||||
${rtphone_engine}/endpoint/EP_Session.h
|
||||
${E}/engine_config.h
|
||||
${E}/media/MT_Statistics.cpp
|
||||
${E}/media/MT_WebRtc.cpp
|
||||
${E}/media/MT_Stream.cpp
|
||||
${E}/media/MT_SrtpHelper.cpp
|
||||
${E}/media/MT_SingleAudioStream.cpp
|
||||
${E}/media/MT_NativeRtpSender.cpp
|
||||
${E}/media/MT_Dtmf.cpp
|
||||
${E}/media/MT_CodecList.cpp
|
||||
${E}/media/MT_Codec.cpp
|
||||
${E}/media/MT_Box.cpp
|
||||
${E}/media/MT_AudioStream.cpp
|
||||
${E}/media/MT_AudioReceiver.cpp
|
||||
${E}/media/MT_AudioCodec.cpp
|
||||
${E}/media/MT_CngHelper.cpp
|
||||
${E}/agent/Agent_Impl.cpp
|
||||
${E}/agent/Agent_Impl.h
|
||||
${E}/agent/Agent_AudioManager.cpp
|
||||
${E}/agent/Agent_AudioManager.h
|
||||
${E}/endpoint/EP_Account.cpp
|
||||
${E}/endpoint/EP_Account.h
|
||||
${E}/endpoint/EP_AudioProvider.cpp
|
||||
${E}/endpoint/EP_AudioProvider.h
|
||||
${E}/endpoint/EP_DataProvider.cpp
|
||||
${E}/endpoint/EP_DataProvider.h
|
||||
${E}/endpoint/EP_Engine.cpp
|
||||
${E}/endpoint/EP_Engine.h
|
||||
${E}/endpoint/EP_NetworkQueue.cpp
|
||||
${E}/endpoint/EP_NetworkQueue.h
|
||||
${E}/endpoint/EP_Observer.cpp
|
||||
${E}/endpoint/EP_Observer.h
|
||||
${E}/endpoint/EP_Session.cpp
|
||||
${E}/endpoint/EP_Session.h
|
||||
|
||||
${rtphone_engine}/media/MT_Statistics.h
|
||||
${rtphone_engine}/media/MT_WebRtc.h
|
||||
${rtphone_engine}/media/MT_Stream.h
|
||||
${rtphone_engine}/media/MT_SrtpHelper.h
|
||||
${rtphone_engine}/media/MT_SingleAudioStream.h
|
||||
${rtphone_engine}/media/MT_NativeRtpSender.h
|
||||
${rtphone_engine}/media/MT_Dtmf.h
|
||||
${rtphone_engine}/media/MT_CodecList.h
|
||||
${rtphone_engine}/media/MT_Codec.h
|
||||
${rtphone_engine}/media/MT_Box.h
|
||||
${rtphone_engine}/media/MT_AudioStream.h
|
||||
${rtphone_engine}/media/MT_AudioReceiver.h
|
||||
${rtphone_engine}/media/MT_AudioCodec.h
|
||||
${rtphone_engine}/media/MT_CngHelper.h
|
||||
${E}/media/MT_Statistics.h
|
||||
${E}/media/MT_WebRtc.h
|
||||
${E}/media/MT_Stream.h
|
||||
${E}/media/MT_SrtpHelper.h
|
||||
${E}/media/MT_SingleAudioStream.h
|
||||
${E}/media/MT_NativeRtpSender.h
|
||||
${E}/media/MT_Dtmf.h
|
||||
${E}/media/MT_CodecList.h
|
||||
${E}/media/MT_Codec.h
|
||||
${E}/media/MT_Box.h
|
||||
${E}/media/MT_AudioStream.h
|
||||
${E}/media/MT_AudioReceiver.h
|
||||
${E}/media/MT_AudioCodec.h
|
||||
${E}/media/MT_CngHelper.h
|
||||
|
||||
${E}/media/MT_Statistics.cpp
|
||||
${E}/media/MT_WebRtc.cpp
|
||||
${E}/media/MT_Stream.cpp
|
||||
${E}/media/MT_SrtpHelper.cpp
|
||||
${E}/media/MT_SingleAudioStream.cpp
|
||||
${E}/media/MT_NativeRtpSender.cpp
|
||||
${E}/media/MT_Dtmf.cpp
|
||||
${E}/media/MT_CodecList.cpp
|
||||
${E}/media/MT_Codec.cpp
|
||||
${E}/media/MT_Box.cpp
|
||||
${E}/media/MT_AudioStream.cpp
|
||||
${E}/media/MT_AudioReceiver.cpp
|
||||
${E}/media/MT_AudioCodec.cpp
|
||||
${E}/media/MT_CngHelper.cpp
|
||||
${E}/media/MT_AmrCodec.cpp
|
||||
${E}/media/MT_EvsCodec.cpp
|
||||
${E}/media/MT_Statistics.h
|
||||
${E}/media/MT_WebRtc.h
|
||||
${E}/media/MT_Stream.h
|
||||
${E}/media/MT_SrtpHelper.h
|
||||
${E}/media/MT_SingleAudioStream.h
|
||||
${E}/media/MT_NativeRtpSender.h
|
||||
${E}/media/MT_Dtmf.h
|
||||
${E}/media/MT_CodecList.h
|
||||
${E}/media/MT_Codec.h
|
||||
${E}/media/MT_Box.h
|
||||
${E}/media/MT_AudioStream.h
|
||||
${E}/media/MT_AudioReceiver.h
|
||||
${E}/media/MT_AudioCodec.h
|
||||
${E}/media/MT_CngHelper.h
|
||||
${E}/media/MT_AmrCodec.h
|
||||
${E}/media/MT_EvsCodec.h
|
||||
|
||||
${E}/helper/HL_AsyncCommand.cpp
|
||||
${E}/helper/HL_AsyncCommand.h
|
||||
${E}/helper/HL_Base64.h
|
||||
${E}/helper/HL_ByteBuffer.h
|
||||
${E}/helper/HL_Calculator.cpp
|
||||
${E}/helper/HL_Calculator.h
|
||||
${E}/helper/HL_CrashRpt.cpp
|
||||
${E}/helper/HL_CrashRpt.h
|
||||
${E}/helper/HL_CsvReader.cpp
|
||||
${E}/helper/HL_CsvReader.h
|
||||
${E}/helper/HL_Epoll.cpp
|
||||
${E}/helper/HL_Epoll.h
|
||||
${E}/helper/HL_Exception.h
|
||||
${E}/helper/HL_File.cpp
|
||||
${E}/helper/HL_File.h
|
||||
${E}/helper/HL_HepSupport.cpp
|
||||
${E}/helper/HL_HepSupport.h
|
||||
${E}/helper/HL_InternetAddress.h
|
||||
${E}/helper/HL_IuUP.cpp
|
||||
${E}/helper/HL_IuUP.h
|
||||
${E}/helper/HL_Log.cpp
|
||||
${E}/helper/HL_Log.h
|
||||
${E}/helper/HL_NetworkFrame.cpp
|
||||
${E}/helper/HL_NetworkFrame.h
|
||||
${E}/helper/HL_NetworkSocket.cpp
|
||||
${E}/helper/HL_NetworkSocket.h
|
||||
${E}/helper/HL_Optional.hpp
|
||||
${E}/helper/HL_OsVersion.cpp
|
||||
${E}/helper/HL_OsVersion.h
|
||||
${E}/helper/HL_Pointer.cpp
|
||||
${E}/helper/HL_Pointer.h
|
||||
${E}/helper/HL_Process.cpp
|
||||
${E}/helper/HL_Process.h
|
||||
${E}/helper/HL_Rtp.cpp
|
||||
${E}/helper/HL_Rtp.h
|
||||
${E}/helper/HL_Singletone.cpp
|
||||
${E}/helper/HL_Singletone.h
|
||||
${E}/helper/HL_SocketHeap.cpp
|
||||
${E}/helper/HL_SocketHeap.h
|
||||
${E}/helper/HL_Statistics.cpp
|
||||
${E}/helper/HL_Statistics.h
|
||||
${E}/helper/HL_StreamState.h
|
||||
${E}/helper/HL_String.cpp
|
||||
${E}/helper/HL_String.h
|
||||
${E}/helper/HL_Sync.cpp
|
||||
${E}/helper/HL_Sync.h
|
||||
${E}/helper/HL_ThreadPool.cpp
|
||||
${E}/helper/HL_ThreadPool.h
|
||||
${E}/helper/HL_Time.cpp
|
||||
${E}/helper/HL_Time.h
|
||||
${E}/helper/HL_Types.h
|
||||
${E}/helper/HL_Types.cpp
|
||||
${E}/helper/HL_Usb.cpp
|
||||
${E}/helper/HL_Usb.h
|
||||
${E}/helper/HL_Uuid.cpp
|
||||
${E}/helper/HL_Uuid.h
|
||||
${E}/helper/HL_VariantMap.cpp
|
||||
${E}/helper/HL_VariantMap.h
|
||||
${E}/helper/HL_Xcap.cpp
|
||||
${E}/helper/HL_Xcap.h
|
||||
|
||||
${E}/audio/Audio_Resampler.cpp
|
||||
${E}/audio/Audio_Resampler.h
|
||||
${E}/audio/Audio_Quality.cpp
|
||||
${E}/audio/Audio_Quality.h
|
||||
${E}/audio/Audio_Mixer.cpp
|
||||
${E}/audio/Audio_Mixer.h
|
||||
${E}/audio/Audio_Interface.cpp
|
||||
${E}/audio/Audio_Interface.h
|
||||
${E}/audio/Audio_Helper.cpp
|
||||
${E}/audio/Audio_Helper.h
|
||||
${E}/audio/Audio_DataWindow.cpp
|
||||
${E}/audio/Audio_DataWindow.h
|
||||
${E}/audio/Audio_DevicePair.cpp
|
||||
${E}/audio/Audio_DevicePair.h
|
||||
${E}/audio/Audio_Player.cpp
|
||||
${E}/audio/Audio_Player.h
|
||||
${E}/audio/Audio_Null.cpp
|
||||
${E}/audio/Audio_Null.h
|
||||
${E}/audio/Audio_CoreAudio.cpp
|
||||
${E}/audio/Audio_CoreAudio.h
|
||||
${E}/audio/Audio_DirectSound.cpp
|
||||
${E}/audio/Audio_DirectSound.h
|
||||
${E}/audio/Audio_AndroidOboe.cpp
|
||||
${E}/audio/Audio_AndroidOboe.h
|
||||
${E}/audio/Audio_WavFile.cpp
|
||||
${E}/audio/Audio_WavFile.h
|
||||
|
||||
${L}/ice/hmac_sha1_impl.cpp
|
||||
${L}/ice/hmac_sha1_impl.h
|
||||
${L}/ice/ICEAction.h
|
||||
${L}/ice/ICEAddress.cpp
|
||||
${L}/ice/ICEAddress.h
|
||||
${L}/ice/ICEAuthTransaction.cpp
|
||||
${L}/ice/ICEAuthTransaction.h
|
||||
${L}/ice/ICEBinding.cpp
|
||||
${L}/ice/ICEBinding.h
|
||||
${L}/ice/ICEBox.cpp
|
||||
${L}/ice/ICEBox.h
|
||||
${L}/ice/ICEBoxImpl.cpp
|
||||
${L}/ice/ICEBoxImpl.h
|
||||
${L}/ice/ICEByteBuffer.cpp
|
||||
${L}/ice/ICEByteBuffer.h
|
||||
${L}/ice/ICECandidate.cpp
|
||||
${L}/ice/ICECandidate.h
|
||||
${L}/ice/ICECandidatePair.cpp
|
||||
${L}/ice/ICECandidatePair.h
|
||||
${L}/ice/ICECheckList.cpp
|
||||
${L}/ice/ICECheckList.h
|
||||
${L}/ice/ICECRC32.cpp
|
||||
${L}/ice/ICECRC32.h
|
||||
${L}/ice/ICEError.cpp
|
||||
${L}/ice/ICEError.h
|
||||
${L}/ice/ICEEvent.h
|
||||
${L}/ice/ICELog.cpp
|
||||
${L}/ice/ICELog.h
|
||||
${L}/ice/ICEMD5.cpp
|
||||
${L}/ice/ICEMD5.h
|
||||
${L}/ice/ICENetworkHelper.cpp
|
||||
${L}/ice/ICENetworkHelper.h
|
||||
${L}/ice/ICEPacketTimer.cpp
|
||||
${L}/ice/ICEPacketTimer.h
|
||||
${L}/ice/ICEPlatform.cpp
|
||||
${L}/ice/ICEPlatform.h
|
||||
${L}/ice/ICERelaying.cpp
|
||||
${L}/ice/ICERelaying.h
|
||||
${L}/ice/ICESession.cpp
|
||||
${L}/ice/ICESession.h
|
||||
${L}/ice/ICESHA1.cpp
|
||||
${L}/ice/ICESHA1.h
|
||||
${L}/ice/ICESocket.h
|
||||
${L}/ice/ICEStream.cpp
|
||||
${L}/ice/ICEStream.h
|
||||
${L}/ice/ICEStunAttributes.cpp
|
||||
${L}/ice/ICEStunAttributes.h
|
||||
${L}/ice/ICEStunConfig.cpp
|
||||
${L}/ice/ICEStunConfig.h
|
||||
${L}/ice/ICEStunMessage.cpp
|
||||
${L}/ice/ICEStunMessage.h
|
||||
${L}/ice/ICEStunTransaction.cpp
|
||||
${L}/ice/ICEStunTransaction.h
|
||||
${L}/ice/ICESync.cpp
|
||||
${L}/ice/ICESync.h
|
||||
${L}/ice/ICETime.cpp
|
||||
${L}/ice/ICETime.h
|
||||
${L}/ice/ICETransactionList.cpp
|
||||
${L}/ice/ICETransactionList.h
|
||||
${L}/ice/ICETypes.h
|
||||
${L}/ice/md5_impl.cpp
|
||||
${L}/ice/md5_impl.h
|
||||
)
|
||||
|
||||
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)
|
||||
add_definitions(-DUSE_OPUS_CODEC)
|
||||
set (DEFINES ${DEFINES} -DUSE_OPUS_CODEC)
|
||||
endif()
|
||||
|
||||
add_library (rtphone STATIC ${RTPHONE_SOURCES})
|
||||
|
||||
add_subdirectory(${rtphone_libs}/resiprocate)
|
||||
add_subdirectory(${rtphone_libs}/ice)
|
||||
add_subdirectory(${rtphone_libs}/jrtplib/src)
|
||||
add_subdirectory(${rtphone_libs}/libg729)
|
||||
add_subdirectory(${L}/resiprocate)
|
||||
add_subdirectory(${L}/jrtplib/src)
|
||||
add_subdirectory(${L}/libg729)
|
||||
|
||||
if (USE_EVS_CODEC)
|
||||
add_subdirectory(${rtphone_libs}/libevs)
|
||||
add_subdirectory(${L}/libevs)
|
||||
endif()
|
||||
|
||||
add_subdirectory(${rtphone_libs}/libgsm)
|
||||
add_subdirectory(${rtphone_libs}/gsmhr)
|
||||
add_subdirectory(${rtphone_libs}/g722)
|
||||
add_subdirectory(${rtphone_libs}/speexdsp)
|
||||
add_subdirectory(${rtphone_libs}/srtp)
|
||||
add_subdirectory(${rtphone_libs}/webrtc)
|
||||
add_subdirectory(${rtphone_engine}/helper)
|
||||
add_subdirectory(${rtphone_engine}/audio)
|
||||
add_subdirectory(${rtphone_engine}/media)
|
||||
add_subdirectory(${L}/libgsm)
|
||||
add_subdirectory(${L}/gsmhr)
|
||||
add_subdirectory(${L}/g722)
|
||||
add_subdirectory(${L}/speexdsp)
|
||||
add_subdirectory(${L}/libsrtp)
|
||||
add_subdirectory(${L}/webrtc)
|
||||
add_subdirectory(${L}/opus)
|
||||
|
||||
set (LIBS ice_stack jrtplib g729_codec gsm_codec
|
||||
gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
|
||||
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Win*")
|
||||
set (LIBS ${LIBS} )
|
||||
else ()
|
||||
set (LIBS ${LIBS} dl)
|
||||
endif ()
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Android")
|
||||
set (LIBS ${LIBS} oboe)
|
||||
if (USE_AMR_CODEC)
|
||||
include (${LIB_PLATFORM}/platform_libs.cmake)
|
||||
message("Media: AMR NB and WB codecs will be included.")
|
||||
set (DEFINES ${DEFINES} -DUSE_AMR_CODEC)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
||||
endif()
|
||||
|
||||
if (USE_EVS_CODEC)
|
||||
message("Media: EVS codec will be included.")
|
||||
set (DEFINES ${DEFINES} -DUSE_EVS_CODEC)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} evs_codec)
|
||||
endif()
|
||||
|
||||
target_link_libraries(rtphone
|
||||
${LIBS}
|
||||
${OPENSSL_SSL}
|
||||
${OPENSSL_CRYPTO}
|
||||
)
|
||||
target_compile_definitions(rtphone PUBLIC ${DEFINES})
|
||||
|
||||
if (TARGET_LINUX)
|
||||
target_link_options(rtphone PUBLIC -Wl,-Bstatic)
|
||||
endif()
|
||||
target_link_libraries(rtphone PUBLIC ${LIBS_STATIC} ${OPENSSL_SSL} ${OPENSSL_CRYPTO})
|
||||
|
||||
if (TARGET_LINUX)
|
||||
target_link_options(rtphone PUBLIC -Wl,-Bdynamic)
|
||||
endif()
|
||||
|
||||
target_include_directories(rtphone
|
||||
PUBLIC
|
||||
|
|
@ -214,15 +363,18 @@ target_include_directories(rtphone
|
|||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
||||
${LIB_PLATFORM}/opus/include
|
||||
${E}/helper
|
||||
${E}/audio
|
||||
${E}/media
|
||||
${L}
|
||||
${L}/ice
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
|
||||
${L}/libevs/lib_com
|
||||
${L}/libevs/lib_enc
|
||||
${L}/libevs/lib_dec
|
||||
${L}/speex/include
|
||||
${L}/libs/json
|
||||
)
|
||||
|
||||
# For MSVC static builds
|
||||
configure_msvc_runtime()
|
||||
# set_property(TARGET rtphone PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "Agent_AudioManager.h"
|
||||
#include "../engine/audio/Audio_WavFile.h"
|
||||
#include "../engine/audio/Audio_Null.h"
|
||||
#include "HL_String.h"
|
||||
|
||||
#if defined(TARGET_ANDROID)
|
||||
# include "../engine/audio/Audio_Android.h"
|
||||
|
|
@ -15,7 +16,7 @@
|
|||
|
||||
|
||||
AudioManager::AudioManager()
|
||||
:mTerminal(nullptr), mAudioMonitoring(nullptr)
|
||||
:mTerminal(nullptr), mAudioMonitoring(nullptr)
|
||||
{
|
||||
mPlayer.setDelegate(this);
|
||||
}
|
||||
|
|
@ -171,7 +172,7 @@ void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarg
|
|||
// Check if file exists
|
||||
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
||||
#ifdef TARGET_WIN
|
||||
r->open(StringHelper::makeTstring(path));
|
||||
r->open(strx::makeTstring(path));
|
||||
#else
|
||||
r->open(path);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -481,41 +481,6 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &ans
|
|||
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)] = strx::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 = strx::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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef __AUDIO_DSOUND_H
|
||||
#define __AUDIO_DSOUND_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
|
|
|||
|
|
@ -29,10 +29,6 @@ TimeSource::TimeSource(int quantTime, int nrOfQuants)
|
|||
mTailTime = 0;
|
||||
}
|
||||
|
||||
TimeSource::~TimeSource()
|
||||
{
|
||||
}
|
||||
|
||||
void TimeSource::start()
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
|
|
@ -107,7 +103,7 @@ unsigned TimeSource::time()
|
|||
|
||||
// --- StubTimer ---
|
||||
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
||||
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false)
|
||||
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false), mCurrentTime(0)
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace Audio
|
|||
|
||||
public:
|
||||
TimeSource(int quantTime, int nrOfQuants);
|
||||
~TimeSource();
|
||||
~TimeSource() = default;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#define __AUDIO_INTERFACE_H
|
||||
|
||||
#include <string>
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "../helper/HL_Types.h"
|
||||
#include "../helper/HL_VariantMap.h"
|
||||
#include "../helper/HL_Pointer.h"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef _RX_MIXER_H
|
||||
#define _RX_MIXER_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "../helper/HL_ByteBuffer.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "Audio_Resampler.h"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "Audio_Null.h"
|
||||
#include "helper/HL_Log.h"
|
||||
#include <assert.h>
|
||||
#include <chrono>
|
||||
#define LOG_SUBSYSTEM "NULL audio"
|
||||
|
||||
using namespace Audio;
|
||||
|
|
@ -59,7 +60,7 @@ NullInputDevice::NullInputDevice()
|
|||
|
||||
NullInputDevice::~NullInputDevice()
|
||||
{
|
||||
close();
|
||||
internalClose();
|
||||
}
|
||||
|
||||
bool NullInputDevice::open()
|
||||
|
|
@ -72,7 +73,7 @@ bool NullInputDevice::open()
|
|||
return true;
|
||||
}
|
||||
|
||||
void NullInputDevice::close()
|
||||
void NullInputDevice::internalClose()
|
||||
{
|
||||
mTimer.reset();
|
||||
if (mBuffer)
|
||||
|
|
@ -83,6 +84,10 @@ void NullInputDevice::close()
|
|||
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||
}
|
||||
|
||||
void NullInputDevice::close()
|
||||
{
|
||||
internalClose();
|
||||
}
|
||||
Format NullInputDevice::getFormat()
|
||||
{
|
||||
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
||||
|
|
@ -105,7 +110,7 @@ NullOutputDevice::NullOutputDevice()
|
|||
|
||||
NullOutputDevice::~NullOutputDevice()
|
||||
{
|
||||
close();
|
||||
internalClose();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -118,13 +123,18 @@ bool NullOutputDevice::open()
|
|||
return true;
|
||||
}
|
||||
|
||||
void NullOutputDevice::close()
|
||||
void NullOutputDevice::internalClose()
|
||||
{
|
||||
mTimer.reset();
|
||||
free(mBuffer); mBuffer = nullptr;
|
||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||
}
|
||||
|
||||
void NullOutputDevice::close()
|
||||
{
|
||||
internalClose();
|
||||
}
|
||||
|
||||
Format NullOutputDevice::getFormat()
|
||||
{
|
||||
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ namespace Audio
|
|||
void* mBuffer = nullptr;
|
||||
std::shared_ptr<NullTimer> mTimer;
|
||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||
void internalClose();
|
||||
|
||||
public:
|
||||
NullInputDevice();
|
||||
virtual ~NullInputDevice();
|
||||
|
|
@ -55,6 +57,8 @@ namespace Audio
|
|||
std::shared_ptr<NullTimer> mTimer;
|
||||
void* mBuffer = nullptr;
|
||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||
|
||||
void internalClose();
|
||||
public:
|
||||
NullOutputDevice();
|
||||
virtual ~NullOutputDevice();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "Audio_Quality.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
#include "../helper/HL_Types.h"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#ifndef __AUDIO_QUALITY_H
|
||||
#define __AUDIO_QUALITY_H
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include <vector>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "Audio_Resampler.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
|
@ -18,7 +18,7 @@ namespace Audio
|
|||
|
||||
|
||||
SpeexResampler::SpeexResampler()
|
||||
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0)
|
||||
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0), mChannels(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -125,12 +125,13 @@ int SpeexResampler::destRate()
|
|||
|
||||
size_t SpeexResampler::getDestLength(size_t sourceLen)
|
||||
{
|
||||
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f) / 2 * 2;
|
||||
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f);
|
||||
}
|
||||
|
||||
size_t SpeexResampler::getSourceLength(size_t destLen)
|
||||
{
|
||||
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f) / 2 * 2;
|
||||
// Here we want to get 'destLen' number of samples
|
||||
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f);
|
||||
}
|
||||
|
||||
// Returns instance + speex resampler size in bytes
|
||||
|
|
@ -273,7 +274,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
|
|||
PResampler r;
|
||||
if (resamplerIter == mResamplerMap.end())
|
||||
{
|
||||
r = PResampler(new Resampler());
|
||||
r = std::make_shared<Resampler>();
|
||||
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
||||
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include "helper/HL_Exception.h"
|
||||
#include "helper/HL_String.h"
|
||||
#include "helper/HL_Log.h"
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
|
|
@ -40,7 +40,7 @@ using namespace Audio;
|
|||
|
||||
// ---------------------- WavFileReader -------------------------
|
||||
WavFileReader::WavFileReader()
|
||||
:mHandle(nullptr), mSamplerate(0), mLastError(0)
|
||||
:mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
|
||||
{
|
||||
mDataOffset = 0;
|
||||
}
|
||||
|
|
@ -53,38 +53,51 @@ WavFileReader::~WavFileReader()
|
|||
|
||||
std::string WavFileReader::readChunk()
|
||||
{
|
||||
char name[5];
|
||||
if (fread(name, 1, 4, mHandle) != 4)
|
||||
THROW_READERROR;
|
||||
char name[5] = {0};
|
||||
readBuffer(name, 4);
|
||||
|
||||
name[4] = 0;
|
||||
std::string result = name;
|
||||
unsigned size;
|
||||
if (fread(&size, 4, 1, mHandle) != 1)
|
||||
THROW_READERROR;
|
||||
uint32_t size = 0;
|
||||
readBuffer(&size, 4);
|
||||
|
||||
if (result == "fact")
|
||||
fread(&mDataLength, 4, 1, mHandle);
|
||||
{
|
||||
uint32_t dataLength = 0;
|
||||
readBuffer(&dataLength, sizeof dataLength);
|
||||
mDataLength = dataLength;
|
||||
}
|
||||
else
|
||||
if (result != "data")
|
||||
fseek(mHandle, size, SEEK_CUR);
|
||||
mInput->seekg(size, std::ios_base::beg);
|
||||
else
|
||||
mDataLength = size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WavFileReader::open(const std::tstring& filename)
|
||||
void WavFileReader::readBuffer(void* buffer, size_t sz)
|
||||
{
|
||||
auto p = mInput->tellg();
|
||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
||||
if (mInput->tellg() - p != sz)
|
||||
throw Exception(ERR_WAVFILE_FAILED);
|
||||
}
|
||||
|
||||
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
|
||||
{
|
||||
auto p = mInput->tellg();
|
||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
||||
return mInput->tellg() - p;
|
||||
}
|
||||
|
||||
bool WavFileReader::open(const std::filesystem::path& p)
|
||||
{
|
||||
LOCK;
|
||||
try
|
||||
{
|
||||
#ifdef WIN32
|
||||
mHandle = _wfopen(filename.c_str(), L"rb");
|
||||
#else
|
||||
mHandle = fopen(strx::makeUtf8(filename).c_str(), "rb");
|
||||
#endif
|
||||
if (NULL == mHandle)
|
||||
mPath = p;
|
||||
mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in);
|
||||
if (!mInput->is_open())
|
||||
{
|
||||
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
mLastError = errno;
|
||||
|
|
@ -98,75 +111,62 @@ bool WavFileReader::open(const std::tstring& filename)
|
|||
|
||||
// Read the .WAV header
|
||||
char riff[4];
|
||||
if (fread(riff, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
readBuffer(riff, sizeof riff);
|
||||
|
||||
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
|
||||
THROW_READERROR;
|
||||
|
||||
// Read the file size
|
||||
uint32_t filesize = 0;
|
||||
if (fread(&filesize, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
readBuffer(&filesize, sizeof(filesize));
|
||||
|
||||
char wavefmt[9];
|
||||
if (fread(wavefmt, 8, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
wavefmt[8] = 0;
|
||||
char wavefmt[9] = {0};
|
||||
readBuffer(wavefmt, 8);
|
||||
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
||||
THROW_READERROR;
|
||||
|
||||
uint32_t fmtSize = 0;
|
||||
if (fread(&fmtSize, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
readBuffer(&fmtSize, sizeof(fmtSize));
|
||||
|
||||
uint32_t fmtStart = ftell(mHandle);
|
||||
auto fmtStart = mInput->tellg();
|
||||
|
||||
uint16_t formattag = 0;
|
||||
if (fread(&formattag, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
readBuffer(&formattag, sizeof(formattag));
|
||||
|
||||
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
||||
THROW_READERROR;
|
||||
|
||||
if (fread(&mChannels, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
mChannels = 0;
|
||||
readBuffer(&mChannels, sizeof(mChannels));
|
||||
|
||||
mSamplerate = 0;
|
||||
if (fread(&mSamplerate, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
readBuffer(&mSamplerate, sizeof(mSamplerate));
|
||||
|
||||
unsigned int avgbytespersec = 0;
|
||||
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
uint32_t avgbytespersec = 0;
|
||||
readBuffer(&avgbytespersec, sizeof(avgbytespersec));
|
||||
|
||||
unsigned short blockalign = 0;
|
||||
if (fread(&blockalign, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
uint16_t blockalign = 0;
|
||||
readBuffer(&blockalign, sizeof(blockalign));
|
||||
|
||||
mBits = 0;
|
||||
if (fread(&mBits, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
readBuffer(&mBits, sizeof(mBits));
|
||||
|
||||
if (mBits !=8 && mBits != 16)
|
||||
THROW_READERROR;
|
||||
|
||||
// Read the "chunk"
|
||||
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
|
||||
//unsigned pos = ftell(mHandle);
|
||||
// Look for the chunk 'data'
|
||||
mInput->seekg(fmtStart + std::streampos(fmtSize));
|
||||
|
||||
mDataLength = 0;
|
||||
while (readChunk() != "data")
|
||||
;
|
||||
|
||||
mFileName = filename;
|
||||
mDataOffset = ftell(mHandle);
|
||||
|
||||
mDataOffset = mInput->tellg();
|
||||
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fclose(mHandle); mHandle = nullptr;
|
||||
mInput.reset();
|
||||
mLastError = static_cast<unsigned>(-1);
|
||||
}
|
||||
return isOpened();
|
||||
|
|
@ -175,10 +175,7 @@ bool WavFileReader::open(const std::tstring& filename)
|
|||
void WavFileReader::close()
|
||||
{
|
||||
LOCK;
|
||||
|
||||
if (nullptr != mHandle)
|
||||
fclose(mHandle);
|
||||
mHandle = nullptr;
|
||||
mInput.reset();
|
||||
}
|
||||
|
||||
int WavFileReader::samplerate() const
|
||||
|
|
@ -205,30 +202,38 @@ size_t WavFileReader::read(short* buffer, size_t samples)
|
|||
{
|
||||
LOCK;
|
||||
|
||||
if (!mHandle)
|
||||
if (!mInput)
|
||||
return 0;
|
||||
|
||||
// Get number of samples that must be read from source file
|
||||
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
||||
void* temp = alloca(requiredBytes);
|
||||
bool useHeap = requiredBytes > sizeof mTempBuffer;
|
||||
void* temp;
|
||||
if (useHeap)
|
||||
temp = malloc(requiredBytes);
|
||||
else
|
||||
temp = mTempBuffer;
|
||||
|
||||
memset(temp, 0, requiredBytes);
|
||||
|
||||
// Find required size of input buffer
|
||||
if (mDataLength)
|
||||
{
|
||||
unsigned filePosition = ftell(mHandle);
|
||||
auto filePosition = mInput->tellg();
|
||||
|
||||
// Check how much data we can read
|
||||
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
|
||||
}
|
||||
|
||||
size_t readBytes = fread(temp, 1, requiredBytes, mHandle);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -237,54 +242,50 @@ size_t WavFileReader::readRaw(short* buffer, size_t samples)
|
|||
{
|
||||
LOCK;
|
||||
|
||||
if (!mHandle)
|
||||
if (!mInput)
|
||||
return 0;
|
||||
|
||||
// Get number of samples that must be read from source file
|
||||
int requiredBytes = samples * channels() * sizeof(short);
|
||||
size_t requiredBytes = samples * channels() * sizeof(short);
|
||||
|
||||
// Find required size of input buffer
|
||||
if (mDataLength)
|
||||
{
|
||||
unsigned filePosition = ftell(mHandle);
|
||||
auto filePosition = mInput->tellg();
|
||||
|
||||
// Check how much data we can read
|
||||
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
|
||||
}
|
||||
|
||||
size_t readBytes = fread(buffer, 1, requiredBytes, mHandle);
|
||||
|
||||
|
||||
size_t readBytes = tryReadBuffer(buffer, requiredBytes);
|
||||
return readBytes / channels() / sizeof(short);
|
||||
}
|
||||
|
||||
bool WavFileReader::isOpened()
|
||||
{
|
||||
LOCK;
|
||||
|
||||
return (mHandle != 0);
|
||||
if (!mInput)
|
||||
return false;
|
||||
return mInput->is_open();
|
||||
}
|
||||
|
||||
void WavFileReader::rewind()
|
||||
{
|
||||
LOCK;
|
||||
|
||||
if (mHandle)
|
||||
fseek(mHandle, mDataOffset, SEEK_SET);
|
||||
if (mInput)
|
||||
mInput->seekg(mDataOffset);
|
||||
}
|
||||
|
||||
std::tstring WavFileReader::filename() const
|
||||
std::filesystem::path WavFileReader::path() const
|
||||
{
|
||||
LOCK;
|
||||
|
||||
return mFileName;
|
||||
return mPath;
|
||||
}
|
||||
|
||||
size_t WavFileReader::size() const
|
||||
{
|
||||
LOCK;
|
||||
|
||||
return mDataLength;
|
||||
}
|
||||
|
||||
|
|
@ -298,9 +299,8 @@ unsigned WavFileReader::lastError() const
|
|||
#define BITS_PER_CHANNEL 16
|
||||
|
||||
WavFileWriter::WavFileWriter()
|
||||
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||
{
|
||||
}
|
||||
:mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||
{}
|
||||
|
||||
WavFileWriter::~WavFileWriter()
|
||||
{
|
||||
|
|
@ -313,68 +313,74 @@ void WavFileWriter::checkWriteResult(int result)
|
|||
throw Exception(ERR_WAVFILE_FAILED, errno);
|
||||
}
|
||||
|
||||
bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
|
||||
void WavFileWriter::writeBuffer(const void* buffer, size_t sz)
|
||||
{
|
||||
if (!mOutput)
|
||||
return;
|
||||
|
||||
auto p = mOutput->tellp();
|
||||
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
|
||||
if (mOutput->tellp() - p != sz)
|
||||
throw Exception(ERR_WAVFILE_FAILED);
|
||||
}
|
||||
|
||||
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
|
||||
{
|
||||
LOCK;
|
||||
|
||||
close();
|
||||
|
||||
mRate = rate;
|
||||
mSamplerate = samplerate;
|
||||
mChannels = channels;
|
||||
|
||||
#ifdef WIN32
|
||||
mHandle = _wfopen(filename.c_str(), L"wb");
|
||||
#else
|
||||
mHandle = fopen(strx::makeUtf8(filename).c_str(), "wb");
|
||||
#endif
|
||||
if (nullptr == mHandle)
|
||||
mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc);
|
||||
if (!mOutput)
|
||||
{
|
||||
ICELogError(<< "Failed to create .wav file: filename = " << strx::makeUtf8(filename) << " , error = " << errno);
|
||||
int errorcode = errno;
|
||||
ICELogError(<< "Failed to create .wav file: filename = " << p << " , error = " << errorcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the .WAV header
|
||||
const char* riff = "RIFF";
|
||||
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
|
||||
writeBuffer(riff, 4);
|
||||
|
||||
// Write the file size
|
||||
unsigned int filesize = 0;
|
||||
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
|
||||
uint32_t filesize = 0;
|
||||
writeBuffer(&filesize, sizeof filesize);
|
||||
|
||||
const char* wavefmt = "WAVEfmt ";
|
||||
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
|
||||
writeBuffer(wavefmt, 8);
|
||||
|
||||
// Set the format description
|
||||
DWORD dwFmtSize = 16; /*= 16L*/;
|
||||
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
|
||||
uint32_t dwFmtSize = 16; /*= 16L*/;
|
||||
writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
|
||||
|
||||
WaveFormatEx format;
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
|
||||
writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag));
|
||||
|
||||
format.nChannels = mChannels;
|
||||
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
|
||||
writeBuffer(&format.nChannels, sizeof(format.nChannels));
|
||||
|
||||
format.nSamplesPerSec = mRate;
|
||||
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
|
||||
format.nSamplesPerSec = mSamplerate;
|
||||
writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec));
|
||||
|
||||
format.nAvgBytesPerSec = mRate * 2 * mChannels;
|
||||
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
|
||||
format.nAvgBytesPerSec = mSamplerate * 2 * mChannels;
|
||||
writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec));
|
||||
|
||||
format.nBlockAlign = 2 * mChannels;
|
||||
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
|
||||
writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign));
|
||||
|
||||
format.wBitsPerSample = BITS_PER_CHANNEL;
|
||||
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
|
||||
writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample));
|
||||
|
||||
const char* data = "data";
|
||||
checkWriteResult( fwrite(data, 4, 1, mHandle));
|
||||
writeBuffer(data, 4);
|
||||
|
||||
mFileName = filename;
|
||||
mPath = p;
|
||||
mWritten = 0;
|
||||
|
||||
mLengthOffset = ftell(mHandle);
|
||||
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||
mLengthOffset = mOutput->tellp();
|
||||
writeBuffer(&mWritten, sizeof mWritten);
|
||||
|
||||
return isOpened();
|
||||
}
|
||||
|
|
@ -382,51 +388,44 @@ bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
|
|||
void WavFileWriter::close()
|
||||
{
|
||||
LOCK;
|
||||
|
||||
if (mHandle)
|
||||
{
|
||||
fclose(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
mOutput.reset();
|
||||
}
|
||||
|
||||
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
||||
{
|
||||
LOCK;
|
||||
|
||||
if (!mHandle)
|
||||
if (!mOutput)
|
||||
return 0;
|
||||
|
||||
// Seek the end of file
|
||||
fseek(mHandle, 0, SEEK_END);
|
||||
// Seek the end of file - here new data will be written
|
||||
mOutput->seekp(0, std::ios_base::end);
|
||||
mWritten += bytes;
|
||||
|
||||
// Write the data
|
||||
fwrite(buffer, bytes, 1, mHandle);
|
||||
writeBuffer(buffer, bytes);
|
||||
|
||||
// Write file length
|
||||
fseek(mHandle, 4, SEEK_SET);
|
||||
int32_t fl = mWritten + 36;
|
||||
fwrite(&fl, sizeof(fl), 1, mHandle);
|
||||
mOutput->seekp(4, std::ios_base::beg);
|
||||
uint32_t fl = mWritten + 36;
|
||||
writeBuffer(&fl, sizeof(fl));
|
||||
|
||||
// Write data length
|
||||
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
|
||||
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||
mOutput->seekp(mLengthOffset, std::ios_base::beg);
|
||||
writeBuffer(&mWritten, sizeof(mWritten));
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool WavFileWriter::isOpened()
|
||||
bool WavFileWriter::isOpened() const
|
||||
{
|
||||
LOCK;
|
||||
|
||||
return (mHandle != nullptr);
|
||||
return mOutput.get();
|
||||
}
|
||||
|
||||
std::tstring WavFileWriter::filename()
|
||||
std::filesystem::path WavFileWriter::path() const
|
||||
{
|
||||
LOCK;
|
||||
|
||||
return mFileName;
|
||||
return mPath;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,32 +12,37 @@
|
|||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
|
||||
class WavFileReader
|
||||
{
|
||||
protected:
|
||||
FILE* mHandle;
|
||||
uint16_t mChannels;
|
||||
uint16_t mBits;
|
||||
int mSamplerate;
|
||||
std::tstring mFileName;
|
||||
mutable std::recursive_mutex
|
||||
mFileMtx;
|
||||
size_t mDataOffset;
|
||||
size_t mDataLength;
|
||||
class WavFileReader
|
||||
{
|
||||
protected:
|
||||
uint16_t mChannels = 0;
|
||||
uint16_t mBits = 0;
|
||||
int mSamplerate = 0;
|
||||
std::filesystem::path mPath;
|
||||
mutable std::recursive_mutex mFileMtx;
|
||||
size_t mDataOffset = 0;
|
||||
size_t mDataLength = 0;
|
||||
Resampler mResampler;
|
||||
unsigned mLastError;
|
||||
unsigned mLastError = 0;
|
||||
std::unique_ptr<std::ifstream> mInput;
|
||||
uint8_t mTempBuffer[16384];
|
||||
|
||||
std::string readChunk();
|
||||
public:
|
||||
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:
|
||||
WavFileReader();
|
||||
~WavFileReader();
|
||||
|
||||
bool open(const std::tstring& filename);
|
||||
bool open(const std::filesystem::path& p);
|
||||
void close();
|
||||
bool isOpened();
|
||||
void rewind();
|
||||
|
|
@ -52,39 +57,39 @@ namespace Audio
|
|||
size_t read(short* buffer, size_t samples);
|
||||
size_t readRaw(short* buffer, size_t samples);
|
||||
|
||||
std::tstring filename() const;
|
||||
std::filesystem::path path() const;
|
||||
size_t size() const;
|
||||
|
||||
unsigned lastError() const;
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<WavFileReader> PWavFileReader;
|
||||
typedef std::shared_ptr<WavFileReader> PWavFileReader;
|
||||
|
||||
class WavFileWriter
|
||||
{
|
||||
protected:
|
||||
FILE* mHandle; /// Handle of audio file.
|
||||
std::tstring mFileName; /// Path to requested audio file.
|
||||
std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||
size_t mWritten; /// Amount of written data (in bytes)
|
||||
size_t mLengthOffset; /// Position of length field.
|
||||
int mRate,
|
||||
mChannels;
|
||||
class WavFileWriter
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
|
||||
std::filesystem::path mPath; /// Path to requested audio file.
|
||||
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||
size_t mWritten = 0; /// Amount of written data (in bytes)
|
||||
size_t mLengthOffset = 0; /// Position of length field.
|
||||
int mSamplerate = 0,
|
||||
mChannels = 0;
|
||||
|
||||
void checkWriteResult(int result);
|
||||
|
||||
public:
|
||||
void writeBuffer(const void* buffer, size_t sz);
|
||||
public:
|
||||
WavFileWriter();
|
||||
~WavFileWriter();
|
||||
|
||||
bool open(const std::tstring& filename, int rate, int channels);
|
||||
bool open(const std::filesystem::path& p, int samplerate, int channels);
|
||||
void close();
|
||||
bool isOpened();
|
||||
bool isOpened() const;
|
||||
size_t write(const void* buffer, size_t bytes);
|
||||
std::tstring filename();
|
||||
};
|
||||
std::filesystem::path path() const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
||||
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#ifdef TARGET_WIN
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
project (audio_lib)
|
||||
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
/* Copyright(C) 2007-2020 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __TOOLKIT_CONFIG_H
|
||||
#define __TOOLKIT_CONFIG_H
|
||||
|
||||
#define USE_SPEEX_AEC
|
||||
|
||||
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
|
||||
//#define USE_WEBRTC_AEC
|
||||
#define USER
|
||||
|
||||
|
||||
#define AUDIO_SAMPLE_WIDTH 16
|
||||
#define AUDIO_CHANNELS 1
|
||||
|
||||
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
|
||||
#define AUDIO_SAMPLERATE 8000
|
||||
#define AUDIO_MIC_BUFFER_COUNT 16
|
||||
#define AUDIO_MIC_BUFFER_LENGTH 10
|
||||
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_SPK_BUFFER_COUNT 16
|
||||
#define AUDIO_SPK_BUFFER_LENGTH 10
|
||||
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_MIX_CHANNEL_COUNT 16
|
||||
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
|
||||
|
||||
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
|
||||
#define AUDIO_RESAMPLER_QUALITY 1
|
||||
#define AEC_FRAME_TIME 10
|
||||
#define AEC_TAIL_TIME 160
|
||||
|
||||
|
||||
// Defined these two lines to get dumping of audio input/output
|
||||
//#define AUDIO_DUMPINPUT
|
||||
//#define AUDIO_DUMPOUTPUT
|
||||
|
||||
|
||||
#define UA_REGISTRATION_TIME 3600
|
||||
#define UA_MEDIA_PORT_START 20000
|
||||
#define UA_MEDIA_PORT_FINISH 30000
|
||||
#define UA_MAX_UDP_PACKET_SIZE 576
|
||||
#define UA_PUBLICATION_ID "314"
|
||||
|
||||
#define MT_SAMPLERATE AUDIO_SAMPLERATE
|
||||
|
||||
#define MT_MAXAUDIOFRAME 1440
|
||||
#define MT_MAXRTPPACKET 1500
|
||||
#define MT_DTMF_END_PACKETS 3
|
||||
|
||||
#define RTP_BUFFER_HIGH 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
|
||||
|
|
@ -24,8 +24,8 @@ class Session;
|
|||
|
||||
class Account: public resip::DnsResultSink
|
||||
{
|
||||
friend class UserAgent;
|
||||
friend class NATDecorator;
|
||||
friend class UserAgent;
|
||||
friend class NATDecorator;
|
||||
public:
|
||||
Account(PVariantMap config, UserAgent& agent);
|
||||
~Account();
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
|||
else
|
||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
||||
}
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
|
||||
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
||||
if (mAvailableCodecs.empty())
|
||||
|
|
@ -114,7 +113,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
|||
if (mRemoteTelephoneCodec)
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Publish stream state
|
||||
const char* attr = nullptr;
|
||||
|
|
@ -229,10 +228,8 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
|||
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
||||
{
|
||||
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
||||
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!mAvailableCodecs.size())
|
||||
|
|
@ -303,26 +300,12 @@ std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
|
|||
if (!mActiveStream)
|
||||
return "";
|
||||
|
||||
// Use tag 1 - it is ok, as we use only single crypto attribute
|
||||
int srtpTag = 1;
|
||||
|
||||
// Print key to base64 string
|
||||
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
||||
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
||||
resip::Data keyText = d.base64encode();
|
||||
|
||||
// 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;
|
||||
return std::format("{} {} inline:{}", 1, toString(suite), keyText.c_str());
|
||||
}
|
||||
|
||||
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
|
||||
|
|
@ -343,15 +326,7 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu
|
|||
resip::Data rawkey = keyText.base64decode();
|
||||
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
||||
|
||||
// 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;
|
||||
return toSrtpSuite(suite);
|
||||
}
|
||||
|
||||
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ public:
|
|||
void setupMirror(bool enable);
|
||||
|
||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
||||
|
||||
protected:
|
||||
// SDP's stream name
|
||||
|
|
@ -109,7 +110,6 @@ protected:
|
|||
void* mMediaObserverTag = nullptr;
|
||||
|
||||
std::string createCryptoAttribute(SrtpSuite suite);
|
||||
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
||||
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
||||
|
||||
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ void UserAgent::start()
|
|||
#if defined(TARGET_WIN)
|
||||
s = new resip::WinSecurity();
|
||||
#elif defined(TARGET_OSX)
|
||||
s = new resip::MacSecurity();
|
||||
s = new resip::Security();
|
||||
#elif defined(TARGET_LINUX)
|
||||
s = new resip::Security("/etc/ssl/certs");
|
||||
#elif defined(TARGET_ANDROID)
|
||||
|
|
@ -486,6 +486,8 @@ void UserAgent::process()
|
|||
void UserAgent::addRootCert(const ByteBuffer& data)
|
||||
{
|
||||
LOCK;
|
||||
if (!mStack)
|
||||
return;
|
||||
resip::Data b(data.data(), data.size());
|
||||
try {
|
||||
mStack->getSecurity()->addRootCertPEM(b);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
#include "../ice/ICETime.h"
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "EP_Session.h"
|
||||
#include "EP_Observer.h"
|
||||
#include "EP_DataProvider.h"
|
||||
|
|
@ -153,7 +153,7 @@ class UserAgent: public resip::ClientRegistrationHandler,
|
|||
public resip::ClientPagerMessageHandler,
|
||||
public resip::ServerPagerMessageHandler,
|
||||
public resip::ClientPublicationHandler
|
||||
//public resip::InternalTransport::TransportLogger
|
||||
//public resip::InternalTransport::TransportLogger
|
||||
{
|
||||
friend class Account;
|
||||
friend class Session;
|
||||
|
|
@ -356,7 +356,7 @@ public:
|
|||
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg) override;
|
||||
#pragma endregion
|
||||
|
||||
// ClientRegistrationHandler implementation
|
||||
// ClientRegistrationHandler implementation
|
||||
#pragma region ClientRegistrationHandler implementation
|
||||
/// Called when registraion succeeds or each time it is sucessfully
|
||||
/// refreshed.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#include "EP_Engine.h"
|
||||
|
||||
WatcherQueue::WatcherQueue(UserAgent& ua)
|
||||
:mActiveId(0), mAgent(ua)
|
||||
:mActiveId(0), mAgent(ua)
|
||||
{}
|
||||
|
||||
WatcherQueue::~WatcherQueue()
|
||||
|
|
|
|||
|
|
@ -445,7 +445,6 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
|||
MT::Statistics stat;
|
||||
|
||||
// Iterate all session providers
|
||||
stat.reset();
|
||||
Stream* media = nullptr;
|
||||
for (Stream& stream: mStreamList)
|
||||
{
|
||||
|
|
@ -469,7 +468,7 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
|||
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
||||
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
||||
if (stat.mFirstRtpTime)
|
||||
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - *(stat.mFirstRtpTime)).count());
|
||||
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - *(stat.mFirstRtpTime)).count());
|
||||
else
|
||||
info[SessionInfo_Duration] = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
#include <atomic>
|
||||
#include <time.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "EP_Account.h"
|
||||
#include "EP_DataProvider.h"
|
||||
#include "EP_AudioProvider.h"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __TOOLKIT_CONFIG_H
|
||||
#define __TOOLKIT_CONFIG_H
|
||||
|
||||
#define USE_SPEEX_AEC
|
||||
|
||||
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
|
||||
//#define USE_WEBRTC_AEC
|
||||
#define USER
|
||||
|
||||
|
||||
#define AUDIO_SAMPLE_WIDTH 16
|
||||
#define AUDIO_CHANNELS 1
|
||||
|
||||
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
|
||||
#define AUDIO_SAMPLERATE 48000
|
||||
#define AUDIO_MIC_BUFFER_COUNT 16
|
||||
#define AUDIO_MIC_BUFFER_LENGTH 10
|
||||
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_SPK_BUFFER_COUNT 16
|
||||
#define AUDIO_SPK_BUFFER_LENGTH 10
|
||||
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_MIX_CHANNEL_COUNT 16
|
||||
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
|
||||
|
||||
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
|
||||
#define AUDIO_RESAMPLER_QUALITY 1
|
||||
#define AEC_FRAME_TIME 10
|
||||
#define AEC_TAIL_TIME 160
|
||||
|
||||
|
||||
// Defined these two lines to get dumping of audio input/output
|
||||
//#define AUDIO_DUMPINPUT
|
||||
//#define AUDIO_DUMPOUTPUT
|
||||
|
||||
|
||||
#define UA_REGISTRATION_TIME 3600
|
||||
#define UA_MEDIA_PORT_START 20000
|
||||
#define UA_MEDIA_PORT_FINISH 30000
|
||||
#define UA_MAX_UDP_PACKET_SIZE 576
|
||||
#define UA_PUBLICATION_ID "314"
|
||||
|
||||
#define MT_SAMPLERATE AUDIO_SAMPLERATE
|
||||
|
||||
#define MT_MAXAUDIOFRAME 1440
|
||||
#define MT_MAXRTPPACKET 1500
|
||||
#define MT_DTMF_END_PACKETS 3
|
||||
|
||||
#define RTP_BUFFER_HIGH 0
|
||||
#define RTP_BUFFER_LOW 0
|
||||
#define RTP_BUFFER_PREBUFFER 0
|
||||
|
||||
// #define RTP_BUFFER_HIGH 160
|
||||
// #define RTP_BUFFER_LOW 10
|
||||
// #define RTP_BUFFER_PREBUFFER 160
|
||||
#define RTP_DECODED_CAPACITY 2048
|
||||
|
||||
#define DEFAULT_SUBSCRIPTION_TIME 1200
|
||||
#define DEFAULT_SUBSCRIPTION_REFRESHTIME 500
|
||||
|
||||
#define PRESENCE_IN_REG_HEADER "PresenceInReg"
|
||||
|
||||
// Maximum UDP packet length
|
||||
#define MAX_UDPPACKET_SIZE 65535
|
||||
#define MAX_VALID_UDPPACKET_SIZE 2048
|
||||
|
||||
// AMR codec defines - it requires USE_AMR_CODEC defined
|
||||
// #define USE_AMR_CODEC
|
||||
#define MT_AMRNB_PAYLOADTYPE 112
|
||||
#define MT_AMRNB_CODECNAME "amr"
|
||||
|
||||
#define MT_AMRNB_OCTET_PAYLOADTYPE 113
|
||||
|
||||
#define MT_AMRWB_PAYLOADTYPE 96
|
||||
#define MT_AMRWB_CODECNAME "amr-wb"
|
||||
|
||||
#define MT_AMRWB_OCTET_PAYLOADTYPE 97
|
||||
|
||||
#define MT_GSMEFR_PAYLOADTYPE 126
|
||||
#define MT_GSMEFR_CODECNAME "GERAN-EFR"
|
||||
|
||||
#define MT_EVS_PAYLOADTYPE 127
|
||||
#define MT_EVS_CODECNAME "EVS"
|
||||
|
||||
// OPUS codec defines
|
||||
// #define USE_OPUS_CODEC
|
||||
#define MT_OPUS_CODEC_PT 106
|
||||
|
||||
// ILBC codec defines
|
||||
#define MT_ILBC20_PAYLOADTYPE -1
|
||||
#define MT_ILBC30_PAYLOADTYPE -1
|
||||
|
||||
// ISAC codec defines
|
||||
#define MT_ISAC16K_PAYLOADTYPE -1
|
||||
#define MT_ISAC32K_PAYLOADTYPE -1
|
||||
|
||||
// GSM HR payload type
|
||||
#define MT_GSMHR_PAYLOADTYPE -1
|
||||
|
||||
// Mirror buffer capacity
|
||||
#define MT_MIRROR_CAPACITY 32768
|
||||
|
||||
// Mirror buffer readiness threshold - 50 milliseconds
|
||||
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
# define TEXT(X) X
|
||||
#endif
|
||||
|
||||
// In milliseconds
|
||||
#define MT_SEVANA_FRAME_TIME 680
|
||||
|
||||
#endif
|
||||
|
|
@ -2,11 +2,9 @@ cmake_minimum_required (VERSION 3.15)
|
|||
project (helper_lib)
|
||||
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
option (USE_NULL_UUID "When enabled linking to libuuid is avoided" OFF)
|
||||
|
||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
||||
|
|
@ -17,3 +15,6 @@ set_property(TARGET helper_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<C
|
|||
# Private include directories
|
||||
target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .)
|
||||
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
||||
if (TARGET_LINUX)
|
||||
target_link_libraries (helper_lib PUBLIC uuid)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -32,23 +32,24 @@ void FileHelper::remove(const char* s)
|
|||
::remove(s);
|
||||
}
|
||||
|
||||
std::string FileHelper::gettempname()
|
||||
{
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
mkstemp(template_filename);
|
||||
return template_filename;
|
||||
#elif defined(TARGET_WIN)
|
||||
char buffer[L_tmpnam];
|
||||
tmpnam(buffer);
|
||||
// std::string FileHelper::gettempname()
|
||||
// {
|
||||
// #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
// int code = mkstemp(template_filename);
|
||||
|
||||
return buffer;
|
||||
#elif defined(TARGET_OSX)
|
||||
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
mktemp(template_filename);
|
||||
return template_filename;
|
||||
#endif
|
||||
}
|
||||
// return template_filename;
|
||||
// #elif defined(TARGET_WIN)
|
||||
// char buffer[L_tmpnam];
|
||||
// tmpnam(buffer);
|
||||
|
||||
// return buffer;
|
||||
// #elif defined(TARGET_OSX)
|
||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
// mktemp(template_filename);
|
||||
// return template_filename;
|
||||
// #endif
|
||||
// }
|
||||
|
||||
bool FileHelper::isAbsolute(const std::string& s)
|
||||
{
|
||||
|
|
@ -109,3 +110,32 @@ size_t FileHelper::getFreespace(const std::string& path)
|
|||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string FileHelper::expandUserHome(const std::string &path)
|
||||
{
|
||||
if (path.empty() || path[0] != '~')
|
||||
return path; // No expansion needed
|
||||
|
||||
const char* home_dir = nullptr;
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
home_dir = std::getenv("USERPROFILE");
|
||||
if (!home_dir)
|
||||
{
|
||||
home_dir = std::getenv("HOMEDRIVE");
|
||||
const char* homepath = std::getenv("HOMEPATH");
|
||||
if (home_dir && homepath) {
|
||||
std::string fullpath(home_dir);
|
||||
fullpath += homepath;
|
||||
return fullpath + path.substr(1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
home_dir = std::getenv("HOME");
|
||||
#endif
|
||||
|
||||
if (!home_dir)
|
||||
throw std::runtime_error("Unable to determine the home directory");
|
||||
|
||||
return std::string(home_dir) + path.substr(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public:
|
|||
static void remove(const std::string& s);
|
||||
static void remove(const char* s);
|
||||
|
||||
static std::string gettempname();
|
||||
// static std::string gettempname();
|
||||
static bool isAbsolute(const std::string& s);
|
||||
|
||||
static std::string getCurrentDir();
|
||||
|
|
@ -23,6 +23,7 @@ public:
|
|||
// Returns free space on volume for path
|
||||
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
||||
static size_t getFreespace(const std::string& path);
|
||||
static std::string expandUserHome(const std::string& path);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# include <asm/ioctls.h>
|
||||
#endif
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "HL_NetworkSocket.h"
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
|
|
@ -27,7 +27,7 @@ DatagramSocket::DatagramSocket()
|
|||
|
||||
DatagramSocket::~DatagramSocket()
|
||||
{
|
||||
closeSocket();
|
||||
internalClose();
|
||||
}
|
||||
|
||||
void DatagramSocket::open(int family)
|
||||
|
|
@ -85,7 +85,7 @@ unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void DatagramSocket::closeSocket()
|
||||
void DatagramSocket::internalClose()
|
||||
{
|
||||
if (mHandle != INVALID_SOCKET)
|
||||
{
|
||||
|
|
@ -98,6 +98,11 @@ void DatagramSocket::closeSocket()
|
|||
}
|
||||
}
|
||||
|
||||
void DatagramSocket::closeSocket()
|
||||
{
|
||||
internalClose();
|
||||
}
|
||||
|
||||
bool DatagramSocket::isValid() const
|
||||
{
|
||||
return mHandle != INVALID_SOCKET;
|
||||
|
|
@ -163,7 +168,7 @@ unsigned DatagramAgreggator::count()
|
|||
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
||||
{
|
||||
PDatagramSocket socket = mSocketVector[index];
|
||||
return (FD_ISSET(socket->mHandle, &mReadSet) != 0);
|
||||
return FD_ISSET(socket->mHandle, &mReadSet);
|
||||
}
|
||||
|
||||
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
||||
|
|
|
|||
|
|
@ -36,10 +36,12 @@ public:
|
|||
virtual SOCKET socket() const;
|
||||
|
||||
virtual void open(int family);
|
||||
|
||||
protected:
|
||||
int mFamily;
|
||||
SOCKET mHandle;
|
||||
int mLocalPort;
|
||||
void internalClose();
|
||||
};
|
||||
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
|||
PROCESS_INFORMATION pi = { 0 };
|
||||
|
||||
char* cmdline = (char*)_alloca(cmd.size()+1);
|
||||
strcpy(cmdline, StringHelper::replace(cmd, "/", "\\").c_str());
|
||||
strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str());
|
||||
|
||||
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
||||
if (! fSuccess)
|
||||
|
|
@ -114,7 +114,7 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
|||
memset(&pi, 0, sizeof pi);
|
||||
|
||||
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
||||
strcpy(cmdbuffer, StringHelper::replace(cmdline, "/", "\\").c_str());
|
||||
strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str());
|
||||
|
||||
|
||||
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
||||
|
|
@ -160,14 +160,14 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
|||
{
|
||||
std::string line(buf, cr - buf -1);
|
||||
if (callback)
|
||||
callback(StringHelper::trim(line));
|
||||
callback(strx::trim(line));
|
||||
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
||||
}
|
||||
}
|
||||
} //for
|
||||
|
||||
if (buf[0])
|
||||
callback(StringHelper::trim(std::string(buf)));
|
||||
callback(strx::trim(std::string(buf)));
|
||||
|
||||
char ctrlc = 3;
|
||||
//if (finish_flag)
|
||||
|
|
@ -278,12 +278,12 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
|||
}
|
||||
while (r == sizeof(buffer) - 1);
|
||||
|
||||
if (lines.find("\n") != std::string::npos && line_callback)
|
||||
if (lines.find('\n') != std::string::npos && line_callback)
|
||||
{
|
||||
std::string::size_type p = 0;
|
||||
while (p < lines.size())
|
||||
{
|
||||
std::string::size_type d = lines.find("\n", p);
|
||||
std::string::size_type d = lines.find('\n', p);
|
||||
if (d != std::string::npos)
|
||||
{
|
||||
if (line_callback)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@
|
|||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "HL_Rtp.h"
|
||||
#include "HL_Exception.h"
|
||||
#include "HL_String.h"
|
||||
|
|
@ -52,8 +56,12 @@ bool RtpHelper::isRtp(const void* buffer, size_t length)
|
|||
if (length < 12)
|
||||
return false;
|
||||
|
||||
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
|
||||
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35);
|
||||
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
|
||||
if (h->version != 0b10)
|
||||
return false;
|
||||
|
||||
unsigned char pt = h->pt;
|
||||
bool rtp = ( (pt & 0x7F) >= 96 && (pt & 0x7F) <= 127) || ((pt & 0x7F) < 35);
|
||||
return rtp;
|
||||
}
|
||||
|
||||
|
|
@ -62,9 +70,8 @@ bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
|
|||
{
|
||||
if (length < 12)
|
||||
return false;
|
||||
unsigned char b = ((const unsigned char*)buffer)[0];
|
||||
|
||||
return (b & 0xC0 ) == 128;
|
||||
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
|
||||
return h->version == 0b10;
|
||||
}
|
||||
|
||||
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||
|
|
@ -75,9 +82,9 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
|||
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
||||
{
|
||||
if (isRtp(buffer, length))
|
||||
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
|
||||
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
|
||||
else
|
||||
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
|
||||
return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc);
|
||||
}
|
||||
|
||||
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
@ -10,11 +10,8 @@
|
|||
# include "jrtplib/src/rtppacket.h"
|
||||
#endif
|
||||
|
||||
#include "HL_Uuid.h"
|
||||
#include "HL_InternetAddress.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Class to carry rtp/rtcp socket pair
|
||||
template<class T>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
|
@ -127,7 +127,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||
if (sock == INVALID_SOCKET)
|
||||
{
|
||||
// Return null socket
|
||||
PDatagramSocket result(new DatagramSocket());
|
||||
auto result = std::make_shared<DatagramSocket>();
|
||||
result->mLocalPort = port;
|
||||
result->mFamily = family;
|
||||
return result;
|
||||
|
|
@ -170,7 +170,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||
closesocket(sock);
|
||||
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
||||
}
|
||||
PDatagramSocket resultObject(new DatagramSocket());
|
||||
auto resultObject = std::make_shared<DatagramSocket>();
|
||||
resultObject->mLocalPort = testport;
|
||||
resultObject->mHandle = sock;
|
||||
if (!resultObject->setBlocking(false))
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef __SOCKET_HEAP_H
|
||||
#define __SOCKET_HEAP_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
|
|
|||
|
|
@ -93,7 +93,11 @@ int strx::toInt(const char *s, int defaultValue, bool* isOk)
|
|||
uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||
{
|
||||
uint64_t result = def;
|
||||
if (sscanf(s, "%" SCNu64, &result) != 1)
|
||||
#if defined(TARGET_WIN)
|
||||
if (sscanf(s, "%I64d", &result) != 1)
|
||||
#else
|
||||
if (sscanf(s, "%llu", &result) != 1)
|
||||
#endif
|
||||
{
|
||||
if (isOk)
|
||||
*isOk = false;
|
||||
|
|
@ -408,3 +412,46 @@ std::string strx::uppercase(const std::string& s)
|
|||
std::transform(r.begin(), r.end(), r.begin(), ::toupper);
|
||||
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,6 +9,7 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cstdint>
|
||||
#include "HL_Types.h"
|
||||
|
||||
#ifdef TARGET_OSX
|
||||
|
|
@ -19,6 +20,9 @@
|
|||
class strx
|
||||
{
|
||||
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 toUtf8(const std::tstring& arg);
|
||||
static std::tstring makeTstring(const std::string& arg);
|
||||
|
|
@ -29,15 +33,13 @@ public:
|
|||
static std::string toHex(const void* ptr);
|
||||
static std::string toHex(const uint8_t* input, size_t inputLength);
|
||||
static std::string intToString(int value);
|
||||
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
||||
static std::string doubleToString(double value, int precision);
|
||||
static int fromHex2Int(const std::string& s);
|
||||
static std::string fromHex2String(const std::string& s);
|
||||
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
|
||||
|
||||
static 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 void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
|
||||
|
|
@ -49,7 +51,7 @@ public:
|
|||
std::ostringstream s;
|
||||
for (const auto& i : v)
|
||||
{
|
||||
if (&i != &v[0])
|
||||
if (&i != &v.front())
|
||||
s << delimiter;
|
||||
s << i;
|
||||
}
|
||||
|
|
@ -67,8 +69,10 @@ public:
|
|||
static bool startsWith(const std::string& s, const std::string& prefix);
|
||||
static bool endsWith(const std::string& s, const std::string& suffix);
|
||||
static int stringToDuration(const std::string& s);
|
||||
static std::string uppercase(const std::string& s);
|
||||
|
||||
static std::string uppercase(const std::string& s);
|
||||
static std::string lowercase(const std::string& s);
|
||||
static std::string removeQuotes(const std::string& s);
|
||||
};
|
||||
|
||||
class XcapHelper
|
||||
|
|
@ -81,4 +85,11 @@ public:
|
|||
};
|
||||
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
|
||||
// MSVC++ lacks memmem support
|
||||
extern const void *memmem(const void *haystack, size_t haystack_len,
|
||||
const void * const needle, const size_t needle_len);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -77,19 +77,21 @@ uint64_t ThreadHelper::getCurrentId()
|
|||
// ------------------- TimeHelper ---------------
|
||||
using namespace std::chrono;
|
||||
|
||||
// Milliseconds starting from the epoch
|
||||
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
||||
|
||||
// Seconds starting from the epoch
|
||||
static time_t TimestampBase = time(nullptr);
|
||||
|
||||
uint64_t TimeHelper::getTimestamp()
|
||||
// Returns number of milliseconds starting from 01 Jan 1970 GMT
|
||||
uint64_t chronox::getTimestamp()
|
||||
{
|
||||
time_point<steady_clock> t = steady_clock::now();
|
||||
|
||||
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
||||
|
||||
return ms - TimestampStartPoint + TimestampBase * 1000;
|
||||
}
|
||||
|
||||
uint64_t TimeHelper::getUptime()
|
||||
uint64_t chronox::getUptime()
|
||||
{
|
||||
time_point<steady_clock> t = steady_clock::now();
|
||||
|
||||
|
|
@ -98,7 +100,7 @@ uint64_t TimeHelper::getUptime()
|
|||
return ms - TimestampStartPoint;
|
||||
}
|
||||
|
||||
uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier)
|
||||
uint32_t chronox::getDelta(uint32_t later, uint32_t earlier)
|
||||
{
|
||||
if (later > earlier)
|
||||
return later - earlier;
|
||||
|
|
@ -109,14 +111,42 @@ uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier)
|
|||
return 0;
|
||||
}
|
||||
|
||||
TimeHelper::ExecutionTime::ExecutionTime()
|
||||
timespec chronox::toTimespec(uint64_t milliseconds)
|
||||
{
|
||||
mStart = TimeHelper::getTimestamp();
|
||||
timespec r;
|
||||
r.tv_sec = milliseconds / 1000;
|
||||
r.tv_nsec = milliseconds % 1000;
|
||||
r.tv_nsec *= 1000 * 1000;
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t TimeHelper::ExecutionTime::getSpentTime() const
|
||||
uint64_t chronox::toTimestamp(const timeval& ts)
|
||||
{
|
||||
return TimeHelper::getTimestamp() - mStart;
|
||||
return ts.tv_sec * 1000 + ts.tv_usec / 1000;
|
||||
}
|
||||
|
||||
int64_t chronox::getDelta(const timespec& a, const timespec& b)
|
||||
{
|
||||
uint64_t ms_a = a.tv_sec * 1000 + a.tv_nsec / 10000000;
|
||||
uint64_t ms_b = b.tv_sec * 1000 + a.tv_nsec / 10000000;
|
||||
return ms_a - ms_b;
|
||||
}
|
||||
|
||||
int64_t chronox::getDelta(const timeval& a, const timeval& b)
|
||||
{
|
||||
int64_t diff_seconds = a.tv_sec - b.tv_sec;
|
||||
int64_t diff_microseconds = a.tv_usec - b.tv_usec;
|
||||
return diff_seconds * 1000 + diff_microseconds / 1000;
|
||||
}
|
||||
|
||||
chronox::ExecutionTime::ExecutionTime()
|
||||
{
|
||||
mStart = chronox::getTimestamp();
|
||||
}
|
||||
|
||||
uint64_t chronox::ExecutionTime::getSpentTime() const
|
||||
{
|
||||
return chronox::getTimestamp() - mStart;
|
||||
}
|
||||
|
||||
// --------------- BufferQueue -----------------
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@
|
|||
#include <functional>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
typedef std::recursive_mutex Mutex;
|
||||
typedef std::unique_lock<std::recursive_mutex> Lock;
|
||||
|
||||
|
|
@ -46,7 +51,7 @@ public:
|
|||
static uint64_t getCurrentId();
|
||||
};
|
||||
|
||||
class TimeHelper
|
||||
class chronox
|
||||
{
|
||||
public:
|
||||
// Returns current timestamp in milliseconds
|
||||
|
|
@ -59,6 +64,14 @@ public:
|
|||
// Handles cases when clock is wrapped.
|
||||
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
||||
|
||||
// Converts number of milliseconds starting from Epoch begin to timespec.
|
||||
static timespec toTimespec(uint64_t milliseconds);
|
||||
static uint64_t toTimestamp(const timeval& ts);
|
||||
|
||||
// Returns difference between timestamps in milliseconds
|
||||
static int64_t getDelta(const timespec& a, const timespec& b);
|
||||
static int64_t getDelta(const timeval& a, const timeval& b);
|
||||
|
||||
class ExecutionTime
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ thread_pool::thread_pool(size_t num_of_threads, const std::string& name)
|
|||
num_of_threads = std::thread::hardware_concurrency();
|
||||
|
||||
for(size_t idx = 0; idx < num_of_threads; idx++)
|
||||
this->workers.push_back(std::thread(&thread_pool::run_worker, this));
|
||||
this->workers.emplace_back(std::thread(&thread_pool::run_worker, this));
|
||||
}
|
||||
|
||||
// Add new work item to the pool
|
||||
|
|
@ -20,6 +20,22 @@ void thread_pool::enqueue(const thread_pool::task& t)
|
|||
this->condition.notify_one();
|
||||
}
|
||||
|
||||
void thread_pool::wait(std::chrono::milliseconds interval)
|
||||
{
|
||||
while (size() != 0)
|
||||
std::this_thread::sleep_for(interval);
|
||||
}
|
||||
|
||||
size_t thread_pool::size()
|
||||
{
|
||||
std::unique_lock l(this->queue_mutex);
|
||||
return this->tasks.size();
|
||||
}
|
||||
|
||||
size_t thread_pool::threads()
|
||||
{
|
||||
return this->workers.size();
|
||||
}
|
||||
|
||||
// the destructor joins all threads
|
||||
thread_pool::~thread_pool()
|
||||
|
|
@ -37,7 +53,7 @@ void thread_pool::run_worker()
|
|||
{
|
||||
ThreadHelper::setName(this->name);
|
||||
task t;
|
||||
while(!this->stop)
|
||||
while (!this->stop)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@ class thread_pool
|
|||
public:
|
||||
typedef std::function<void()> task;
|
||||
|
||||
thread_pool(size_t num_of_threads, const std::string&);
|
||||
thread_pool(size_t num_of_threads, const std::string& thread_name);
|
||||
~thread_pool();
|
||||
|
||||
void enqueue(const task& task);
|
||||
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
|
||||
size_t size();
|
||||
size_t threads();
|
||||
|
||||
private:
|
||||
// need to keep track of threads so we can join them
|
||||
|
|
@ -33,7 +36,7 @@ private:
|
|||
// synchronization
|
||||
std::mutex queue_mutex;
|
||||
std::condition_variable condition;
|
||||
bool stop = false;
|
||||
std::atomic_bool stop = false;
|
||||
|
||||
// thread name prefix for worker threads
|
||||
std::string name;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,53 @@
|
|||
#include "HL_Time.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <chrono>
|
||||
/* 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;
|
||||
clock_gettime(CLOCK_MONOTONIC, &res);
|
||||
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
|
||||
#endif
|
||||
}
|
||||
|
||||
int compare_timespec(const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
if (lhs.tv_sec < rhs.tv_sec)
|
||||
return -1;
|
||||
if (lhs.tv_sec > rhs.tv_sec)
|
||||
return 1;
|
||||
|
||||
if (lhs.tv_nsec < rhs.tv_nsec)
|
||||
return -1;
|
||||
if (lhs.tv_nsec > rhs.tv_nsec)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator < (const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
bool operator == (const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) == 0;
|
||||
}
|
||||
|
||||
bool operator > (const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) > 0;
|
||||
}
|
||||
|
||||
bool is_zero(const timespec& ts)
|
||||
{
|
||||
return !ts.tv_sec && !ts.tv_nsec;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
#ifndef __HELPER_TIME_H
|
||||
#define __HELPER_TIME_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
// Return current monotonic number of milliseconds starting from some point
|
||||
extern double now_ms();
|
||||
|
||||
// Compare the timespec.
|
||||
// Returns -1 if lhs < rhs, 1 if lhs > rhs, 0 if equal
|
||||
extern int compare_timespec(const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator < (const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator == (const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator > (const timespec& lhs, const timespec& rhs);
|
||||
extern bool is_zero(const timespec& ts);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
#include "HL_Types.h"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
@ -26,4 +26,139 @@ enum SdpDirection
|
|||
Sdp_Offer
|
||||
};
|
||||
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <map>
|
||||
|
||||
template<
|
||||
class K, class V,
|
||||
class HashK = std::hash<K>, class EqK = std::equal_to<K>,
|
||||
class HashV = std::hash<V>, class EqV = std::equal_to<V>
|
||||
>
|
||||
class BiMap {
|
||||
public:
|
||||
using key_type = K;
|
||||
using mapped_type = V;
|
||||
|
||||
BiMap(const std::map<K,V>& initializers) {
|
||||
for (const auto& item: initializers) {
|
||||
insert(item.first, item.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert a new (key, value) pair. Returns false if either key or value already exists.
|
||||
bool insert(const K& k, const V& v) {
|
||||
if (contains_key(k) || contains_value(v)) return false;
|
||||
auto ok = forward_.emplace(k, v);
|
||||
try {
|
||||
auto ov = reverse_.emplace(v, k);
|
||||
if (!ov.second) { // shouldn't happen given the guard above
|
||||
forward_.erase(k);
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
forward_.erase(k);
|
||||
throw;
|
||||
}
|
||||
return ok.second;
|
||||
}
|
||||
|
||||
bool insert(K&& k, V&& v) {
|
||||
if (contains_key(k) || contains_value(v)) return false;
|
||||
auto ok = forward_.emplace(std::move(k), std::move(v));
|
||||
try {
|
||||
auto ov = reverse_.emplace(ok.first->second, ok.first->first); // use stored refs
|
||||
if (!ov.second) {
|
||||
forward_.erase(ok.first);
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
forward_.erase(ok.first);
|
||||
throw;
|
||||
}
|
||||
return ok.second;
|
||||
}
|
||||
|
||||
// Replace value for existing key (and update reverse map). Returns false if value is already bound elsewhere.
|
||||
bool replace_by_key(const K& k, const V& new_v) {
|
||||
auto it = forward_.find(k);
|
||||
if (it == forward_.end()) return false;
|
||||
if (contains_value(new_v)) return false;
|
||||
// remove old reverse, insert new reverse, then update forward
|
||||
reverse_.erase(it->second);
|
||||
reverse_.emplace(new_v, k);
|
||||
it->second = new_v;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replace key for existing value (and update forward map). Returns false if key is already bound elsewhere.
|
||||
bool replace_by_value(const V& v, const K& new_k) {
|
||||
auto it = reverse_.find(v);
|
||||
if (it == reverse_.end()) return false;
|
||||
if (contains_key(new_k)) return false;
|
||||
forward_.erase(it->second);
|
||||
forward_.emplace(new_k, v);
|
||||
it->second = new_k;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Erase by key/value. Return number erased (0 or 1).
|
||||
size_t erase_key(const K& k) {
|
||||
auto it = forward_.find(k);
|
||||
if (it == forward_.end()) return 0;
|
||||
reverse_.erase(it->second);
|
||||
forward_.erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t erase_value(const V& v) {
|
||||
auto it = reverse_.find(v);
|
||||
if (it == reverse_.end()) return 0;
|
||||
forward_.erase(it->second);
|
||||
reverse_.erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lookup
|
||||
bool contains_key(const K& k) const { return forward_.find(k) != forward_.end(); }
|
||||
bool contains_value(const V& v) const { return reverse_.find(v) != reverse_.end(); }
|
||||
|
||||
const V* find_by_key(const K& k) const {
|
||||
auto it = forward_.find(k);
|
||||
return (it == forward_.end()) ? nullptr : &it->second;
|
||||
}
|
||||
const K* find_by_value(const V& v) const {
|
||||
auto it = reverse_.find(v);
|
||||
return (it == reverse_.end()) ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
// at() variants throw std::out_of_range on missing entries
|
||||
const V& at_key(const K& k) const { return forward_.at(k); }
|
||||
const K& at_value(const V& v) const { return reverse_.at(v); }
|
||||
|
||||
void clear() noexcept {
|
||||
forward_.clear();
|
||||
reverse_.clear();
|
||||
}
|
||||
|
||||
bool empty() const noexcept { return forward_.empty(); }
|
||||
size_t size() const noexcept { return forward_.size(); }
|
||||
|
||||
// Reserve buckets for performance (optional)
|
||||
void reserve(size_t n) {
|
||||
forward_.reserve(n);
|
||||
reverse_.reserve(n);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<K, V, HashK, EqK> forward_;
|
||||
std::unordered_map<V, K, HashV, EqV> reverse_;
|
||||
};
|
||||
|
||||
#include <chrono>
|
||||
typedef std::chrono::steady_clock::time_point timepoint_t;
|
||||
typedef std::chrono::steady_clock monoclock_t;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,77 +1,57 @@
|
|||
#include "HL_Uuid.h"
|
||||
#include <memory.h>
|
||||
#include <random>
|
||||
#include <span>
|
||||
|
||||
#if defined(TARGET_LINUX)
|
||||
#define UUID_SYSTEM_GENERATOR
|
||||
#endif
|
||||
|
||||
#include "uuid.h"
|
||||
|
||||
Uuid::Uuid()
|
||||
{
|
||||
#if defined(TARGET_WIN) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
memset(&mUuid, 0, sizeof mUuid);
|
||||
#endif
|
||||
}
|
||||
|
||||
Uuid Uuid::generateOne()
|
||||
{
|
||||
Uuid result;
|
||||
#if !defined(USE_NULL_UUID)
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
uuid_generate(result.mUuid);
|
||||
#endif
|
||||
#if defined(TARGET_WIN)
|
||||
UuidCreate(&result.mUuid);
|
||||
#endif
|
||||
#endif
|
||||
// #if defined(TARGET_LINUX)
|
||||
// auto id = uuids::uuid_system_generator{}();
|
||||
// #else
|
||||
std::random_device rd;
|
||||
auto seed_data = std::array<int, std::mt19937::state_size> {};
|
||||
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
|
||||
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
|
||||
std::mt19937 generator(seq);
|
||||
uuids::uuid_random_generator gen{generator};
|
||||
|
||||
auto id = gen();
|
||||
// #endif
|
||||
memcpy(result.mUuid, id.as_bytes().data(), id.as_bytes().size_bytes());
|
||||
return result;
|
||||
}
|
||||
|
||||
Uuid Uuid::parse(const std::string &s)
|
||||
{
|
||||
Uuid result;
|
||||
#if !defined(USE_NULL_UUID)
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
uuid_parse(s.c_str(), result.mUuid);
|
||||
#endif
|
||||
auto id = uuids::uuid::from_string(s);
|
||||
if (id)
|
||||
memcpy(result.mUuid, id->as_bytes().data(), id->as_bytes().size_bytes());
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
UuidFromStringA((RPC_CSTR)s.c_str(), &result.mUuid);
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Uuid::toString() const
|
||||
{
|
||||
#if defined(USE_NULL_UUID)
|
||||
return "UUID_disabled";
|
||||
#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
|
||||
auto s = std::span<uuids::uuid::value_type, 16>{(uuids::uuid::value_type*)mUuid, 16};
|
||||
uuids::uuid id(s);
|
||||
return uuids::to_string(id);
|
||||
}
|
||||
|
||||
bool Uuid::operator < (const Uuid& right) const
|
||||
{
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
return memcmp(&mUuid, &right.mUuid, sizeof(mUuid)) < 0;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,7 @@
|
|||
#define __HL_UUID_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#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
|
||||
#include <stdint.h>
|
||||
|
||||
class Uuid
|
||||
{
|
||||
|
|
@ -21,20 +14,7 @@ public:
|
|||
bool operator < (const Uuid& right) const;
|
||||
|
||||
protected:
|
||||
#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
|
||||
uint8_t mUuid[16];
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
project (media_lib)
|
||||
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
if (NOT DEFINED LIB_PLATFORM)
|
||||
message(FATAL_ERROR media_lib project requires LIB_PLATFORM to be set - it uses libraries from that directory)
|
||||
endif()
|
||||
|
||||
# Rely on C++ 20
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Produce PIC code always
|
||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
option (USE_RESIP_INTEGRATION "Use some resiprocate specific routines" OFF)
|
||||
|
||||
set (SOURCES
|
||||
MT_Statistics.cpp
|
||||
|
|
@ -23,6 +26,8 @@ set (SOURCES
|
|||
MT_AudioReceiver.cpp
|
||||
MT_AudioCodec.cpp
|
||||
MT_CngHelper.cpp
|
||||
MT_AmrCodec.cpp
|
||||
MT_EvsCodec.cpp
|
||||
|
||||
MT_Statistics.h
|
||||
MT_WebRtc.h
|
||||
|
|
@ -38,48 +43,48 @@ set (SOURCES
|
|||
MT_AudioReceiver.h
|
||||
MT_AudioCodec.h
|
||||
MT_CngHelper.h
|
||||
MT_AmrCodec.h
|
||||
MT_EvsCodec.h
|
||||
)
|
||||
|
||||
add_library(media_lib ${SOURCES})
|
||||
set (LIBS_CODEC)
|
||||
|
||||
if (USE_AMR_CODEC)
|
||||
include (${LIB_PLATFORM}/platform_libs.cmake)
|
||||
message("Media: AMR NB and WB codecs will be included.")
|
||||
add_definitions(-DUSE_AMR_CODEC)
|
||||
set (SOURCES ${SOURCES} MT_AmrCodec.cpp MT_AmrCodec.h)
|
||||
set (LIBS_CODEC)
|
||||
target_compile_definitions(media_lib PUBLIC USE_AMR_CODEC)
|
||||
list (APPEND LIBS_CODEC ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
||||
endif()
|
||||
|
||||
if (USE_EVS_CODEC)
|
||||
message("Media: EVS codec will be included.")
|
||||
add_definitions (-DUSE_EVS_CODEC)
|
||||
set (SOURCES ${SOURCES} MT_EvsCodec.cpp MT_EvsCodec.h)
|
||||
set (LIBS_CODEC evs_codec)
|
||||
target_compile_definitions (media_lib PUBLIC USE_EVS_CODEC)
|
||||
list (APPEND LIBS_CODEC evs_codec)
|
||||
endif()
|
||||
|
||||
if (USE_OPUS_CODEC)
|
||||
message("Media: Opus codec will be included.")
|
||||
add_definitions(-DUSE_OPUS_CODEC)
|
||||
target_compile_definitions(media_lib PUBLIC USE_OPUS_CODEC)
|
||||
list (APPEND LIBS_CODEC opus)
|
||||
endif()
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM MATCHES "Linux*")
|
||||
add_definitions(-DHAVE_NETINET_IN_H)
|
||||
if(CMAKE_SYSTEM MATCHES "Linux*" OR CMAKE_SYSTEM MATCHES "Darwin*")
|
||||
target_compile_definitions(media_lib PUBLIC HAVE_NETINET_IN_H)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM MATCHES "Darwin*")
|
||||
# OS X Specific flags
|
||||
add_definitions(-DHAVE_NETINET_IN_H)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||
# Windows Specific flags - MSVC expected
|
||||
add_definitions(
|
||||
-D_CRT_SECURE_NO_WARNINGS
|
||||
-DHAVE_WINSOCK2_H
|
||||
-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
|
||||
-DUNICODE
|
||||
-D_UNICODE )
|
||||
target_compile_definitions(media_lib PRIVATE
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
HAVE_WINSOCK2_H
|
||||
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
|
||||
UNICODE
|
||||
_UNICODE )
|
||||
endif()
|
||||
|
||||
add_library(media_lib ${SOURCES})
|
||||
|
||||
|
||||
# Dependency on ice_stack - Linux build requires it
|
||||
# Codec libraries as well
|
||||
|
|
@ -94,9 +99,9 @@ target_include_directories(media_lib
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/opus/include/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs
|
||||
${LIB_PLATFORM}/opus/include
|
||||
)
|
||||
|
||||
target_include_directories(media_lib
|
||||
|
|
@ -107,8 +112,4 @@ target_include_directories(media_lib
|
|||
${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,85 +5,30 @@
|
|||
#include "../helper/HL_ByteBuffer.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
#include "../helper/HL_IuUP.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define LOG_SUBSYSTEM "AmrCodec"
|
||||
using namespace MT;
|
||||
|
||||
|
||||
static const uint8_t amr_block_size[16]={ 13, 14, 16, 18, 20, 21, 27, 32,
|
||||
6 , 0 , 0 , 0 , 0 , 0 , 0 , 1 };
|
||||
// Constant of AMR-NB frame lengths in bytes.
|
||||
const uint8_t amrnb_framelen[9] =
|
||||
{12, 13, 15, 17, 19, 20, 26, 31, 5};
|
||||
|
||||
|
||||
/**
|
||||
* Constant of AMR-NB frame lengths in bytes.
|
||||
*/
|
||||
const uint8_t amrnb_framelen[16] =
|
||||
{12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
/**
|
||||
* Constant of AMR-NB frame lengths in bits.
|
||||
*/
|
||||
const uint16_t amrnb_framelenbits[9] =
|
||||
{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[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.
|
||||
*/
|
||||
// Constant of AMR-WB frame lengths in bytes.
|
||||
const uint8_t amrwb_framelen[10] =
|
||||
{17, 23, 32, 37, 40, 46, 50, 58, 60, 5 /* SID packet */};
|
||||
const uint16_t amrwb_framelenbits[10] =
|
||||
{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};
|
||||
|
||||
{132, 177, 253, 285, 317, 365, 397, 461, 477, 40 /* SID packet */};
|
||||
|
||||
// Helper routines
|
||||
|
||||
/*static int8_t bitrateToMode(uint16_t bitrate)
|
||||
{
|
||||
int8_t mode = -1;
|
||||
|
||||
switch (bitrate)
|
||||
{
|
||||
// AMR NB
|
||||
case 4750: mode = 0; break;
|
||||
case 5150: mode = 1; break;
|
||||
case 5900: mode = 2; break;
|
||||
case 6700: mode = 3; break;
|
||||
case 7400: mode = 4; break;
|
||||
case 7950: mode = 5; break;
|
||||
case 10200: mode = 6; break;
|
||||
case 12200: mode = 7; break;
|
||||
|
||||
// AMRWB
|
||||
case 6600: mode = 0; break;
|
||||
case 8850: mode = 1; break;
|
||||
case 12650: mode = 2; break;
|
||||
case 14250: mode = 3; break;
|
||||
case 15850: mode = 4; break;
|
||||
case 18250: mode = 5; break;
|
||||
case 19850: mode = 6; break;
|
||||
case 23050: mode = 7; break;
|
||||
case 23850: mode = 8; break;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}*/
|
||||
|
||||
struct AmrPayloadInfo
|
||||
{
|
||||
const uint8_t* mPayload;
|
||||
|
|
@ -119,12 +64,13 @@ struct AmrPayload
|
|||
static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
||||
{
|
||||
AmrPayload result;
|
||||
|
||||
// Do not skip packet by default; I suppose packet is good enough by default.
|
||||
result.mDiscardPacket = false;
|
||||
|
||||
// Wrap incoming data with ByteArray to make bit dequeuing easy
|
||||
ByteBuffer dataIn(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
||||
BitReader br(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
||||
ByteBuffer byte_reader(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
||||
BitReader bit_reader (input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
||||
|
||||
// In bandwidth-efficient mode, the payload header simply consists of a
|
||||
// 4-bit codec mode request:
|
||||
|
|
@ -135,16 +81,15 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
|||
// AMR, as defined in Table 1a in [2], or 0-8 for AMR-WB, as defined
|
||||
// in Table 1a in [4]. CMR value 15 indicates that no mode request
|
||||
// is present, and other values are for future use.
|
||||
result.mCodeModeRequest = static_cast<uint8_t>(br.readBits(4));
|
||||
//ICELogMedia(<< "CMR: " << result.mCodeModeRequest);
|
||||
result.mCodeModeRequest = static_cast<uint8_t>(bit_reader.readBits(4));
|
||||
|
||||
// Consume extra 4 bits for octet aligned profile
|
||||
if (input.mOctetAligned)
|
||||
br.readBits(4);
|
||||
bit_reader.readBits(4);
|
||||
|
||||
// Skip interleaving flags for now for octet aligned mode
|
||||
if (input.mInterleaving && input.mOctetAligned)
|
||||
br.readBits(8);
|
||||
bit_reader.readBits(8);
|
||||
|
||||
// Silence codec mode constant (it differs for wideband and narrowband codecs)
|
||||
uint8_t SID_FT = input.mWideband ? 9 : 8;
|
||||
|
|
@ -158,105 +103,121 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
|||
// F (1 bit): If set to 1, indicates that this frame is followed by
|
||||
// another speech frame in this payload; if set to 0, indicates that
|
||||
// this frame is the last frame in this payload.
|
||||
F = br.readBit();
|
||||
F = bit_reader.readBit();
|
||||
|
||||
// FT (4 bits): Frame type index, indicating either the AMR or AMR-WB
|
||||
// speech coding mode or comfort noise (SID) mode of the
|
||||
// corresponding frame carried in this payload.
|
||||
FT = static_cast<uint8_t>(br.readBits(4));
|
||||
FT = static_cast<uint8_t>(bit_reader.readBits(4));
|
||||
|
||||
// Q (1 bit): Frame quality indicator. If set to 0, indicates the
|
||||
// corresponding frame is severely damaged, and the receiver should
|
||||
// set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD
|
||||
// depending on the frame type (FT).
|
||||
Q = br.readBit();
|
||||
Q = bit_reader.readBit();
|
||||
|
||||
// Handle padding for octet alignment
|
||||
if (input.mOctetAligned)
|
||||
bit_reader.readBits(2);
|
||||
|
||||
AmrFrame frame;
|
||||
frame.mFrameType = FT;
|
||||
frame.mSTI = 0;
|
||||
frame.mMode = FT < SID_FT ? FT : 0xFF;
|
||||
frame.mGoodQuality = Q == 1;
|
||||
frame.mTimestamp = input.mCurrentTimestamp;
|
||||
result.mFrames.push_back(frame);
|
||||
input.mCurrentTimestamp += input.mWideband ? 320 : 160;
|
||||
}
|
||||
while (F != 0);
|
||||
|
||||
for (size_t frameIndex=0; frameIndex < result.mFrames.size() && !result.mDiscardPacket; frameIndex++)
|
||||
{
|
||||
AmrFrame& f = result.mFrames[frameIndex];
|
||||
|
||||
// If receiving a ToC entry with a FT value in the range 9-14 for AMR or
|
||||
// 10-13 for AMR-WB, the whole packet SHOULD be discarded. This is to
|
||||
// avoid the loss of data synchronization in the depacketization
|
||||
// process, which can result in a huge degradation in speech quality.
|
||||
if ((input.mWideband && (FT >= 10 && FT <= 13)) ||
|
||||
(!input.mWideband && (FT >= 9 && FT <= 14)))
|
||||
bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14);
|
||||
// discard |= input.mWideband ? f.mFrameType >= 14 : f.mFrameType >= 15;
|
||||
if (discard)
|
||||
{
|
||||
ICELogMedia(<< "Discard corrupted packet");
|
||||
// Discard bad packet
|
||||
result.mDiscardPacket = true;
|
||||
return result;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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++)
|
||||
if (input.mWideband && f.mFrameType == 15)
|
||||
{
|
||||
AmrFrame& frame = result.mFrames[frameIndex];
|
||||
size_t bitsLength = input.mWideband ? amrwb_framelenbits[frame.mFrameType] : amrnb_framelenbits[frame.mFrameType];
|
||||
size_t byteLength = input.mWideband ? amrwb_framelen[frame.mFrameType] : amrnb_framelen[frame.mFrameType];
|
||||
ICELogMedia(<< "New AMR speech frame: frame type = " << FT <<
|
||||
", mode = " << frame.mMode <<
|
||||
", timestamp = " << static_cast<int>(frame.mTimestamp) <<
|
||||
", bits length = " << static_cast<int>(bitsLength) <<
|
||||
", byte length =" << static_cast<int>(byteLength) <<
|
||||
", remaining packet length = " << static_cast<int>(dataIn.size()));
|
||||
// DTX, no sense to decode the data
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input.mWideband && f.mFrameType == 14)
|
||||
{
|
||||
// Speech lost code only
|
||||
continue;
|
||||
}
|
||||
|
||||
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 (input.mOctetAligned)
|
||||
{
|
||||
if (dataIn.size() < byteLength)
|
||||
frame.mGoodQuality = false;
|
||||
if (byte_reader.size() < byteLength)
|
||||
f.mGoodQuality = false;
|
||||
else
|
||||
{
|
||||
// It is octet aligned scheme, so we are on byte boundary now
|
||||
size_t byteOffset = br.count() / 8;
|
||||
size_t byteOffset = bit_reader.position() / 8;
|
||||
|
||||
// Copy data of AMR frame
|
||||
frame.mData = std::make_shared<ByteBuffer>(input.mPayload + byteOffset, byteLength);
|
||||
if (byteOffset + byteLength <= input.mPayloadLength)
|
||||
{
|
||||
f.mData = std::make_shared<ByteBuffer>();
|
||||
f.mData->resize(byteLength + 1); // payload + header
|
||||
memcpy(f.mData->mutableData() + 1, input.mPayload + byteOffset, byteLength);
|
||||
|
||||
// Add header for decoder
|
||||
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
ICELogError(<< "Problem parsing AMR header: octet-aligned is set, available " << int(input.mPayloadLength - byteOffset)
|
||||
<< " bytes but requested " << (int)byteLength);
|
||||
result.mDiscardPacket = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate place for copying
|
||||
frame.mData = std::make_shared<ByteBuffer>();
|
||||
frame.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
|
||||
f.mData = std::make_shared<ByteBuffer>();
|
||||
f.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
|
||||
|
||||
// Add header for decoder
|
||||
frame.mData->mutableData()[0] = (frame.mFrameType << 3) | (1 << 2);
|
||||
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
|
||||
|
||||
// Read bits
|
||||
if (br.readBits(frame.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
|
||||
frame.mGoodQuality = false;
|
||||
if (bit_reader.readBits(f.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
|
||||
f.mGoodQuality = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Padding bits are skipped
|
||||
|
||||
/*if (br.count() / 8 != br.position() / 8 &&
|
||||
br.count() / 8 != br.position() / 8 + 1)
|
||||
throw std::runtime_error("Failed to parse AMR frame");*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*static void predecodeAmrFrame(AmrFrame& frame, ByteBuffer& data)
|
||||
{
|
||||
// Data are already moved into
|
||||
}*/
|
||||
|
||||
AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
|
||||
:mConfig(config)
|
||||
{
|
||||
|
|
@ -278,12 +239,9 @@ int AmrNbCodec::CodecFactory::payloadType()
|
|||
return mConfig.mPayloadType;
|
||||
}
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
|
||||
void AmrNbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
int AmrNbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||
{
|
||||
|
|
@ -295,7 +253,6 @@ void AmrNbCodec::CodecFactory::create(CodecMap& codecs)
|
|||
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig));
|
||||
}
|
||||
|
||||
#endif
|
||||
PCodec AmrNbCodec::CodecFactory::create()
|
||||
{
|
||||
return PCodec(new AmrNbCodec(mConfig));
|
||||
|
|
@ -378,6 +335,9 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
|
|||
#define AMR_BITRATE_DTX 15
|
||||
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||
{
|
||||
if (mConfig.mOctetAligned)
|
||||
return 0;
|
||||
|
||||
if (mConfig.mIuUP)
|
||||
{
|
||||
// Try to parse IuUP frame
|
||||
|
|
@ -485,7 +445,7 @@ int AmrNbCodec::plc(int lostFrames, void* output, int outputCapacity)
|
|||
|
||||
for (int i=0; i < lostFrames; i++)
|
||||
{
|
||||
unsigned char buffer[32];
|
||||
uint8_t buffer[32];
|
||||
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
|
||||
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
|
||||
dataOut += L_FRAME;
|
||||
|
|
@ -519,12 +479,8 @@ int AmrWbCodec::CodecFactory::payloadType()
|
|||
return mConfig.mPayloadType;
|
||||
}
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
|
||||
void AmrWbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
int AmrWbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||
{
|
||||
|
|
@ -536,19 +492,17 @@ void AmrWbCodec::CodecFactory::create(CodecMap& codecs)
|
|||
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PCodec AmrWbCodec::CodecFactory::create()
|
||||
{
|
||||
return PCodec(new AmrWbCodec(mConfig));
|
||||
}
|
||||
|
||||
AmrWbStatistics MT::GAmrWbStatistics;
|
||||
|
||||
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
|
||||
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config),
|
||||
mSwitchCounter(0), mPreviousPacketLength(0)
|
||||
{
|
||||
//mEncoderCtx = E_IF_init();
|
||||
mDecoderCtx = D_IF_init();
|
||||
mCurrentDecoderTimestamp = 0;
|
||||
}
|
||||
|
|
@ -595,36 +549,16 @@ int AmrWbCodec::samplerate()
|
|||
|
||||
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||
{
|
||||
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();
|
||||
throw Exception(ERR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
#define L_FRAME 160
|
||||
#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;
|
||||
if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame))
|
||||
if (!IuUP::parse2(input.data(), input.size(), frame))
|
||||
return 0;
|
||||
|
||||
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
|
||||
|
|
@ -633,9 +567,6 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Build first byte to help decoder
|
||||
//ICELogDebug(<< "Decoding AMR frame length = " << frame.mPayloadSize);
|
||||
|
||||
// Reserve space
|
||||
ByteBuffer dataToDecode;
|
||||
dataToDecode.resize(1 + frame.mPayloadSize);
|
||||
|
|
@ -652,16 +583,17 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
|||
|
||||
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
|
||||
|
||||
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0);
|
||||
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0);
|
||||
return pcmLength();
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output)
|
||||
{
|
||||
AmrPayloadInfo info;
|
||||
info.mCurrentTimestamp = mCurrentDecoderTimestamp;
|
||||
info.mOctetAligned = mConfig.mOctetAligned;
|
||||
info.mPayload = (const uint8_t*)input;
|
||||
info.mPayloadLength = inputBytes;
|
||||
info.mPayload = input.data();
|
||||
info.mPayloadLength = input.size();
|
||||
info.mWideband = true;
|
||||
info.mInterleaving = false;
|
||||
|
||||
|
|
@ -672,6 +604,7 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
|||
}
|
||||
catch(...)
|
||||
{
|
||||
GAmrWbStatistics.mNonParsed++;
|
||||
ICELogDebug(<< "Failed to decode AMR payload");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -680,13 +613,17 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
|||
|
||||
// Check if packet is corrupted
|
||||
if (ap.mDiscardPacket)
|
||||
{
|
||||
GAmrWbStatistics.mDiscarded++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check for output buffer capacity
|
||||
if (outputCapacity < (int)ap.mFrames.size() * pcmLength())
|
||||
if (output.size() < (int)ap.mFrames.size() * pcmLength())
|
||||
return 0;
|
||||
|
||||
short* dataOut = (short*)output;
|
||||
short* dataOut = (short*)output.data();
|
||||
size_t dataOutSizeInBytes = 0;
|
||||
for (AmrFrame& frame: ap.mFrames)
|
||||
{
|
||||
memset(dataOut, 0, static_cast<size_t>(pcmLength()));
|
||||
|
|
@ -695,10 +632,21 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
|||
{
|
||||
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
|
||||
dataOut += pcmLength() / 2;
|
||||
dataOutSizeInBytes += pcmLength();
|
||||
}
|
||||
}
|
||||
return pcmLength() * ap.mFrames.size();
|
||||
}
|
||||
return dataOutSizeInBytes;
|
||||
}
|
||||
|
||||
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||
{
|
||||
auto inputBuffer = std::span<const uint8_t>((uint8_t*)input, (size_t)inputBytes);
|
||||
auto outputBuffer = std::span<uint8_t>((uint8_t*)output, (size_t)outputCapacity);
|
||||
|
||||
if (mConfig.mIuUP)
|
||||
return decodeIuup(inputBuffer, outputBuffer);
|
||||
else
|
||||
return decodePlain(inputBuffer, outputBuffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -750,8 +698,6 @@ int GsmEfrCodec::GsmEfrFactory::payloadType()
|
|||
return mPayloadType;
|
||||
}
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
|
||||
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||
{
|
||||
|
||||
|
|
@ -767,8 +713,6 @@ void GsmEfrCodec::GsmEfrFactory::create(CodecMap& codecs)
|
|||
codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PCodec GsmEfrCodec::GsmEfrFactory::create()
|
||||
{
|
||||
return PCodec(new GsmEfrCodec(mIuUP));
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#ifndef MT_AMRCODEC_H
|
||||
#define MT_AMRCODEC_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include <map>
|
||||
#include <span>
|
||||
|
||||
#include "MT_Codec.h"
|
||||
#include "../helper/HL_Pointer.h"
|
||||
|
||||
|
|
@ -14,16 +16,16 @@
|
|||
|
||||
namespace MT
|
||||
{
|
||||
struct AmrCodecConfig
|
||||
{
|
||||
bool mIuUP;
|
||||
bool mOctetAligned;
|
||||
int mPayloadType;
|
||||
};
|
||||
struct AmrCodecConfig
|
||||
{
|
||||
bool mIuUP = false;
|
||||
bool mOctetAligned = false;
|
||||
int mPayloadType = -1;
|
||||
};
|
||||
|
||||
class AmrNbCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
class AmrNbCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
void* mEncoderCtx;
|
||||
void* mDecoderCtx;
|
||||
AmrCodecConfig mConfig;
|
||||
|
|
@ -31,7 +33,7 @@ namespace MT
|
|||
int mSwitchCounter;
|
||||
int mPreviousPacketLength;
|
||||
|
||||
public:
|
||||
public:
|
||||
class CodecFactory: public Factory
|
||||
{
|
||||
public:
|
||||
|
|
@ -41,11 +43,10 @@ namespace MT
|
|||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
void create(CodecMap& codecs) override;
|
||||
#endif
|
||||
|
||||
PCodec create() override;
|
||||
|
||||
protected:
|
||||
|
|
@ -64,11 +65,18 @@ namespace MT
|
|||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
int getSwitchCounter() const;
|
||||
};
|
||||
};
|
||||
|
||||
class AmrWbCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
struct AmrWbStatistics
|
||||
{
|
||||
int mDiscarded = 0;
|
||||
int mNonParsed = 0;
|
||||
};
|
||||
extern AmrWbStatistics GAmrWbStatistics;
|
||||
|
||||
class AmrWbCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
void* mEncoderCtx;
|
||||
void* mDecoderCtx;
|
||||
AmrCodecConfig mConfig;
|
||||
|
|
@ -76,7 +84,10 @@ namespace MT
|
|||
int mSwitchCounter;
|
||||
int mPreviousPacketLength;
|
||||
|
||||
public:
|
||||
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:
|
||||
class CodecFactory: public Factory
|
||||
{
|
||||
public:
|
||||
|
|
@ -86,11 +97,10 @@ namespace MT
|
|||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
void create(CodecMap& codecs) override;
|
||||
#endif
|
||||
|
||||
PCodec create() override;
|
||||
|
||||
protected:
|
||||
|
|
@ -109,16 +119,16 @@ namespace MT
|
|||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
int getSwitchCounter() const;
|
||||
};
|
||||
};
|
||||
|
||||
class GsmEfrCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
class GsmEfrCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
void* mEncoderCtx;
|
||||
void* mDecoderCtx;
|
||||
bool mIuUP;
|
||||
|
||||
public:
|
||||
public:
|
||||
class GsmEfrFactory: public Factory
|
||||
{
|
||||
public:
|
||||
|
|
@ -128,11 +138,10 @@ namespace MT
|
|||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
void create(CodecMap& codecs) override;
|
||||
#endif
|
||||
|
||||
PCodec create() override;
|
||||
|
||||
protected:
|
||||
|
|
@ -151,7 +160,7 @@ namespace MT
|
|||
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
};
|
||||
};
|
||||
|
||||
} // End of MT namespace
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#define NOMINMAX
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "MT_AudioCodec.h"
|
||||
#include "MT_CodecList.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
|
|
@ -58,18 +58,14 @@ int G729Codec::G729Factory::payloadType()
|
|||
return 18;
|
||||
}
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PCodec G729Codec::G729Factory::create()
|
||||
{
|
||||
return std::make_shared<G729Codec>();
|
||||
|
|
@ -241,7 +237,6 @@ OpusCodec::Params::Params()
|
|||
mTargetBitrate = OPUS_TARGET_BITRATE;
|
||||
}
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
resip::Data OpusCodec::Params::toString() const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
|
@ -324,7 +319,7 @@ void OpusCodec::Params::parse(const resip::Data ¶ms)
|
|||
mPtime = strx::toInt(paramIter->mValue.c_str(), 20);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
OpusCodec::OpusFactory::OpusFactory(int samplerate, int channels, int ptype)
|
||||
{
|
||||
mSamplerate = samplerate;
|
||||
|
|
@ -352,7 +347,6 @@ int OpusCodec::OpusFactory::payloadType()
|
|||
return mPType;
|
||||
}
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void OpusCodec::OpusFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||
{
|
||||
// Put opus codec record
|
||||
|
|
@ -394,7 +388,6 @@ int OpusCodec::OpusFactory::processSdp(const resip::SdpContents::Session::Medium
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
PCodec OpusCodec::OpusFactory::create()
|
||||
{
|
||||
|
|
@ -407,15 +400,13 @@ PCodec OpusCodec::OpusFactory::create()
|
|||
}
|
||||
|
||||
OpusCodec::OpusCodec(int samplerate, int channels, int ptime)
|
||||
:mEncoderCtx(NULL), mDecoderCtx(NULL), mChannels(channels), mPTime(ptime), mSamplerate(samplerate)
|
||||
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(channels), mPTime(ptime), mSamplerate(samplerate), mDecoderChannels(0)
|
||||
{
|
||||
int status;
|
||||
mEncoderCtx = opus_encoder_create(48000, mChannels, OPUS_APPLICATION_VOIP, &status);
|
||||
mEncoderCtx = opus_encoder_create(mSamplerate, mChannels, OPUS_APPLICATION_VOIP, &status);
|
||||
if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_COMPLEXITY(OPUS_CODEC_COMPLEXITY)))
|
||||
ICELogError(<< "Failed to set Opus encoder complexity");
|
||||
//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);
|
||||
// Decoder creation is postponed until first packet arriving (because it may use different channel number
|
||||
}
|
||||
|
||||
void OpusCodec::applyParams(const Params ¶ms)
|
||||
|
|
@ -433,7 +424,7 @@ void OpusCodec::applyParams(const Params ¶ms)
|
|||
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));
|
||||
|
||||
mDecodeResampler.start(channels(), 48000, mSamplerate);
|
||||
// mDecodeResampler.start(channels(), 48000, mSamplerate);
|
||||
}
|
||||
|
||||
OpusCodec::~OpusCodec()
|
||||
|
|
@ -441,13 +432,13 @@ OpusCodec::~OpusCodec()
|
|||
if (mDecoderCtx)
|
||||
{
|
||||
opus_decoder_destroy(mDecoderCtx);
|
||||
mDecoderCtx = NULL;
|
||||
mDecoderCtx = nullptr;
|
||||
}
|
||||
|
||||
if (mEncoderCtx)
|
||||
{
|
||||
opus_encoder_destroy(mEncoderCtx);
|
||||
mEncoderCtx = NULL;
|
||||
mEncoderCtx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -493,42 +484,113 @@ 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 nr_of_channels = opus_packet_get_nb_channels((const unsigned char*)input);
|
||||
assert(nr_of_channels == channels());
|
||||
int result = 0;
|
||||
|
||||
int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char*)input, inputBytes);
|
||||
if (nr_of_frames > 0)
|
||||
// Examine the number of channels available in incoming packet
|
||||
int nr_of_channels = opus_packet_get_nb_channels((const unsigned char *) input);
|
||||
|
||||
// Recreate decoder if needed
|
||||
if (mDecoderChannels != nr_of_channels)
|
||||
{
|
||||
// Send number of bytes for input and number of samples for output
|
||||
int capacity = nr_of_frames * sizeof(opus_int16) * channels();
|
||||
if (mDecoderCtx)
|
||||
{
|
||||
opus_decoder_destroy(mDecoderCtx);
|
||||
mDecoderCtx = nullptr;
|
||||
}
|
||||
mDecoderChannels = nr_of_channels;
|
||||
}
|
||||
|
||||
// Dangerous !
|
||||
opus_int16* buffer = (opus_int16*)alloca(capacity);
|
||||
int decoded = opus_decode(mDecoderCtx, (const unsigned char*)input, inputBytes, (opus_int16*)buffer, capacity / (sizeof(short) * channels()), 0 /* FEC decoding is for lost packets */);
|
||||
if (!mDecoderCtx)
|
||||
{
|
||||
int status = 0;
|
||||
mDecoderCtx = opus_decoder_create(mSamplerate, mDecoderChannels, &status);
|
||||
if (status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char *) input,
|
||||
inputBytes);
|
||||
if (nr_of_frames <= 0)
|
||||
return 0;
|
||||
|
||||
// We support stereo and mono here.
|
||||
int buffer_capacity = nr_of_frames * sizeof(opus_int16) * nr_of_channels;
|
||||
opus_int16 *buffer_decode = (opus_int16 *)alloca(buffer_capacity);
|
||||
int decoded = opus_decode(mDecoderCtx,
|
||||
reinterpret_cast<const unsigned char *>(input), inputBytes,
|
||||
buffer_decode, nr_of_frames, 0);
|
||||
if (decoded < 0)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
// Resample 48000 to requested samplerate
|
||||
size_t processed = 0;
|
||||
return mDecodeResampler.processBuffer(buffer, decoded * sizeof(short) * channels(), processed, output, outputCapacity);
|
||||
}
|
||||
}
|
||||
else
|
||||
ICELogCritical(<< "opus_decode() returned " << decoded);
|
||||
return 0;
|
||||
}
|
||||
|
||||
opus_int16 *buffer_stereo = nullptr;
|
||||
int buffer_stereo_capacity = buffer_capacity * 2;
|
||||
|
||||
switch (nr_of_channels) {
|
||||
case 1:
|
||||
// Convert to stereo before
|
||||
buffer_stereo = (opus_int16 *) alloca(buffer_stereo_capacity);
|
||||
for (int i = 0; i < nr_of_frames; i++) {
|
||||
buffer_stereo[i * 2 + 1] = buffer_decode[i];
|
||||
buffer_stereo[i * 2] = buffer_decode[i];
|
||||
}
|
||||
assert(buffer_stereo_capacity <= outputCapacity);
|
||||
memcpy(output, buffer_stereo, buffer_stereo_capacity);
|
||||
result = buffer_stereo_capacity;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
assert(buffer_capacity <= outputCapacity);
|
||||
memcpy(output, buffer_decode, buffer_capacity);
|
||||
result = buffer_capacity;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int OpusCodec::plc(int lostFrames, void* output, int outputCapacity)
|
||||
int OpusCodec::plc(int lostPackets, void* output, int outputCapacity)
|
||||
{
|
||||
int nrOfSamplesInFrame = pcmLength() / (sizeof(short) * channels());
|
||||
// Find how much frames do we need to produce and prefill it with silence
|
||||
int frames_per_packet = (int)pcmLength() / (sizeof(opus_int16) * channels());
|
||||
memset(output, 0, outputCapacity);
|
||||
|
||||
int nr_of_decoded_samples = 0;
|
||||
for (int i=0; i<lostFrames; i++)
|
||||
// Use this pointer as output
|
||||
opus_int16* data_output = reinterpret_cast<opus_int16*>(output);
|
||||
|
||||
int nr_of_decoded_frames = 0;
|
||||
|
||||
// Buffer for single lost frame
|
||||
opus_int16* buffer_plc = (opus_int16*)alloca(frames_per_packet * mDecoderChannels * sizeof(opus_int16));
|
||||
for (int i=0; i<lostPackets; i++)
|
||||
{
|
||||
nr_of_decoded_samples += opus_decode(mDecoderCtx, nullptr, 0, (opus_int16*)output + nrOfSamplesInFrame * i, nrOfSamplesInFrame, 0);
|
||||
nr_of_decoded_frames = opus_decode(mDecoderCtx, nullptr, 0, buffer_plc, frames_per_packet, 0);
|
||||
assert(nr_of_decoded_frames == frames_per_packet);
|
||||
switch (mDecoderChannels)
|
||||
{
|
||||
case 1:
|
||||
// Convert mono to stereo
|
||||
for (int i=0; i < nr_of_decoded_frames; i++)
|
||||
{
|
||||
data_output[i * 2] = buffer_plc[i];
|
||||
data_output[i * 2 + 1] = buffer_plc[i+1];
|
||||
}
|
||||
return nr_of_decoded_samples ? lostFrames * pcmLength() : 0;
|
||||
data_output += frames_per_packet * mChannels;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Just copy data
|
||||
memcpy(data_output, buffer_plc, frames_per_packet * sizeof(opus_int16) * mDecoderChannels);
|
||||
data_output += frames_per_packet * mChannels;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ((char*)data_output - (char*)output) * sizeof(opus_int16);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -538,7 +600,7 @@ int OpusCodec::plc(int lostFrames, void* output, int outputCapacity)
|
|||
#define ILBC_CODEC_NAME "ILBC"
|
||||
|
||||
IlbcCodec::IlbcCodec(int packetTime)
|
||||
:mPacketTime(packetTime), mEncoderCtx(nullptr), mDecoderCtx(nullptr)
|
||||
:mPacketTime(packetTime), mEncoderCtx(nullptr), mDecoderCtx(nullptr)
|
||||
{
|
||||
WebRtcIlbcfix_EncoderCreate(&mEncoderCtx);
|
||||
WebRtcIlbcfix_DecoderCreate(&mDecoderCtx);
|
||||
|
|
@ -652,7 +714,6 @@ PCodec IlbcCodec::IlbcFactory::create()
|
|||
return PCodec(new IlbcCodec(mPtime));
|
||||
}
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void IlbcCodec::IlbcFactory::create(CodecMap& codecs)
|
||||
{
|
||||
codecs[mPType20ms] = PCodec(create());
|
||||
|
|
@ -714,13 +775,11 @@ int IlbcCodec::IlbcFactory::processSdp(const resip::SdpContents::Session::Medium
|
|||
return pt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// --- IsacCodec(s) ---
|
||||
#define ISAC_CODEC_NAME "ISAC"
|
||||
|
||||
IsacCodec::IsacCodec(int samplerate)
|
||||
:mSamplerate(samplerate)
|
||||
:mSamplerate(samplerate)
|
||||
{
|
||||
// This code initializes isac encoder to automatic mode - it will adjust its bitrate automatically.
|
||||
// Frame time is 60 ms
|
||||
|
|
@ -860,7 +919,7 @@ PCodec IsacCodec::IsacFactory32K::create()
|
|||
#define ALAW_CODEC_NAME "PCMA"
|
||||
|
||||
G711Codec::G711Codec(int type)
|
||||
:mType(type)
|
||||
:mType(type)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -1093,8 +1152,8 @@ int GsmCodec::plc(int lostFrames, void* output, int outputCapacity)
|
|||
|
||||
G722Codec::G722Codec()
|
||||
{
|
||||
mEncoder = g722_encode_init(NULL, 64000, 0);
|
||||
mDecoder = g722_decode_init(NULL, 64000, 0);
|
||||
mEncoder = g722_encode_init(nullptr, 64000, 0);
|
||||
mDecoder = g722_decode_init(nullptr, 64000, 0);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef __AUDIO_CODEC_H
|
||||
#define __AUDIO_CODEC_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include <map>
|
||||
#include "MT_Codec.h"
|
||||
#include "../audio/Audio_Resampler.h"
|
||||
|
|
@ -46,10 +46,9 @@ public:
|
|||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
#endif
|
||||
|
||||
PCodec create() override;
|
||||
};
|
||||
G729Codec();
|
||||
|
|
@ -73,7 +72,8 @@ protected:
|
|||
OpusEncoder *mEncoderCtx;
|
||||
OpusDecoder *mDecoderCtx;
|
||||
int mPTime, mSamplerate, mChannels;
|
||||
Audio::SpeexResampler mDecodeResampler;
|
||||
// Audio::SpeexResampler mDecodeResampler;
|
||||
int mDecoderChannels;
|
||||
public:
|
||||
struct Params
|
||||
{
|
||||
|
|
@ -81,10 +81,8 @@ public:
|
|||
int mPtime, mTargetBitrate, mExpectedPacketLoss;
|
||||
|
||||
Params();
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
resip::Data toString() const;
|
||||
void parse(const resip::Data& params);
|
||||
#endif
|
||||
};
|
||||
|
||||
class OpusFactory: public Factory
|
||||
|
|
@ -102,10 +100,8 @@ public:
|
|||
int channels() override;
|
||||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
#endif
|
||||
PCodec create() override;
|
||||
};
|
||||
|
||||
|
|
@ -145,11 +141,9 @@ public:
|
|||
const char* name();
|
||||
int samplerate();
|
||||
int payloadType();
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
void create(CodecMap& codecs);
|
||||
#endif
|
||||
PCodec create();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
* 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/. */
|
||||
|
||||
#define NOMINMAX
|
||||
#if defined(TARGET_WIN) && !defined(NOMINMAX)
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "MT_AudioReceiver.h"
|
||||
#include "MT_AudioCodec.h"
|
||||
#include "MT_CngHelper.h"
|
||||
|
|
@ -14,6 +16,7 @@
|
|||
#include "../audio/Audio_Interface.h"
|
||||
#include "../audio/Audio_Resampler.h"
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC)
|
||||
# include "MT_AmrCodec.h"
|
||||
|
|
@ -62,6 +65,8 @@ std::vector<short>& RtpBuffer::Packet::pcm()
|
|||
RtpBuffer::RtpBuffer(Statistics& stat)
|
||||
:mStat(stat)
|
||||
{
|
||||
if (mStat.mPacketLoss)
|
||||
std::cout << "Warning: packet loss is not zero" << std::endl;
|
||||
}
|
||||
|
||||
RtpBuffer::~RtpBuffer()
|
||||
|
|
@ -117,7 +122,6 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPa
|
|||
|
||||
Lock l(mGuard);
|
||||
|
||||
|
||||
// Update statistics
|
||||
if (mLastAddTime == 0.0)
|
||||
mLastAddTime = now_ms();
|
||||
|
|
@ -197,13 +201,14 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
|||
// See if there is enough information in buffer
|
||||
int total = findTimelength();
|
||||
|
||||
while (total > mHigh && mPacketList.size())
|
||||
while (total > mHigh && mPacketList.size() && 0 != mHigh)
|
||||
{
|
||||
ICELogMedia( << "Dropping RTP packets from jitter buffer");
|
||||
total -= mPacketList.front()->timelength();
|
||||
|
||||
// Save it as last packet however - to not confuse loss packet counter
|
||||
mFetchedPacket = mPacketList.front();
|
||||
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||
|
||||
// Erase from packet list
|
||||
mPacketList.erase(mPacketList.begin());
|
||||
|
|
@ -213,47 +218,48 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
|||
}
|
||||
|
||||
if (total < mLow)
|
||||
{
|
||||
// Still not prebuffered
|
||||
result = FetchResult::NoPacket;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Did we fetch any packet before ?
|
||||
bool is_fetched_packet = mFetchedPacket.get() != nullptr;
|
||||
if (is_fetched_packet)
|
||||
is_fetched_packet &= mFetchedPacket->rtp().get() != nullptr;
|
||||
|
||||
if (is_fetched_packet)
|
||||
if (mLastSeqno.has_value())
|
||||
{
|
||||
if (mPacketList.empty())
|
||||
{
|
||||
result = FetchResult::NoPacket;
|
||||
mStat.mPacketLoss++;
|
||||
// Don't increase counter of lost packets here; maybe it is DTX
|
||||
}
|
||||
else
|
||||
{
|
||||
// Current sequence number ?
|
||||
unsigned seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||
uint32_t seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||
|
||||
// Gap between new packet and previous on
|
||||
int gap = (int64_t)seqno - (int64_t)mFetchedPacket->rtp()->GetExtendedSequenceNumber() - 1;
|
||||
int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
|
||||
gap = std::min(gap, 127);
|
||||
if (gap > 0 && mPacketList.empty())
|
||||
if (gap > 0)
|
||||
{
|
||||
// std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl;
|
||||
mStat.mPacketLoss++;
|
||||
//mStat.mLoss[gap]++;
|
||||
mLastSeqno = *mLastSeqno + 1;
|
||||
result = FetchResult::Gap;
|
||||
mStat.mPacketLoss += gap;
|
||||
mStat.mLoss[gap]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gap > 0)
|
||||
{
|
||||
mStat.mPacketLoss += gap;
|
||||
mStat.mLoss[gap]++;
|
||||
}
|
||||
|
||||
result = FetchResult::RegularPacket;
|
||||
rl.push_back(mPacketList.front());
|
||||
|
||||
// Save last returned normal packet
|
||||
mFetchedPacket = mPacketList.front();
|
||||
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||
|
||||
// Remove returned packet from the list
|
||||
mPacketList.erase(mPacketList.begin());
|
||||
|
|
@ -263,7 +269,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
|||
else
|
||||
{
|
||||
// See if prebuffer limit is reached
|
||||
if (findTimelength() >= mPrebuffer)
|
||||
if (findTimelength() >= mPrebuffer && !mPacketList.empty())
|
||||
{
|
||||
// Normal packet will be returned
|
||||
result = FetchResult::RegularPacket;
|
||||
|
|
@ -273,6 +279,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
|||
|
||||
// Remember returned packet
|
||||
mFetchedPacket = mPacketList.front();
|
||||
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||
|
||||
// Remove returned packet from buffer list
|
||||
mPacketList.erase(mPacketList.begin());
|
||||
|
|
@ -331,6 +338,7 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
|
|||
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
|
||||
|
||||
// Init codecs
|
||||
mCodecList.setSettings(settings);
|
||||
mCodecList.fillCodecMap(mCodecMap);
|
||||
|
||||
#if defined(DUMP_DECODED)
|
||||
|
|
@ -355,11 +363,17 @@ void AudioReceiver::setCodecSettings(const CodecList::Settings& codecSettings)
|
|||
return;
|
||||
|
||||
mCodecSettings = codecSettings;
|
||||
mCodecMap.clear();
|
||||
mCodecList.setSettings(mCodecSettings);
|
||||
mCodecList.setSettings(mCodecSettings); // This builds factory list with proper payload types according to payload types in settings
|
||||
|
||||
// Rebuild codec map from factory list
|
||||
mCodecList.fillCodecMap(mCodecMap);
|
||||
}
|
||||
|
||||
CodecList::Settings& AudioReceiver::getCodecSettings()
|
||||
{
|
||||
return mCodecSettings;
|
||||
}
|
||||
|
||||
size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t output_capacity)
|
||||
{
|
||||
// How much data was produced
|
||||
|
|
@ -396,46 +410,56 @@ size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t out
|
|||
return result;
|
||||
}
|
||||
|
||||
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec)
|
||||
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** detectedCodec)
|
||||
{
|
||||
// Estimate time length
|
||||
int time_length = 0,
|
||||
samplerate = 8000,
|
||||
payloadLength = p->GetPayloadLength(),
|
||||
ptype = p->GetPayloadType();
|
||||
|
||||
// ICELogInfo(<< "Adding packet No " << p->GetSequenceNumber());
|
||||
// Increase codec counter
|
||||
mStat.mCodecCount[p->GetPayloadType()]++;
|
||||
mStat.mCodecCount[ptype]++;
|
||||
|
||||
// Check if codec can be handled
|
||||
CodecMap::iterator codecIter = mCodecMap.find(p->GetPayloadType());
|
||||
Codec* codec = nullptr;
|
||||
CodecMap::iterator codecIter = mCodecMap.find(ptype);
|
||||
if (codecIter == mCodecMap.end())
|
||||
{
|
||||
ICELogMedia(<< "Cannot find codec in available codecs");
|
||||
return false; // Reject packet with unknown payload type
|
||||
time_length = 10;
|
||||
}
|
||||
|
||||
// Check if codec is created actually
|
||||
else
|
||||
{
|
||||
// Check if codec is creating lazily
|
||||
if (!codecIter->second)
|
||||
{
|
||||
// Look for ptype
|
||||
for (int codecIndex = 0; codecIndex < mCodecList.count(); codecIndex++)
|
||||
if (mCodecList.codecAt(codecIndex).payloadType() == p->GetPayloadType())
|
||||
codecIter->second = mCodecList.codecAt(codecIndex).create();
|
||||
codecIter->second = mCodecList.createCodecByPayloadType(ptype);
|
||||
}
|
||||
codec = codecIter->second.get();
|
||||
|
||||
// Return pointer to codec if needed.get()
|
||||
if (codec)
|
||||
*codec = codecIter->second.get();
|
||||
if (detectedCodec)
|
||||
*detectedCodec = codec;
|
||||
|
||||
if (mStat.mCodecName.empty())
|
||||
mStat.mCodecName = codecIter->second->name();
|
||||
if (mStat.mCodecName.empty() && codec)
|
||||
mStat.mCodecName = codec->name();
|
||||
|
||||
// Estimate time length
|
||||
int time_length = 0, payloadLength = p->GetPayloadLength(), ptype = p->GetPayloadType();
|
||||
|
||||
if (!codecIter->second->rtpLength())
|
||||
time_length = codecIter->second->frameTime();
|
||||
if (!codec)
|
||||
time_length = 10;
|
||||
else
|
||||
time_length = lround(double(payloadLength) / codecIter->second->rtpLength() * codecIter->second->frameTime());
|
||||
if (!codec->rtpLength())
|
||||
time_length = codec->frameTime();
|
||||
else
|
||||
time_length = lround(double(payloadLength) / codec->rtpLength() * codec->frameTime());
|
||||
|
||||
if (codec)
|
||||
samplerate = codec->samplerate();
|
||||
}
|
||||
|
||||
// Process jitter
|
||||
mJitterStats.process(p.get(), codecIter->second->samplerate());
|
||||
mJitterStats.process(p.get(), samplerate);
|
||||
mStat.mJitter = static_cast<float>(mJitterStats.get());
|
||||
|
||||
// Check if packet is CNG
|
||||
|
|
@ -447,23 +471,26 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** co
|
|||
{
|
||||
// It will cause statistics to report about bad RTP packet
|
||||
// I have to replay last packet payload here to avoid report about lost packet
|
||||
mBuffer.add(p, time_length, codecIter->second->samplerate());
|
||||
mBuffer.add(p, time_length, samplerate);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Queue packet to buffer
|
||||
auto packet = mBuffer.add(p, time_length, codecIter->second->samplerate()).get();
|
||||
auto packet = mBuffer.add(p, time_length, samplerate).get();
|
||||
|
||||
if (packet)
|
||||
{
|
||||
// Check if early decoding configured
|
||||
if (mEarlyDecode && *codec)
|
||||
if (mEarlyDecode && codec)
|
||||
{
|
||||
// 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);
|
||||
memcpy(packet->pcm().data(), mDecodedFrame, available / 2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
|
@ -513,7 +540,17 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
|
|||
if (options & DecodeOptions_SkipDecode)
|
||||
mDecodedLength = 0;
|
||||
else
|
||||
{
|
||||
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)
|
||||
|
|
@ -530,7 +567,6 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
|
|||
|
||||
case RtpBuffer::FetchResult::RegularPacket:
|
||||
mFailedCount = 0;
|
||||
|
||||
for (std::shared_ptr<RtpBuffer::Packet>& p: rl)
|
||||
{
|
||||
assert(p);
|
||||
|
|
@ -637,8 +673,7 @@ AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, i
|
|||
// Decode frame by frame
|
||||
mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
|
||||
frameLength, mDecodedFrame, sizeof mDecodedFrame);
|
||||
// mDecodedLength = 3840; // Opus 20 ms stereo
|
||||
if (mDecodedLength)
|
||||
if (mDecodedLength > 0)
|
||||
processDecoded(output, options);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
@ -8,22 +8,16 @@
|
|||
|
||||
#include "MT_Stream.h"
|
||||
#include "MT_CodecList.h"
|
||||
#include "MT_AudioCodec.h"
|
||||
#include "MT_CngHelper.h"
|
||||
|
||||
#include "../helper/HL_Pointer.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "../helper/HL_Optional.hpp"
|
||||
|
||||
#include "jrtplib/src/rtppacket.h"
|
||||
#include "jrtplib/src/rtcppacket.h"
|
||||
#include "jrtplib/src/rtpsourcedata.h"
|
||||
#include "../audio/Audio_DataWindow.h"
|
||||
#include "../audio/Audio_Resampler.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
// #define DUMP_DECODED
|
||||
#include <optional>
|
||||
|
||||
namespace MT
|
||||
{
|
||||
|
|
@ -101,6 +95,7 @@ namespace MT
|
|||
bool mFirstPacketWillGo = true;
|
||||
jrtplib::RTPSourceStats mRtpStats;
|
||||
std::shared_ptr<Packet> mFetchedPacket;
|
||||
std::optional<uint32_t> mLastSeqno;
|
||||
|
||||
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
|
||||
float mLastAddTime = 0.0;
|
||||
|
|
@ -124,7 +119,7 @@ namespace MT
|
|||
|
||||
// Update codec settings
|
||||
void setCodecSettings(const CodecList::Settings& codecSettings);
|
||||
|
||||
CodecList::Settings& getCodecSettings();
|
||||
// Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding.
|
||||
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
|
||||
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
|
||||
|
|
|
|||
|
|
@ -306,18 +306,22 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
|||
int receiveLength = length;
|
||||
memcpy(mReceiveBuffer, buffer, length);
|
||||
|
||||
bool srtpResult;
|
||||
if (mSrtpSession.active())
|
||||
{
|
||||
bool srtpResult;
|
||||
size_t srcLength = length; size_t dstLength = sizeof mSrtpDecodeBuffer;
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength);
|
||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
||||
else
|
||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength);
|
||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
||||
if (!srtpResult)
|
||||
{
|
||||
ICELogError(<<"Cannot decrypt SRTP packet.");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(mReceiveBuffer, mSrtpDecodeBuffer, dstLength);
|
||||
receiveLength = dstLength;
|
||||
}
|
||||
|
||||
switch (source.family())
|
||||
|
|
@ -343,8 +347,8 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
|||
mStat.mReceived += length;
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
{
|
||||
if (!mStat.mFirstRtpTime.is_initialized())
|
||||
mStat.mFirstRtpTime = std::chrono::system_clock::now();
|
||||
if (!mStat.mFirstRtpTime)
|
||||
mStat.mFirstRtpTime = std::chrono::steady_clock::now();
|
||||
mStat.mReceivedRtp++;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef __MT_AUDIOSTREAM_H
|
||||
#define __MT_AUDIOSTREAM_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "MT_Stream.h"
|
||||
#include "MT_NativeRtpSender.h"
|
||||
#include "MT_SingleAudioStream.h"
|
||||
|
|
@ -87,7 +87,8 @@ protected:
|
|||
mCaptureResampler32,
|
||||
mCaptureResampler48;
|
||||
DtmfContext mDtmfContext;
|
||||
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE];
|
||||
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE],
|
||||
mSrtpDecodeBuffer[MAX_VALID_UDPPACKET_SIZE];
|
||||
|
||||
struct
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
|
||||
#include "MT_CngHelper.h"
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ int Codec::Factory::channels()
|
|||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void Codec::Factory::create(CodecMap& codecs)
|
||||
{
|
||||
codecs[payloadType()] = std::shared_ptr<Codec>(create());
|
||||
|
|
@ -38,4 +36,4 @@ int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecC
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
#ifndef __MT_CODEC_H
|
||||
#define __MT_CODEC_H
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
# include "resiprocate/resip/stack/SdpContents.hxx"
|
||||
#endif
|
||||
#include "resiprocate/resip/stack/SdpContents.hxx"
|
||||
#include "../helper/HL_Types.h"
|
||||
#include <map>
|
||||
#include "../helper/HL_Pointer.h"
|
||||
|
|
@ -36,14 +34,12 @@ public:
|
|||
virtual PCodec create() = 0;
|
||||
|
||||
virtual int channels();
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
typedef std::map<int, PCodec > CodecMap;
|
||||
virtual void create(CodecMap& codecs);
|
||||
virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
// Returns payload type from chosen codec if success. -1 is returned for negative result.
|
||||
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
resip::Codec resipCodec();
|
||||
#endif
|
||||
};
|
||||
virtual ~Codec() {}
|
||||
virtual const char* name() = 0;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "MT_CodecList.h"
|
||||
#include "MT_AudioCodec.h"
|
||||
|
||||
|
|
@ -25,33 +25,95 @@ using namespace MT;
|
|||
|
||||
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::ostringstream oss;
|
||||
oss << "wrap IuUP: " << mWrapIuUP << std::endl
|
||||
<< "skip decode: " << mSkipDecode << std::endl;
|
||||
for (int ptype: mAmrWbPayloadType)
|
||||
oss << "AMR WB ptype: " << ptype << std::endl;
|
||||
for (int ptype: mAmrWbOctetPayloadType)
|
||||
oss << "AMR WB octet-aligned ptype: " << ptype << std::endl;
|
||||
for (int ptype: mAmrNbPayloadType)
|
||||
oss << "AMR NB ptype: " << ptype << std::endl;
|
||||
for (int ptype: mAmrNbOctetPayloadType)
|
||||
oss << "AMR NB octet-aligned ptype:" << ptype << std::endl;
|
||||
// oss << "wrap IuUP: " << mWrapIuUP << std::endl
|
||||
// << "skip decode: " << mSkipDecode << std::endl;
|
||||
|
||||
oss << "ISAC 16Khz ptype: " << mIsac16KPayloadType << std::endl
|
||||
<< "ISAC 32Khz ptype: " << mIsac32KPayloadType << std::endl
|
||||
<< "iLBC 20ms ptype: " << mIlbc20PayloadType << std::endl
|
||||
<< "iLBC 30ms ptype: " << mIlbc30PayloadType << std::endl
|
||||
<< "GSM FR ptype: " << mGsmFrPayloadType << ", GSM FR plength: " << mGsmFrPayloadLength << std::endl
|
||||
<< "GSM HR ptype: " << mGsmHrPayloadType << std::endl
|
||||
<< "GSM EFR ptype: " << mGsmEfrPayloadType << std::endl;
|
||||
if (!mAmrWbPayloadType.empty())
|
||||
{
|
||||
oss << "AMR WB ptype: ";
|
||||
for (int ptype: mAmrWbPayloadType)
|
||||
oss << ptype << " ";
|
||||
}
|
||||
if (!mAmrWbOctetPayloadType.empty())
|
||||
{
|
||||
oss << "AMR WB octet ptype: ";
|
||||
for (int ptype: mAmrWbOctetPayloadType)
|
||||
oss << ptype << " ";
|
||||
}
|
||||
|
||||
if (!mAmrNbPayloadType.empty())
|
||||
{
|
||||
oss << "AMR ptype: ";
|
||||
for (int ptype: mAmrNbPayloadType)
|
||||
oss << ptype << " ";
|
||||
}
|
||||
|
||||
if (!mAmrNbOctetPayloadType.empty())
|
||||
{
|
||||
oss << "AMR octet ptype: ";
|
||||
for (int ptype: mAmrNbOctetPayloadType)
|
||||
oss << ptype << " ";
|
||||
}
|
||||
if (mIsac16KPayloadType != -1)
|
||||
oss << "ISAC 16Khz ptype: " << mIsac16KPayloadType << " ";
|
||||
|
||||
if (mIsac32KPayloadType != -1)
|
||||
oss << "ISAC 32Khz ptype: " << mIsac32KPayloadType << " ";
|
||||
|
||||
if (mIlbc20PayloadType != -1)
|
||||
oss << "iLBC 20ms ptype: " << mIlbc20PayloadType << " ";
|
||||
|
||||
if (mIlbc30PayloadType != -1)
|
||||
oss << "iLBC 30ms ptype: " << mIlbc30PayloadType << " ";
|
||||
|
||||
if (mGsmFrPayloadType != -1)
|
||||
oss << "GSM FR ptype: " << mGsmFrPayloadType << ", GSM FR plength: " << mGsmFrPayloadLength << " ";
|
||||
|
||||
if (mGsmHrPayloadType != -1)
|
||||
oss << "GSM HR ptype: " << mGsmHrPayloadType << " ";
|
||||
|
||||
if (mGsmEfrPayloadType != -1)
|
||||
oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " ";
|
||||
|
||||
for (auto& spec: mEvsSpec)
|
||||
{
|
||||
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << std::endl;
|
||||
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " ";
|
||||
}
|
||||
|
||||
for (auto& spec: mOpusSpec)
|
||||
|
|
@ -62,6 +124,24 @@ std::string CodecList::Settings::toString() const
|
|||
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
|
||||
{
|
||||
return mPayloadType >= 96 && mPayloadType <= 127;
|
||||
|
|
@ -71,7 +151,7 @@ CodecList::Settings::EvsSpec CodecList::Settings::EvsSpec::parse(const std::stri
|
|||
{
|
||||
EvsSpec result;
|
||||
|
||||
auto parts = strx::split(spec, "-/");
|
||||
auto parts = strx::split(spec, "-/ ,:");
|
||||
if (parts.size() == 3)
|
||||
{
|
||||
// Payload type number
|
||||
|
|
@ -116,7 +196,7 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
|
|||
{
|
||||
OpusSpec result;
|
||||
|
||||
auto parts = strx::split(spec, "-");
|
||||
auto parts = strx::split(spec, "-/ ,:");
|
||||
if (parts.size() == 3)
|
||||
{
|
||||
result.mPayloadType = strx::toInt(strx::trim(parts.front()).c_str(), -1);
|
||||
|
|
@ -126,7 +206,21 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
|
|||
return result;
|
||||
}
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
static int findOctetMode(const char* line)
|
||||
{
|
||||
const char* param_name = "octet-align=";
|
||||
auto p = strstr(line, param_name);
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
p += strlen(param_name);
|
||||
char int_buf[8] = {0};
|
||||
size_t int_buf_offset = 0;
|
||||
while (*p && isdigit(*p) && int_buf_offset < sizeof(int_buf))
|
||||
int_buf[int_buf_offset++] = *p++;
|
||||
return atoi(int_buf);
|
||||
}
|
||||
|
||||
CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>& codeclist)
|
||||
{
|
||||
CodecList::Settings r{DefaultSettings};
|
||||
|
|
@ -137,20 +231,50 @@ CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>&
|
|||
int samplerate = c.getRate();
|
||||
int ptype = c.payloadType();
|
||||
|
||||
auto enc_params = c.encodingParameters(); // This must channels number for Opus codec
|
||||
auto params = c.parameters();
|
||||
|
||||
// Dynamic payload type codecs only - ISAC / iLBC / Speex / etc.
|
||||
if (codec_name == "OPUS")
|
||||
{
|
||||
// Check the parameters
|
||||
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);
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool CodecList::Settings::operator == (const Settings& rhs) const
|
||||
{
|
||||
|
|
@ -200,21 +324,12 @@ CodecList::CodecList(const Settings& settings)
|
|||
|
||||
void CodecList::init(const Settings& settings)
|
||||
{
|
||||
for (auto f: mFactoryList)
|
||||
delete f;
|
||||
mFactoryList.clear();
|
||||
|
||||
mSettings = settings;
|
||||
#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)
|
||||
{
|
||||
mFactoryList.push_back(new OpusCodec::OpusFactory(spec.mRate, spec.mChannels, spec.mPayloadType));
|
||||
}
|
||||
mFactoryList.push_back(std::make_shared<OpusCodec::OpusFactory>(spec.mRate, spec.mChannels, spec.mPayloadType));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -222,28 +337,29 @@ void CodecList::init(const Settings& settings)
|
|||
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI)
|
||||
#if defined(USE_AMR_CODEC)
|
||||
for (int pt: mSettings.mAmrWbPayloadType)
|
||||
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt}));
|
||||
mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
|
||||
for (int pt: mSettings.mAmrWbOctetPayloadType)
|
||||
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt}));
|
||||
mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
|
||||
|
||||
for (int pt: mSettings.mAmrNbPayloadType)
|
||||
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt}));
|
||||
mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
|
||||
for (int pt: mSettings.mAmrNbOctetPayloadType)
|
||||
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt}));
|
||||
mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
|
||||
|
||||
mFactoryList.push_back(new GsmEfrCodec::GsmEfrFactory(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
|
||||
if (mSettings.mGsmEfrPayloadType != -1)
|
||||
mFactoryList.push_back(std::make_shared<GsmEfrCodec::GsmEfrFactory>(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
|
||||
#endif
|
||||
#endif
|
||||
// mFactoryList.push_back(new IsacCodec::IsacFactory16K(mSettings.mIsac16KPayloadType));
|
||||
// mFactoryList.push_back(new IlbcCodec::IlbcFactory(mSettings.mIlbc20PayloadType, mSettings.mIlbc30PayloadType));
|
||||
mFactoryList.push_back(new G711Codec::AlawFactory());
|
||||
mFactoryList.push_back(new G711Codec::UlawFactory());
|
||||
mFactoryList.push_back(std::make_shared<G711Codec::AlawFactory>());
|
||||
mFactoryList.push_back(std::make_shared<G711Codec::UlawFactory>());
|
||||
|
||||
mFactoryList.push_back(new GsmCodec::GsmFactory(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
|
||||
// mFactoryList.push_back(new G722Codec::G722Factory());
|
||||
// mFactoryList.push_back(new G729Codec::G729Factory());
|
||||
if (mSettings.mGsmFrPayloadType != -1)
|
||||
mFactoryList.push_back(std::make_shared<GsmCodec::GsmFactory>(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
|
||||
mFactoryList.push_back(std::make_shared<G722Codec::G722Factory>());
|
||||
mFactoryList.push_back(std::make_shared<G729Codec::G729Factory>());
|
||||
#ifndef TARGET_ANDROID
|
||||
mFactoryList.push_back(new GsmHrCodec::GsmHrFactory(mSettings.mGsmHrPayloadType));
|
||||
if (mSettings.mGsmHrPayloadType != -1)
|
||||
mFactoryList.push_back(std::make_shared<GsmHrCodec::GsmHrFactory>(mSettings.mGsmHrPayloadType));
|
||||
#endif
|
||||
|
||||
#if !defined(TARGET_ANDROID) && defined(USE_EVS_CODEC)
|
||||
|
|
@ -255,15 +371,14 @@ void CodecList::init(const Settings& settings)
|
|||
evs_params.ptime = 20;
|
||||
evs_params.ptype = spec.mPayloadType;
|
||||
|
||||
mFactoryList.push_back(new EVSCodec::EVSFactory(evs_params));
|
||||
mFactoryList.push_back(std::make_shared<EVSCodec::EVSFactory>(evs_params));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CodecList::~CodecList()
|
||||
{
|
||||
for (auto f: mFactoryList)
|
||||
delete f;
|
||||
mFactoryList.clear();
|
||||
}
|
||||
|
||||
int CodecList::count() const
|
||||
|
|
@ -292,21 +407,26 @@ void CodecList::fillCodecMap(CodecMap& cm)
|
|||
for (auto& factory: mFactoryList)
|
||||
{
|
||||
// Create codec here. Although they are not needed right now - they can be needed to find codec's info.
|
||||
PCodec c = factory->create();
|
||||
cm.insert({factory->payloadType(), c});
|
||||
// PCodec c = factory->create();
|
||||
cm.insert({factory->payloadType(), PCodec()});
|
||||
}
|
||||
}
|
||||
|
||||
PCodec CodecList::createCodecByPayloadType(int payloadType)
|
||||
{
|
||||
for (auto& factory: mFactoryList)
|
||||
{
|
||||
if (factory->payloadType() == payloadType)
|
||||
return factory->create();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
CodecListPriority::CodecListPriority()
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
CodecListPriority::~CodecListPriority()
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
bool CodecListPriority::isNegativePriority(const CodecListPriority::Item& item)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,15 +6,14 @@
|
|||
#ifndef __MT_CODEC_LIST_H
|
||||
#define __MT_CODEC_LIST_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
# include "resiprocate/resip/stack/SdpContents.hxx"
|
||||
#endif
|
||||
#include "resiprocate/resip/stack/SdpContents.hxx"
|
||||
|
||||
#include "MT_Codec.h"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include "../helper/HL_VariantMap.h"
|
||||
|
||||
#define ALL_CODECS_STRING "OPUS,ISAC,ILBC,PCMU,PCMA,G722,GSM"
|
||||
|
|
@ -30,22 +29,22 @@ public:
|
|||
bool mSkipDecode = false;
|
||||
|
||||
// AMR payload types
|
||||
std::set<int> mAmrWbPayloadType = { MT_AMRWB_PAYLOADTYPE };
|
||||
std::set<int> mAmrNbPayloadType = { MT_AMRNB_PAYLOADTYPE };
|
||||
std::set<int> mAmrWbOctetPayloadType = { MT_AMRWB_OCTET_PAYLOADTYPE };
|
||||
std::set<int> mAmrNbOctetPayloadType = { MT_AMRNB_OCTET_PAYLOADTYPE };
|
||||
std::set<int64_t> mAmrWbPayloadType = { };
|
||||
std::set<int64_t> mAmrNbPayloadType = { };
|
||||
std::set<int64_t> mAmrWbOctetPayloadType = { };
|
||||
std::set<int64_t> mAmrNbOctetPayloadType = { };
|
||||
|
||||
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; }
|
||||
|
||||
int mIsac16KPayloadType = MT_ISAC16K_PAYLOADTYPE;
|
||||
int mIsac32KPayloadType = MT_ISAC32K_PAYLOADTYPE;
|
||||
int mIlbc20PayloadType = MT_ILBC20_PAYLOADTYPE;
|
||||
int mIlbc30PayloadType = MT_ILBC30_PAYLOADTYPE;
|
||||
int mGsmFrPayloadType = 3; // GSM is codec with fixed payload type. But sometimes it has to be overwritten.
|
||||
int mIsac16KPayloadType = -1;
|
||||
int mIsac32KPayloadType = -1;
|
||||
int mIlbc20PayloadType = -1;
|
||||
int mIlbc30PayloadType = -1;
|
||||
int mGsmFrPayloadType = -1; // GSM is codec with fixed payload type. But sometimes it has to be overwritten.
|
||||
int mGsmFrPayloadLength = 33; // Expected GSM payload length
|
||||
int mGsmHrPayloadType = MT_GSMHR_PAYLOADTYPE;
|
||||
int mGsmEfrPayloadType = MT_GSMEFR_PAYLOADTYPE;
|
||||
int mGsmHrPayloadType = -1;
|
||||
int mGsmEfrPayloadType = -1;
|
||||
|
||||
struct EvsSpec
|
||||
{
|
||||
|
|
@ -58,7 +57,7 @@ public:
|
|||
Bandwidth_FB
|
||||
};
|
||||
|
||||
Bandwidth mBandwidth = Bandwidth_NB;
|
||||
Bandwidth mBandwidth = Bandwidth_FB;
|
||||
|
||||
enum Encoding
|
||||
{
|
||||
|
|
@ -71,7 +70,7 @@ public:
|
|||
static EvsSpec parse(const std::string& spec);
|
||||
|
||||
bool operator == (const EvsSpec& rhs) const { return std::tie(mPayloadType, mBandwidth, mEncodingType) == std::tie(rhs.mPayloadType, rhs.mBandwidth, rhs.mEncodingType);}
|
||||
bool operator != (const EvsSpec& rhs) const { return ! (operator ==) (rhs);};
|
||||
bool operator != (const EvsSpec& rhs) const { return ! (operator ==) (rhs);}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -89,35 +88,42 @@ public:
|
|||
|
||||
bool isValid() const;
|
||||
bool operator == (const OpusSpec& rhs) const { return std::tie(mPayloadType, mRate, mChannels) == std::tie(rhs.mPayloadType, rhs.mRate, rhs.mChannels);}
|
||||
bool operator != (const OpusSpec& rhs) const { return ! (operator ==) (rhs);};
|
||||
bool operator != (const OpusSpec& rhs) const { return ! (operator ==) (rhs);}
|
||||
|
||||
static OpusSpec parse(const std::string& spec);
|
||||
};
|
||||
std::vector<OpusSpec> mOpusSpec;
|
||||
|
||||
// Payload type
|
||||
bool contains(int ptype) const;
|
||||
|
||||
// Textual representation - used in logging
|
||||
std::string toString() const;
|
||||
void clear();
|
||||
|
||||
static Settings DefaultSettings;
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
static Settings parseSdp(const std::list<resip::Codec>& codeclist);
|
||||
#endif
|
||||
|
||||
bool operator == (const Settings& rhs) const;
|
||||
};
|
||||
|
||||
CodecList(const Settings& settings);
|
||||
~CodecList();
|
||||
void setSettings(const Settings& settings) { init(settings); }
|
||||
void setSettings(const Settings& settings)
|
||||
{
|
||||
init(settings);
|
||||
}
|
||||
|
||||
int count() const;
|
||||
Codec::Factory& codecAt(int index) const;
|
||||
int findCodec(const std::string& name) const;
|
||||
void fillCodecMap(CodecMap& cm);
|
||||
|
||||
PCodec createCodecByPayloadType(int payloadType);
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
typedef std::vector<Codec::Factory*> FactoryList;
|
||||
typedef std::vector<std::shared_ptr<Codec::Factory>> FactoryList;
|
||||
FactoryList mFactoryList;
|
||||
Settings mSettings;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "MT_Dtmf.h"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef MT_DTMF
|
||||
#define MT_DTMF
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../helper/HL_ByteBuffer.h"
|
||||
|
|
|
|||
|
|
@ -146,8 +146,7 @@ PCodec EVSCodec::EVSFactory::create()
|
|||
}
|
||||
|
||||
EVSCodec::EVSCodec(): EVSCodec(StreamParameters())
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
EVSCodec::EVSCodec(const StreamParameters &sp)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
#ifndef __MT_EVS_CODEC_H
|
||||
#define __MT_EVS_CODEC_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
|
@ -10,7 +12,6 @@
|
|||
#include <assert.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "config.h"
|
||||
#include "MT_Codec.h"
|
||||
|
||||
#include "libevs/lib_com/prot.h"
|
||||
|
|
@ -40,7 +41,7 @@ public:
|
|||
|
||||
public:
|
||||
EVSFactory(StreamParameters& sp);
|
||||
const char* name() { return MT_EVS_CODECNAME; };
|
||||
const char* name() { return MT_EVS_CODECNAME; }
|
||||
int samplerate();
|
||||
int payloadType();
|
||||
PCodec create();
|
||||
|
|
@ -51,7 +52,7 @@ public:
|
|||
EVSCodec(const StreamParameters& sp);
|
||||
~EVSCodec() override;
|
||||
|
||||
const char* name() override { return MT_EVS_CODECNAME; } ;
|
||||
const char* name() override { return MT_EVS_CODECNAME; }
|
||||
int samplerate() override;
|
||||
int pcmLength() override;
|
||||
int frameTime() override;
|
||||
|
|
@ -68,3 +69,5 @@ private:
|
|||
};
|
||||
|
||||
} // End of namespace
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
#ifndef __MT_NATIVE_RTP_SENDER_H
|
||||
#define __MT_NATIVE_RTP_SENDER_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "jrtplib/src/rtpexternaltransmitter.h"
|
||||
#include "srtp/include/srtp.h"
|
||||
#include "libsrtp/include/srtp.h"
|
||||
#include "../helper/HL_NetworkSocket.h"
|
||||
#include "../helper/HL_InternetAddress.h"
|
||||
#include "../helper/HL_Rtp.h"
|
||||
|
|
|
|||
|
|
@ -1,42 +1,90 @@
|
|||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "MT_SrtpHelper.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
#include "../helper/HL_Rtp.h"
|
||||
#include <openssl/rand.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 ---
|
||||
void initSrtpStream(SrtpStream& s, unsigned ssrc, SrtpSuite suite)
|
||||
static void configureSrtpStream(SrtpStream& s, uint16_t ssrc, SrtpSuite suite)
|
||||
{
|
||||
s.second.ssrc.type = ssrc_specific;
|
||||
s.second.ssrc.value = ntohl(ssrc);
|
||||
s.second.next = NULL;
|
||||
switch (suite)
|
||||
{
|
||||
case SRTP_AES_128_AUTH_80:
|
||||
crypto_policy_set_aes_cm_128_hmac_sha1_80(&s.second.rtp);
|
||||
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);
|
||||
}
|
||||
s.second.next = nullptr;
|
||||
set_srtp_policy_function func = findPolicyFunction(suite);
|
||||
if (!func)
|
||||
throw std::runtime_error(std::format("SRTP suite {} is not supported", toString(suite)));
|
||||
func(&s.second.rtp);
|
||||
func(&s.second.rtcp);
|
||||
}
|
||||
|
||||
SrtpSession::SrtpSession()
|
||||
:mInboundSession(NULL), mOutboundSession(NULL)
|
||||
:mInboundSession(nullptr), mOutboundSession(nullptr)
|
||||
{
|
||||
mInboundSession = NULL;
|
||||
mOutboundSession = NULL;
|
||||
mSuite = SRTP_NONE;
|
||||
|
||||
memset(&mInboundPolicy, 0, sizeof mInboundPolicy);
|
||||
|
|
@ -45,15 +93,18 @@ SrtpSession::SrtpSession()
|
|||
memset(&mOutboundPolicy, 0, sizeof mOutboundPolicy);
|
||||
mOutboundPolicy.ssrc.type = ssrc_specific;
|
||||
|
||||
// Generate outgoing keys
|
||||
|
||||
mOutgoingKey[SRTP_AES_128_AUTH_80-1].first = PByteBuffer(new ByteBuffer());
|
||||
mOutgoingKey[SRTP_AES_128_AUTH_80-1].first->resize(30);
|
||||
crypto_get_random((unsigned char*)mOutgoingKey[SRTP_AES_128_AUTH_80-1].first->mutableData(), 30);
|
||||
|
||||
mOutgoingKey[SRTP_AES_256_AUTH_80-1].first = PByteBuffer(new ByteBuffer());
|
||||
mOutgoingKey[SRTP_AES_256_AUTH_80-1].first->resize(46);
|
||||
crypto_get_random((unsigned char*)mOutgoingKey[SRTP_AES_256_AUTH_80-1].first->mutableData(), 46);
|
||||
// Generate outgoing keys for all ciphers
|
||||
auto putKey = [this](SrtpSuite suite, size_t length){
|
||||
auto key = std::make_shared<ByteBuffer>();
|
||||
key->resize(length);
|
||||
RAND_bytes(key->mutableData(), key->size());
|
||||
mOutgoingKey[suite].first = key;
|
||||
};
|
||||
putKey(SRTP_AES_128_AUTH_80, 30); putKey(SRTP_AES_128_AUTH_32, 30);
|
||||
putKey(SRTP_AES_192_AUTH_80, 38); putKey(SRTP_AES_192_AUTH_32, 38);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +112,7 @@ SrtpSession::~SrtpSession()
|
|||
{
|
||||
}
|
||||
|
||||
void SrtpSession::addSsrc(unsigned ssrc, SsrcDirection d)
|
||||
/*void SrtpSession::addSsrc(unsigned ssrc, SsrcDirection d)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
assert(mSuite != SRTP_NONE);
|
||||
|
|
@ -76,7 +127,7 @@ void SrtpSession::addSsrc(unsigned ssrc, SsrcDirection d)
|
|||
if (streamIter != mIncomingMap.end())
|
||||
return;
|
||||
|
||||
initSrtpStream(s, ssrc, mSuite);
|
||||
configureSrtpStream(s, ssrc, mSuite);
|
||||
s.second.key = (unsigned char*)mIncomingKey.first->mutableData();
|
||||
mIncomingMap[ssrc] = s;
|
||||
srtp_add_stream(mInboundSession, &s.second);
|
||||
|
|
@ -86,13 +137,13 @@ void SrtpSession::addSsrc(unsigned ssrc, SsrcDirection d)
|
|||
streamIter = mOutgoingMap.find(ssrc);
|
||||
if (streamIter != mOutgoingMap.end())
|
||||
return;
|
||||
initSrtpStream(s, ssrc, mSuite);
|
||||
configureSrtpStream(s, ssrc, mSuite);
|
||||
s.second.key = (unsigned char*)mOutgoingKey[int(mSuite)-1].first->mutableData();
|
||||
mOutgoingMap[ssrc] = s;
|
||||
srtp_add_stream(mOutboundSession, &s.second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void SrtpSession::open(ByteBuffer& incomingKey, SrtpSuite suite)
|
||||
{
|
||||
|
|
@ -106,33 +157,26 @@ void SrtpSession::open(ByteBuffer& incomingKey, SrtpSuite suite)
|
|||
mSuite = suite;
|
||||
|
||||
// Save key
|
||||
mIncomingKey.first = PByteBuffer(new ByteBuffer(incomingKey));
|
||||
mIncomingKey.first = std::make_shared<ByteBuffer>(incomingKey);
|
||||
|
||||
// Update policy
|
||||
switch (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;
|
||||
auto policyFunction = findPolicyFunction(suite);
|
||||
if (!policyFunction)
|
||||
throw std::runtime_error(std::format("SRTP suite {} not found", toString(suite)));
|
||||
|
||||
case SRTP_AES_256_AUTH_80:
|
||||
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mInboundPolicy.rtp);
|
||||
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mInboundPolicy.rtcp);
|
||||
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mOutboundPolicy.rtp);
|
||||
crypto_policy_set_aes_cm_256_hmac_sha1_80(&mOutboundPolicy.rtcp);
|
||||
break;
|
||||
// Configure policies
|
||||
policyFunction(&mInboundPolicy.rtp);
|
||||
policyFunction(&mInboundPolicy.rtcp);
|
||||
policyFunction(&mOutboundPolicy.rtp);
|
||||
policyFunction(&mOutboundPolicy.rtcp);
|
||||
|
||||
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.ssrc.type = ssrc_any_inbound;
|
||||
|
||||
// Create SRTP session
|
||||
err_status_t err;
|
||||
srtp_err_status_t err;
|
||||
|
||||
err = srtp_create(&mOutboundSession, &mOutboundPolicy);
|
||||
if (err)
|
||||
|
|
@ -156,13 +200,13 @@ void SrtpSession::close()
|
|||
if (mOutboundSession)
|
||||
{
|
||||
srtp_dealloc(mOutboundSession);
|
||||
mOutboundSession = NULL;
|
||||
mOutboundSession = nullptr;
|
||||
}
|
||||
|
||||
if (mInboundSession)
|
||||
{
|
||||
srtp_dealloc(mInboundSession);
|
||||
mInboundSession = NULL;
|
||||
mInboundSession = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,44 +219,70 @@ SrtpKeySalt& SrtpSession::outgoingKey(SrtpSuite suite)
|
|||
|
||||
bool SrtpSession::protectRtp(void* buffer, int* length)
|
||||
{
|
||||
addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
||||
// addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
||||
|
||||
Lock l(mGuard);
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SrtpSession::protectRtcp(void* buffer, int* length)
|
||||
{
|
||||
addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
||||
//addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
|
||||
|
||||
Lock l(mGuard);
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SrtpSession::unprotectRtp(void* buffer, int* length)
|
||||
bool SrtpSession::unprotectRtp(const void* src, size_t srcLength, void* dst, size_t* dstLength)
|
||||
{
|
||||
addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
||||
//addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
||||
|
||||
Lock l(mGuard);
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SrtpSession::unprotectRtcp(void* buffer, int* length)
|
||||
bool SrtpSession::unprotectRtcp(const void* src, size_t srcLength, void* dst, size_t* dstLength)
|
||||
{
|
||||
addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
||||
//addSsrc(RtpHelper::findSsrc(buffer, *length), sdIncoming);
|
||||
|
||||
Lock l(mGuard);
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
|
@ -223,9 +293,9 @@ void SrtpSession::initSrtp()
|
|||
if (GSrtpInitialized)
|
||||
return;
|
||||
|
||||
err_status_t err = srtp_init();
|
||||
auto err = srtp_init();
|
||||
|
||||
if (err != err_status_ok)
|
||||
if (err != srtp_err_status_ok)
|
||||
throw Exception(ERR_SRTP, err);
|
||||
GSrtpInitialized = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,35 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "srtp/include/srtp.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "../helper/HL_ByteBuffer.h"
|
||||
#include "libsrtp/include/srtp.h"
|
||||
#include "HL_Sync.h"
|
||||
#include "HL_ByteBuffer.h"
|
||||
#include "HL_Types.h"
|
||||
|
||||
#define SRTP_SUITE_NAME_2 "AES_CM_256_HMAC_SHA1_80"
|
||||
#define SRTP_SUITE_NAME_1 "AES_CM_128_HMAC_SHA1_80"
|
||||
#define NAME_SRTP_AES_256_AUTH_80 "AES_CM_256_HMAC_SHA1_80"
|
||||
#define NAME_SRTP_AES_128_AUTH_80 "AES_CM_128_HMAC_SHA1_80"
|
||||
|
||||
enum SrtpSuite
|
||||
{
|
||||
SRTP_NONE,
|
||||
SRTP_AES_128_AUTH_80,
|
||||
SRTP_AES_256_AUTH_80,
|
||||
SRTP_LAST = SRTP_AES_256_AUTH_80
|
||||
SRTP_AES_192_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<unsigned, srtp_policy_t> SrtpStream;
|
||||
|
||||
|
|
@ -48,8 +62,8 @@ public:
|
|||
/* 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 protectRtcp(void* buffer, int* length);
|
||||
bool unprotectRtp(void* buffer, int* length);
|
||||
bool unprotectRtcp(void* buffer, int* length);
|
||||
bool unprotectRtp(const void* src, size_t srcLength, void* dst, size_t* dstLength);
|
||||
bool unprotectRtcp(const void* src, size_t srcLength, void* dst, size_t* dstLength);
|
||||
|
||||
|
||||
static void initSrtp();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
#include <math.h>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include "MT_Statistics.h"
|
||||
#include "audio/Audio_Interface.h"
|
||||
#include "helper/HL_Log.h"
|
||||
#define LOG_SUBSYSTEM "Statistics"
|
||||
|
||||
using namespace MT;
|
||||
|
|
@ -13,7 +12,7 @@ void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
|
|||
uint32_t timestamp = packet->GetTimestamp();
|
||||
jrtplib::RTPTime receiveTime = packet->GetReceiveTime();
|
||||
|
||||
if (!mLastJitter.is_initialized())
|
||||
if (!mLastJitter)
|
||||
{
|
||||
// First packet
|
||||
mReceiveTime = receiveTime;
|
||||
|
|
@ -55,8 +54,11 @@ void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
|
|||
mReceiveTime = receiveTime;
|
||||
mReceiveTimestamp = timestamp;
|
||||
|
||||
// And mJitter are in seconds again
|
||||
mJitter.process(mLastJitter.value() / float(rate));
|
||||
// And mJitter are in milliseconds again
|
||||
float jitter_s = mLastJitter.value() / (float(rate));
|
||||
// std::cout << "Jitter (in seconds): " << std::dec << jitter_s << std::endl;
|
||||
|
||||
mJitter.process(jitter_s);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,40 +67,10 @@ void JitterStatistics::process(jrtplib::RTPPacket* packet, int rate)
|
|||
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
|
@ -127,7 +99,7 @@ void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
|||
}
|
||||
else
|
||||
*burstr = 0;
|
||||
//printf("total loss: %d\n", lost);
|
||||
|
||||
if (mReceivedRtp > 0)
|
||||
*lossr = (double)((double)lost / (double)mReceivedRtp);
|
||||
else
|
||||
|
|
@ -199,16 +171,16 @@ Statistics& Statistics::operator += (const Statistics& src)
|
|||
mCodecName = src.mCodecName;
|
||||
|
||||
// Find minimal
|
||||
if (mFirstRtpTime.is_initialized())
|
||||
if (mFirstRtpTime)
|
||||
{
|
||||
if (src.mFirstRtpTime.is_initialized())
|
||||
if (src.mFirstRtpTime)
|
||||
{
|
||||
if (mFirstRtpTime.value() > src.mFirstRtpTime.value())
|
||||
mFirstRtpTime = src.mFirstRtpTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (src.mFirstRtpTime.is_initialized())
|
||||
if (src.mFirstRtpTime)
|
||||
mFirstRtpTime = src.mFirstRtpTime;
|
||||
|
||||
mBitrateSwitchCounter += src.mBitrateSwitchCounter;
|
||||
|
|
@ -231,8 +203,8 @@ Statistics& Statistics::operator -= (const Statistics& src)
|
|||
mOldRtp -= src.mOldRtp;
|
||||
mPacketLoss -= src.mPacketLoss;
|
||||
mPacketDropped -= src.mPacketDropped;
|
||||
|
||||
mAudioTime -= src.mAudioTime;
|
||||
|
||||
for (auto codecStat: src.mCodecCount)
|
||||
{
|
||||
if (mCodecCount.find(codecStat.first) != mCodecCount.end())
|
||||
|
|
|
|||
|
|
@ -3,15 +3,17 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <array>
|
||||
|
||||
#include "audio/Audio_DataWindow.h"
|
||||
#include "helper/HL_Optional.hpp"
|
||||
#include "helper/HL_Statistics.h"
|
||||
#include "helper/HL_Types.h"
|
||||
|
||||
#include "jrtplib/src/rtptimeutilities.h"
|
||||
#include "jrtplib/src/rtppacket.h"
|
||||
|
||||
using std::experimental::optional;
|
||||
|
||||
namespace MT
|
||||
{
|
||||
|
|
@ -39,7 +41,7 @@ protected:
|
|||
uint32_t mReceiveTimestamp = 0;
|
||||
|
||||
// It is classic jitter value in units
|
||||
optional<float> mLastJitter;
|
||||
std::optional<float> mLastJitter;
|
||||
|
||||
// Some statistics for jitter value in seconds
|
||||
TestResult<float> mJitter;
|
||||
|
|
@ -51,39 +53,36 @@ protected:
|
|||
class Statistics
|
||||
{
|
||||
public:
|
||||
size_t mReceived, // Received traffic in bytes
|
||||
mSent, // Sent traffic in bytes
|
||||
mReceivedRtp, // Number of received rtp packets
|
||||
mSentRtp, // Number of sent rtp packets
|
||||
mReceivedRtcp, // Number of received rtcp packets
|
||||
mSentRtcp, // Number of sent rtcp packets
|
||||
mDuplicatedRtp, // Number of received duplicated rtp packets
|
||||
mOldRtp, // Number of late rtp packets
|
||||
mPacketLoss, // Number of lost packets
|
||||
mPacketDropped, // Number of dropped packets (due to time unsync when playing)б
|
||||
mIllegalRtp; // Number of rtp packets with bad payload type
|
||||
size_t mReceived = 0, // Received traffic in bytes
|
||||
mSent = 0, // Sent traffic in bytes
|
||||
mReceivedRtp = 0, // Number of received rtp packets
|
||||
mSentRtp = 0, // Number of sent rtp packets
|
||||
mReceivedRtcp = 0, // Number of received rtcp packets
|
||||
mSentRtcp = 0, // Number of sent rtcp packets
|
||||
mDuplicatedRtp = 0, // Number of received duplicated rtp packets
|
||||
mOldRtp = 0, // Number of late rtp packets
|
||||
mPacketLoss = 0, // Number of lost packets
|
||||
mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б
|
||||
mIllegalRtp = 0; // Number of rtp packets with bad payload type
|
||||
|
||||
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
|
||||
mDecodeRequested, // Average amount of requested audio frames to play
|
||||
mPacketInterval; // Average interval between packet adding to jitter buffer
|
||||
|
||||
int mLoss[128]; // Every item is number of loss of corresping length
|
||||
size_t mAudioTime; // Decoded/found time in milliseconds
|
||||
size_t mDecodedSize; // Number of decoded bytes
|
||||
uint16_t mSsrc; // Last known SSRC ID in a RTP stream
|
||||
std::array<float, 128> mLoss = {0}; // Every item is number of loss of corresping length
|
||||
size_t mAudioTime = 0; // Decoded/found time in milliseconds
|
||||
size_t mDecodedSize = 0; // Number of decoded bytes
|
||||
uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream
|
||||
ice::NetworkAddress mRemotePeer; // Last known remote RTP address
|
||||
|
||||
// AMR codec bitrate switch counter
|
||||
int mBitrateSwitchCounter;
|
||||
|
||||
int mBitrateSwitchCounter = 0;
|
||||
std::string mCodecName;
|
||||
|
||||
float mJitter; // Jitter
|
||||
|
||||
float mJitter = 0.0f; // Jitter
|
||||
TestResult<float> mRttDelay; // RTT delay
|
||||
|
||||
// Timestamp when first RTP packet has arrived
|
||||
optional<std::chrono::system_clock::time_point> mFirstRtpTime;
|
||||
std::optional<timepoint_t> mFirstRtpTime;
|
||||
|
||||
std::map<int, int> mCodecCount; // Stats on used codecs
|
||||
|
||||
|
|
@ -98,11 +97,7 @@ public:
|
|||
Statistics& operator += (const Statistics& src);
|
||||
Statistics& operator -= (const Statistics& src);
|
||||
|
||||
float mNetworkMos = 0.0;
|
||||
#if defined(USE_PVQA_LIBRARY) && !defined(PVQA_SERVER)
|
||||
float mPvqaMos = 0.0;
|
||||
std::string mPvqaReport;
|
||||
#endif
|
||||
float mNetworkMos = 0.0f;
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../config.h"
|
||||
#include "../engine_config.h"
|
||||
#include "MT_WebRtc.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# Run manually to reformat a file:
|
||||
# clang-format -i --style=file <file>
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentCaseLabels: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
DerivePointerAlignment: false
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
.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
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
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 ()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
|
|
@ -0,0 +1,531 @@
|
|||
.. image:: https://user-images.githubusercontent.com/
|
||||
576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png
|
||||
:width: 25%
|
||||
:alt: {fmt}
|
||||
|
||||
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
|
||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
|
||||
|
||||
.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg
|
||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos
|
||||
|
||||
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
|
||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
|
||||
|
||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||
:alt: fmt is continuously fuzzed at oss-fuzz
|
||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
||||
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
|
||||
Summary&q=proj%3Dfmt&can=1
|
||||
|
||||
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
|
||||
:alt: Ask questions at StackOverflow with the tag fmt
|
||||
:target: https://stackoverflow.com/questions/tagged/fmt
|
||||
|
||||
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||
alternative to C stdio and C++ iostreams.
|
||||
|
||||
If you like this project, please consider donating to one of the funds that
|
||||
help victims of the war in Ukraine: https://www.stopputin.net/.
|
||||
|
||||
`Documentation <https://fmt.dev>`__
|
||||
|
||||
`Cheat Sheets <https://hackingcpp.com/cpp/libs/fmt.html>`__
|
||||
|
||||
Q&A: ask questions on `StackOverflow with the tag fmt
|
||||
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||
|
||||
Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
|
||||
for localization
|
||||
* Implementation of `C++20 std::format
|
||||
<https://en.cppreference.com/w/cpp/utility/format>`__
|
||||
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
|
||||
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
|
||||
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
|
||||
round-trip guarantees
|
||||
* Safe `printf implementation
|
||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
|
||||
extension for positional arguments
|
||||
* Extensibility: `support for user-defined types
|
||||
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_
|
||||
* High performance: faster than common standard library implementations of
|
||||
``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_
|
||||
and `Converting a hundred million integers to strings per second
|
||||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
||||
* Small code size both in terms of source code with the minimum configuration
|
||||
consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``,
|
||||
and compiled code; see `Compile time and code bloat`_
|
||||
* Reliability: the library has an extensive set of `tests
|
||||
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed
|
||||
<https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20
|
||||
Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_
|
||||
* Safety: the library is fully type safe, errors in format strings can be
|
||||
reported at compile time, automatic memory management prevents buffer overflow
|
||||
errors
|
||||
* Ease of use: small self-contained code base, no external dependencies,
|
||||
permissive MIT `license
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
||||
consistent output across platforms and support for older compilers
|
||||
* Clean warning-free codebase even on high warning levels such as
|
||||
``-Wall -Wextra -pedantic``
|
||||
* Locale-independence by default
|
||||
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
|
||||
|
||||
See the `documentation <https://fmt.dev>`_ for more details.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main() {
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
||||
|
||||
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format("The answer is {}.", 42);
|
||||
// s == "The answer is 42."
|
||||
|
||||
**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
||||
// s == "I'd rather be happy than right."
|
||||
|
||||
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
int main() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
fmt::print("Default format: {} {}\n", 42s, 100ms);
|
||||
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
|
||||
}
|
||||
|
||||
Output::
|
||||
|
||||
Default format: 42s 100ms
|
||||
strftime-like format: 03:15:30
|
||||
|
||||
**Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <vector>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
int main() {
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
fmt::print("{}\n", v);
|
||||
}
|
||||
|
||||
Output::
|
||||
|
||||
[1, 2, 3]
|
||||
|
||||
**Check a format string at compile time**
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format("{:d}", "I am not a number");
|
||||
|
||||
This gives a compile-time error in C++20 because ``d`` is an invalid format
|
||||
specifier for a string.
|
||||
|
||||
**Write a file from a single thread**
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/os.h>
|
||||
|
||||
int main() {
|
||||
auto out = fmt::output_file("guide.txt");
|
||||
out.print("Don't {}", "Panic");
|
||||
}
|
||||
|
||||
This can be `5 to 9 times faster than fprintf
|
||||
<http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_.
|
||||
|
||||
**Print with colors and text styles**
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/color.h>
|
||||
|
||||
int main() {
|
||||
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
|
||||
"Hello, {}!\n", "world");
|
||||
fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
|
||||
fmt::emphasis::underline, "Hello, {}!\n", "мир");
|
||||
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
|
||||
"Hello, {}!\n", "世界");
|
||||
}
|
||||
|
||||
Output on a modern terminal:
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/
|
||||
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
Speed tests
|
||||
~~~~~~~~~~~
|
||||
|
||||
================= ============= ===========
|
||||
Library Method Run Time, s
|
||||
================= ============= ===========
|
||||
libc printf 1.04
|
||||
libc++ std::ostream 3.05
|
||||
{fmt} 6.1.1 fmt::print 0.75
|
||||
Boost Format 1.67 boost::format 7.24
|
||||
Folly Format folly::format 2.23
|
||||
================= ============= ===========
|
||||
|
||||
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
|
||||
|
||||
The above results were generated by building ``tinyformat_test.cpp`` on macOS
|
||||
10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
|
||||
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
|
||||
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
||||
further details refer to the `source
|
||||
<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
|
||||
|
||||
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
|
||||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||
and faster than `double-conversion <https://github.com/google/double-conversion>`_ and
|
||||
`ryu <https://github.com/ulfjack/ryu>`_:
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/576385/
|
||||
95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png
|
||||
:target: https://fmt.dev/unknown_mac64_clang12.0.html
|
||||
|
||||
Compile time and code bloat
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The script `bloat-test.py
|
||||
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
|
||||
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
|
||||
tests compile time and code bloat for nontrivial projects.
|
||||
It generates 100 translation units and uses ``printf()`` or its alternative
|
||||
five times in each to simulate a medium sized project. The resulting
|
||||
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
|
||||
macOS Sierra, best of three) is shown in the following tables.
|
||||
|
||||
**Optimized build (-O3)**
|
||||
|
||||
============= =============== ==================== ==================
|
||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||
============= =============== ==================== ==================
|
||||
printf 2.6 29 26
|
||||
printf+string 16.4 29 26
|
||||
iostreams 31.1 59 55
|
||||
{fmt} 19.0 37 34
|
||||
Boost Format 91.9 226 203
|
||||
Folly Format 115.7 101 88
|
||||
============= =============== ==================== ==================
|
||||
|
||||
As you can see, {fmt} has 60% less overhead in terms of resulting binary code
|
||||
size compared to iostreams and comes pretty close to ``printf``. Boost Format
|
||||
and Folly Format have the largest overheads.
|
||||
|
||||
``printf+string`` is the same as ``printf`` but with extra ``<string>``
|
||||
include to measure the overhead of the latter.
|
||||
|
||||
**Non-optimized build**
|
||||
|
||||
============= =============== ==================== ==================
|
||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||
============= =============== ==================== ==================
|
||||
printf 2.2 33 30
|
||||
printf+string 16.0 33 30
|
||||
iostreams 28.3 56 52
|
||||
{fmt} 18.2 59 50
|
||||
Boost Format 54.1 365 303
|
||||
Folly Format 79.9 445 430
|
||||
============= =============== ==================== ==================
|
||||
|
||||
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
|
||||
compare formatting function overhead only. Boost Format is a
|
||||
header-only library so it doesn't provide any linkage options.
|
||||
|
||||
Running the tests
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Please refer to `Building the library`__ for the instructions on how to build
|
||||
the library and run the unit tests.
|
||||
|
||||
__ https://fmt.dev/latest/usage.html#building-the-library
|
||||
|
||||
Benchmarks reside in a separate repository,
|
||||
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
|
||||
so to run the benchmarks you first need to clone this repository and
|
||||
generate Makefiles with CMake::
|
||||
|
||||
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
|
||||
$ cd format-benchmark
|
||||
$ cmake .
|
||||
|
||||
Then you can run the speed test::
|
||||
|
||||
$ make speed-test
|
||||
|
||||
or the bloat test::
|
||||
|
||||
$ make bloat-test
|
||||
|
||||
Migrating code
|
||||
--------------
|
||||
|
||||
`clang-tidy-fmt <https://github.com/mikecrowe/clang-tidy-fmt>`_ provides clang
|
||||
tidy checks for converting occurrences of ``printf`` and ``fprintf`` to
|
||||
``fmt::print``.
|
||||
|
||||
Projects using this library
|
||||
---------------------------
|
||||
|
||||
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
|
||||
real-time strategy game
|
||||
|
||||
* `2GIS <https://2gis.ru/>`_: free business listings with a city map
|
||||
|
||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||
an open-source library for mathematical programming
|
||||
|
||||
* `Aseprite <https://github.com/aseprite/aseprite>`_:
|
||||
animated sprite editor & pixel art tool
|
||||
|
||||
* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft
|
||||
operations suite
|
||||
|
||||
* `Blizzard Battle.net <https://battle.net/>`_: an online gaming platform
|
||||
|
||||
* `Celestia <https://celestia.space/>`_: real-time 3D visualization of space
|
||||
|
||||
* `Ceph <https://ceph.com/>`_: a scalable distributed storage system
|
||||
|
||||
* `ccache <https://ccache.dev/>`_: a compiler cache
|
||||
|
||||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
|
||||
management system
|
||||
|
||||
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||
vehicle
|
||||
|
||||
* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox
|
||||
for nonlinear dynamical systems (MIT)
|
||||
|
||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||
(Lyft)
|
||||
|
||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
||||
|
||||
* `fmtlog <https://github.com/MengRao/fmtlog>`_: a performant fmtlib-style
|
||||
logging library with latency in nanoseconds
|
||||
|
||||
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
|
||||
|
||||
* `GemRB <https://gemrb.org/>`_: a portable open-source implementation of
|
||||
Bioware’s Infinity Engine
|
||||
|
||||
* `Grand Mountain Adventure
|
||||
<https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_:
|
||||
a beautiful open-world ski & snowboarding game
|
||||
|
||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||
Player vs Player Gaming Network with tweaks
|
||||
|
||||
* `KBEngine <https://github.com/kbengine/kbengine>`_: an open-source MMOG server
|
||||
engine
|
||||
|
||||
* `Keypirinha <https://keypirinha.com/>`_: a semantic launcher for Windows
|
||||
|
||||
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): home theater software
|
||||
|
||||
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
|
||||
|
||||
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
|
||||
research programming language for concurrent ownership
|
||||
|
||||
* `MongoDB <https://mongodb.com/>`_: distributed document database
|
||||
|
||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: a small tool to
|
||||
generate randomized datasets
|
||||
|
||||
* `OpenSpace <https://openspaceproject.com/>`_: an open-source
|
||||
astrovisualization framework
|
||||
|
||||
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
||||
an MMO server, compatible with most Ultima Online clients
|
||||
|
||||
* `PyTorch <https://github.com/pytorch/pytorch>`_: an open-source machine
|
||||
learning library
|
||||
|
||||
* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance,
|
||||
associative database
|
||||
|
||||
* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library
|
||||
|
||||
* `QKW <https://github.com/ravijanjam/qkw>`_: generalizing aliasing to simplify
|
||||
navigation, and executing complex multi-line terminal command sequences
|
||||
|
||||
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: a Redis cluster
|
||||
proxy
|
||||
|
||||
* `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement
|
||||
for mission critical systems written in C++
|
||||
|
||||
* `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client
|
||||
library
|
||||
|
||||
* `Salesforce Analytics Cloud
|
||||
<https://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||
business intelligence software
|
||||
|
||||
* `Scylla <https://www.scylladb.com/>`_: a Cassandra-compatible NoSQL data store
|
||||
that can handle 1 million transactions per second on a single server
|
||||
|
||||
* `Seastar <http://www.seastar-project.org/>`_: an advanced, open-source C++
|
||||
framework for high-performance server applications on modern hardware
|
||||
|
||||
* `spdlog <https://github.com/gabime/spdlog>`_: super fast C++ logging library
|
||||
|
||||
* `Stellar <https://www.stellar.org/>`_: financial platform
|
||||
|
||||
* `Touch Surgery <https://www.touchsurgery.com/>`_: surgery simulator
|
||||
|
||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
|
||||
MMORPG framework
|
||||
|
||||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
|
||||
terminal
|
||||
|
||||
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
||||
|
||||
If you are aware of other projects using this library, please let me know
|
||||
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
|
||||
`issue <https://github.com/fmtlib/fmt/issues>`_.
|
||||
|
||||
Motivation
|
||||
----------
|
||||
|
||||
So why yet another formatting library?
|
||||
|
||||
There are plenty of methods for doing this task, from standard ones like
|
||||
the printf family of function and iostreams to Boost Format and FastFormat
|
||||
libraries. The reason for creating a new library is that every existing
|
||||
solution that I found either had serious issues or didn't provide
|
||||
all the features I needed.
|
||||
|
||||
printf
|
||||
~~~~~~
|
||||
|
||||
The good thing about ``printf`` is that it is pretty fast and readily available
|
||||
being a part of the C standard library. The main drawback is that it
|
||||
doesn't support user-defined types. ``printf`` also has safety issues although
|
||||
they are somewhat mitigated with `__attribute__ ((format (printf, ...))
|
||||
<https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
|
||||
There is a POSIX extension that adds positional arguments required for
|
||||
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
|
||||
to ``printf`` but it is not a part of C99 and may not be available on some
|
||||
platforms.
|
||||
|
||||
iostreams
|
||||
~~~~~~~~~
|
||||
|
||||
The main issue with iostreams is best illustrated with an example:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
|
||||
|
||||
which is a lot of typing compared to printf:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
printf("%.2f\n", 1.23456);
|
||||
|
||||
Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
|
||||
don't support positional arguments by design.
|
||||
|
||||
The good part is that iostreams support user-defined types and are safe although
|
||||
error handling is awkward.
|
||||
|
||||
Boost Format
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This is a very powerful library which supports both ``printf``-like format
|
||||
strings and positional arguments. Its main drawback is performance. According to
|
||||
various benchmarks, it is much slower than other methods considered here. Boost
|
||||
Format also has excessive build times and severe code bloat issues (see
|
||||
`Benchmarks`_).
|
||||
|
||||
FastFormat
|
||||
~~~~~~~~~~
|
||||
|
||||
This is an interesting library which is fast, safe and has positional arguments.
|
||||
However, it has significant limitations, citing its author:
|
||||
|
||||
Three features that have no hope of being accommodated within the
|
||||
current design are:
|
||||
|
||||
* Leading zeros (or any other non-space padding)
|
||||
* Octal/hexadecimal encoding
|
||||
* Runtime width/alignment specification
|
||||
|
||||
It is also quite big and has a heavy dependency, STLSoft, which might be too
|
||||
restrictive for using it in some projects.
|
||||
|
||||
Boost Spirit.Karma
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is not really a formatting library but I decided to include it here for
|
||||
completeness. As iostreams, it suffers from the problem of mixing verbatim text
|
||||
with arguments. The library is pretty fast, but slower on integer formatting
|
||||
than ``fmt::format_to`` with format string compilation on Karma's own benchmark,
|
||||
see `Converting a hundred million integers to strings per second
|
||||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
{fmt} is distributed under the MIT `license
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
|
||||
|
||||
Documentation License
|
||||
---------------------
|
||||
|
||||
The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_
|
||||
section in the documentation is based on the one from Python `string module
|
||||
documentation <https://docs.python.org/3/library/string.html#module-string>`_.
|
||||
For this reason the documentation is distributed under the Python Software
|
||||
Foundation license available in `doc/python-license.txt
|
||||
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
|
||||
It only applies if you distribute the documentation of {fmt}.
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
|
||||
The {fmt} library is maintained by Victor Zverovich (`vitaut
|
||||
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
|
||||
<https://github.com/foonathan>`_) with contributions from many other people.
|
||||
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
|
||||
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
|
||||
Let us know if your contribution is not listed or mentioned incorrectly and
|
||||
we'll make it right.
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
find_program(DOXYGEN doxygen
|
||||
PATHS "$ENV{ProgramFiles}/doxygen/bin"
|
||||
"$ENV{ProgramFiles\(x86\)}/doxygen/bin")
|
||||
if (NOT DOXYGEN)
|
||||
message(STATUS "Target 'doc' disabled (requires doxygen)")
|
||||
return ()
|
||||
endif ()
|
||||
|
||||
# Find the Python interpreter and set the PYTHON_EXECUTABLE variable.
|
||||
if (CMAKE_VERSION VERSION_LESS 3.12)
|
||||
# This logic is deprecated in CMake after 3.12.
|
||||
find_package(PythonInterp QUIET REQUIRED)
|
||||
else ()
|
||||
find_package(Python QUIET REQUIRED)
|
||||
set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
|
||||
endif ()
|
||||
|
||||
add_custom_target(doc
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/build.py
|
||||
${FMT_VERSION}
|
||||
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL
|
||||
PATTERN ".doctrees" EXCLUDE)
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
/* -- breathe specific styles ----------------------------------------------- */
|
||||
|
||||
/* So enum value descriptions are displayed inline to the item */
|
||||
.breatheenumvalues li tt + p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* So parameter descriptions are displayed inline to the item */
|
||||
.breatheparameterlist li tt + p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.container .breathe-sectiondef {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.github-btn {
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.jumbotron {
|
||||
background-size: 100% 4px;
|
||||
background-repeat: repeat-y;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
|
||||
<font-face units-per-em="1200" ascent="960" descent="-240" />
|
||||
<missing-glyph horiz-adv-x="500" />
|
||||
<glyph />
|
||||
<glyph />
|
||||
<glyph unicode="
" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
|
||||
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="434" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="163" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="72" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode="€" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
|
||||
<glyph unicode="−" d="M200 400h900v300h-900v-300z" />
|
||||
<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" />
|
||||
<glyph unicode="☁" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
|
||||
<glyph unicode="✉" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
|
||||
<glyph unicode="✏" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
|
||||
<glyph unicode="" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
|
||||
<glyph unicode="" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
|
||||
<glyph unicode="" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
|
||||
<glyph unicode="" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
|
||||
<glyph unicode="" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
|
||||
<glyph unicode="" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
|
||||
<glyph unicode="" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
|
||||
<glyph unicode="" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
|
||||
<glyph unicode="" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
|
||||
<glyph unicode="" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
|
||||
<glyph unicode="" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
|
||||
<glyph unicode="" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
|
||||
<glyph unicode="" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
|
||||
<glyph unicode="" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
|
||||
<glyph unicode="" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
|
||||
<glyph unicode="" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
|
||||
<glyph unicode="" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
|
||||
<glyph unicode="" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
|
||||
<glyph unicode="" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
|
||||
<glyph unicode="" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
|
||||
<glyph unicode="" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
|
||||
<glyph unicode="" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
|
||||
<glyph unicode="" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
|
||||
<glyph unicode="" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
|
||||
<glyph unicode="" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
|
||||
<glyph unicode="" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
|
||||
<glyph unicode="" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
|
||||
<glyph unicode="" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
|
||||
<glyph unicode="" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
|
||||
<glyph unicode="" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
|
||||
<glyph unicode="" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
|
||||
<glyph unicode="" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
|
||||
<glyph unicode="" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
|
||||
<glyph unicode="" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
|
||||
<glyph unicode="" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
|
||||
<glyph unicode="" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
|
||||
<glyph unicode="" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
|
||||
<glyph unicode="" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
|
||||
<glyph unicode="" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
|
||||
<glyph unicode="" d="M200 0l900 550l-900 550v-1100z" />
|
||||
<glyph unicode="" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
|
||||
<glyph unicode="" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
|
||||
<glyph unicode="" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
|
||||
<glyph unicode="" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
|
||||
<glyph unicode="" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
|
||||
<glyph unicode="" d="M0 547l600 453v-300h600v-300h-600v-301z" />
|
||||
<glyph unicode="" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
|
||||
<glyph unicode="" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
|
||||
<glyph unicode="" d="M104 600h296v600h300v-600h298l-449 -600z" />
|
||||
<glyph unicode="" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
|
||||
<glyph unicode="" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
|
||||
<glyph unicode="" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
|
||||
<glyph unicode="" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
|
||||
<glyph unicode="" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
|
||||
<glyph unicode="" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
|
||||
<glyph unicode="" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
|
||||
<glyph unicode="" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
|
||||
<glyph unicode="" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
|
||||
<glyph unicode="" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
|
||||
<glyph unicode="" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
|
||||
<glyph unicode="" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
|
||||
<glyph unicode="" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
|
||||
<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
|
||||
<glyph unicode="" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
|
||||
<glyph unicode="" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
|
||||
<glyph unicode="" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
|
||||
<glyph unicode="" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
|
||||
<glyph unicode="" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
|
||||
<glyph unicode="" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
|
||||
<glyph unicode="" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
|
||||
<glyph unicode="" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
|
||||
<glyph unicode="" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
|
||||
<glyph unicode="" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
|
||||
<glyph unicode="" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
|
||||
<glyph unicode="" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
|
||||
<glyph unicode="" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
|
||||
<glyph unicode="" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
|
||||
<glyph unicode="" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
|
||||
<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
|
||||
<glyph unicode="" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
|
||||
<glyph unicode="" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
|
||||
<glyph unicode="" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
|
||||
<glyph unicode="" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
|
||||
<glyph unicode="" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
|
||||
<glyph unicode="" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
|
||||
<glyph unicode="" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
|
||||
<glyph unicode="" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
|
||||
<glyph unicode="" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
|
||||
<glyph unicode="" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
|
||||
<glyph unicode="" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
|
||||
<glyph unicode="" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
|
||||
<glyph unicode="" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
|
||||
<glyph unicode="" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
|
||||
<glyph unicode="" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,148 @@
|
|||
{% extends "!layout.html" %}
|
||||
|
||||
{% block extrahead %}
|
||||
<meta name="description" content="Small, safe and fast formatting library">
|
||||
<meta name="keywords" content="C++, formatting, printf, string, library">
|
||||
<meta name="author" content="Victor Zverovich">
|
||||
<link rel="stylesheet" href="_static/fmt.css">
|
||||
{# Google Analytics #}
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'UA-20116650-4');
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{%- macro searchform(classes, button) %}
|
||||
<form class="{{classes}}" role="search" action="{{ pathto('search') }}"
|
||||
method="get">
|
||||
<div class="form-group">
|
||||
<input type="text" name="q" class="form-control"
|
||||
{{ 'placeholder="Search"' if not button }} >
|
||||
</div>
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
{% if button %}
|
||||
<input type="submit" class="btn btn-default" value="search">
|
||||
{% endif %}
|
||||
</form>
|
||||
{%- endmacro %}
|
||||
|
||||
{% block header %}
|
||||
<nav class="navbar navbar-inverse">
|
||||
<div class="tb-container">
|
||||
<div class="row">
|
||||
<div class="navbar-content">
|
||||
{# Brand and toggle get grouped for better mobile display #}
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed"
|
||||
data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">{fmt}</a>
|
||||
</div>
|
||||
|
||||
{# Collect the nav links, forms, and other content for toggling #}
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false">{{ version }}
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% for v in versions.split(',') %}
|
||||
<li><a href="https://fmt.dev/{{v}}">{{v}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% for name in ['Contents', 'Usage', 'API', 'Syntax'] %}
|
||||
{% if pagename == name.lower() %}
|
||||
<li class="active"><a href="{{name.lower()}}.html">{{name}}
|
||||
<span class="sr-only">(current)</span></a></li>
|
||||
{%else%}
|
||||
<li><a href="{{name.lower()}}.html">{{name}}</a></li>
|
||||
{%endif%}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if pagename != 'search' %}
|
||||
{{ searchform('navbar-form navbar-right', False) }}
|
||||
{%endif%}
|
||||
</div> {# /.navbar-collapse #}
|
||||
</div> {# /.col-md-offset-2 #}
|
||||
</div> {# /.row #}
|
||||
</div> {# /.tb-container #}
|
||||
</nav>
|
||||
{% if pagename == "index" %}
|
||||
{% set download_url = 'https://github.com/fmtlib/fmt/releases/download' %}
|
||||
<div class="jumbotron">
|
||||
<div class="tb-container">
|
||||
<h1>{fmt}</h1>
|
||||
<p class="lead">A modern formatting library</p>
|
||||
<div class="btn-group" role="group">
|
||||
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
|
||||
<a class="btn btn-success"
|
||||
href="{{download_url}}/{{version}}/{{name}}-{{version}}.zip">
|
||||
<span class="glyphicon glyphicon-download"></span> Download
|
||||
</a>
|
||||
<button type="button" class="btn btn-success dropdown-toggle"
|
||||
data-toggle="dropdown"><span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
{% for v in versions.split(',') %}
|
||||
{% set name = 'fmt' if v.split('.')[0]|int >= 3 else 'cppformat' %}
|
||||
<li><a href="{{download_url}}/{{v}}/{{name}}-{{v}}.zip">Version {{v}}
|
||||
</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{# Disable relbars. #}
|
||||
{% block relbar1 %}
|
||||
{% endblock %}
|
||||
{% block relbar2 %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="tb-container">
|
||||
<div class="row">
|
||||
{# Sidebar is currently disabled.
|
||||
<div class="bs-sidebar">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
{%- block sidebarlogo %}
|
||||
{%- if logo %}
|
||||
<p class="logo"><a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}"
|
||||
alt="Logo"/>
|
||||
</a></p>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- for sidebartemplate in sidebars %}
|
||||
{%- include sidebartemplate %}
|
||||
{%- endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#}
|
||||
|
||||
<div class="content">
|
||||
{% block body %} {% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
{{ super() }}
|
||||
{# Placed at the end of the document so the pages load faster. #}
|
||||
<script src="_static/bootstrap.min.js"></script>
|
||||
{% endblock %}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue