Compare commits
184 Commits
| 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 | |
|
|
42cc9ee096 | |
|
|
3beeac2d0c | |
|
|
6b8549346c | |
|
|
b3c894379e | |
|
|
2aba79353a | |
|
|
21fb865d80 | |
|
|
defe8531e7 | |
|
|
7653fcf138 | |
|
|
65e27eec0b | |
|
|
125063b411 | |
|
|
c6e7574976 | |
|
|
dd6c01ca0d | |
|
|
0eb5ad7cf7 | |
|
|
95fbaf5379 | |
|
|
55445f6630 | |
|
|
76aa7861f5 | |
|
|
7db4c8cb53 | |
|
|
4f1486b257 | |
|
|
bb48e1a777 | |
|
|
0607bd1c47 | |
|
|
d90940c907 | |
|
|
8ee306fb76 | |
|
|
5182cb8379 | |
|
|
f01c5040e1 | |
|
|
b1cc4d1509 | |
|
|
1b41ac9e16 | |
|
|
5bbdec8452 | |
|
|
83dd8f88b7 | |
|
|
1fbeae18c3 | |
|
|
89de7a7db9 | |
|
|
95f50d5b43 | |
|
|
691501c405 | |
|
|
c2e9985bc2 | |
|
|
d07eab796f | |
|
|
eabc1b5c3d | |
|
|
93d53957a2 | |
|
|
c3c59ddf03 | |
|
|
621afead4b | |
|
|
2fa4d0c3e6 | |
|
|
ca32a483db | |
|
|
aeedeb0626 | |
|
|
616498e8a2 | |
|
|
634fc3ac8c | |
|
|
f03f5fad2b | |
|
|
2ab0a3c26d | |
|
|
4c1241cd1e | |
|
|
7efef755fa | |
|
|
1bfd84ec34 | |
|
|
23b4283b89 | |
|
|
ead9979db7 | |
|
|
74b5aa69cb | |
|
|
ed39725641 | |
|
|
cb9c2b693e | |
|
|
4aae4c36e8 |
|
|
@ -1,8 +0,0 @@
|
||||||
build_exe:
|
|
||||||
script:
|
|
||||||
- mkdir -p build
|
|
||||||
- cd build
|
|
||||||
- git clone https://gitlab.com/dmytro.bogovych/libraries.git
|
|
||||||
- cmake -D LIB_PLATFORM=libraries ../src
|
|
||||||
- cmake --build .
|
|
||||||
|
|
||||||
|
|
@ -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,15 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rm -rf build_android
|
||||||
|
mkdir -p build_android
|
||||||
|
cd build_android
|
||||||
|
|
||||||
|
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
|
||||||
|
-DANDROID_NDK=$ANDROID_NDK_HOME \
|
||||||
|
-DANDROID_PLATFORM=24 \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DANDROID_ABI="arm64-v8a" \
|
||||||
|
../src
|
||||||
|
|
||||||
|
cmake --build . -j8
|
||||||
|
|
||||||
|
|
@ -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}')
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SEND_MSG="/var/ci/conformance_ci/send_telegram_message.py"
|
||||||
|
|
||||||
|
mkdir -p build
|
||||||
|
cd build
|
||||||
|
|
||||||
|
# Configure
|
||||||
|
cmake ../src
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
/usr/bin/python3 $SEND_MSG "rtphone cmake failed. $BUILD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build
|
||||||
|
make -j2
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
/usr/bin/python3 $SEND_MSG "rtphone build failed. $BUILD_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
/usr/bin/python3 $SEND_MSG "rtphone builds ok. $BUILD_URL"
|
||||||
|
|
@ -1,215 +1,361 @@
|
||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
project(rtphone)
|
project(rtphone)
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.0)
|
# Rely on C++ 20
|
||||||
|
set (CMAKE_CXX_STANDARD 20)
|
||||||
macro(configure_msvc_runtime)
|
|
||||||
if(MSVC)
|
|
||||||
# Default to statically-linked runtime.
|
|
||||||
if("${MSVC_RUNTIME}" STREQUAL "")
|
|
||||||
set(MSVC_RUNTIME "static")
|
|
||||||
endif()
|
|
||||||
# Set compiler options.
|
|
||||||
set(variables
|
|
||||||
CMAKE_C_FLAGS_DEBUG
|
|
||||||
CMAKE_C_FLAGS_MINSIZEREL
|
|
||||||
CMAKE_C_FLAGS_RELEASE
|
|
||||||
CMAKE_C_FLAGS_RELWITHDEBINFO
|
|
||||||
CMAKE_CXX_FLAGS_DEBUG
|
|
||||||
CMAKE_CXX_FLAGS_MINSIZEREL
|
|
||||||
CMAKE_CXX_FLAGS_RELEASE
|
|
||||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
|
||||||
)
|
|
||||||
if(${MSVC_RUNTIME} STREQUAL "static")
|
|
||||||
message(STATUS
|
|
||||||
"rtphone: MSVC -> forcing use of statically-linked runtime."
|
|
||||||
)
|
|
||||||
foreach(variable ${variables})
|
|
||||||
if(${variable} MATCHES "/MD")
|
|
||||||
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
else()
|
|
||||||
message(STATUS
|
|
||||||
"rtphone: MSVC -> forcing use of dynamically-linked runtime."
|
|
||||||
)
|
|
||||||
foreach(variable ${variables})
|
|
||||||
if(${variable} MATCHES "/MT")
|
|
||||||
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
foreach(variable ${variables})
|
|
||||||
string(REGEX REPLACE "/Z[iI7]" ""
|
|
||||||
${variable}
|
|
||||||
"${${variable}}")
|
|
||||||
|
|
||||||
set(${variable} "${${variable}} /Zi /Oy-")
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
|
|
||||||
# Rely on C++ 11
|
|
||||||
set (CMAKE_CXX_STANDARD 11)
|
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set (rtphone_libs libs)
|
set (L libs)
|
||||||
set (rtphone_engine engine)
|
set (E engine)
|
||||||
|
|
||||||
set (USE_AMR_CODEC OFF CACHE BOOL "Use AMR codec. Requires libraries.")
|
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
|
||||||
set (USE_EVS_CODEC OFF CACHE BOOL "Use EVS codec." )
|
option (USE_EVS_CODEC "Use EVS codec." ON)
|
||||||
set (USE_OPUS_CODEC OFF CACHE BOOL "Use Opus codec." )
|
option (USE_OPUS_CODEC "Use Opus codec." ON)
|
||||||
set (USE_PVQA_LIB OFF CACHE BOOL "Build with Sevana PVQA library" )
|
option (USE_MUSL "Build with MUSL library" OFF)
|
||||||
set (USE_AQUA_LIB OFF CACHE BOOL "Build with Sevana AQuA library" )
|
|
||||||
|
|
||||||
# PIC code by default
|
# PIC code by default
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
set (RUNTIME_CPU_CAPABILITY_DETECTION ON)
|
||||||
|
|
||||||
|
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/libs/libraries)
|
||||||
|
include (${LIB_PLATFORM}/platform_libs.cmake)
|
||||||
|
|
||||||
if (NOT DEFINED LIB_PLATFORM)
|
|
||||||
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message("Libraries: ${LIB_PLATFORM}")
|
message("Libraries: ${LIB_PLATFORM}")
|
||||||
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.0/include)
|
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.1/include)
|
||||||
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
||||||
|
message ("Using OpenSSL libs: ${OPENSSL_SSL} and ${OPENSSL_CRYPTO}")
|
||||||
|
include_directories(${OPENSSL_INCLUDE})
|
||||||
|
|
||||||
|
# Used defines for our project
|
||||||
|
set (DEFINES -DUSE_OPENSSL)
|
||||||
|
|
||||||
|
# Libraries for our project
|
||||||
|
set (LIBS_STATIC "")
|
||||||
|
set (LIBS_DYNAMIC "")
|
||||||
|
|
||||||
|
# Try to prefer static libraries anyway
|
||||||
|
set (CMAKE_FIND_LIBRARY_SUFFIXES .a .so .dylib)
|
||||||
|
|
||||||
|
# Windows-specific definitions
|
||||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||||
add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
set (TARGET_WIN ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Linux-specific definitions
|
||||||
if (CMAKE_SYSTEM MATCHES "Linux*")
|
if (CMAKE_SYSTEM MATCHES "Linux*")
|
||||||
add_definitions (-DTARGET_LINUX)
|
set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H)
|
||||||
|
set (TARGET_LINUX ON)
|
||||||
|
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# macOS-specific definitions
|
||||||
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
||||||
add_definitions (-DTARGET_OSX)
|
set (DEFINES ${DEFINES} -DTARGET_OSX)
|
||||||
|
set (TARGET_OSX ON)
|
||||||
|
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_AQUA_LIB)
|
#
|
||||||
message("Use AQuA library")
|
if (CMAKE_SYSTEM MATCHES "Android")
|
||||||
add_definitions( -DUSE_AQUA_LIBRARY )
|
message("Adding the Oboe library")
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include)
|
set (OBOE_DIR libs/oboe)
|
||||||
|
add_subdirectory (${OBOE_DIR} ./oboe)
|
||||||
|
include_directories (${OBOE_DIR}/include)
|
||||||
|
set (DEFINES ${DEFINES} -DTARGET_ANDROID -DHAVE_NETINET_IN_H)
|
||||||
|
set (TARGET_ANDROID ON)
|
||||||
|
set (LIBS_STATIC ${LIBS} oboe)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_PVQA_LIBRARY)
|
if (USE_MUSL)
|
||||||
message("Use PVQA libraries")
|
set (DEFINES ${DEFINES} -DTARGET_MUSL)
|
||||||
add_definitions( -DUSE_PVQA_LIBRARY )
|
set (TARGET_MUSL ON)
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set (RTPHONE_SOURCES
|
set (RTPHONE_SOURCES
|
||||||
${rtphone_engine}/media/MT_Statistics.cpp
|
${E}/engine_config.h
|
||||||
${rtphone_engine}/media/MT_WebRtc.cpp
|
${E}/media/MT_Statistics.cpp
|
||||||
${rtphone_engine}/media/MT_Stream.cpp
|
${E}/media/MT_WebRtc.cpp
|
||||||
${rtphone_engine}/media/MT_SrtpHelper.cpp
|
${E}/media/MT_Stream.cpp
|
||||||
${rtphone_engine}/media/MT_SingleAudioStream.cpp
|
${E}/media/MT_SrtpHelper.cpp
|
||||||
${rtphone_engine}/media/MT_NativeRtpSender.cpp
|
${E}/media/MT_SingleAudioStream.cpp
|
||||||
${rtphone_engine}/media/MT_Dtmf.cpp
|
${E}/media/MT_NativeRtpSender.cpp
|
||||||
${rtphone_engine}/media/MT_CodecList.cpp
|
${E}/media/MT_Dtmf.cpp
|
||||||
${rtphone_engine}/media/MT_Codec.cpp
|
${E}/media/MT_CodecList.cpp
|
||||||
${rtphone_engine}/media/MT_Box.cpp
|
${E}/media/MT_Codec.cpp
|
||||||
${rtphone_engine}/media/MT_AudioStream.cpp
|
${E}/media/MT_Box.cpp
|
||||||
${rtphone_engine}/media/MT_AudioReceiver.cpp
|
${E}/media/MT_AudioStream.cpp
|
||||||
${rtphone_engine}/media/MT_AudioCodec.cpp
|
${E}/media/MT_AudioReceiver.cpp
|
||||||
${rtphone_engine}/media/MT_CngHelper.cpp
|
${E}/media/MT_AudioCodec.cpp
|
||||||
${rtphone_engine}/agent/Agent_Impl.cpp
|
${E}/media/MT_CngHelper.cpp
|
||||||
${rtphone_engine}/agent/Agent_AudioManager.cpp
|
${E}/agent/Agent_Impl.cpp
|
||||||
${rtphone_engine}/endpoint/EP_Account.cpp
|
${E}/agent/Agent_Impl.h
|
||||||
${rtphone_engine}/endpoint/EP_AudioProvider.cpp
|
${E}/agent/Agent_AudioManager.cpp
|
||||||
${rtphone_engine}/endpoint/EP_DataProvider.cpp
|
${E}/agent/Agent_AudioManager.h
|
||||||
${rtphone_engine}/endpoint/EP_Engine.cpp
|
${E}/endpoint/EP_Account.cpp
|
||||||
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
|
${E}/endpoint/EP_Account.h
|
||||||
${rtphone_engine}/endpoint/EP_Observer.cpp
|
${E}/endpoint/EP_AudioProvider.cpp
|
||||||
${rtphone_engine}/endpoint/EP_Session.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
|
${E}/media/MT_Statistics.h
|
||||||
${rtphone_engine}/media/MT_WebRtc.h
|
${E}/media/MT_WebRtc.h
|
||||||
${rtphone_engine}/media/MT_Stream.h
|
${E}/media/MT_Stream.h
|
||||||
${rtphone_engine}/media/MT_SrtpHelper.h
|
${E}/media/MT_SrtpHelper.h
|
||||||
${rtphone_engine}/media/MT_SingleAudioStream.h
|
${E}/media/MT_SingleAudioStream.h
|
||||||
${rtphone_engine}/media/MT_NativeRtpSender.h
|
${E}/media/MT_NativeRtpSender.h
|
||||||
${rtphone_engine}/media/MT_Dtmf.h
|
${E}/media/MT_Dtmf.h
|
||||||
${rtphone_engine}/media/MT_CodecList.h
|
${E}/media/MT_CodecList.h
|
||||||
${rtphone_engine}/media/MT_Codec.h
|
${E}/media/MT_Codec.h
|
||||||
${rtphone_engine}/media/MT_Box.h
|
${E}/media/MT_Box.h
|
||||||
${rtphone_engine}/media/MT_AudioStream.h
|
${E}/media/MT_AudioStream.h
|
||||||
${rtphone_engine}/media/MT_AudioReceiver.h
|
${E}/media/MT_AudioReceiver.h
|
||||||
${rtphone_engine}/media/MT_AudioCodec.h
|
${E}/media/MT_AudioCodec.h
|
||||||
|
${E}/media/MT_CngHelper.h
|
||||||
|
|
||||||
${rtphone_engine}/media/MT_CngHelper.h
|
${E}/media/MT_Statistics.cpp
|
||||||
${rtphone_engine}/agent/Agent_Impl.h
|
${E}/media/MT_WebRtc.cpp
|
||||||
${rtphone_engine}/agent/Agent_AudioManager.h
|
${E}/media/MT_Stream.cpp
|
||||||
${rtphone_engine}/endpoint/EP_Account.h
|
${E}/media/MT_SrtpHelper.cpp
|
||||||
${rtphone_engine}/endpoint/EP_AudioProvider.h
|
${E}/media/MT_SingleAudioStream.cpp
|
||||||
${rtphone_engine}/endpoint/EP_DataProvider.h
|
${E}/media/MT_NativeRtpSender.cpp
|
||||||
${rtphone_engine}/endpoint/EP_Engine.h
|
${E}/media/MT_Dtmf.cpp
|
||||||
${rtphone_engine}/endpoint/EP_NetworkQueue.h
|
${E}/media/MT_CodecList.cpp
|
||||||
${rtphone_engine}/endpoint/EP_Observer.h
|
${E}/media/MT_Codec.cpp
|
||||||
${rtphone_engine}/endpoint/EP_Session.h
|
${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)
|
if (USE_OPUS_CODEC)
|
||||||
add_definitions(-DUSE_OPUS_CODEC)
|
set (DEFINES ${DEFINES} -DUSE_OPUS_CODEC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library (rtphone STATIC ${RTPHONE_SOURCES})
|
add_library (rtphone STATIC ${RTPHONE_SOURCES})
|
||||||
|
|
||||||
add_subdirectory(${rtphone_libs}/resiprocate)
|
add_subdirectory(${L}/resiprocate)
|
||||||
add_subdirectory(${rtphone_libs}/ice)
|
add_subdirectory(${L}/jrtplib/src)
|
||||||
add_subdirectory(${rtphone_libs}/jrtplib/src)
|
add_subdirectory(${L}/libg729)
|
||||||
add_subdirectory(${rtphone_libs}/libg729)
|
|
||||||
|
|
||||||
if (USE_EVS_CODEC)
|
if (USE_EVS_CODEC)
|
||||||
add_subdirectory(${rtphone_libs}/libevs)
|
add_subdirectory(${L}/libevs)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(${rtphone_libs}/libgsm)
|
add_subdirectory(${L}/libgsm)
|
||||||
add_subdirectory(${rtphone_libs}/gsmhr)
|
add_subdirectory(${L}/gsmhr)
|
||||||
add_subdirectory(${rtphone_libs}/g722)
|
add_subdirectory(${L}/g722)
|
||||||
add_subdirectory(${rtphone_libs}/speexdsp)
|
add_subdirectory(${L}/speexdsp)
|
||||||
add_subdirectory(${rtphone_libs}/srtp)
|
add_subdirectory(${L}/libsrtp)
|
||||||
add_subdirectory(${rtphone_libs}/webrtc)
|
add_subdirectory(${L}/webrtc)
|
||||||
add_subdirectory(${rtphone_engine}/helper)
|
add_subdirectory(${L}/opus)
|
||||||
add_subdirectory(${rtphone_engine}/audio)
|
|
||||||
add_subdirectory(${rtphone_engine}/media)
|
|
||||||
|
|
||||||
set (LIBS ice_stack jrtplib g729_codec gsm_codec
|
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
|
||||||
gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp
|
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
|
||||||
uuid)
|
|
||||||
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Win*")
|
|
||||||
set (LIBS ${LIBS} )
|
|
||||||
else ()
|
|
||||||
set (LIBS ${LIBS} dl uuid)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (USE_AMR_CODEC)
|
if (USE_AMR_CODEC)
|
||||||
set (LIBS ${LIBS})
|
include (${LIB_PLATFORM}/platform_libs.cmake)
|
||||||
endif (USE_AMR_CODEC)
|
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()
|
||||||
|
|
||||||
target_link_libraries(rtphone
|
if (USE_EVS_CODEC)
|
||||||
ice_stack jrtplib g729_codec gsm_codec
|
message("Media: EVS codec will be included.")
|
||||||
gsmhr_codec g722_codec srtp resiprocate
|
set (DEFINES ${DEFINES} -DUSE_EVS_CODEC)
|
||||||
helper_lib
|
set (LIBS_STATIC ${LIBS_STATIC} evs_codec)
|
||||||
audio_lib
|
endif()
|
||||||
webrtc
|
|
||||||
speexdsp
|
|
||||||
uuid
|
|
||||||
${OPENSSL_SSL}
|
|
||||||
${OPENSSL_CRYPTO}
|
|
||||||
${LIBS})
|
|
||||||
|
|
||||||
|
target_compile_definitions(rtphone PUBLIC ${DEFINES})
|
||||||
|
|
||||||
|
if (TARGET_LINUX)
|
||||||
|
target_link_options(rtphone PUBLIC -Wl,-Bstatic)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(rtphone PUBLIC ${LIBS_STATIC} ${OPENSSL_SSL} ${OPENSSL_CRYPTO})
|
||||||
|
|
||||||
|
if (TARGET_LINUX)
|
||||||
|
target_link_options(rtphone PUBLIC -Wl,-Bdynamic)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(rtphone
|
target_include_directories(rtphone
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
|
@ -217,15 +363,18 @@ target_include_directories(rtphone
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
||||||
${LIB_PLATFORM}/opus/include
|
${LIB_PLATFORM}/opus/include
|
||||||
|
${E}/helper
|
||||||
|
${E}/audio
|
||||||
|
${E}/media
|
||||||
|
${L}
|
||||||
|
${L}/ice
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/
|
${L}/libevs/lib_com
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com
|
${L}/libevs/lib_enc
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc
|
${L}/libevs/lib_dec
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec
|
${L}/speex/include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include
|
${L}/libs/json
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# For MSVC static builds
|
# For MSVC static builds
|
||||||
configure_msvc_runtime()
|
# set_property(TARGET rtphone PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "Agent_AudioManager.h"
|
#include "Agent_AudioManager.h"
|
||||||
#include "../engine/audio/Audio_WavFile.h"
|
#include "../engine/audio/Audio_WavFile.h"
|
||||||
#include "../engine/helper/HL_String.h"
|
|
||||||
#include "../engine/audio/Audio_Null.h"
|
#include "../engine/audio/Audio_Null.h"
|
||||||
|
#include "HL_String.h"
|
||||||
|
|
||||||
#if defined(TARGET_ANDROID)
|
#if defined(TARGET_ANDROID)
|
||||||
# include "../engine/audio/Audio_Android.h"
|
# include "../engine/audio/Audio_Android.h"
|
||||||
|
|
@ -15,11 +15,8 @@
|
||||||
#define LOG_SUBSYSTEM "AudioManager"
|
#define LOG_SUBSYSTEM "AudioManager"
|
||||||
|
|
||||||
|
|
||||||
// ---------------- AudioManager -------------
|
|
||||||
//static AudioManager GAudioManager;
|
|
||||||
|
|
||||||
AudioManager::AudioManager()
|
AudioManager::AudioManager()
|
||||||
:mTerminal(nullptr)
|
:mTerminal(nullptr), mAudioMonitoring(nullptr)
|
||||||
{
|
{
|
||||||
mPlayer.setDelegate(this);
|
mPlayer.setDelegate(this);
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +46,16 @@ MT::Terminal* AudioManager::terminal()
|
||||||
return mTerminal;
|
return mTerminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioManager::setAudioMonitoring(Audio::DataConnection* monitoring)
|
||||||
|
{
|
||||||
|
mAudioMonitoring = monitoring;
|
||||||
|
}
|
||||||
|
|
||||||
|
Audio::DataConnection* AudioManager::audioMonitoring()
|
||||||
|
{
|
||||||
|
return mAudioMonitoring;
|
||||||
|
}
|
||||||
|
|
||||||
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
|
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
|
||||||
void AudioManager::start(int usageId)
|
void AudioManager::start(int usageId)
|
||||||
{
|
{
|
||||||
|
|
@ -68,7 +75,14 @@ void AudioManager::start(int usageId)
|
||||||
// Disable AEC for now - because PVQA conflicts with speex AEC.
|
// Disable AEC for now - because PVQA conflicts with speex AEC.
|
||||||
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
|
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
|
||||||
if (!mTerminal->audio())
|
if (!mTerminal->audio())
|
||||||
mTerminal->setAudio(std::make_shared<Audio::DevicePair>(false, true));
|
{
|
||||||
|
auto audio = std::make_shared<Audio::DevicePair>();
|
||||||
|
audio->setAgc(true);
|
||||||
|
audio->setAec(false);
|
||||||
|
audio->setMonitoring(mAudioMonitoring);
|
||||||
|
|
||||||
|
mTerminal->setAudio(audio);
|
||||||
|
}
|
||||||
|
|
||||||
if (!mAudioInput)
|
if (!mAudioInput)
|
||||||
{
|
{
|
||||||
|
|
@ -158,7 +172,7 @@ void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarg
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
r->open(StringHelper::makeTstring(path));
|
r->open(strx::makeTstring(path));
|
||||||
#else
|
#else
|
||||||
r->open(path);
|
r->open(path);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@
|
||||||
|
|
||||||
#include "../engine/audio/Audio_Interface.h"
|
#include "../engine/audio/Audio_Interface.h"
|
||||||
#include "../engine/audio/Audio_Player.h"
|
#include "../engine/audio/Audio_Player.h"
|
||||||
#include "../engine/endpoint/EP_Engine.h"
|
|
||||||
#include "../engine/media/MT_Box.h"
|
#include "../engine/media/MT_Box.h"
|
||||||
#include "../engine/helper/HL_Log.h"
|
|
||||||
#include "../engine/helper/HL_Sync.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -49,6 +46,9 @@ public:
|
||||||
void setTerminal(MT::Terminal* terminal);
|
void setTerminal(MT::Terminal* terminal);
|
||||||
MT::Terminal* terminal();
|
MT::Terminal* terminal();
|
||||||
|
|
||||||
|
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||||
|
Audio::DataConnection* audioMonitoring();
|
||||||
|
|
||||||
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
|
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
|
||||||
void start(int usageId);
|
void start(int usageId);
|
||||||
void stop(int usageId);
|
void stop(int usageId);
|
||||||
|
|
@ -69,6 +69,7 @@ public:
|
||||||
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
|
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
|
||||||
void stopPlayFile(int usageId);
|
void stopPlayFile(int usageId);
|
||||||
|
|
||||||
|
|
||||||
void onFilePlayed(Audio::Player::PlaylistItem& item);
|
void onFilePlayed(Audio::Player::PlaylistItem& item);
|
||||||
|
|
||||||
// Must be called from main loop to release used audio devices
|
// Must be called from main loop to release used audio devices
|
||||||
|
|
@ -79,6 +80,7 @@ protected:
|
||||||
Audio::POutputDevice mAudioOutput;
|
Audio::POutputDevice mAudioOutput;
|
||||||
Audio::Player mPlayer;
|
Audio::Player mPlayer;
|
||||||
MT::Terminal* mTerminal;
|
MT::Terminal* mTerminal;
|
||||||
|
Audio::DataConnection* mAudioMonitoring;
|
||||||
|
|
||||||
std::map<int, int> UsageMap;
|
std::map<int, int> UsageMap;
|
||||||
UsageCounter mUsage;
|
UsageCounter mUsage;
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,6 @@
|
||||||
#include "helper/HL_Base64.h"
|
#include "helper/HL_Base64.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY)
|
|
||||||
# include "pvqa++.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
# include "aqua++.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const std::string Status_Ok = "ok";
|
const std::string Status_Ok = "ok";
|
||||||
const std::string Status_SessionNotFound = "session not found";
|
const std::string Status_SessionNotFound = "session not found";
|
||||||
|
|
@ -39,6 +32,24 @@ AgentImpl::~AgentImpl()
|
||||||
stopAgentAndThread();
|
stopAgentAndThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get access to internal audio manager. Value can be nullptr.
|
||||||
|
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
|
||||||
|
{
|
||||||
|
return mAudioManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
|
||||||
|
{
|
||||||
|
mAudioMonitoring = monitoring;
|
||||||
|
if (mAudioManager)
|
||||||
|
mAudioManager->setAudioMonitoring(monitoring);
|
||||||
|
}
|
||||||
|
|
||||||
|
Audio::DataConnection* AgentImpl::monitoring() const
|
||||||
|
{
|
||||||
|
return mAudioMonitoring;
|
||||||
|
}
|
||||||
|
|
||||||
void AgentImpl::run()
|
void AgentImpl::run()
|
||||||
{
|
{
|
||||||
while (!mShutdown)
|
while (!mShutdown)
|
||||||
|
|
@ -139,7 +150,9 @@ std::string AgentImpl::command(const std::string& command)
|
||||||
{
|
{
|
||||||
answer["status"] = e.what();
|
answer["status"] = e.what();
|
||||||
}
|
}
|
||||||
return answer.toStyledString();
|
std::string result = answer.toStyledString();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AgentImpl::waitForData(int /*milliseconds*/)
|
bool AgentImpl::waitForData(int /*milliseconds*/)
|
||||||
|
|
@ -156,25 +169,14 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||||
|
|
||||||
#if !defined(TARGET_ANDROID) && defined(USE_PVQA_LIBRARY)
|
|
||||||
// It works for desktop OSes only
|
|
||||||
// Because Android requires special initializing procedure (valid JNI environment context)
|
|
||||||
std::string pvqaLicense = d["pvqa-license"].asString(), pvqaConfig = d["pvqa-config"].asString();
|
|
||||||
if (!pvqaLicense.empty() && !pvqaConfig.empty())
|
|
||||||
sevana::pvqa::initialize(pvqaLicense, pvqaConfig);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(TARGET_ANDROID) && defined(USE_AQUA_LIBRARY)
|
|
||||||
std::string aquaLicense = d["aqua-license"].asString();
|
|
||||||
if (!aquaLicense.empty())
|
|
||||||
sevana::aqua::initialize(aquaLicense.c_str(), aquaLicense.size());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string transport = d["transport"].asString();
|
std::string transport = d["transport"].asString();
|
||||||
config()[CONFIG_TRANSPORT] = (transport == "any") ? 0 : (transport == "udp" ? 1 : (transport == "tcp" ? 2 : 3));
|
config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
|
||||||
config()[CONFIG_IPV4] = d["ipv4"].asBool();
|
config()[CONFIG_IPV4] = d["ipv4"].asBool();
|
||||||
config()[CONFIG_IPV6] = d["ipv6"].asBool();
|
config()[CONFIG_IPV6] = d["ipv6"].asBool();
|
||||||
|
|
||||||
|
if (transport == "tls")
|
||||||
|
config()[CONFIG_SIPS] = true;
|
||||||
|
|
||||||
// Log file
|
// Log file
|
||||||
std::string logfile = d["logfile"].asString();
|
std::string logfile = d["logfile"].asString();
|
||||||
ice::Logger& logger = ice::GLogger;
|
ice::Logger& logger = ice::GLogger;
|
||||||
|
|
@ -186,10 +188,13 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
|
||||||
|
|
||||||
mUseNativeAudio = d["nativeaudio"].asBool();
|
mUseNativeAudio = d["nativeaudio"].asBool();
|
||||||
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
|
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
|
||||||
|
config()[CONFIG_SIPS] = d["secure"].asBool();
|
||||||
|
config()[CONFIG_STUNSERVER_IP] = d["stun_server"].asString();
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentImpl::processStart(JsonCpp::Value& /*request*/, JsonCpp::Value &answer)
|
void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||||
if (mThread)
|
if (mThread)
|
||||||
|
|
@ -198,6 +203,9 @@ void AgentImpl::processStart(JsonCpp::Value& /*request*/, JsonCpp::Value &answer
|
||||||
return; // Started already
|
return; // Started already
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process config (can be sent via start command as well)
|
||||||
|
// processConfig(request, answer);
|
||||||
|
|
||||||
// Start socket thread
|
// Start socket thread
|
||||||
SocketHeap::instance().start();
|
SocketHeap::instance().start();
|
||||||
|
|
||||||
|
|
@ -222,8 +230,10 @@ void AgentImpl::processStart(JsonCpp::Value& /*request*/, JsonCpp::Value &answer
|
||||||
// Enable audio
|
// Enable audio
|
||||||
mAudioManager = std::make_shared<AudioManager>();
|
mAudioManager = std::make_shared<AudioManager>();
|
||||||
mAudioManager->setTerminal(mTerminal.get());
|
mAudioManager->setTerminal(mTerminal.get());
|
||||||
|
if (mAudioMonitoring)
|
||||||
|
mAudioManager->setAudioMonitoring(mAudioMonitoring);
|
||||||
|
|
||||||
// Do not start here. Start right before call.
|
// Do not start audio manager here. Start right before call.
|
||||||
|
|
||||||
// Initialize endpoint
|
// Initialize endpoint
|
||||||
start();
|
start();
|
||||||
|
|
@ -250,7 +260,7 @@ void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
|
||||||
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
|
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
|
||||||
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
|
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
|
||||||
|
|
||||||
auto nameAndPort = StringHelper::parseHost(d["stun_server"].asString(), 3478);
|
auto nameAndPort = strx::parseHost(d["stun_server"].asString(), 3478);
|
||||||
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
|
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
|
||||||
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
|
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
|
||||||
|
|
||||||
|
|
@ -332,7 +342,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||||
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
||||||
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
/*#if defined(USE_AQUA_LIBRARY)
|
||||||
std::string path_faults = request["path_faults"].asString();
|
std::string path_faults = request["path_faults"].asString();
|
||||||
|
|
||||||
sevana::aqua::config config = {
|
sevana::aqua::config config = {
|
||||||
|
|
@ -356,8 +366,8 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||||
};
|
};
|
||||||
|
|
||||||
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
|
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
|
||||||
/*if (temp_path.size())
|
// if (temp_path.size())
|
||||||
config += " -fau " + temp_path; */
|
// config += " -fau " + temp_path;
|
||||||
|
|
||||||
auto qc = std::make_shared<sevana::aqua>();
|
auto qc = std::make_shared<sevana::aqua>();
|
||||||
if (!qc->is_open())
|
if (!qc->is_open())
|
||||||
|
|
@ -370,6 +380,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||||
mAquaMap[sessionIter->first] = qc;
|
mAquaMap[sessionIter->first] = qc;
|
||||||
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
|
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
|
||||||
#endif
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
// TODO: support SRTP via StreamState::Srtp option in audio provider state
|
// TODO: support SRTP via StreamState::Srtp option in audio provider state
|
||||||
|
|
||||||
|
|
@ -444,9 +455,9 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
|
||||||
auto sessionIter = mSessionMap.find(sessionId);
|
auto sessionIter = mSessionMap.find(sessionId);
|
||||||
if (sessionIter != mSessionMap.end())
|
if (sessionIter != mSessionMap.end())
|
||||||
mSessionMap.erase(sessionIter);
|
mSessionMap.erase(sessionIter);
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
//#if defined(USE_AQUA_LIBRARY)
|
||||||
closeAqua(sessionId);
|
// closeAqua(sessionId);
|
||||||
#endif
|
//#endif
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,41 +481,6 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &ans
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY)
|
|
||||||
static JsonCpp::Value CsvReportToJson(const std::string& report)
|
|
||||||
{
|
|
||||||
JsonCpp::Value detectorValues;
|
|
||||||
std::istringstream iss(report);
|
|
||||||
CsvReader reader(iss);
|
|
||||||
std::vector<std::string> cells;
|
|
||||||
if (reader.readLine(cells))
|
|
||||||
{
|
|
||||||
JsonCpp::Value detectorNames;
|
|
||||||
for (size_t nameIndex = 0; nameIndex < cells.size(); nameIndex++)
|
|
||||||
detectorNames[static_cast<int>(nameIndex)] = StringHelper::trim(cells[nameIndex]);
|
|
||||||
// Put first line name of columns
|
|
||||||
detectorValues[0] = detectorNames;
|
|
||||||
|
|
||||||
int rowIndex = 1;
|
|
||||||
while (reader.readLine(cells))
|
|
||||||
{
|
|
||||||
// Skip last column for now
|
|
||||||
JsonCpp::Value row;
|
|
||||||
for (size_t valueIndex = 0; valueIndex < cells.size(); valueIndex++)
|
|
||||||
{
|
|
||||||
bool isFloat = true;
|
|
||||||
float v = StringHelper::toFloat(cells[valueIndex], 0.0, &isFloat);
|
|
||||||
if (isFloat)
|
|
||||||
row[static_cast<int>(valueIndex)] = static_cast<double>(v);
|
|
||||||
else
|
|
||||||
row[static_cast<int>(valueIndex)] = cells[valueIndex];
|
|
||||||
}
|
|
||||||
detectorValues[rowIndex++] = row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return detectorValues;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
|
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
|
||||||
{
|
{
|
||||||
|
|
@ -515,24 +491,13 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
||||||
{
|
{
|
||||||
PSession session = sessionIter->second;
|
PSession session = sessionIter->second;
|
||||||
VariantMap result;
|
VariantMap result;
|
||||||
bool includePvqa = request["include_pvqa"].asBool();
|
session->getSessionInfo(Session::InfoOptions::Detailed,
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
bool includeAqua = request["include_aqua"].asBool();
|
|
||||||
std::string aquaReference = request["aqua_reference_audio"].asString();
|
|
||||||
#endif
|
|
||||||
session->getSessionInfo(includePvqa ? Session::InfoOptions::Detailed : Session::InfoOptions::Standard,
|
|
||||||
result);
|
result);
|
||||||
|
|
||||||
if (result.exists(SessionInfo_AudioCodec))
|
if (result.exists(SessionInfo_AudioCodec))
|
||||||
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
|
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
|
||||||
if (result.exists(SessionInfo_NetworkMos))
|
if (result.exists(SessionInfo_NetworkMos))
|
||||||
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
|
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
|
||||||
#if defined(USE_PVQA_LIBRARY)
|
|
||||||
if (result.exists(SessionInfo_PvqaMos))
|
|
||||||
answer["pvqa_mos"] = result[SessionInfo_PvqaMos].asFloat();
|
|
||||||
if (result.exists(SessionInfo_PvqaReport))
|
|
||||||
answer["pvqa_report"] = CsvReportToJson(result[SessionInfo_PvqaReport].asStdString());
|
|
||||||
#endif
|
|
||||||
if (result.exists(SessionInfo_PacketLoss))
|
if (result.exists(SessionInfo_PacketLoss))
|
||||||
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
|
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
|
||||||
if (result.exists(SessionInfo_DroppedRtp))
|
if (result.exists(SessionInfo_DroppedRtp))
|
||||||
|
|
@ -555,74 +520,6 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
||||||
if (result.exists(SessionInfo_RemotePeer))
|
if (result.exists(SessionInfo_RemotePeer))
|
||||||
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
|
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
if (includeAqua)
|
|
||||||
{
|
|
||||||
answer["incoming_audio"] = mAquaIncoming.hexstring();
|
|
||||||
answer["incoming_audio_samplerate"] = AUDIO_SAMPLERATE;
|
|
||||||
answer["incoming_audio_channels"] = AUDIO_CHANNELS;
|
|
||||||
|
|
||||||
ICELogInfo(<< "Running AQuA analyzer.");
|
|
||||||
ByteBuffer referenceAudio;
|
|
||||||
// Read AQuA reference audio from file if available
|
|
||||||
if (aquaReference.empty())
|
|
||||||
{
|
|
||||||
ICELogCritical(<< "AQuA reference audio file is not set, skipping analyzing.");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto sa = mAquaMap[sessionIter->first];
|
|
||||||
if (sa) {
|
|
||||||
Audio::WavFileReader reader;
|
|
||||||
reader.open(StringHelper::makeTstring(aquaReference));
|
|
||||||
|
|
||||||
if (reader.isOpened()) {
|
|
||||||
char buffer[1024];
|
|
||||||
int wasRead = 0;
|
|
||||||
do {
|
|
||||||
wasRead = reader.read(buffer, 1024);
|
|
||||||
if (wasRead > 0)
|
|
||||||
referenceAudio.appendBuffer(buffer, wasRead);
|
|
||||||
} while (wasRead == 1024);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ICELogCritical(<< "Failed to read AQuA reference audio, error code: " << reader.lastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
sevana::aqua::audio_buffer test(mAquaIncoming.data(), mAquaIncoming.size()),
|
|
||||||
reference(referenceAudio.data(), referenceAudio.size());
|
|
||||||
test.mRate = AUDIO_SAMPLERATE;
|
|
||||||
reference.mRate = AUDIO_SAMPLERATE;
|
|
||||||
test.mChannels = AUDIO_CHANNELS;
|
|
||||||
reference.mChannels = AUDIO_CHANNELS;
|
|
||||||
ICELogInfo(
|
|
||||||
<< "Comparing test audio " << mAquaIncoming.size() << " bytes with reference audio " << referenceAudio.size() << " bytes.");
|
|
||||||
auto r = sa->compare(reference, test);
|
|
||||||
if (r.mErrorCode) {
|
|
||||||
ICELogInfo(
|
|
||||||
<< "Error code: " << r.mErrorCode << ", msg: " << r.mErrorMessage);
|
|
||||||
} else {
|
|
||||||
ICELogInfo(<< "MOS: " << r.mMos << ", faults: " << r.mFaultsText);
|
|
||||||
}
|
|
||||||
answer["aqua_mos"] = r.mMos;
|
|
||||||
answer["aqua_report"] = r.mFaultsText;
|
|
||||||
/*std::string aqua_audio_text;
|
|
||||||
if (Base64::Encode(std::string(reinterpret_cast<const char*>(mAquaIncoming.data()), mAquaIncoming.size()), &aqua_audio_text))
|
|
||||||
{
|
|
||||||
answer["aqua_audio"] = aqua_audio_text;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (r.mErrorCode) {
|
|
||||||
answer["aqua_error_code"] = r.mErrorCode;
|
|
||||||
answer["aqua_error_message"] = r.mErrorMessage;
|
|
||||||
}
|
|
||||||
closeAqua(sessionIter->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove test audio
|
|
||||||
mAquaIncoming.clear(); mAquaOutgoing.clear();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -643,18 +540,16 @@ void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answ
|
||||||
std::string pem = request["cert"].asString();
|
std::string pem = request["cert"].asString();
|
||||||
|
|
||||||
std::string::size_type pb = 0, pe = 0;
|
std::string::size_type pb = 0, pe = 0;
|
||||||
|
while (pb != std::string::npos && pe != std::string::npos) {
|
||||||
|
pb = pem.find(BeginCertificate, pb);
|
||||||
|
pe = pem.find(EndCertificate, pe);
|
||||||
|
|
||||||
for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe);
|
if (pb != std::string::npos && pe != std::string::npos && pe > pb) {
|
||||||
pb != std::string::npos && pe != std::string::npos;
|
std::string cert = pem.substr(pb, pe - pb + EndCertificate.size());
|
||||||
pb = pem.find(BeginCertificate, pb + BeginCertificate.size()), pe = pem.find(EndCertificate, pe + EndCertificate.size()))
|
|
||||||
{
|
|
||||||
// Get single certificate
|
|
||||||
std::string cert = pem.substr(pb, pe + EndCertificate.size());
|
|
||||||
//int size = cert.size();
|
|
||||||
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
|
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
|
||||||
|
|
||||||
// Delete processed part
|
pb = ++pe;
|
||||||
pem.erase(0, pe + EndCertificate.size());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
|
|
@ -742,7 +637,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
|
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
|
||||||
if (!reader->open(StringHelper::makeTstring(path)))
|
if (!reader->open(strx::makeTstring(path)))
|
||||||
answer["status"] = Status_FailedToOpenFile;
|
answer["status"] = Status_FailedToOpenFile;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -763,7 +658,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
|
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
|
||||||
if (!writer->open(StringHelper::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
|
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
|
||||||
answer["status"] = Status_FailedToOpenFile;
|
answer["status"] = Status_FailedToOpenFile;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -786,25 +681,14 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
|
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
|
||||||
{
|
{
|
||||||
/* if (mIncomingAudioDump && direction == MT::Stream::MediaDirection::Incoming)
|
/*switch (direction)
|
||||||
mIncomingAudioDump->write(data, length);
|
|
||||||
|
|
||||||
if (mOutgoingAudioDump && direction == MT::Stream::MediaDirection::Outgoing)
|
|
||||||
mOutgoingAudioDump->write(data, length);*/
|
|
||||||
|
|
||||||
// User tag points to accumulator object which includes
|
|
||||||
// auto* sa = reinterpret_cast<sevana::aqua*>(userTag);
|
|
||||||
|
|
||||||
switch (direction)
|
|
||||||
{
|
{
|
||||||
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
|
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
|
||||||
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
|
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// Called on new incoming session; providers shoukld
|
// Called on new incoming session; providers shoukld
|
||||||
|
|
@ -961,12 +845,12 @@ void AgentImpl::addEvent(const JsonCpp::Value& v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
void AgentImpl::closeAqua(int sessionId)
|
/*void AgentImpl::closeAqua(int sessionId)
|
||||||
{
|
{
|
||||||
auto aquaIter = mAquaMap.find(sessionId);
|
auto aquaIter = mAquaMap.find(sessionId);
|
||||||
if (aquaIter != mAquaMap.end()) {
|
if (aquaIter != mAquaMap.end()) {
|
||||||
aquaIter->second->close();
|
aquaIter->second->close();
|
||||||
mAquaMap.erase(aquaIter);
|
mAquaMap.erase(aquaIter);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,9 @@
|
||||||
#include "Agent_AudioManager.h"
|
#include "Agent_AudioManager.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
# include "aqua++.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY)
|
|
||||||
# include "pvqa++.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class AgentImpl: public UserAgent
|
class AgentImpl: public UserAgent, public MT::Stream::MediaObserver
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
, public MT::Stream::MediaObserver
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::recursive_mutex mAgentMutex;
|
std::recursive_mutex mAgentMutex;
|
||||||
|
|
@ -39,19 +30,12 @@ protected:
|
||||||
typedef std::map<int, PSession> SessionMap;
|
typedef std::map<int, PSession> SessionMap;
|
||||||
SessionMap mSessionMap;
|
SessionMap mSessionMap;
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
// Keys are the same as used in mSessionMap
|
|
||||||
typedef std::map<int, std::shared_ptr<sevana::aqua>> AquaMap;
|
|
||||||
AquaMap mAquaMap;
|
|
||||||
ByteBuffer mAquaIncoming, mAquaOutgoing;
|
|
||||||
void closeAqua(int sessionId);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::shared_ptr<std::thread> mThread;
|
std::shared_ptr<std::thread> mThread;
|
||||||
volatile bool mShutdown;
|
volatile bool mShutdown;
|
||||||
std::shared_ptr<MT::Terminal> mTerminal;
|
std::shared_ptr<MT::Terminal> mTerminal;
|
||||||
std::shared_ptr<AudioManager> mAudioManager;
|
std::shared_ptr<AudioManager> mAudioManager;
|
||||||
//Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump;
|
Audio::DataConnection* mAudioMonitoring = nullptr;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void addEvent(const JsonCpp::Value& v);
|
void addEvent(const JsonCpp::Value& v);
|
||||||
|
|
@ -82,6 +66,12 @@ public:
|
||||||
bool waitForData(int milliseconds);
|
bool waitForData(int milliseconds);
|
||||||
std::string read();
|
std::string read();
|
||||||
|
|
||||||
|
// Get access to internal audio manager. Value can be nullptr.
|
||||||
|
const std::shared_ptr<AudioManager>& audioManager() const;
|
||||||
|
|
||||||
|
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||||
|
Audio::DataConnection* monitoring() const;
|
||||||
|
|
||||||
// UserAgent overrides
|
// UserAgent overrides
|
||||||
// Called on new incoming session; providers shoukld
|
// Called on new incoming session; providers shoukld
|
||||||
PDataProvider onProviderNeeded(const std::string& name) override;
|
PDataProvider onProviderNeeded(const std::string& name) override;
|
||||||
|
|
@ -131,10 +121,8 @@ public:
|
||||||
// Called when problem with SIP connection(s) detected
|
// Called when problem with SIP connection(s) detected
|
||||||
void onSipConnectionFailed() override;
|
void onSipConnectionFailed() override;
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
|
||||||
// Called on incoming & outgoing audio for voice sessions
|
// Called on incoming & outgoing audio for voice sessions
|
||||||
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -365,8 +365,9 @@ void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void *
|
||||||
// ------------ AndroidOutputDevice -----------------
|
// ------------ AndroidOutputDevice -----------------
|
||||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
|
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidOutputDevice::~AndroidOutputDevice()
|
AndroidOutputDevice::~AndroidOutputDevice()
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
{
|
{
|
||||||
|
|
||||||
class AndroidEnumerator: public Enumerator
|
class AndroidEnumerator: public Enumerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,245 @@
|
||||||
|
#include "Audio_AndroidOboe.h"
|
||||||
|
#include "../helper/HL_Sync.h"
|
||||||
|
#include "../helper/HL_Log.h"
|
||||||
|
#include <mutex>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "../helper/HL_String.h"
|
||||||
|
#include "../helper/HL_Time.h"
|
||||||
|
|
||||||
|
#ifdef TARGET_ANDROID
|
||||||
|
|
||||||
|
#define LOG_SUBSYSTEM "Audio"
|
||||||
|
|
||||||
|
using namespace Audio;
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------- AndroidEnumerator -----------------------------
|
||||||
|
|
||||||
|
AndroidEnumerator::AndroidEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
|
AndroidEnumerator::~AndroidEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
|
int AndroidEnumerator::indexOfDefaultDevice()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AndroidEnumerator::count()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AndroidEnumerator::idAt(int index)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AndroidEnumerator::nameAt(int index)
|
||||||
|
{
|
||||||
|
return "Audio";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidEnumerator::open(int direction)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void AndroidEnumerator::close()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// --------------- Input implementation ----------------
|
||||||
|
AndroidInputDevice::AndroidInputDevice(int devId)
|
||||||
|
{}
|
||||||
|
|
||||||
|
AndroidInputDevice::~AndroidInputDevice()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidInputDevice::open()
|
||||||
|
{
|
||||||
|
if (active())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
oboe::AudioStreamBuilder builder;
|
||||||
|
builder.setDirection(oboe::Direction::Input);
|
||||||
|
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||||
|
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||||
|
builder.setFormat(oboe::AudioFormat::I16);
|
||||||
|
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||||
|
builder.setCallback(this);
|
||||||
|
oboe::Result rescode = builder.openStream(&mRecordingStream);
|
||||||
|
if (rescode != oboe::Result::OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mDeviceRate = mRecordingStream->getSampleRate();
|
||||||
|
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
|
||||||
|
mActive = true;
|
||||||
|
|
||||||
|
rescode = mRecordingStream->requestStart();
|
||||||
|
if (rescode != oboe::Result::OK)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
mActive = false;
|
||||||
|
}
|
||||||
|
return mActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidInputDevice::close()
|
||||||
|
{
|
||||||
|
// There is no check for active() value because close() can be called to cleanup after bad open() call.
|
||||||
|
if (mRecordingStream != nullptr)
|
||||||
|
{
|
||||||
|
mRecordingStream->close();
|
||||||
|
delete mRecordingStream; mRecordingStream = nullptr;
|
||||||
|
}
|
||||||
|
mActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
oboe::DataCallbackResult
|
||||||
|
AndroidInputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> l(mMutex);
|
||||||
|
|
||||||
|
// Send data to AudioPair
|
||||||
|
if (mConnection)
|
||||||
|
mConnection->onMicData(getFormat(), audioData, numFrames);
|
||||||
|
|
||||||
|
return oboe::DataCallbackResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Format AndroidInputDevice::getFormat()
|
||||||
|
{
|
||||||
|
return Format(mDeviceRate, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidInputDevice::active() const
|
||||||
|
{
|
||||||
|
return mActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidInputDevice::fakeMode()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidInputDevice::setFakeMode(bool fakemode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int AndroidInputDevice::readBuffer(void* buffer)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("AndroidInputDevice::readBuffer() is not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------ AndroidOutputDevice -----------------
|
||||||
|
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||||
|
{
|
||||||
|
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||||
|
}
|
||||||
|
AndroidOutputDevice::~AndroidOutputDevice()
|
||||||
|
{
|
||||||
|
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidOutputDevice::open()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> l(mMutex);
|
||||||
|
|
||||||
|
if (mActive)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
mRequestedFrames = 0;
|
||||||
|
mStartTime = 0.0;
|
||||||
|
mEndTime = 0.0;
|
||||||
|
|
||||||
|
oboe::AudioStreamBuilder builder;
|
||||||
|
builder.setDirection(oboe::Direction::Output);
|
||||||
|
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||||
|
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||||
|
builder.setFormat(oboe::AudioFormat::I16);
|
||||||
|
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||||
|
// builder.setDataCallback(this);
|
||||||
|
builder.setCallback(this);
|
||||||
|
//builder.setErrorCallback(this)
|
||||||
|
|
||||||
|
oboe::Result rescode = builder.openStream(&mPlayingStream);
|
||||||
|
if (rescode != oboe::Result::OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mDeviceRate = mPlayingStream->getSampleRate();
|
||||||
|
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
|
||||||
|
mActive = true;
|
||||||
|
|
||||||
|
rescode = mPlayingStream->requestStart();
|
||||||
|
if (rescode != oboe::Result::OK)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
mActive = false;
|
||||||
|
}
|
||||||
|
return mActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidOutputDevice::close()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> l(mMutex);
|
||||||
|
if (!mActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mPlayingStream != nullptr)
|
||||||
|
{
|
||||||
|
mPlayingStream->close();
|
||||||
|
delete mPlayingStream; mPlayingStream = nullptr;
|
||||||
|
}
|
||||||
|
mEndTime = now_ms();
|
||||||
|
mActive = false;
|
||||||
|
|
||||||
|
ICELogInfo(<< "For time " << mEndTime - mStartTime << " ms was requested "
|
||||||
|
<< float(mRequestedFrames) / getFormat().mRate * 1000 << " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
Format AndroidOutputDevice::getFormat()
|
||||||
|
{
|
||||||
|
return {mDeviceRate, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidOutputDevice::fakeMode()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidOutputDevice::setFakeMode(bool /*fakemode*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
|
||||||
|
{
|
||||||
|
if (mInShutdown)
|
||||||
|
return oboe::DataCallbackResult::Stop;
|
||||||
|
|
||||||
|
if (mStartTime == 0.0)
|
||||||
|
mStartTime = now_ms();
|
||||||
|
|
||||||
|
// Ask producer about data
|
||||||
|
memset(audioData, 0, numFrames * 2);
|
||||||
|
if (mConnection)
|
||||||
|
{
|
||||||
|
Format f = getFormat();
|
||||||
|
if (f.mRate != 0)
|
||||||
|
mConnection->onSpkData(f, audioData, numFrames * 2);
|
||||||
|
}
|
||||||
|
mRequestedFrames += numFrames;
|
||||||
|
|
||||||
|
return oboe::DataCallbackResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - special case https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
|
||||||
|
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result) {
|
||||||
|
if (result == oboe::Result::ErrorDisconnected) {
|
||||||
|
// LOGI("Restarting AudioStream after disconnect");
|
||||||
|
// soundEngine.restart(); // please check oboe samples for soundEngine.restart(); call
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // TARGET_ANDROID
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __AUDIO_ANDROID_OBOE_H
|
||||||
|
#define __AUDIO_ANDROID_OBOE_H
|
||||||
|
|
||||||
|
#ifdef TARGET_ANDROID
|
||||||
|
|
||||||
|
#include "Audio_Interface.h"
|
||||||
|
#include "Audio_Helper.h"
|
||||||
|
#include "Audio_Resampler.h"
|
||||||
|
#include "Audio_DataWindow.h"
|
||||||
|
#include "../helper/HL_Pointer.h"
|
||||||
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
|
#include "../helper/HL_Exception.h"
|
||||||
|
#include "../helper/HL_Statistics.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "oboe/Oboe.h"
|
||||||
|
|
||||||
|
namespace Audio
|
||||||
|
{
|
||||||
|
class AndroidEnumerator: public Enumerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AndroidEnumerator();
|
||||||
|
~AndroidEnumerator();
|
||||||
|
|
||||||
|
void open(int direction);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
int count();
|
||||||
|
std::string nameAt(int index);
|
||||||
|
int idAt(int index);
|
||||||
|
int indexOfDefaultDevice();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
class AndroidInputDevice: public InputDevice, public oboe::AudioStreamCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AndroidInputDevice(int devId);
|
||||||
|
~AndroidInputDevice();
|
||||||
|
|
||||||
|
bool open();
|
||||||
|
void close();
|
||||||
|
Format getFormat();
|
||||||
|
|
||||||
|
bool fakeMode();
|
||||||
|
void setFakeMode(bool fakemode);
|
||||||
|
int readBuffer(void* buffer);
|
||||||
|
bool active() const;
|
||||||
|
|
||||||
|
oboe::DataCallbackResult
|
||||||
|
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool mActive = false;
|
||||||
|
oboe::AudioStream* mRecordingStream = nullptr;
|
||||||
|
PResampler mResampler;
|
||||||
|
DataWindow mDeviceRateCache, mSdkRateCache;
|
||||||
|
int mDeviceRate; // Actual rate of opened recorder
|
||||||
|
int mBufferSize; // Size of buffer used for recording (at native sample rate)
|
||||||
|
DataWindow mRecorderBuffer;
|
||||||
|
std::condition_variable mDataCondVar;
|
||||||
|
int mRecorderBufferIndex;
|
||||||
|
std::mutex mMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AndroidOutputDevice: public OutputDevice, public oboe::AudioStreamCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AndroidOutputDevice(int devId);
|
||||||
|
~AndroidOutputDevice();
|
||||||
|
|
||||||
|
bool open();
|
||||||
|
void close();
|
||||||
|
Format getFormat();
|
||||||
|
|
||||||
|
bool fakeMode();
|
||||||
|
void setFakeMode(bool fakemode);
|
||||||
|
|
||||||
|
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
|
||||||
|
void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::mutex mMutex;
|
||||||
|
int mDeviceRate = 0;
|
||||||
|
oboe::AudioStream* mPlayingStream = nullptr;
|
||||||
|
DataWindow mPlayBuffer;
|
||||||
|
int mBufferIndex = 0, mBufferSize = 0;
|
||||||
|
bool mInShutdown = false;
|
||||||
|
bool mActive = false;
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TARGET_ANDROID
|
||||||
|
|
||||||
|
#endif // __AUDIO_ANDROID_H
|
||||||
|
|
@ -56,17 +56,25 @@ void DataWindow::add(const void* data, int length)
|
||||||
|
|
||||||
if (length > mCapacity)
|
if (length > mCapacity)
|
||||||
{
|
{
|
||||||
|
// Use latest bytes from data buffer in this case.
|
||||||
data = (char*)data + length - mCapacity;
|
data = (char*)data + length - mCapacity;
|
||||||
length = mCapacity;
|
length = mCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check how much free space we have
|
||||||
int avail = mCapacity - mFilled;
|
int avail = mCapacity - mFilled;
|
||||||
|
|
||||||
if (avail < length)
|
if (avail < length)
|
||||||
{
|
{
|
||||||
memmove(mData, mData + length - avail, mFilled - (length - avail));
|
// Find the portion of data to move & save
|
||||||
mFilled -= length - avail;
|
int delta = length - avail;
|
||||||
|
|
||||||
|
// Move the data
|
||||||
|
if (mFilled - delta > 0)
|
||||||
|
memmove(mData, mData + delta, mFilled - delta);
|
||||||
|
mFilled -= delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(mData + mFilled, data, length);
|
memcpy(mData + mFilled, data, length);
|
||||||
mFilled += length;
|
mFilled += length;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
// --- DevicePair ---
|
// --- DevicePair ---
|
||||||
DevicePair::DevicePair(bool aec, bool agc)
|
DevicePair::DevicePair()
|
||||||
:mConfig(NULL), mDelegate(NULL), mAec(aec), mAgc(agc), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS)
|
:mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS),
|
||||||
|
mMonitoring(nullptr)
|
||||||
{
|
{
|
||||||
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
||||||
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
||||||
|
|
@ -28,26 +29,50 @@ DevicePair::~DevicePair()
|
||||||
if (mInput)
|
if (mInput)
|
||||||
{
|
{
|
||||||
if (mInput->connection() == this)
|
if (mInput->connection() == this)
|
||||||
mInput->setConnection(NULL);
|
mInput->setConnection(nullptr);
|
||||||
mInput.reset();
|
mInput.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mOutput)
|
if (mOutput)
|
||||||
{
|
{
|
||||||
if (mOutput->connection() == this)
|
if (mOutput->connection() == this)
|
||||||
mOutput->setConnection(NULL);
|
mOutput->setConnection(nullptr);
|
||||||
mOutput.reset();
|
mOutput.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DevicePair& DevicePair::setAec(bool aec)
|
||||||
|
{
|
||||||
|
mAec = aec;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DevicePair::aec()
|
||||||
|
{
|
||||||
|
return mAec;
|
||||||
|
}
|
||||||
|
|
||||||
|
DevicePair& DevicePair::setAgc(bool agc)
|
||||||
|
{
|
||||||
|
mAgc = agc;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DevicePair::agc()
|
||||||
|
{
|
||||||
|
return mAgc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
VariantMap* DevicePair::config()
|
VariantMap* DevicePair::config()
|
||||||
{
|
{
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::setConfig(VariantMap* config)
|
DevicePair& DevicePair::setConfig(VariantMap* config)
|
||||||
{
|
{
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PInputDevice DevicePair::input()
|
PInputDevice DevicePair::input()
|
||||||
|
|
@ -55,15 +80,17 @@ PInputDevice DevicePair::input()
|
||||||
return mInput;
|
return mInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::setInput(PInputDevice input)
|
DevicePair& DevicePair::setInput(PInputDevice input)
|
||||||
{
|
{
|
||||||
if (mInput == input)
|
if (mInput == input)
|
||||||
return;
|
return *this;
|
||||||
|
|
||||||
mInput = input;
|
mInput = input;
|
||||||
mInput->setConnection(this);
|
mInput->setConnection(this);
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->deviceChanged(this);
|
mDelegate->deviceChanged(this);
|
||||||
|
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
POutputDevice DevicePair::output()
|
POutputDevice DevicePair::output()
|
||||||
|
|
@ -71,14 +98,17 @@ POutputDevice DevicePair::output()
|
||||||
return mOutput;
|
return mOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::setOutput(POutputDevice output)
|
DevicePair& DevicePair::setOutput(POutputDevice output)
|
||||||
{
|
{
|
||||||
if (output == mOutput)
|
if (output == mOutput)
|
||||||
return;
|
return *this;
|
||||||
|
|
||||||
mOutput = output;
|
mOutput = output;
|
||||||
mOutput->setConnection(this);
|
mOutput->setConnection(this);
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->deviceChanged(this);
|
mDelegate->deviceChanged(this);
|
||||||
|
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DevicePair::start()
|
bool DevicePair::start()
|
||||||
|
|
@ -88,6 +118,7 @@ bool DevicePair::start()
|
||||||
result = mInput->open();
|
result = mInput->open();
|
||||||
if (mOutput && result)
|
if (mOutput && result)
|
||||||
result &= mOutput->open();
|
result &= mOutput->open();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,9 +130,10 @@ void DevicePair::stop()
|
||||||
mOutput->close();
|
mOutput->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::setDelegate(Delegate* dc)
|
DevicePair& DevicePair::setDelegate(Delegate* dc)
|
||||||
{
|
{
|
||||||
mDelegate = dc;
|
mDelegate = dc;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair::Delegate* DevicePair::delegate()
|
DevicePair::Delegate* DevicePair::delegate()
|
||||||
|
|
@ -109,6 +141,17 @@ DevicePair::Delegate* DevicePair::delegate()
|
||||||
return mDelegate;
|
return mDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
|
||||||
|
{
|
||||||
|
mMonitoring = monitoring;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataConnection* DevicePair::monitoring()
|
||||||
|
{
|
||||||
|
return mMonitoring;
|
||||||
|
}
|
||||||
|
|
||||||
Player& DevicePair::player()
|
Player& DevicePair::player()
|
||||||
{
|
{
|
||||||
return mPlayer;
|
return mPlayer;
|
||||||
|
|
@ -185,6 +228,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
{
|
{
|
||||||
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
|
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
|
||||||
|
|
||||||
|
// Ask audio data on main AUDIO_SAMPLERATE frequency
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
|
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
|
||||||
|
|
||||||
|
|
@ -197,8 +241,12 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
|
|
||||||
// Resample these 10 milliseconds it to native format
|
// Resample these 10 milliseconds it to native format
|
||||||
size_t wasProcessed = 0;
|
size_t wasProcessed = 0;
|
||||||
size_t wasProduced = mSpkResampler.resample(AUDIO_SAMPLERATE, mOutput10msBuffer.data(), mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
|
size_t wasProduced = mSpkResampler.resample(Format().mRate,
|
||||||
mOutputNativeData.mutableData() + mOutputNativeData.filled(), mOutputNativeData.capacity() - mOutputNativeData.filled());
|
mOutput10msBuffer.data(),
|
||||||
|
mOutput10msBuffer.capacity(),
|
||||||
|
wasProcessed, f.mRate,
|
||||||
|
mOutputNativeData.mutableData() + mOutputNativeData.filled(),
|
||||||
|
mOutputNativeData.capacity() - mOutputNativeData.filled());
|
||||||
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
|
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
|
||||||
#ifdef CONSOLE_LOGGING
|
#ifdef CONSOLE_LOGGING
|
||||||
printf("Resampled %d to %d\n", wasProcessed, wasProduced);
|
printf("Resampled %d to %d\n", wasProcessed, wasProduced);
|
||||||
|
|
@ -206,7 +254,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(mOutputNativeData.filled() >= length);
|
// assert(mOutputNativeData.filled() >= length);
|
||||||
#ifdef DUMP_NATIVEOUTPUT
|
#ifdef DUMP_NATIVEOUTPUT
|
||||||
if (mNativeOutputDump)
|
if (mNativeOutputDump)
|
||||||
mNativeOutputDump->write(mOutputNativeData.data(), length);
|
mNativeOutputDump->write(mOutputNativeData.data(), length);
|
||||||
|
|
@ -214,6 +262,10 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
|
|
||||||
mOutputNativeData.read(buffer, length);
|
mOutputNativeData.read(buffer, length);
|
||||||
|
|
||||||
|
// Send data to monitoring if needed
|
||||||
|
if (mMonitoring)
|
||||||
|
mMonitoring->onSpkData(f, buffer, length);
|
||||||
|
|
||||||
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
|
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
|
||||||
|
|
||||||
// AEC filter wants frames.
|
// AEC filter wants frames.
|
||||||
|
|
@ -224,7 +276,6 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
|
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
|
||||||
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
|
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
|
||||||
}
|
}
|
||||||
//ICELogMedia(<< "Audio::DevicePair::onSpkData() end")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::processMicData(const Format& f, void* buffer, int length)
|
void DevicePair::processMicData(const Format& f, void* buffer, int length)
|
||||||
|
|
|
||||||
|
|
@ -26,29 +26,32 @@ namespace Audio
|
||||||
virtual void deviceChanged(DevicePair* dpair) = 0;
|
virtual void deviceChanged(DevicePair* dpair) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
DevicePair(bool aec = true, bool agc = true);
|
DevicePair();
|
||||||
virtual ~DevicePair();
|
virtual ~DevicePair();
|
||||||
|
|
||||||
void setAec(bool aec);
|
DevicePair& setAec(bool aec);
|
||||||
bool aec();
|
bool aec();
|
||||||
void setAgc(bool agc);
|
DevicePair& setAgc(bool agc);
|
||||||
bool agc();
|
bool agc();
|
||||||
|
|
||||||
VariantMap* config();
|
VariantMap* config();
|
||||||
void setConfig(VariantMap* config);
|
DevicePair& setConfig(VariantMap* config);
|
||||||
|
|
||||||
PInputDevice input();
|
PInputDevice input();
|
||||||
void setInput(PInputDevice input);
|
DevicePair& setInput(PInputDevice input);
|
||||||
|
|
||||||
POutputDevice output();
|
POutputDevice output();
|
||||||
void setOutput(POutputDevice output);
|
DevicePair& setOutput(POutputDevice output);
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void setDelegate(Delegate* dc);
|
DevicePair& setDelegate(Delegate* dc);
|
||||||
Delegate* delegate();
|
Delegate* delegate();
|
||||||
|
|
||||||
|
DevicePair& setMonitoring(DataConnection* monitoring);
|
||||||
|
DataConnection* monitoring();
|
||||||
|
|
||||||
Player& player();
|
Player& player();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -63,6 +66,7 @@ namespace Audio
|
||||||
Player mPlayer;
|
Player mPlayer;
|
||||||
UniversalResampler mMicResampler, mSpkResampler;
|
UniversalResampler mMicResampler, mSpkResampler;
|
||||||
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
||||||
|
DataConnection* mMonitoring;
|
||||||
|
|
||||||
#ifdef DUMP_NATIVEOUTPUT
|
#ifdef DUMP_NATIVEOUTPUT
|
||||||
std::shared_ptr<WavFileWriter> mNativeOutputDump;
|
std::shared_ptr<WavFileWriter> mNativeOutputDump;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __AUDIO_DSOUND_H
|
#ifndef __AUDIO_DSOUND_H
|
||||||
#define __AUDIO_DSOUND_H
|
#define __AUDIO_DSOUND_H
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
|
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,6 @@ TimeSource::TimeSource(int quantTime, int nrOfQuants)
|
||||||
mTailTime = 0;
|
mTailTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeSource::~TimeSource()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimeSource::start()
|
void TimeSource::start()
|
||||||
{
|
{
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
|
|
@ -102,11 +98,12 @@ unsigned TimeSource::time()
|
||||||
#if defined(TARGET_ANDROID)
|
#if defined(TARGET_ANDROID)
|
||||||
assert(0);
|
assert(0);
|
||||||
#endif
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- StubTimer ---
|
// --- StubTimer ---
|
||||||
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
||||||
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false)
|
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false), mCurrentTime(0)
|
||||||
{
|
{
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ namespace Audio
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TimeSource(int quantTime, int nrOfQuants);
|
TimeSource(int quantTime, int nrOfQuants);
|
||||||
~TimeSource();
|
~TimeSource() = default;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,8 @@ OsEngine* OsEngine::instance()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TARGET_ANDROID
|
#ifdef TARGET_ANDROID
|
||||||
return &OpenSLEngine::instance();
|
return nullptr; // As we use Oboe library for now
|
||||||
|
//return &OpenSLEngine::instance();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#define __AUDIO_INTERFACE_H
|
#define __AUDIO_INTERFACE_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
#include "../helper/HL_VariantMap.h"
|
#include "../helper/HL_VariantMap.h"
|
||||||
#include "../helper/HL_Pointer.h"
|
#include "../helper/HL_Pointer.h"
|
||||||
|
|
@ -57,6 +57,27 @@ namespace Audio
|
||||||
sprintf(buffer, "%dHz %dch", mRate, mChannels);
|
sprintf(buffer, "%dHz %dch", mRate, mChannels);
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator == (const Format& rhs) const
|
||||||
|
{
|
||||||
|
return mRate == rhs.mRate && mChannels == rhs.mChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const Format& rhs) const
|
||||||
|
{
|
||||||
|
return mRate != rhs.mRate || mChannels != rhs.mChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rate() const
|
||||||
|
{
|
||||||
|
return mRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int channels() const
|
||||||
|
{
|
||||||
|
return mChannels;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataConnection
|
class DataConnection
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
|
|
||||||
|
|
@ -227,7 +227,11 @@ void Mixer::mix()
|
||||||
{
|
{
|
||||||
// Copy much samples as we have
|
// Copy much samples as we have
|
||||||
Stream& audio = *channelList[0];
|
Stream& audio = *channelList[0];
|
||||||
|
|
||||||
|
// Copy the decoded data
|
||||||
mOutput.add(audio.data().data(), audio.data().filled());
|
mOutput.add(audio.data().data(), audio.data().filled());
|
||||||
|
|
||||||
|
// Erase copied audio samples
|
||||||
audio.data().erase(audio.data().filled());
|
audio.data().erase(audio.data().filled());
|
||||||
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
|
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef _RX_MIXER_H
|
#ifndef _RX_MIXER_H
|
||||||
#define _RX_MIXER_H
|
#define _RX_MIXER_H
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "Audio_Resampler.h"
|
#include "Audio_Resampler.h"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Audio_Null.h"
|
#include "Audio_Null.h"
|
||||||
#include "helper/HL_Log.h"
|
#include "helper/HL_Log.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <chrono>
|
||||||
#define LOG_SUBSYSTEM "NULL audio"
|
#define LOG_SUBSYSTEM "NULL audio"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
@ -59,7 +60,7 @@ NullInputDevice::NullInputDevice()
|
||||||
|
|
||||||
NullInputDevice::~NullInputDevice()
|
NullInputDevice::~NullInputDevice()
|
||||||
{
|
{
|
||||||
close();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullInputDevice::open()
|
bool NullInputDevice::open()
|
||||||
|
|
@ -72,7 +73,7 @@ bool NullInputDevice::open()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::close()
|
void NullInputDevice::internalClose()
|
||||||
{
|
{
|
||||||
mTimer.reset();
|
mTimer.reset();
|
||||||
if (mBuffer)
|
if (mBuffer)
|
||||||
|
|
@ -83,6 +84,10 @@ void NullInputDevice::close()
|
||||||
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NullInputDevice::close()
|
||||||
|
{
|
||||||
|
internalClose();
|
||||||
|
}
|
||||||
Format NullInputDevice::getFormat()
|
Format NullInputDevice::getFormat()
|
||||||
{
|
{
|
||||||
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
||||||
|
|
@ -105,7 +110,7 @@ NullOutputDevice::NullOutputDevice()
|
||||||
|
|
||||||
NullOutputDevice::~NullOutputDevice()
|
NullOutputDevice::~NullOutputDevice()
|
||||||
{
|
{
|
||||||
close();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -118,13 +123,18 @@ bool NullOutputDevice::open()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullOutputDevice::close()
|
void NullOutputDevice::internalClose()
|
||||||
{
|
{
|
||||||
mTimer.reset();
|
mTimer.reset();
|
||||||
free(mBuffer); mBuffer = nullptr;
|
free(mBuffer); mBuffer = nullptr;
|
||||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NullOutputDevice::close()
|
||||||
|
{
|
||||||
|
internalClose();
|
||||||
|
}
|
||||||
|
|
||||||
Format NullOutputDevice::getFormat()
|
Format NullOutputDevice::getFormat()
|
||||||
{
|
{
|
||||||
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
|
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ namespace Audio
|
||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||||
|
void internalClose();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NullInputDevice();
|
NullInputDevice();
|
||||||
virtual ~NullInputDevice();
|
virtual ~NullInputDevice();
|
||||||
|
|
@ -55,6 +57,8 @@ namespace Audio
|
||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||||
|
|
||||||
|
void internalClose();
|
||||||
public:
|
public:
|
||||||
NullOutputDevice();
|
NullOutputDevice();
|
||||||
virtual ~NullOutputDevice();
|
virtual ~NullOutputDevice();
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "Audio_Player.h"
|
#include "Audio_Player.h"
|
||||||
|
|
||||||
|
#include "../helper/HL_Log.h"
|
||||||
|
|
||||||
|
#define LOG_SUBSYSTEM "Player"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
// -------------- Player -----------
|
// -------------- Player -----------
|
||||||
Player::Player()
|
Player::Player()
|
||||||
:mDelegate(NULL), mPlayedTime(0)
|
:mDelegate(nullptr), mPlayedTime(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,7 +103,7 @@ void Player::onFilePlayed()
|
||||||
void Player::obtain(int usage)
|
void Player::obtain(int usage)
|
||||||
{
|
{
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
UsageMap::iterator usageIter = mUsage.find(usage);
|
auto usageIter = mUsage.find(usage);
|
||||||
if (usageIter == mUsage.end())
|
if (usageIter == mUsage.end())
|
||||||
mUsage[usage] = 1;
|
mUsage[usage] = 1;
|
||||||
else
|
else
|
||||||
|
|
@ -132,7 +136,7 @@ int Player::releasePlayed()
|
||||||
{
|
{
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
int result = mFinishedUsages.size();
|
int result = mFinishedUsages.size();
|
||||||
while (mFinishedUsages.size())
|
while (!mFinishedUsages.empty())
|
||||||
{
|
{
|
||||||
release(mFinishedUsages.front());
|
release(mFinishedUsages.front());
|
||||||
mFinishedUsages.erase(mFinishedUsages.begin());
|
mFinishedUsages.erase(mFinishedUsages.begin());
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
|
#include "../helper/HL_Statistics.h"
|
||||||
#include "Audio_Interface.h"
|
#include "Audio_Interface.h"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
@ -48,15 +49,18 @@ namespace Audio
|
||||||
void onMicData(const Format& f, const void* buffer, int length);
|
void onMicData(const Format& f, const void* buffer, int length);
|
||||||
void onSpkData(const Format& f, void* buffer, int length);
|
void onSpkData(const Format& f, void* buffer, int length);
|
||||||
void onFilePlayed();
|
void onFilePlayed();
|
||||||
void scheduleRelease();
|
|
||||||
void obtain(int usageId);
|
void obtain(int usageId);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Player();
|
Player();
|
||||||
~Player();
|
~Player();
|
||||||
|
|
||||||
void setDelegate(EndOfAudioDelegate* d);
|
void setDelegate(EndOfAudioDelegate* d);
|
||||||
EndOfAudioDelegate* getDelegate() const;
|
EndOfAudioDelegate* getDelegate() const;
|
||||||
|
|
||||||
void setOutput(POutputDevice output);
|
void setOutput(POutputDevice output);
|
||||||
POutputDevice getOutput() const;
|
POutputDevice getOutput() const;
|
||||||
|
|
||||||
void add(int usageId, PWavFileReader file, bool loop, int timelength);
|
void add(int usageId, PWavFileReader file, bool loop, int timelength);
|
||||||
void release(int usageId);
|
void release(int usageId);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "Audio_Quality.h"
|
#include "Audio_Quality.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
|
|
@ -17,7 +17,10 @@
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
|
#ifndef SHRT_MAX
|
||||||
# define SHRT_MAX 32767 /* maximum (signed) short value */
|
# define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||||
|
#endif
|
||||||
|
|
||||||
AgcFilter::AgcFilter(int channels)
|
AgcFilter::AgcFilter(int channels)
|
||||||
{
|
{
|
||||||
static const float DefaultLevel = 0.8f;
|
static const float DefaultLevel = 0.8f;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#ifndef __AUDIO_QUALITY_H
|
#ifndef __AUDIO_QUALITY_H
|
||||||
#define __AUDIO_QUALITY_H
|
#define __AUDIO_QUALITY_H
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "Audio_Resampler.h"
|
#include "Audio_Resampler.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Audio
|
||||||
|
|
||||||
|
|
||||||
SpeexResampler::SpeexResampler()
|
SpeexResampler::SpeexResampler()
|
||||||
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0)
|
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0), mChannels(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,12 +125,13 @@ int SpeexResampler::destRate()
|
||||||
|
|
||||||
size_t SpeexResampler::getDestLength(size_t sourceLen)
|
size_t SpeexResampler::getDestLength(size_t sourceLen)
|
||||||
{
|
{
|
||||||
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f) / 2 * 2;
|
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SpeexResampler::getSourceLength(size_t destLen)
|
size_t SpeexResampler::getSourceLength(size_t destLen)
|
||||||
{
|
{
|
||||||
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f) / 2 * 2;
|
// Here we want to get 'destLen' number of samples
|
||||||
|
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns instance + speex resampler size in bytes
|
// Returns instance + speex resampler size in bytes
|
||||||
|
|
@ -273,7 +274,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
|
||||||
PResampler r;
|
PResampler r;
|
||||||
if (resamplerIter == mResamplerMap.end())
|
if (resamplerIter == mResamplerMap.end())
|
||||||
{
|
{
|
||||||
r = PResampler(new Resampler());
|
r = std::make_shared<Resampler>();
|
||||||
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
||||||
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@
|
||||||
#include "helper/HL_Exception.h"
|
#include "helper/HL_Exception.h"
|
||||||
#include "helper/HL_String.h"
|
#include "helper/HL_String.h"
|
||||||
#include "helper/HL_Log.h"
|
#include "helper/HL_Log.h"
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
|
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#ifndef WORD
|
#ifndef WORD
|
||||||
# define WORD unsigned short
|
# define WORD unsigned short
|
||||||
|
|
@ -39,7 +40,7 @@ using namespace Audio;
|
||||||
|
|
||||||
// ---------------------- WavFileReader -------------------------
|
// ---------------------- WavFileReader -------------------------
|
||||||
WavFileReader::WavFileReader()
|
WavFileReader::WavFileReader()
|
||||||
:mHandle(nullptr), mRate(0), mLastError(0)
|
:mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
|
||||||
{
|
{
|
||||||
mDataOffset = 0;
|
mDataOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -52,38 +53,51 @@ WavFileReader::~WavFileReader()
|
||||||
|
|
||||||
std::string WavFileReader::readChunk()
|
std::string WavFileReader::readChunk()
|
||||||
{
|
{
|
||||||
char name[5];
|
char name[5] = {0};
|
||||||
if (fread(name, 1, 4, mHandle) != 4)
|
readBuffer(name, 4);
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
name[4] = 0;
|
|
||||||
std::string result = name;
|
std::string result = name;
|
||||||
unsigned size;
|
uint32_t size = 0;
|
||||||
if (fread(&size, 4, 1, mHandle) != 1)
|
readBuffer(&size, 4);
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
if (result == "fact")
|
if (result == "fact")
|
||||||
fread(&mDataLength, 4, 1, mHandle);
|
{
|
||||||
|
uint32_t dataLength = 0;
|
||||||
|
readBuffer(&dataLength, sizeof dataLength);
|
||||||
|
mDataLength = dataLength;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
if (result != "data")
|
if (result != "data")
|
||||||
fseek(mHandle, size, SEEK_CUR);
|
mInput->seekg(size, std::ios_base::beg);
|
||||||
else
|
else
|
||||||
mDataLength = size;
|
mDataLength = size;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WavFileReader::open(const std::tstring& filename)
|
void WavFileReader::readBuffer(void* buffer, size_t sz)
|
||||||
|
{
|
||||||
|
auto p = mInput->tellg();
|
||||||
|
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
||||||
|
if (mInput->tellg() - p != sz)
|
||||||
|
throw Exception(ERR_WAVFILE_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
|
||||||
|
{
|
||||||
|
auto p = mInput->tellg();
|
||||||
|
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
||||||
|
return mInput->tellg() - p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WavFileReader::open(const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
mPath = p;
|
||||||
mHandle = _wfopen(filename.c_str(), L"rb");
|
mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in);
|
||||||
#else
|
if (!mInput->is_open())
|
||||||
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "rb");
|
|
||||||
#endif
|
|
||||||
if (NULL == mHandle)
|
|
||||||
{
|
{
|
||||||
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
mLastError = errno;
|
mLastError = errno;
|
||||||
|
|
@ -97,76 +111,62 @@ bool WavFileReader::open(const std::tstring& filename)
|
||||||
|
|
||||||
// Read the .WAV header
|
// Read the .WAV header
|
||||||
char riff[4];
|
char riff[4];
|
||||||
if (fread(riff, 4, 1, mHandle) < 1)
|
readBuffer(riff, sizeof riff);
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
|
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
// Read the file size
|
// Read the file size
|
||||||
unsigned int filesize = 0;
|
uint32_t filesize = 0;
|
||||||
if (fread(&filesize, 4, 1, mHandle) < 1)
|
readBuffer(&filesize, sizeof(filesize));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
char wavefmt[9];
|
char wavefmt[9] = {0};
|
||||||
if (fread(wavefmt, 8, 1, mHandle) < 1)
|
readBuffer(wavefmt, 8);
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
wavefmt[8] = 0;
|
|
||||||
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
unsigned fmtSize = 0;
|
uint32_t fmtSize = 0;
|
||||||
if (fread(&fmtSize, 4, 1, mHandle) < 1)
|
readBuffer(&fmtSize, sizeof(fmtSize));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
unsigned fmtStart = ftell(mHandle);
|
auto fmtStart = mInput->tellg();
|
||||||
|
|
||||||
unsigned short formattag = 0;
|
uint16_t formattag = 0;
|
||||||
if (fread(&formattag, 2, 1, mHandle) < 1)
|
readBuffer(&formattag, sizeof(formattag));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
mChannels = 0;
|
mChannels = 0;
|
||||||
if (fread(&mChannels, 2, 1, mHandle) < 1)
|
readBuffer(&mChannels, sizeof(mChannels));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
mRate = 0;
|
mSamplerate = 0;
|
||||||
if (fread(&mRate, 4, 1, mHandle) < 1)
|
readBuffer(&mSamplerate, sizeof(mSamplerate));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
unsigned int avgbytespersec = 0;
|
uint32_t avgbytespersec = 0;
|
||||||
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
|
readBuffer(&avgbytespersec, sizeof(avgbytespersec));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
unsigned short blockalign = 0;
|
uint16_t blockalign = 0;
|
||||||
if (fread(&blockalign, 2, 1, mHandle) < 1)
|
readBuffer(&blockalign, sizeof(blockalign));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
mBits = 0;
|
mBits = 0;
|
||||||
if (fread(&mBits, 2, 1, mHandle) < 1)
|
readBuffer(&mBits, sizeof(mBits));
|
||||||
THROW_READERROR;
|
|
||||||
|
|
||||||
if (mBits !=8 && mBits != 16)
|
if (mBits !=8 && mBits != 16)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
// Read the "chunk"
|
// Look for the chunk 'data'
|
||||||
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
|
mInput->seekg(fmtStart + std::streampos(fmtSize));
|
||||||
//unsigned pos = ftell(mHandle);
|
|
||||||
mDataLength = 0;
|
mDataLength = 0;
|
||||||
while (readChunk() != "data")
|
while (readChunk() != "data")
|
||||||
;
|
;
|
||||||
|
|
||||||
mFileName = filename;
|
mDataOffset = mInput->tellg();
|
||||||
mDataOffset = ftell(mHandle);
|
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
|
||||||
|
|
||||||
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
|
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
fclose(mHandle); mHandle = nullptr;
|
mInput.reset();
|
||||||
mLastError = static_cast<unsigned>(-1);
|
mLastError = static_cast<unsigned>(-1);
|
||||||
}
|
}
|
||||||
return isOpened();
|
return isOpened();
|
||||||
|
|
@ -175,78 +175,117 @@ bool WavFileReader::open(const std::tstring& filename)
|
||||||
void WavFileReader::close()
|
void WavFileReader::close()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
mInput.reset();
|
||||||
if (nullptr != mHandle)
|
|
||||||
fclose(mHandle);
|
|
||||||
mHandle = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int WavFileReader::rate() const
|
int WavFileReader::samplerate() const
|
||||||
{
|
{
|
||||||
return mRate;
|
return mSamplerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned WavFileReader::read(void* buffer, unsigned bytes)
|
int WavFileReader::channels() const
|
||||||
|
{
|
||||||
|
return mChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WavFileReader::read(void* buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
|
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned WavFileReader::read(short* buffer, unsigned samples)
|
size_t WavFileReader::readRaw(void* buffer, size_t bytes)
|
||||||
|
{
|
||||||
|
return readRaw((short*)buffer, bytes / channels() / sizeof(short)) * channels() * sizeof(short);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WavFileReader::read(short* buffer, size_t samples)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
if (!mHandle)
|
if (!mInput)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Get number of samples that must be read from source file
|
// Get number of samples that must be read from source file
|
||||||
int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
||||||
void* temp = alloca(requiredBytes);
|
bool useHeap = requiredBytes > sizeof mTempBuffer;
|
||||||
|
void* temp;
|
||||||
|
if (useHeap)
|
||||||
|
temp = malloc(requiredBytes);
|
||||||
|
else
|
||||||
|
temp = mTempBuffer;
|
||||||
|
|
||||||
memset(temp, 0, requiredBytes);
|
memset(temp, 0, requiredBytes);
|
||||||
|
|
||||||
// Find required size of input buffer
|
// Find required size of input buffer
|
||||||
if (mDataLength)
|
if (mDataLength)
|
||||||
{
|
{
|
||||||
unsigned filePosition = ftell(mHandle);
|
auto filePosition = mInput->tellg();
|
||||||
|
|
||||||
// Check how much data we can read
|
// Check how much data we can read
|
||||||
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
|
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||||
|
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t readBytes = tryReadBuffer(temp, requiredBytes);
|
||||||
|
|
||||||
|
size_t processedBytes = 0;
|
||||||
|
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes,
|
||||||
|
buffer, samples * 2 * AUDIO_CHANNELS);
|
||||||
|
|
||||||
|
if (useHeap)
|
||||||
|
free(temp);
|
||||||
|
return result / 2 / AUDIO_CHANNELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t WavFileReader::readRaw(short* buffer, size_t samples)
|
||||||
|
{
|
||||||
|
LOCK;
|
||||||
|
|
||||||
|
if (!mInput)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Get number of samples that must be read from source file
|
||||||
|
size_t requiredBytes = samples * channels() * sizeof(short);
|
||||||
|
|
||||||
|
// Find required size of input buffer
|
||||||
|
if (mDataLength)
|
||||||
|
{
|
||||||
|
auto filePosition = mInput->tellg();
|
||||||
|
|
||||||
|
// Check how much data we can read
|
||||||
|
size_t fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||||
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
|
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
|
size_t readBytes = tryReadBuffer(buffer, requiredBytes);
|
||||||
size_t processedBytes = 0;
|
return readBytes / channels() / sizeof(short);
|
||||||
size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
|
|
||||||
buffer, samples * 2 * AUDIO_CHANNELS);
|
|
||||||
|
|
||||||
return result / 2 / AUDIO_CHANNELS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WavFileReader::isOpened()
|
bool WavFileReader::isOpened()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
if (!mInput)
|
||||||
return (mHandle != 0);
|
return false;
|
||||||
|
return mInput->is_open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WavFileReader::rewind()
|
void WavFileReader::rewind()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
if (mInput)
|
||||||
if (mHandle)
|
mInput->seekg(mDataOffset);
|
||||||
fseek(mHandle, mDataOffset, SEEK_SET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tstring WavFileReader::filename() const
|
std::filesystem::path WavFileReader::path() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
return mPath;
|
||||||
return mFileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned WavFileReader::size() const
|
size_t WavFileReader::size() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
return mDataLength;
|
return mDataLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,9 +299,8 @@ unsigned WavFileReader::lastError() const
|
||||||
#define BITS_PER_CHANNEL 16
|
#define BITS_PER_CHANNEL 16
|
||||||
|
|
||||||
WavFileWriter::WavFileWriter()
|
WavFileWriter::WavFileWriter()
|
||||||
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
:mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
WavFileWriter::~WavFileWriter()
|
WavFileWriter::~WavFileWriter()
|
||||||
{
|
{
|
||||||
|
|
@ -275,68 +313,74 @@ void WavFileWriter::checkWriteResult(int result)
|
||||||
throw Exception(ERR_WAVFILE_FAILED, errno);
|
throw Exception(ERR_WAVFILE_FAILED, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
|
void WavFileWriter::writeBuffer(const void* buffer, size_t sz)
|
||||||
|
{
|
||||||
|
if (!mOutput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto p = mOutput->tellp();
|
||||||
|
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
|
||||||
|
if (mOutput->tellp() - p != sz)
|
||||||
|
throw Exception(ERR_WAVFILE_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
mSamplerate = samplerate;
|
||||||
mRate = rate;
|
|
||||||
mChannels = channels;
|
mChannels = channels;
|
||||||
|
|
||||||
#ifdef WIN32
|
mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc);
|
||||||
mHandle = _wfopen(filename.c_str(), L"wb");
|
if (!mOutput)
|
||||||
#else
|
|
||||||
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "wb");
|
|
||||||
#endif
|
|
||||||
if (nullptr == mHandle)
|
|
||||||
{
|
{
|
||||||
ICELogError(<< "Failed to create .wav file: filename = " << StringHelper::makeUtf8(filename) << " , error = " << errno);
|
int errorcode = errno;
|
||||||
|
ICELogError(<< "Failed to create .wav file: filename = " << p << " , error = " << errorcode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the .WAV header
|
// Write the .WAV header
|
||||||
const char* riff = "RIFF";
|
const char* riff = "RIFF";
|
||||||
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
|
writeBuffer(riff, 4);
|
||||||
|
|
||||||
// Write the file size
|
// Write the file size
|
||||||
unsigned int filesize = 0;
|
uint32_t filesize = 0;
|
||||||
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
|
writeBuffer(&filesize, sizeof filesize);
|
||||||
|
|
||||||
const char* wavefmt = "WAVEfmt ";
|
const char* wavefmt = "WAVEfmt ";
|
||||||
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
|
writeBuffer(wavefmt, 8);
|
||||||
|
|
||||||
// Set the format description
|
// Set the format description
|
||||||
DWORD dwFmtSize = 16; /*= 16L*/;
|
uint32_t dwFmtSize = 16; /*= 16L*/;
|
||||||
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
|
writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
|
||||||
|
|
||||||
WaveFormatEx format;
|
WaveFormatEx format;
|
||||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
|
writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag));
|
||||||
|
|
||||||
format.nChannels = mChannels;
|
format.nChannels = mChannels;
|
||||||
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
|
writeBuffer(&format.nChannels, sizeof(format.nChannels));
|
||||||
|
|
||||||
format.nSamplesPerSec = mRate;
|
format.nSamplesPerSec = mSamplerate;
|
||||||
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
|
writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec));
|
||||||
|
|
||||||
format.nAvgBytesPerSec = mRate * 2 * mChannels;
|
format.nAvgBytesPerSec = mSamplerate * 2 * mChannels;
|
||||||
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
|
writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec));
|
||||||
|
|
||||||
format.nBlockAlign = 2 * mChannels;
|
format.nBlockAlign = 2 * mChannels;
|
||||||
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
|
writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign));
|
||||||
|
|
||||||
format.wBitsPerSample = BITS_PER_CHANNEL;
|
format.wBitsPerSample = BITS_PER_CHANNEL;
|
||||||
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
|
writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample));
|
||||||
|
|
||||||
const char* data = "data";
|
const char* data = "data";
|
||||||
checkWriteResult( fwrite(data, 4, 1, mHandle));
|
writeBuffer(data, 4);
|
||||||
|
|
||||||
mFileName = filename;
|
mPath = p;
|
||||||
mWritten = 0;
|
mWritten = 0;
|
||||||
|
|
||||||
mLengthOffset = ftell(mHandle);
|
mLengthOffset = mOutput->tellp();
|
||||||
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
writeBuffer(&mWritten, sizeof mWritten);
|
||||||
|
|
||||||
return isOpened();
|
return isOpened();
|
||||||
}
|
}
|
||||||
|
|
@ -344,51 +388,44 @@ bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
|
||||||
void WavFileWriter::close()
|
void WavFileWriter::close()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
mOutput.reset();
|
||||||
if (mHandle)
|
|
||||||
{
|
|
||||||
fclose(mHandle);
|
|
||||||
mHandle = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
if (!mHandle)
|
if (!mOutput)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Seek the end of file
|
// Seek the end of file - here new data will be written
|
||||||
fseek(mHandle, 0, SEEK_END);
|
mOutput->seekp(0, std::ios_base::end);
|
||||||
mWritten += bytes;
|
mWritten += bytes;
|
||||||
|
|
||||||
// Write the data
|
// Write the data
|
||||||
fwrite(buffer, bytes, 1, mHandle);
|
writeBuffer(buffer, bytes);
|
||||||
|
|
||||||
// Write file length
|
// Write file length
|
||||||
fseek(mHandle, 4, SEEK_SET);
|
mOutput->seekp(4, std::ios_base::beg);
|
||||||
int32_t fl = mWritten + 36;
|
uint32_t fl = mWritten + 36;
|
||||||
fwrite(&fl, sizeof(fl), 1, mHandle);
|
writeBuffer(&fl, sizeof(fl));
|
||||||
|
|
||||||
// Write data length
|
// Write data length
|
||||||
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
|
mOutput->seekp(mLengthOffset, std::ios_base::beg);
|
||||||
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
writeBuffer(&mWritten, sizeof(mWritten));
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WavFileWriter::isOpened()
|
bool WavFileWriter::isOpened() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
return mOutput.get();
|
||||||
return (mHandle != nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tstring WavFileWriter::filename()
|
std::filesystem::path WavFileWriter::path() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
return mPath;
|
||||||
return mFileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
|
|
@ -20,35 +22,43 @@ namespace Audio
|
||||||
class WavFileReader
|
class WavFileReader
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
FILE* mHandle;
|
uint16_t mChannels = 0;
|
||||||
short mChannels;
|
uint16_t mBits = 0;
|
||||||
short mBits;
|
int mSamplerate = 0;
|
||||||
int mRate;
|
std::filesystem::path mPath;
|
||||||
std::tstring mFileName;
|
|
||||||
mutable std::recursive_mutex mFileMtx;
|
mutable std::recursive_mutex mFileMtx;
|
||||||
unsigned mDataOffset;
|
size_t mDataOffset = 0;
|
||||||
unsigned mDataLength;
|
size_t mDataLength = 0;
|
||||||
Resampler mResampler;
|
Resampler mResampler;
|
||||||
unsigned mLastError;
|
unsigned mLastError = 0;
|
||||||
|
std::unique_ptr<std::ifstream> mInput;
|
||||||
|
uint8_t mTempBuffer[16384];
|
||||||
|
|
||||||
std::string readChunk();
|
std::string readChunk();
|
||||||
|
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
|
||||||
|
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WavFileReader();
|
WavFileReader();
|
||||||
~WavFileReader();
|
~WavFileReader();
|
||||||
|
|
||||||
bool open(const std::tstring& filename);
|
bool open(const std::filesystem::path& p);
|
||||||
void close();
|
void close();
|
||||||
bool isOpened();
|
bool isOpened();
|
||||||
void rewind();
|
void rewind();
|
||||||
int rate() const;
|
int samplerate() const;
|
||||||
|
int channels() const;
|
||||||
|
|
||||||
// This method returns number of read bytes
|
// This method returns number of read bytes
|
||||||
unsigned read(void* buffer, unsigned bytes);
|
size_t read(void* buffer, size_t bytes);
|
||||||
|
size_t readRaw(void* buffer, size_t bytes);
|
||||||
|
|
||||||
// This method returns number of read samples
|
// This method returns number of read samples
|
||||||
unsigned read(short* buffer, unsigned samples);
|
size_t read(short* buffer, size_t samples);
|
||||||
std::tstring filename() const;
|
size_t readRaw(short* buffer, size_t samples);
|
||||||
unsigned size() const;
|
|
||||||
|
std::filesystem::path path() const;
|
||||||
|
size_t size() const;
|
||||||
|
|
||||||
unsigned lastError() const;
|
unsigned lastError() const;
|
||||||
};
|
};
|
||||||
|
|
@ -58,25 +68,25 @@ namespace Audio
|
||||||
class WavFileWriter
|
class WavFileWriter
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
FILE* mHandle; /// Handle of audio file.
|
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
|
||||||
std::tstring mFileName; /// Path to requested audio file.
|
std::filesystem::path mPath; /// Path to requested audio file.
|
||||||
std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||||
int mWritten; /// Amount of written data (in bytes)
|
size_t mWritten = 0; /// Amount of written data (in bytes)
|
||||||
int mLengthOffset; /// Position of length field.
|
size_t mLengthOffset = 0; /// Position of length field.
|
||||||
int mRate,
|
int mSamplerate = 0,
|
||||||
mChannels;
|
mChannels = 0;
|
||||||
|
|
||||||
void checkWriteResult(int result);
|
void checkWriteResult(int result);
|
||||||
|
void writeBuffer(const void* buffer, size_t sz);
|
||||||
public:
|
public:
|
||||||
WavFileWriter();
|
WavFileWriter();
|
||||||
~WavFileWriter();
|
~WavFileWriter();
|
||||||
|
|
||||||
bool open(const std::tstring& filename, int rate, int channels);
|
bool open(const std::filesystem::path& p, int samplerate, int channels);
|
||||||
void close();
|
void close();
|
||||||
bool isOpened();
|
bool isOpened() const;
|
||||||
size_t write(const void* buffer, size_t bytes);
|
size_t write(const void* buffer, size_t bytes);
|
||||||
std::tstring filename();
|
std::filesystem::path path() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
|
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,42 @@
|
||||||
project (audio_lib)
|
project (audio_lib)
|
||||||
|
|
||||||
# Rely on C++ 11
|
# Rely on C++ 11
|
||||||
set (CMAKE_CXX_STANDARD 11)
|
set (CMAKE_CXX_STANDARD 20)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
set (AUDIOLIB_SOURCES
|
set (AUDIOLIB_SOURCES
|
||||||
Audio_Resampler.cpp
|
Audio_Resampler.cpp
|
||||||
|
Audio_Resampler.h
|
||||||
Audio_Quality.cpp
|
Audio_Quality.cpp
|
||||||
|
Audio_Quality.h
|
||||||
Audio_Mixer.cpp
|
Audio_Mixer.cpp
|
||||||
|
Audio_Mixer.h
|
||||||
Audio_Interface.cpp
|
Audio_Interface.cpp
|
||||||
|
Audio_Interface.h
|
||||||
Audio_Helper.cpp
|
Audio_Helper.cpp
|
||||||
|
Audio_Helper.h
|
||||||
Audio_DataWindow.cpp
|
Audio_DataWindow.cpp
|
||||||
|
Audio_DataWindow.h
|
||||||
Audio_DevicePair.cpp
|
Audio_DevicePair.cpp
|
||||||
|
Audio_DevicePair.h
|
||||||
Audio_Player.cpp
|
Audio_Player.cpp
|
||||||
|
Audio_Player.h
|
||||||
Audio_Null.cpp
|
Audio_Null.cpp
|
||||||
|
Audio_Null.h
|
||||||
Audio_CoreAudio.cpp
|
Audio_CoreAudio.cpp
|
||||||
|
Audio_CoreAudio.h
|
||||||
Audio_DirectSound.cpp
|
Audio_DirectSound.cpp
|
||||||
|
Audio_DirectSound.h
|
||||||
|
Audio_AndroidOboe.cpp
|
||||||
|
Audio_AndroidOboe.h
|
||||||
Audio_WavFile.cpp
|
Audio_WavFile.cpp
|
||||||
|
Audio_WavFile.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(audio_lib ${AUDIOLIB_SOURCES})
|
add_library(audio_lib ${AUDIOLIB_SOURCES})
|
||||||
|
set_property(TARGET audio_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
|
||||||
##
|
##
|
||||||
target_include_directories(audio_lib
|
target_include_directories(audio_lib
|
||||||
|
|
|
||||||
|
|
@ -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 16000
|
|
||||||
#define AUDIO_MIC_BUFFER_COUNT 16
|
|
||||||
#define AUDIO_MIC_BUFFER_LENGTH 10
|
|
||||||
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
|
||||||
#define AUDIO_SPK_BUFFER_COUNT 16
|
|
||||||
#define AUDIO_SPK_BUFFER_LENGTH 10
|
|
||||||
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
|
||||||
#define AUDIO_MIX_CHANNEL_COUNT 16
|
|
||||||
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
|
|
||||||
|
|
||||||
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
|
|
||||||
#define AUDIO_RESAMPLER_QUALITY 1
|
|
||||||
#define AEC_FRAME_TIME 10
|
|
||||||
#define AEC_TAIL_TIME 160
|
|
||||||
|
|
||||||
|
|
||||||
// Defined these two lines to get dumping of audio input/output
|
|
||||||
//#define AUDIO_DUMPINPUT
|
|
||||||
//#define AUDIO_DUMPOUTPUT
|
|
||||||
|
|
||||||
|
|
||||||
#define UA_REGISTRATION_TIME 3600
|
|
||||||
#define UA_MEDIA_PORT_START 20000
|
|
||||||
#define UA_MEDIA_PORT_FINISH 30000
|
|
||||||
#define UA_MAX_UDP_PACKET_SIZE 576
|
|
||||||
#define UA_PUBLICATION_ID "314"
|
|
||||||
|
|
||||||
#define MT_SAMPLERATE AUDIO_SAMPLERATE
|
|
||||||
|
|
||||||
#define MT_MAXAUDIOFRAME 1440
|
|
||||||
#define MT_MAXRTPPACKET 1500
|
|
||||||
#define MT_DTMF_END_PACKETS 3
|
|
||||||
|
|
||||||
#define RTP_BUFFER_HIGH 480
|
|
||||||
#define RTP_BUFFER_LOW 10
|
|
||||||
#define RTP_BUFFER_PREBUFFER 80
|
|
||||||
#define RTP_DECODED_CAPACITY 2048
|
|
||||||
|
|
||||||
#define DEFAULT_SUBSCRIPTION_TIME 1200
|
|
||||||
#define DEFAULT_SUBSCRIPTION_REFRESHTIME 500
|
|
||||||
|
|
||||||
#define PRESENCE_IN_REG_HEADER "PresenceInReg"
|
|
||||||
|
|
||||||
// Maximum UDP packet length
|
|
||||||
#define MAX_UDPPACKET_SIZE 65535
|
|
||||||
#define MAX_VALID_UDPPACKET_SIZE 2048
|
|
||||||
|
|
||||||
// AMR codec defines - it requires USE_AMR_CODEC defined
|
|
||||||
// #define USE_AMR_CODEC
|
|
||||||
#define MT_AMRNB_PAYLOADTYPE 122
|
|
||||||
#define MT_AMRNB_CODECNAME "amr"
|
|
||||||
|
|
||||||
#define MT_AMRNB_OCTET_PAYLOADTYPE 123
|
|
||||||
|
|
||||||
#define MT_AMRWB_PAYLOADTYPE 124
|
|
||||||
#define MT_AMRWB_CODECNAME "amr-wb"
|
|
||||||
|
|
||||||
#define MT_AMRWB_OCTET_PAYLOADTYPE 125
|
|
||||||
|
|
||||||
#define MT_GSMEFR_PAYLOADTYPE 126
|
|
||||||
#define MT_GSMEFR_CODECNAME "GERAN-EFR"
|
|
||||||
|
|
||||||
#define MT_EVS_PAYLOADTYPE 127
|
|
||||||
#define MT_EVS_CODECNAME "EVS"
|
|
||||||
|
|
||||||
// OPUS codec defines
|
|
||||||
// #define USE_OPUS_CODEC
|
|
||||||
#define MT_OPUS_CODEC_PT 106
|
|
||||||
|
|
||||||
// ILBC codec defines
|
|
||||||
#define MT_ILBC20_PAYLOADTYPE -1
|
|
||||||
#define MT_ILBC30_PAYLOADTYPE -1
|
|
||||||
|
|
||||||
// ISAC codec defines
|
|
||||||
#define MT_ISAC16K_PAYLOADTYPE -1
|
|
||||||
#define MT_ISAC32K_PAYLOADTYPE -1
|
|
||||||
|
|
||||||
// GSM HR payload type
|
|
||||||
#define MT_GSMHR_PAYLOADTYPE -1
|
|
||||||
|
|
||||||
// Mirror buffer capacity
|
|
||||||
#define MT_MIRROR_CAPACITY 32768
|
|
||||||
|
|
||||||
// Mirror buffer readiness threshold - 50 milliseconds
|
|
||||||
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
|
|
||||||
|
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
|
||||||
# define TEXT(X) X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// In milliseconds
|
|
||||||
#define MT_SEVANA_FRAME_TIME 680
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -156,7 +156,7 @@ Account::Account(PVariantMap config, UserAgent& agent)
|
||||||
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
|
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
|
||||||
mRegistration(NULL)
|
mRegistration(NULL)
|
||||||
{
|
{
|
||||||
mProfile = resip::SharedPtr<resip::UserProfile>(new resip::UserProfile(agent.mProfile));
|
mProfile = std::make_shared<resip::UserProfile>(agent.mProfile);
|
||||||
mId = Account::generateId();
|
mId = Account::generateId();
|
||||||
setup(*config);
|
setup(*config);
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +201,7 @@ void Account::setup(VariantMap &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NAT decorator
|
// NAT decorator
|
||||||
mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent)));
|
mProfile->setOutboundDecorator(std::make_shared<NATDecorator>(mAgent));
|
||||||
|
|
||||||
// Rinstance
|
// Rinstance
|
||||||
if (config.exists(CONFIG_INSTANCE_ID))
|
if (config.exists(CONFIG_INSTANCE_ID))
|
||||||
|
|
@ -266,7 +266,7 @@ void Account::start()
|
||||||
|
|
||||||
// Create registration
|
// Create registration
|
||||||
mRegistration = new ResipSession(*mAgent.mDum);
|
mRegistration = new ResipSession(*mAgent.mDum);
|
||||||
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
||||||
|
|
||||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||||
|
|
@ -380,7 +380,7 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
|
||||||
observer->mSessionId = observer->mSession->sessionId();
|
observer->mSessionId = observer->mSession->sessionId();
|
||||||
observer->mPeer = target;
|
observer->mPeer = target;
|
||||||
|
|
||||||
resip::SharedPtr<resip::SipMessage> msg;
|
std::shared_ptr<resip::SipMessage> msg;
|
||||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
||||||
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
||||||
|
|
@ -498,7 +498,7 @@ void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
|
||||||
//config.mDetectNetworkChange = true;
|
//config.mDetectNetworkChange = true;
|
||||||
//config.mNetworkCheckInterval = 5000;
|
//config.mNetworkCheckInterval = 5000;
|
||||||
|
|
||||||
session->mIceStack = resip::SharedPtr<ice::Stack>(ice::Stack::makeICEBox(config));
|
session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config));
|
||||||
session->mIceStack->setEventHandler(session, this);
|
session->mIceStack->setEventHandler(session, this);
|
||||||
session->mIceStack->setRole(icerole);
|
session->mIceStack->setRole(icerole);
|
||||||
}
|
}
|
||||||
|
|
@ -552,9 +552,8 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||||
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const resip::Transport* transport = response.getReceivedTransport();
|
mUsedTransport = response.getReceivedTransportTuple().getType();
|
||||||
mUsedTransport = transport->transport();
|
//bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS;
|
||||||
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
|
|
||||||
|
|
||||||
// Retry registration for stream based transport too
|
// Retry registration for stream based transport too
|
||||||
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
|
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
|
||||||
|
|
@ -596,7 +595,7 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||||
//mProfile->setDefaultFrom(from);
|
//mProfile->setDefaultFrom(from);
|
||||||
}
|
}
|
||||||
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
||||||
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
||||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||||
|
|
||||||
|
|
@ -740,8 +739,8 @@ Account::UserInfo Account::getUserInfo() const
|
||||||
return mUserInfo;
|
return mUserInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
resip::AtomicCounter Account::IdGenerator;
|
std::atomic_int Account::IdGenerator;
|
||||||
int Account::generateId()
|
int Account::generateId()
|
||||||
{
|
{
|
||||||
return IdGenerator.increment();
|
return ++IdGenerator;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ public:
|
||||||
void setup(VariantMap& config);
|
void setup(VariantMap& config);
|
||||||
|
|
||||||
/* Returns corresponding resiprocate profile */
|
/* Returns corresponding resiprocate profile */
|
||||||
resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; }
|
std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; }
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> UserInfo;
|
typedef std::map<std::string, std::string> UserInfo;
|
||||||
void setUserInfo(const UserInfo& info);
|
void setUserInfo(const UserInfo& info);
|
||||||
|
|
@ -83,7 +83,7 @@ protected:
|
||||||
RegistrationState mRegistrationState;
|
RegistrationState mRegistrationState;
|
||||||
|
|
||||||
ice::NetworkAddress mExternalAddress;
|
ice::NetworkAddress mExternalAddress;
|
||||||
resip::SharedPtr<resip::UserProfile> mProfile;
|
std::shared_ptr<resip::UserProfile> mProfile;
|
||||||
UserAgent& mAgent;
|
UserAgent& mAgent;
|
||||||
bool mPresenceOnline;
|
bool mPresenceOnline;
|
||||||
std::string mPresenceContent;
|
std::string mPresenceContent;
|
||||||
|
|
@ -135,7 +135,7 @@ protected:
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
static int generateId();
|
static int generateId();
|
||||||
static resip::AtomicCounter IdGenerator;
|
static std::atomic_int IdGenerator;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Account> PAccount;
|
typedef std::shared_ptr<Account> PAccount;
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes incoming data
|
// Processes incoming data
|
||||||
void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source)
|
void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source)
|
||||||
{
|
{
|
||||||
if (!mActiveStream)
|
if (!mActiveStream)
|
||||||
return;
|
return;
|
||||||
|
|
@ -77,7 +77,7 @@ void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
void AudioProvider::sendData(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size)
|
void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer, unsigned int size)
|
||||||
{
|
{
|
||||||
s->sendDatagram(destination, buffer, size);
|
s->sendDatagram(destination, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +99,6 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||||
else
|
else
|
||||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
||||||
}
|
}
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
|
|
||||||
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
||||||
if (mAvailableCodecs.empty())
|
if (mAvailableCodecs.empty())
|
||||||
|
|
@ -114,7 +113,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||||
if (mRemoteTelephoneCodec)
|
if (mRemoteTelephoneCodec)
|
||||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Publish stream state
|
// Publish stream state
|
||||||
const char* attr = nullptr;
|
const char* attr = nullptr;
|
||||||
|
|
@ -125,6 +124,8 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||||
{
|
{
|
||||||
case msSendonly: attr = "recvonly"; break;
|
case msSendonly: attr = "recvonly"; break;
|
||||||
case msInactive: attr = "recvonly"; break;
|
case msInactive: attr = "recvonly"; break;
|
||||||
|
case msRecvonly:
|
||||||
|
case msSendRecv: break; // Do nothing here
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -227,10 +228,8 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
||||||
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
||||||
{
|
{
|
||||||
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
||||||
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mAvailableCodecs.size())
|
if (!mAvailableCodecs.size())
|
||||||
|
|
@ -301,26 +300,12 @@ std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
|
||||||
if (!mActiveStream)
|
if (!mActiveStream)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
// Use tag 1 - it is ok, as we use only single crypto attribute
|
|
||||||
int srtpTag = 1;
|
|
||||||
|
|
||||||
// Print key to base64 string
|
// Print key to base64 string
|
||||||
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
||||||
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
||||||
resip::Data keyText = d.base64encode();
|
resip::Data keyText = d.base64encode();
|
||||||
|
|
||||||
// Create "crypto" attribute value
|
return std::format("{} {} inline:{}", 1, toString(suite), keyText.c_str());
|
||||||
char buffer[512];
|
|
||||||
const char* suiteName = NULL;
|
|
||||||
switch (suite)
|
|
||||||
{
|
|
||||||
case SRTP_AES_128_AUTH_80: suiteName = SRTP_SUITE_NAME_1; break;
|
|
||||||
case SRTP_AES_256_AUTH_80: suiteName = SRTP_SUITE_NAME_2; break;
|
|
||||||
default: assert(0);
|
|
||||||
}
|
|
||||||
sprintf(buffer, "%d %s inline:%s", srtpTag, suiteName, keyText.c_str());
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
|
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
|
||||||
|
|
@ -341,15 +326,7 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu
|
||||||
resip::Data rawkey = keyText.base64decode();
|
resip::Data rawkey = keyText.base64decode();
|
||||||
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
||||||
|
|
||||||
// Open srtp
|
return toSrtpSuite(suite);
|
||||||
SrtpSuite result = SRTP_NONE;
|
|
||||||
if (strcmp(suite, SRTP_SUITE_NAME_1) == 0)
|
|
||||||
result = SRTP_AES_128_AUTH_80;
|
|
||||||
else
|
|
||||||
if (strcmp(suite, SRTP_SUITE_NAME_2) == 0)
|
|
||||||
result = SRTP_AES_256_AUTH_80;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -12,9 +12,7 @@
|
||||||
#include "../media/MT_Box.h"
|
#include "../media/MT_Box.h"
|
||||||
#include "../media/MT_Stream.h"
|
#include "../media/MT_Stream.h"
|
||||||
#include "../media/MT_Codec.h"
|
#include "../media/MT_Codec.h"
|
||||||
#include "../audio/Audio_Interface.h"
|
|
||||||
|
|
||||||
#include "rutil/ThreadIf.hxx"
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -28,19 +26,19 @@ public:
|
||||||
virtual ~AudioProvider();
|
virtual ~AudioProvider();
|
||||||
|
|
||||||
// Returns provider RTP name
|
// Returns provider RTP name
|
||||||
std::string streamName();
|
std::string streamName() override;
|
||||||
|
|
||||||
// Returns provider RTP profile name
|
// Returns provider RTP profile name
|
||||||
std::string streamProfile();
|
std::string streamProfile() override;
|
||||||
|
|
||||||
// Sets destination IP address
|
// Sets destination IP address
|
||||||
void setDestinationAddress(const RtpPair<InternetAddress>& addr);
|
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
||||||
|
|
||||||
// Processes incoming data
|
// Processes incoming data
|
||||||
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source);
|
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
|
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
|
||||||
|
|
||||||
// Updates SDP offer
|
// Updates SDP offer
|
||||||
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
||||||
|
|
@ -76,6 +74,7 @@ public:
|
||||||
void setupMirror(bool enable);
|
void setupMirror(bool enable);
|
||||||
|
|
||||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||||
|
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// SDP's stream name
|
// SDP's stream name
|
||||||
|
|
@ -111,7 +110,6 @@ protected:
|
||||||
void* mMediaObserverTag = nullptr;
|
void* mMediaObserverTag = nullptr;
|
||||||
|
|
||||||
std::string createCryptoAttribute(SrtpSuite suite);
|
std::string createCryptoAttribute(SrtpSuite suite);
|
||||||
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
|
||||||
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
||||||
|
|
||||||
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "resip/stack/SdpContents.hxx"
|
#include "resip/stack/SdpContents.hxx"
|
||||||
#include "rutil/SharedPtr.hxx"
|
|
||||||
|
|
||||||
#include "../helper/HL_InternetAddress.h"
|
#include "../helper/HL_InternetAddress.h"
|
||||||
#include "../helper/HL_NetworkSocket.h"
|
#include "../helper/HL_NetworkSocket.h"
|
||||||
|
|
@ -46,10 +45,10 @@ public:
|
||||||
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
||||||
|
|
||||||
// Processes incoming data
|
// Processes incoming data
|
||||||
virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
virtual void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
||||||
|
|
||||||
// Updates SDP offer
|
// Updates SDP offer
|
||||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,60 @@
|
||||||
typedef resip::SdpContents::Session::Medium Medium;
|
typedef resip::SdpContents::Session::Medium Medium;
|
||||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||||
|
|
||||||
|
class TransportLogger: public resip::Transport::SipMessageLoggingHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void outboundMessage(const resip::Tuple &source, const resip::Tuple &destination, const resip::SipMessage &msg) override
|
||||||
|
{
|
||||||
|
std::ostringstream dest_buffer; dest_buffer << destination;
|
||||||
|
std::ostringstream msg_buffer; msg_buffer << msg;
|
||||||
|
std::string msg_text = msg_buffer.str();
|
||||||
|
#if defined(TARGET_ANDROID)
|
||||||
|
if (msg_text.size() > 512)
|
||||||
|
{
|
||||||
|
ICELogDebug(<< "Sent to " << dest_buffer.str() << " :");
|
||||||
|
msg_text = strx::prefixLines(msg_text, "<---");
|
||||||
|
|
||||||
|
auto lines = strx::split(msg_text);
|
||||||
|
for (const auto& l: lines)
|
||||||
|
ICELogDebug(<< l);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
||||||
|
#else
|
||||||
|
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: retransmissions store already encoded messages, so callback doesn't send SipMessage it sends
|
||||||
|
// the encoded version of the SipMessage instead. If you need a SipMessage you will need to
|
||||||
|
// re-parse back into a SipMessage in the callback handler.
|
||||||
|
void outboundRetransmit(const resip::Tuple &source, const resip::Tuple &destination, const resip::SendData &data) override
|
||||||
|
{}
|
||||||
|
|
||||||
|
void inboundMessage(const resip::Tuple& source, const resip::Tuple& destination, const resip::SipMessage &msg) override
|
||||||
|
{
|
||||||
|
std::ostringstream source_buffer; source_buffer << source;
|
||||||
|
std::ostringstream msg_buffer; msg_buffer << msg;
|
||||||
|
std::string msg_text = msg_buffer.str();
|
||||||
|
#if defined(TARGET_ANDROID)
|
||||||
|
if (msg_text.size() > 512)
|
||||||
|
{
|
||||||
|
ICELogDebug(<< "Received from " << source_buffer.str() << " :");
|
||||||
|
msg_text = strx::prefixLines(msg_text, "--->");
|
||||||
|
auto lines = strx::split(msg_text);
|
||||||
|
for (const auto& l: lines)
|
||||||
|
ICELogDebug(<< l);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
||||||
|
#else
|
||||||
|
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_buffer.str(), "--->"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
//-------------- UserAgent -----------------------
|
//-------------- UserAgent -----------------------
|
||||||
UserAgent::UserAgent()
|
UserAgent::UserAgent()
|
||||||
{
|
{
|
||||||
|
|
@ -54,7 +108,7 @@ UserAgent::UserAgent()
|
||||||
mConfig[CONFIG_RTCP_ATTR] = true;
|
mConfig[CONFIG_RTCP_ATTR] = true;
|
||||||
|
|
||||||
// Create master profile
|
// Create master profile
|
||||||
mProfile = resip::SharedPtr<resip::MasterProfile>(new resip::MasterProfile());
|
mProfile = std::make_shared<resip::MasterProfile>();
|
||||||
mProfile->clearSupportedMethods();
|
mProfile->clearSupportedMethods();
|
||||||
mProfile->addSupportedMethod(resip::INVITE);
|
mProfile->addSupportedMethod(resip::INVITE);
|
||||||
mProfile->addSupportedMethod(resip::BYE);
|
mProfile->addSupportedMethod(resip::BYE);
|
||||||
|
|
@ -105,7 +159,7 @@ void UserAgent::start()
|
||||||
|
|
||||||
while (std::getline(ss, line))
|
while (std::getline(ss, line))
|
||||||
{
|
{
|
||||||
line = StringHelper::trim(line);
|
line = strx::trim(line);
|
||||||
ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized
|
ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized
|
||||||
switch (addr.family())
|
switch (addr.family())
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +174,7 @@ void UserAgent::start()
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
s = new resip::WinSecurity();
|
s = new resip::WinSecurity();
|
||||||
#elif defined(TARGET_OSX)
|
#elif defined(TARGET_OSX)
|
||||||
s = new resip::MacSecurity();
|
s = new resip::Security();
|
||||||
#elif defined(TARGET_LINUX)
|
#elif defined(TARGET_LINUX)
|
||||||
s = new resip::Security("/etc/ssl/certs");
|
s = new resip::Security("/etc/ssl/certs");
|
||||||
#elif defined(TARGET_ANDROID)
|
#elif defined(TARGET_ANDROID)
|
||||||
|
|
@ -142,16 +196,18 @@ void UserAgent::start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mStack->setTransportSipMessageLoggingHandler(std::make_shared<TransportLogger>());
|
||||||
|
|
||||||
// Add transports
|
// Add transports
|
||||||
mTransportList.clear();
|
mTransportList.clear();
|
||||||
resip::InternalTransport* t;
|
resip::InternalTransport* t;
|
||||||
|
|
||||||
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
|
||||||
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
|
||||||
|
|
||||||
switch (mConfig[CONFIG_TRANSPORT].asInt())
|
switch (mConfig[CONFIG_TRANSPORT].asInt())
|
||||||
{
|
{
|
||||||
case 0:
|
case TransportType_Any:
|
||||||
if (mConfig[CONFIG_IPV4].asBool())
|
if (mConfig[CONFIG_IPV4].asBool())
|
||||||
{
|
{
|
||||||
ADD_TRANSPORT4(resip::TCP)
|
ADD_TRANSPORT4(resip::TCP)
|
||||||
|
|
@ -166,21 +222,21 @@ void UserAgent::start()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case TransportType_Udp:
|
||||||
if (mConfig[CONFIG_IPV4].asBool())
|
if (mConfig[CONFIG_IPV4].asBool())
|
||||||
ADD_TRANSPORT4(resip::UDP);
|
ADD_TRANSPORT4(resip::UDP);
|
||||||
if (mConfig[CONFIG_IPV6].asBool())
|
if (mConfig[CONFIG_IPV6].asBool())
|
||||||
ADD_TRANSPORT6(resip::UDP);
|
ADD_TRANSPORT6(resip::UDP);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case TransportType_Tcp:
|
||||||
if (mConfig[CONFIG_IPV4].asBool())
|
if (mConfig[CONFIG_IPV4].asBool())
|
||||||
ADD_TRANSPORT4(resip::TCP);
|
ADD_TRANSPORT4(resip::TCP);
|
||||||
if (mConfig[CONFIG_IPV6].asBool())
|
if (mConfig[CONFIG_IPV6].asBool())
|
||||||
ADD_TRANSPORT6(resip::TCP);
|
ADD_TRANSPORT6(resip::TCP);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case TransportType_Tls:
|
||||||
if (mConfig[CONFIG_IPV4].asBool())
|
if (mConfig[CONFIG_IPV4].asBool())
|
||||||
ADD_TRANSPORT4(resip::TLS);
|
ADD_TRANSPORT4(resip::TLS);
|
||||||
if (mConfig[CONFIG_IPV6].asBool())
|
if (mConfig[CONFIG_IPV6].asBool())
|
||||||
|
|
@ -262,16 +318,16 @@ void UserAgent::shutdown()
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
for (auto observerIter: mClientObserverMap)
|
for (auto& observerIter: mClientObserverMap)
|
||||||
observerIter.second->stop();
|
observerIter.second->stop();
|
||||||
|
|
||||||
for (auto observerIter: mServerObserverMap)
|
for (auto& observerIter: mServerObserverMap)
|
||||||
observerIter.second->stop();
|
observerIter.second->stop();
|
||||||
|
|
||||||
for (auto sessionIter: mSessionMap)
|
for (auto& sessionIter: mSessionMap)
|
||||||
sessionIter.second->stop();
|
sessionIter.second->stop();
|
||||||
|
|
||||||
for (auto accountIter: mAccountSet)
|
for (auto& accountIter: mAccountSet)
|
||||||
accountIter->stop();
|
accountIter->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -279,24 +335,24 @@ void UserAgent::shutdown()
|
||||||
bool UserAgent::active()
|
bool UserAgent::active()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mStack != NULL;
|
return mStack != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::refresh()
|
void UserAgent::refresh()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
for (auto acc: mAccountSet)
|
for (auto& acc: mAccountSet)
|
||||||
acc->refresh();
|
acc->refresh();
|
||||||
|
|
||||||
for (auto observer: mClientObserverMap)
|
for (auto& observer: mClientObserverMap)
|
||||||
observer.second->refresh();
|
observer.second->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onDumCanBeDeleted()
|
void UserAgent::onDumCanBeDeleted()
|
||||||
{
|
{
|
||||||
delete mDum; mDum = NULL;
|
delete mDum; mDum = nullptr;
|
||||||
delete mStack; mStack = NULL;
|
delete mStack; mStack = nullptr;
|
||||||
|
|
||||||
mClientObserverMap.clear();
|
mClientObserverMap.clear();
|
||||||
mServerObserverMap.clear();
|
mServerObserverMap.clear();
|
||||||
|
|
@ -315,7 +371,10 @@ void UserAgent::stop()
|
||||||
mTransportList.clear();
|
mTransportList.clear();
|
||||||
|
|
||||||
// Dump statistics here
|
// Dump statistics here
|
||||||
ICELogInfo(<< "Remaining " << Session::InstanceCounter.value() << " session(s), " << ResipSession::InstanceCounter.value() << " resip DialogSet(s), " << resip::ClientRegistration::InstanceCounter.value() << " ClientRegistration(s)");
|
ICELogInfo(<< "Remaining "
|
||||||
|
<< Session::InstanceCounter << " session(s), "
|
||||||
|
<< ResipSession::InstanceCounter << " resip DialogSet(s), "
|
||||||
|
<< resip::ClientRegistration::InstanceCounter << " ClientRegistration(s)");
|
||||||
|
|
||||||
mDum->shutdown(this);
|
mDum->shutdown(this);
|
||||||
onDumCanBeDeleted();
|
onDumCanBeDeleted();
|
||||||
|
|
@ -379,15 +438,14 @@ void UserAgent::process()
|
||||||
|
|
||||||
// Find all sessions
|
// Find all sessions
|
||||||
std::set<int> idSet;
|
std::set<int> idSet;
|
||||||
SessionMap::iterator sessionIter;
|
for (auto sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
||||||
for (sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
|
||||||
idSet.insert(sessionIter->first);
|
idSet.insert(sessionIter->first);
|
||||||
|
|
||||||
// Now process session one by one checking if current is available yet
|
// Now process session one by one checking if current is available yet
|
||||||
std::set<int>::iterator resipIter;
|
std::set<int>::iterator resipIter;
|
||||||
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
|
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
|
||||||
{
|
{
|
||||||
SessionMap::iterator sessionIter = mSessionMap.find(*resipIter);
|
auto sessionIter = mSessionMap.find(*resipIter);
|
||||||
if (sessionIter == mSessionMap.end())
|
if (sessionIter == mSessionMap.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -428,9 +486,16 @@ void UserAgent::process()
|
||||||
void UserAgent::addRootCert(const ByteBuffer& data)
|
void UserAgent::addRootCert(const ByteBuffer& data)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
if (!mStack)
|
||||||
|
return;
|
||||||
resip::Data b(data.data(), data.size());
|
resip::Data b(data.data(), data.size());
|
||||||
|
try {
|
||||||
mStack->getSecurity()->addRootCertPEM(b);
|
mStack->getSecurity()->addRootCertPEM(b);
|
||||||
}
|
}
|
||||||
|
catch(...) {
|
||||||
|
// Ignore silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PAccount UserAgent::createAccount(PVariantMap config)
|
PAccount UserAgent::createAccount(PVariantMap config)
|
||||||
{
|
{
|
||||||
|
|
@ -464,7 +529,7 @@ PSession UserAgent::createSession(PAccount account)
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UserAgent::formatSipAddress(std::string sip)
|
std::string UserAgent::formatSipAddress(const std::string& sip)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
if (sip.size())
|
if (sip.size())
|
||||||
|
|
@ -480,17 +545,18 @@ std::string UserAgent::formatSipAddress(std::string sip)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserAgent::isSipAddressValid(std::string sip)
|
bool UserAgent::isSipAddressValid(const std::string& sip)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (sip.find('<') == std::string::npos)
|
std::string s = sip;
|
||||||
sip = "<" + sip;
|
if (s.find('<') == std::string::npos)
|
||||||
if (sip.find('>') == std::string::npos)
|
s = "<" + s;
|
||||||
sip += ">";
|
if (s.find('>') == std::string::npos)
|
||||||
|
s += ">";
|
||||||
|
|
||||||
resip::Data d(formatSipAddress(sip));
|
resip::Data d(formatSipAddress(s));
|
||||||
resip::Uri uri(d);
|
resip::Uri uri(d);
|
||||||
result = uri.isWellFormed();
|
result = uri.isWellFormed();
|
||||||
if (result)
|
if (result)
|
||||||
|
|
@ -540,7 +606,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserAgent::compareSipAddresses(std::string sip1, std::string sip2)
|
bool UserAgent::compareSipAddresses(const std::string& sip1, const std::string& sip2)
|
||||||
{
|
{
|
||||||
if (sip1.empty() || sip2.empty())
|
if (sip1.empty() || sip2.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -585,7 +651,7 @@ void UserAgent::sendOffer(Session* session)
|
||||||
if (session->mOriginVersion == 1)
|
if (session->mOriginVersion == 1)
|
||||||
{
|
{
|
||||||
// Construct INVITE session
|
// Construct INVITE session
|
||||||
resip::SharedPtr<resip::SipMessage> msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
auto msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
||||||
|
|
||||||
// Include user headers
|
// Include user headers
|
||||||
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++)
|
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++)
|
||||||
|
|
@ -655,13 +721,19 @@ void UserAgent::onFailure(resip::ClientRegistrationHandle h, const resip::SipMes
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
bool UserAgent::operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName, const char* file,
|
bool UserAgent::operator()(resip::Log::Level level,
|
||||||
int line, const resip::Data& message, const resip::Data& messageWithHeaders)
|
const resip::Subsystem& subsystem,
|
||||||
|
const resip::Data& appName,
|
||||||
|
const char* file,
|
||||||
|
int line,
|
||||||
|
const resip::Data& message,
|
||||||
|
const resip::Data& messageWithHeaders,
|
||||||
|
const resip::Data& instanceName)
|
||||||
{
|
{
|
||||||
std::string filename = file;
|
std::string filename = file;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
ss << "File " << StringHelper::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
ss << "File " << strx::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
||||||
if (level <= resip::Log::Crit)
|
if (level <= resip::Log::Crit)
|
||||||
ICELogCritical(<< ss.str())
|
ICELogCritical(<< ss.str())
|
||||||
else
|
else
|
||||||
|
|
@ -948,7 +1020,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
||||||
|
|
||||||
// See if remote stream has "rtcp" or "rtcp-mux" attributes
|
// See if remote stream has "rtcp" or "rtcp-mux" attributes
|
||||||
if (remoteStream.exists("rtcp"))
|
if (remoteStream.exists("rtcp"))
|
||||||
addr2.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
|
addr2.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
|
||||||
else
|
else
|
||||||
if (remoteStream.exists("rtcp-mux"))
|
if (remoteStream.exists("rtcp-mux"))
|
||||||
addr2.setPort( remoteDefaultPort );
|
addr2.setPort( remoteDefaultPort );
|
||||||
|
|
@ -990,7 +1062,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
||||||
s->mIceStack->clear();
|
s->mIceStack->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt64 version = sdp.session().origin().getVersion();
|
uint64_t version = sdp.session().origin().getVersion();
|
||||||
s->mRemoteOriginVersion = version;
|
s->mRemoteOriginVersion = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1015,12 +1087,12 @@ void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& m
|
||||||
if (sdp.session().exists("ice-ufrag"))
|
if (sdp.session().exists("ice-ufrag"))
|
||||||
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
|
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
|
||||||
|
|
||||||
ice::Stack& ice = *s->mIceStack;
|
//ice::Stack& ice = *s->mIceStack;
|
||||||
|
|
||||||
UInt64 version = sdp.session().origin().getVersion();
|
uint64_t version = sdp.session().origin().getVersion();
|
||||||
std::string remoteIp = sdp.session().connection().getAddress().c_str();
|
std::string remoteIp = sdp.session().connection().getAddress().c_str();
|
||||||
int code;
|
int code;
|
||||||
if ((UInt64)-1 == s->mRemoteOriginVersion)
|
if ((uint64_t)-1 == s->mRemoteOriginVersion)
|
||||||
{
|
{
|
||||||
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
|
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
|
||||||
}
|
}
|
||||||
|
|
@ -1521,7 +1593,7 @@ VariantMap& UserAgent::config()
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
/*static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
||||||
{
|
{
|
||||||
resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
|
resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
|
||||||
while (endLine != resip::Data::npos)
|
while (endLine != resip::Data::npos)
|
||||||
|
|
@ -1533,9 +1605,9 @@ static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& outpu
|
||||||
}
|
}
|
||||||
if (0 == startLine)
|
if (0 == startLine)
|
||||||
output.push_back(content);
|
output.push_back(content);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
/*static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
||||||
{
|
{
|
||||||
resip::Data::size_type p = input.find(":");
|
resip::Data::size_type p = input.find(":");
|
||||||
if (p == resip::Data::npos)
|
if (p == resip::Data::npos)
|
||||||
|
|
@ -1556,7 +1628,7 @@ static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& valu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
|
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
|
||||||
{
|
{
|
||||||
|
|
@ -1564,17 +1636,17 @@ void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, con
|
||||||
ice::NetworkAddress address(*addr, addrlen);
|
ice::NetworkAddress address(*addr, addrlen);
|
||||||
std::string addressText = address.toStdString();
|
std::string addressText = address.toStdString();
|
||||||
|
|
||||||
switch (flow)
|
/*switch (flow)
|
||||||
{
|
{
|
||||||
case resip::InternalTransport::TransportLogger::Flow_Received:
|
case resip::InternalTransport::TransportLogger::Flow_Received:
|
||||||
ICELogDebug(<< "Received from " << addressText << ":" << "\n"
|
ICELogDebug(<< "Received from " << addressText << ":" << "\n"
|
||||||
<< StringHelper::prefixLines(d, "--->"));
|
<< strx::prefixLines(d, "--->"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case resip::InternalTransport::TransportLogger::Flow_Sent:
|
case resip::InternalTransport::TransportLogger::Flow_Sent:
|
||||||
ICELogDebug(<< "Sent to " << addressText << "\n" << StringHelper::prefixLines(d, "<---"));
|
ICELogDebug(<< "Sent to " << addressText << "\n" << strx::prefixLines(d, "<---"));
|
||||||
break;
|
break;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
PSession UserAgent::getUserSession(int sessionId)
|
PSession UserAgent::getUserSession(int sessionId)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
#include "../ice/ICETime.h"
|
#include "../ice/ICETime.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "EP_Session.h"
|
#include "EP_Session.h"
|
||||||
#include "EP_Observer.h"
|
#include "EP_Observer.h"
|
||||||
#include "EP_DataProvider.h"
|
#include "EP_DataProvider.h"
|
||||||
|
|
@ -60,6 +60,14 @@
|
||||||
#define RESIPROCATE_SUBSYSTEM Subsystem::TEST
|
#define RESIPROCATE_SUBSYSTEM Subsystem::TEST
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TransportType_Any,
|
||||||
|
TransportType_Udp,
|
||||||
|
TransportType_Tcp,
|
||||||
|
TransportType_Tls
|
||||||
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
CONFIG_IPV4 = 0, // Use IP4
|
CONFIG_IPV4 = 0, // Use IP4
|
||||||
|
|
@ -144,8 +152,8 @@ class UserAgent: public resip::ClientRegistrationHandler,
|
||||||
public resip::ServerSubscriptionHandler,
|
public resip::ServerSubscriptionHandler,
|
||||||
public resip::ClientPagerMessageHandler,
|
public resip::ClientPagerMessageHandler,
|
||||||
public resip::ServerPagerMessageHandler,
|
public resip::ServerPagerMessageHandler,
|
||||||
public resip::ClientPublicationHandler,
|
public resip::ClientPublicationHandler
|
||||||
public resip::InternalTransport::TransportLogger
|
//public resip::InternalTransport::TransportLogger
|
||||||
{
|
{
|
||||||
friend class Account;
|
friend class Account;
|
||||||
friend class Session;
|
friend class Session;
|
||||||
|
|
@ -154,9 +162,9 @@ class UserAgent: public resip::ClientRegistrationHandler,
|
||||||
friend class WatcherQueue;
|
friend class WatcherQueue;
|
||||||
public:
|
public:
|
||||||
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
||||||
static bool compareSipAddresses(std::string sip1, std::string sip2);
|
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
|
||||||
static std::string formatSipAddress(std::string sip);
|
static std::string formatSipAddress(const std::string& sip);
|
||||||
static bool isSipAddressValid(std::string sip);
|
static bool isSipAddressValid(const std::string& sip);
|
||||||
struct SipAddress
|
struct SipAddress
|
||||||
{
|
{
|
||||||
bool mValid;
|
bool mValid;
|
||||||
|
|
@ -375,7 +383,8 @@ public:
|
||||||
const char* file,
|
const char* file,
|
||||||
int line,
|
int line,
|
||||||
const resip::Data& message,
|
const resip::Data& message,
|
||||||
const resip::Data& messageWithHeaders) override;
|
const resip::Data& messageWithHeaders,
|
||||||
|
const resip::Data& instanceName) override;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region DnsResultSink implementation
|
#pragma region DnsResultSink implementation
|
||||||
|
|
@ -389,7 +398,7 @@ public:
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region TransportLogger implementation
|
#pragma region TransportLogger implementation
|
||||||
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
|
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region ClientPublicationHandler
|
#pragma region ClientPublicationHandler
|
||||||
|
|
@ -439,7 +448,7 @@ protected:
|
||||||
Mutex mGuard;
|
Mutex mGuard;
|
||||||
|
|
||||||
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
|
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
|
||||||
resip::SharedPtr<resip::MasterProfile> mProfile;
|
std::shared_ptr<resip::MasterProfile> mProfile;
|
||||||
|
|
||||||
// Resiprocate's SIP stack object pointer
|
// Resiprocate's SIP stack object pointer
|
||||||
resip::SipStack* mStack;
|
resip::SipStack* mStack;
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ void WatcherQueue::process()
|
||||||
if (i == mItemList.end())
|
if (i == mItemList.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
resip::SharedPtr<resip::SipMessage> msg;
|
std::shared_ptr<resip::SipMessage> msg;
|
||||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||||
|
|
||||||
switch (i->mState)
|
switch (i->mState)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -7,11 +7,7 @@
|
||||||
#include "EP_Engine.h"
|
#include "EP_Engine.h"
|
||||||
#include "EP_AudioProvider.h"
|
#include "EP_AudioProvider.h"
|
||||||
#include "../media/MT_Stream.h"
|
#include "../media/MT_Stream.h"
|
||||||
#include "../media/MT_AudioStream.h"
|
|
||||||
#include "../media/MT_Dtmf.h"
|
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_Exception.h"
|
|
||||||
#include "../helper/HL_StreamState.h"
|
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "../helper/HL_String.h"
|
#include "../helper/HL_String.h"
|
||||||
|
|
||||||
|
|
@ -20,7 +16,7 @@
|
||||||
typedef resip::SdpContents::Session::Medium Medium;
|
typedef resip::SdpContents::Session::Medium Medium;
|
||||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||||
|
|
||||||
#define DOMULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
#define IS_MULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
||||||
|
|
||||||
|
|
||||||
//------------ ResipSessionAppDialog ------------
|
//------------ ResipSessionAppDialog ------------
|
||||||
|
|
@ -37,12 +33,12 @@ ResipSessionAppDialog::~ResipSessionAppDialog()
|
||||||
|
|
||||||
#pragma region ResipSession
|
#pragma region ResipSession
|
||||||
|
|
||||||
resip::AtomicCounter ResipSession::InstanceCounter;
|
std::atomic_int ResipSession::InstanceCounter;
|
||||||
|
|
||||||
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
||||||
: resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
|
: resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
|
||||||
{
|
{
|
||||||
ResipSession::InstanceCounter.increment();
|
ResipSession::InstanceCounter++;
|
||||||
mTag = NULL;
|
mTag = NULL;
|
||||||
mTerminated = false;
|
mTerminated = false;
|
||||||
mOnWatchingStartSent = false;
|
mOnWatchingStartSent = false;
|
||||||
|
|
@ -62,7 +58,7 @@ ResipSession::~ResipSession()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ResipSession::InstanceCounter.decrement();
|
ResipSession::InstanceCounter--;
|
||||||
}
|
}
|
||||||
|
|
||||||
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
|
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
|
||||||
|
|
@ -167,12 +163,12 @@ int ResipSession::sessionId()
|
||||||
return mSessionId;
|
return mSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResipSession::setUASProfile(std::shared_ptr<resip::UserProfile> profile)
|
void ResipSession::setUASProfile(const std::shared_ptr<resip::UserProfile>& profile)
|
||||||
{
|
{
|
||||||
mUASProfile = profile;
|
mUASProfile = profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
resip::SharedPtr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
|
std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
|
||||||
{
|
{
|
||||||
assert(mUserAgent != nullptr);
|
assert(mUserAgent != nullptr);
|
||||||
|
|
||||||
|
|
@ -184,7 +180,7 @@ resip::SharedPtr<resip::UserProfile> ResipSession::selectUASUserProfile(const re
|
||||||
else
|
else
|
||||||
return mUserAgent->mProfile;
|
return mUserAgent->mProfile;
|
||||||
}
|
}
|
||||||
return resip::SharedPtr<resip::UserProfile>();
|
return std::shared_ptr<resip::UserProfile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
@ -262,11 +258,11 @@ void Session::Stream::setRtcpMuxAttr(bool value)
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Session
|
#pragma region Session
|
||||||
resip::AtomicCounter Session::InstanceCounter;
|
std::atomic_int Session::InstanceCounter;
|
||||||
|
|
||||||
Session::Session(PAccount account)
|
Session::Session(PAccount account)
|
||||||
{
|
{
|
||||||
InstanceCounter.increment();
|
InstanceCounter++;
|
||||||
mAccount = account;
|
mAccount = account;
|
||||||
mSessionId = Session::generateId();
|
mSessionId = Session::generateId();
|
||||||
mTag = NULL;
|
mTag = NULL;
|
||||||
|
|
@ -278,7 +274,7 @@ Session::Session(PAccount account)
|
||||||
mRole = Acceptor;
|
mRole = Acceptor;
|
||||||
mGatheredCandidates = false;
|
mGatheredCandidates = false;
|
||||||
mTerminated = false;
|
mTerminated = false;
|
||||||
mRemoteOriginVersion = (UInt64)-1;
|
mRemoteOriginVersion = (uint64_t)-1;
|
||||||
mResipSession = NULL;
|
mResipSession = NULL;
|
||||||
mRefCount = 1;
|
mRefCount = 1;
|
||||||
mOfferAnswerCounter = 0;
|
mOfferAnswerCounter = 0;
|
||||||
|
|
@ -296,7 +292,7 @@ Session::~Session()
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{}
|
{}
|
||||||
InstanceCounter.decrement();
|
InstanceCounter--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::start(const std::string& peer)
|
void Session::start(const std::string& peer)
|
||||||
|
|
@ -449,29 +445,19 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||||
MT::Statistics stat;
|
MT::Statistics stat;
|
||||||
|
|
||||||
// Iterate all session providers
|
// Iterate all session providers
|
||||||
stat.reset();
|
Stream* media = nullptr;
|
||||||
Stream* media = NULL;
|
for (Stream& stream: mStreamList)
|
||||||
for (unsigned streamIndex = 0; streamIndex < mStreamList.size(); streamIndex++)
|
|
||||||
{
|
|
||||||
Stream& stream = mStreamList[streamIndex];
|
|
||||||
if (stream.provider())
|
|
||||||
{
|
{
|
||||||
|
if (!stream.provider())
|
||||||
|
continue;
|
||||||
|
|
||||||
media = &stream;
|
media = &stream;
|
||||||
MT::Statistics s = stream.provider()->getStatistics();
|
MT::Statistics s = stream.provider()->getStatistics();
|
||||||
#if defined(USE_PVQA_LIBRARY) && !defined(TARGET_SERVER)
|
|
||||||
if (options != InfoOptions::Standard)
|
|
||||||
{
|
|
||||||
// This information is available AFTER audio stream is deleted
|
|
||||||
info[SessionInfo_PvqaMos] = s.mPvqaMos;
|
|
||||||
info[SessionInfo_PvqaReport] = s.mPvqaReport;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
|
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
|
||||||
info[SessionInfo_AudioCodec] = s.mCodecName;
|
info[SessionInfo_AudioCodec] = s.mCodecName;
|
||||||
|
|
||||||
stat += s;
|
stat += s;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
|
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
|
||||||
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
|
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
|
||||||
|
|
@ -482,7 +468,7 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||||
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
||||||
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
||||||
if (stat.mFirstRtpTime)
|
if (stat.mFirstRtpTime)
|
||||||
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - *(stat.mFirstRtpTime)).count());
|
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - *(stat.mFirstRtpTime)).count());
|
||||||
else
|
else
|
||||||
info[SessionInfo_Duration] = 0;
|
info[SessionInfo_Duration] = 0;
|
||||||
|
|
||||||
|
|
@ -549,12 +535,11 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
|
||||||
{
|
{
|
||||||
// Try to process incoming data by ICE stack
|
// Try to process incoming data by ICE stack
|
||||||
int component = -1, stream = -1;
|
int component = -1, stream = -1;
|
||||||
bool processed;
|
|
||||||
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
|
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
|
||||||
{
|
{
|
||||||
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
||||||
buffer.setRemoteAddress(src);
|
buffer.setRemoteAddress(src);
|
||||||
processed = mIceStack->processIncomingData(stream, component, buffer);
|
/*bool processed = */mIceStack->processIncomingData(stream, component, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -792,8 +777,8 @@ void Session::addProvider(PDataProvider provider)
|
||||||
s.setProvider( provider );
|
s.setProvider( provider );
|
||||||
|
|
||||||
// Allocate socket for provider
|
// Allocate socket for provider
|
||||||
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()) );
|
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()) );
|
||||||
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()) );
|
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()) );
|
||||||
s.provider()->setSocket(s.socket4(), s.socket6());
|
s.provider()->setSocket(s.socket4(), s.socket6());
|
||||||
|
|
||||||
// Create ICE stream/component
|
// Create ICE stream/component
|
||||||
|
|
@ -829,10 +814,10 @@ int Session::sessionId()
|
||||||
return mSessionId;
|
return mSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
resip::AtomicCounter Session::IdGenerator;
|
std::atomic_int Session::IdGenerator;
|
||||||
int Session::generateId()
|
int Session::generateId()
|
||||||
{
|
{
|
||||||
return (int)IdGenerator.increment();
|
return ++IdGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Session::remoteAddress() const
|
std::string Session::remoteAddress() const
|
||||||
|
|
@ -896,8 +881,8 @@ void Session::refreshMediaPath()
|
||||||
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
||||||
|
|
||||||
// Bring new socket to provider and stream
|
// Bring new socket to provider and stream
|
||||||
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX() ),
|
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ),
|
||||||
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX());
|
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX());
|
||||||
|
|
||||||
p->setSocket(s4, s6);
|
p->setSocket(s4, s6);
|
||||||
s.setSocket4(s4);
|
s.setSocket4(s4);
|
||||||
|
|
@ -914,7 +899,7 @@ void Session::refreshMediaPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Received offer with new SDP
|
// Received offer with new SDP
|
||||||
int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
|
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
|
||||||
{
|
{
|
||||||
bool iceRestart = false;
|
bool iceRestart = false;
|
||||||
|
|
@ -959,7 +944,7 @@ int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, s
|
||||||
targetAddr.mRtcp.setPort( remoteStream.port() );
|
targetAddr.mRtcp.setPort( remoteStream.port() );
|
||||||
else
|
else
|
||||||
if (stream.rtcpAttr())
|
if (stream.rtcpAttr())
|
||||||
targetAddr.mRtcp.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
|
targetAddr.mRtcp.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
|
||||||
else
|
else
|
||||||
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
||||||
|
|
||||||
|
|
@ -976,8 +961,8 @@ int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, s
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()));
|
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
|
||||||
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()));
|
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,15 +28,14 @@
|
||||||
#include "rutil/Logger.hxx"
|
#include "rutil/Logger.hxx"
|
||||||
#include "rutil/Random.hxx"
|
#include "rutil/Random.hxx"
|
||||||
#include "rutil/WinLeakCheck.hxx"
|
#include "rutil/WinLeakCheck.hxx"
|
||||||
#include "rutil/AtomicCounter.hxx"
|
|
||||||
|
|
||||||
#include "../ice/ICEBox.h"
|
#include "../ice/ICEBox.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <atomic>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "EP_Session.h"
|
|
||||||
#include "EP_Account.h"
|
#include "EP_Account.h"
|
||||||
#include "EP_DataProvider.h"
|
#include "EP_DataProvider.h"
|
||||||
#include "EP_AudioProvider.h"
|
#include "EP_AudioProvider.h"
|
||||||
|
|
@ -214,7 +213,8 @@ public:
|
||||||
void refreshMediaPath();
|
void refreshMediaPath();
|
||||||
|
|
||||||
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
||||||
int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
// There are passing string objects by value; this is correct; this values will modified on the stack.
|
||||||
|
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
|
||||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
||||||
|
|
||||||
// Session ID
|
// Session ID
|
||||||
|
|
@ -224,7 +224,7 @@ public:
|
||||||
std::vector<Stream> mStreamList;
|
std::vector<Stream> mStreamList;
|
||||||
|
|
||||||
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
||||||
resip::SharedPtr<ice::Stack> mIceStack;
|
std::shared_ptr<ice::Stack> mIceStack;
|
||||||
|
|
||||||
// Pointer to owner user agent instance
|
// Pointer to owner user agent instance
|
||||||
UserAgent* mUserAgent;
|
UserAgent* mUserAgent;
|
||||||
|
|
@ -237,7 +237,7 @@ public:
|
||||||
|
|
||||||
// SDP's origin version for sending
|
// SDP's origin version for sending
|
||||||
int mOriginVersion;
|
int mOriginVersion;
|
||||||
UInt64 mRemoteOriginVersion;
|
uint64_t mRemoteOriginVersion;
|
||||||
|
|
||||||
// SDP's session version
|
// SDP's session version
|
||||||
int mSessionVersion;
|
int mSessionVersion;
|
||||||
|
|
@ -318,8 +318,8 @@ public:
|
||||||
void enqueueOffer();
|
void enqueueOffer();
|
||||||
void processQueuedOffer();
|
void processQueuedOffer();
|
||||||
static int generateId();
|
static int generateId();
|
||||||
static resip::AtomicCounter IdGenerator;
|
static std::atomic_int IdGenerator;
|
||||||
static resip::AtomicCounter InstanceCounter;
|
static std::atomic_int InstanceCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Session> PSession;
|
typedef std::shared_ptr<Session> PSession;
|
||||||
|
|
@ -354,13 +354,13 @@ public:
|
||||||
Type_Call,
|
Type_Call,
|
||||||
Type_Auto
|
Type_Auto
|
||||||
};
|
};
|
||||||
static resip::AtomicCounter InstanceCounter;
|
static std::atomic_int InstanceCounter;
|
||||||
|
|
||||||
|
|
||||||
ResipSession(resip::DialogUsageManager& dum);
|
ResipSession(resip::DialogUsageManager& dum);
|
||||||
virtual ~ResipSession();
|
virtual ~ResipSession();
|
||||||
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
||||||
virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||||
|
|
||||||
void setType(Type type);
|
void setType(Type type);
|
||||||
Type type();
|
Type type();
|
||||||
|
|
@ -384,7 +384,7 @@ public:
|
||||||
|
|
||||||
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
||||||
|
|
||||||
void setUASProfile(std::shared_ptr<resip::UserProfile> profile);
|
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool mTerminated;
|
bool mTerminated;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
|
cmake_minimum_required (VERSION 3.15)
|
||||||
project (helper_lib)
|
project (helper_lib)
|
||||||
|
|
||||||
# Rely on C++ 11
|
# Rely on C++ 11
|
||||||
set (CMAKE_CXX_STANDARD 11)
|
set (CMAKE_CXX_STANDARD 20)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set (USE_NULL_UUID OFF CACHE BOOL "When enabled linking to libuuid is avoided")
|
|
||||||
|
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
||||||
|
|
||||||
add_library(helper_lib ${HELPER_LIB_SOURCES})
|
add_library(helper_lib ${HELPER_LIB_SOURCES})
|
||||||
|
set_property(TARGET helper_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
|
||||||
# Private include directories
|
# Private include directories
|
||||||
target_include_directories(helper_lib PRIVATE ../../libs/ ../../engine ../)
|
target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .)
|
||||||
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
||||||
|
if (TARGET_LINUX)
|
||||||
|
target_link_libraries (helper_lib PUBLIC uuid)
|
||||||
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ namespace Calc
|
||||||
{
|
{
|
||||||
// Dot is here - is it float
|
// Dot is here - is it float
|
||||||
bool isFloat = false;
|
bool isFloat = false;
|
||||||
StringHelper::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
|
strx::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
|
||||||
if (isFloat)
|
if (isFloat)
|
||||||
mCurrentLexem.mType = LexemType::Float;
|
mCurrentLexem.mType = LexemType::Float;
|
||||||
else
|
else
|
||||||
|
|
@ -488,7 +488,7 @@ namespace Calc
|
||||||
|
|
||||||
case LexemType::Hex:
|
case LexemType::Hex:
|
||||||
result->mType = Ast::Type::Number;
|
result->mType = Ast::Type::Number;
|
||||||
result->mValue = StringHelper::fromHex2Int(l.mValue);
|
result->mValue = strx::fromHex2Int(l.mValue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LexemType::Float:
|
case LexemType::Float:
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ bool CsvReader::readLine(std::vector<std::string>& cells)
|
||||||
std::string line;
|
std::string line;
|
||||||
if (!std::getline(mInputStream, line))
|
if (!std::getline(mInputStream, line))
|
||||||
return false;
|
return false;
|
||||||
StringHelper::trim(line);
|
strx::trim(line);
|
||||||
if (line.empty())
|
if (line.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
StringHelper::split(line, cells, ",;");
|
strx::split(line, cells, ",;");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include "HL_File.h"
|
#include "HL_File.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <sys/statvfs.h>
|
# include <sys/statvfs.h>
|
||||||
# include <memory.h>
|
# include <memory.h>
|
||||||
|
|
@ -32,23 +32,24 @@ void FileHelper::remove(const char* s)
|
||||||
::remove(s);
|
::remove(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileHelper::gettempname()
|
// std::string FileHelper::gettempname()
|
||||||
{
|
// {
|
||||||
#if defined(TARGET_LINUX)
|
// #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||||
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||||
mkstemp(template_filename);
|
// int code = mkstemp(template_filename);
|
||||||
return template_filename;
|
|
||||||
#elif defined(TARGET_WIN)
|
|
||||||
char buffer[L_tmpnam];
|
|
||||||
tmpnam(buffer);
|
|
||||||
|
|
||||||
return buffer;
|
// return template_filename;
|
||||||
#elif defined(TARGET_OSX)
|
// #elif defined(TARGET_WIN)
|
||||||
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
// char buffer[L_tmpnam];
|
||||||
mktemp(template_filename);
|
// tmpnam(buffer);
|
||||||
return template_filename;
|
|
||||||
#endif
|
// return buffer;
|
||||||
}
|
// #elif defined(TARGET_OSX)
|
||||||
|
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||||
|
// mktemp(template_filename);
|
||||||
|
// return template_filename;
|
||||||
|
// #endif
|
||||||
|
// }
|
||||||
|
|
||||||
bool FileHelper::isAbsolute(const std::string& s)
|
bool FileHelper::isAbsolute(const std::string& s)
|
||||||
{
|
{
|
||||||
|
|
@ -64,7 +65,7 @@ std::string FileHelper::getCurrentDir()
|
||||||
return std::string();
|
return std::string();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
||||||
char buf[512];
|
char buf[512];
|
||||||
if (getcwd(buf, sizeof buf) != nullptr)
|
if (getcwd(buf, sizeof buf) != nullptr)
|
||||||
return buf;
|
return buf;
|
||||||
|
|
@ -109,3 +110,32 @@ size_t FileHelper::getFreespace(const std::string& path)
|
||||||
#endif
|
#endif
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string FileHelper::expandUserHome(const std::string &path)
|
||||||
|
{
|
||||||
|
if (path.empty() || path[0] != '~')
|
||||||
|
return path; // No expansion needed
|
||||||
|
|
||||||
|
const char* home_dir = nullptr;
|
||||||
|
|
||||||
|
#ifdef TARGET_WIN
|
||||||
|
home_dir = std::getenv("USERPROFILE");
|
||||||
|
if (!home_dir)
|
||||||
|
{
|
||||||
|
home_dir = std::getenv("HOMEDRIVE");
|
||||||
|
const char* homepath = std::getenv("HOMEPATH");
|
||||||
|
if (home_dir && homepath) {
|
||||||
|
std::string fullpath(home_dir);
|
||||||
|
fullpath += homepath;
|
||||||
|
return fullpath + path.substr(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
home_dir = std::getenv("HOME");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!home_dir)
|
||||||
|
throw std::runtime_error("Unable to determine the home directory");
|
||||||
|
|
||||||
|
return std::string(home_dir) + path.substr(1);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public:
|
||||||
static void remove(const std::string& s);
|
static void remove(const std::string& s);
|
||||||
static void remove(const char* s);
|
static void remove(const char* s);
|
||||||
|
|
||||||
static std::string gettempname();
|
// static std::string gettempname();
|
||||||
static bool isAbsolute(const std::string& s);
|
static bool isAbsolute(const std::string& s);
|
||||||
|
|
||||||
static std::string getCurrentDir();
|
static std::string getCurrentDir();
|
||||||
|
|
@ -23,6 +23,7 @@ public:
|
||||||
// Returns free space on volume for path
|
// Returns free space on volume for path
|
||||||
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
||||||
static size_t getFreespace(const std::string& path);
|
static size_t getFreespace(const std::string& path);
|
||||||
|
static std::string expandUserHome(const std::string& path);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1208,7 +1208,7 @@ static const uint16_t byte_crc10_table[256] = {
|
||||||
/* Update the data block's CRC-10 remainder one byte at a time */
|
/* Update the data block's CRC-10 remainder one byte at a time */
|
||||||
uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size)
|
uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size)
|
||||||
{
|
{
|
||||||
register int i;
|
/*register*/ int i;
|
||||||
|
|
||||||
for (i = 0; i < data_blk_size; i++) {
|
for (i = 0; i < data_blk_size; i++) {
|
||||||
crc10_accum = ((crc10_accum << 8) & 0x3ff)
|
crc10_accum = ((crc10_accum << 8) & 0x3ff)
|
||||||
|
|
|
||||||
|
|
@ -11,25 +11,51 @@
|
||||||
#define MPLS_STACK_MASK (0x00000100)
|
#define MPLS_STACK_MASK (0x00000100)
|
||||||
#define MPLS_STACK_SHIFT (8)
|
#define MPLS_STACK_SHIFT (8)
|
||||||
|
|
||||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForRaw(const Packet& data)
|
||||||
{
|
{
|
||||||
PacketData result(packet);
|
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
|
||||||
|
|
||||||
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(packet.mData);
|
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||||
|
return Payload();
|
||||||
|
|
||||||
|
|
||||||
|
switch (ip4->version())
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
return GetUdpPayloadForIp4(data);
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return GetUdpPayloadForIp6(data);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Payload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
||||||
|
{
|
||||||
|
Packet result(data);
|
||||||
|
|
||||||
|
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(data.mData);
|
||||||
|
|
||||||
// Skip ethernet header
|
// Skip ethernet header
|
||||||
packet.mData += sizeof(EthernetHeader);
|
result.mData += sizeof(EthernetHeader);
|
||||||
packet.mLength -= sizeof(EthernetHeader);
|
result.mLength -= sizeof(EthernetHeader);
|
||||||
|
|
||||||
// See if there is Vlan header
|
// See if there is Vlan header
|
||||||
uint16_t proto = 0;
|
uint16_t proto = 0;
|
||||||
if (ethernet->mEtherType == 129)
|
if (ethernet->mEtherType == 129)
|
||||||
{
|
{
|
||||||
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData);
|
// Skip 1 or more VLAN headers
|
||||||
packet.mData += sizeof(VlanHeader);
|
do
|
||||||
packet.mLength -= sizeof(VlanHeader);
|
{
|
||||||
|
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(result.mData);
|
||||||
|
result.mData += sizeof(VlanHeader);
|
||||||
|
result.mLength -= sizeof(VlanHeader);
|
||||||
proto = ntohs(vlan->mData);
|
proto = ntohs(vlan->mData);
|
||||||
}
|
}
|
||||||
|
while (proto == 0x8100);
|
||||||
|
}
|
||||||
|
|
||||||
// Skip MPLS headers
|
// Skip MPLS headers
|
||||||
switch (proto)
|
switch (proto)
|
||||||
|
|
@ -38,10 +64,10 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::Pa
|
||||||
case ETHERTYPE_MPLS_MC:
|
case ETHERTYPE_MPLS_MC:
|
||||||
// Parse MPLS here until marker "bottom of mpls stack"
|
// Parse MPLS here until marker "bottom of mpls stack"
|
||||||
for(bool bottomOfStack = false; !bottomOfStack;
|
for(bool bottomOfStack = false; !bottomOfStack;
|
||||||
bottomOfStack = ((ntohl(*(uint32_t*)(packet.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
||||||
{
|
{
|
||||||
packet.mData += 4;
|
result.mData += 4;
|
||||||
packet.mLength -=4;
|
result.mLength -=4;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -54,86 +80,86 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::Pa
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
|
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(result.mData);
|
||||||
|
|
||||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||||
return PacketData();
|
return Payload();
|
||||||
|
|
||||||
|
|
||||||
switch (ip4->version())
|
switch (ip4->version())
|
||||||
{
|
{
|
||||||
case 4:
|
case 4:
|
||||||
return GetUdpPayloadForIp4(packet, source, destination);
|
return GetUdpPayloadForIp4(result);
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
return GetUdpPayloadForIp6(packet, source, destination);
|
return GetUdpPayloadForIp6(result);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return PacketData();
|
return Payload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForSLL(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForSLL(const Packet& data)
|
||||||
{
|
{
|
||||||
PacketData result(packet);
|
Packet result(data);
|
||||||
|
|
||||||
if (packet.mLength < 16)
|
if (result.mLength < 16)
|
||||||
return PacketData();
|
return Payload();
|
||||||
|
|
||||||
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(packet.mData);
|
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(result.mData);
|
||||||
|
|
||||||
packet.mData += sizeof(LinuxSllHeader);
|
result.mData += sizeof(LinuxSllHeader);
|
||||||
packet.mLength -= sizeof(LinuxSllHeader);
|
result.mLength -= sizeof(LinuxSllHeader);
|
||||||
|
|
||||||
switch (ntohs(sll->mProtocolType))
|
switch (ntohs(sll->mProtocolType))
|
||||||
{
|
{
|
||||||
case 0x0800:
|
case 0x0800:
|
||||||
return GetUdpPayloadForIp4(packet, source, destination);
|
return GetUdpPayloadForIp4(result);
|
||||||
|
|
||||||
case 0x86DD:
|
case 0x86DD:
|
||||||
return GetUdpPayloadForIp6(packet, source, destination);
|
return GetUdpPayloadForIp6(result);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return PacketData();
|
return Payload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForLoopback(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForLoopback(const Packet& data)
|
||||||
{
|
{
|
||||||
PacketData result(packet);
|
Packet result(data);
|
||||||
|
|
||||||
if (packet.mLength < 16)
|
if (result.mLength < 16)
|
||||||
return PacketData();
|
return Payload();
|
||||||
|
|
||||||
struct LoopbackHeader
|
struct LoopbackHeader
|
||||||
{
|
{
|
||||||
uint32_t mProtocolType;
|
uint32_t mProtocolType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(packet.mData);
|
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(result.mData);
|
||||||
|
|
||||||
packet.mData += sizeof(LoopbackHeader);
|
result.mData += sizeof(LoopbackHeader);
|
||||||
packet.mLength -= sizeof(LoopbackHeader);
|
result.mLength -= sizeof(LoopbackHeader);
|
||||||
|
|
||||||
switch (lh->mProtocolType)
|
switch (lh->mProtocolType)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
return GetUdpPayloadForIp4(packet, source, destination);
|
return GetUdpPayloadForIp4(result);
|
||||||
|
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
return GetUdpPayloadForIp6(packet, source, destination);
|
return GetUdpPayloadForIp6(result);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return PacketData();
|
return Payload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
|
||||||
{
|
{
|
||||||
PacketData result(packet);
|
Packet result(data);
|
||||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
|
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
|
||||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||||
return PacketData(nullptr, 0);
|
return Payload();
|
||||||
|
|
||||||
result.mData += ip4->headerLength();
|
result.mData += ip4->headerLength();
|
||||||
result.mLength -= ip4->headerLength();
|
result.mLength -= ip4->headerLength();
|
||||||
|
|
@ -147,13 +173,15 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketD
|
||||||
if (length - sizeof(UdpHeader) < (size_t)result.mLength)
|
if (length - sizeof(UdpHeader) < (size_t)result.mLength)
|
||||||
result.mLength = length - sizeof(UdpHeader);
|
result.mLength = length - sizeof(UdpHeader);
|
||||||
|
|
||||||
source.setIp(ip4->mSource);
|
InternetAddress addr_source;
|
||||||
source.setPort(ntohs(udp->mSourcePort));
|
addr_source.setIp(ip4->mSource);
|
||||||
|
addr_source.setPort(ntohs(udp->mSourcePort));
|
||||||
|
|
||||||
destination.setIp(ip4->mDestination);
|
InternetAddress addr_dest;
|
||||||
destination.setPort(ntohs(udp->mDestinationPort));
|
addr_dest.setIp(ip4->mDestination);
|
||||||
|
addr_dest.setPort(ntohs(udp->mDestinationPort));
|
||||||
|
|
||||||
return result;
|
return {.data = result, .source = addr_source, .dest = addr_dest};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Ip6Header
|
struct Ip6Header
|
||||||
|
|
@ -183,10 +211,10 @@ struct Ip6Header
|
||||||
struct in6_addr dst_ip;
|
struct in6_addr dst_ip;
|
||||||
};
|
};
|
||||||
|
|
||||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
|
||||||
{
|
{
|
||||||
PacketData result(packet);
|
Packet result(data);
|
||||||
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(packet.mData);
|
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData);
|
||||||
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||||
return PacketData(nullptr, 0);
|
return PacketData(nullptr, 0);
|
||||||
*/
|
*/
|
||||||
|
|
@ -198,18 +226,13 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketD
|
||||||
result.mData += sizeof(UdpHeader);
|
result.mData += sizeof(UdpHeader);
|
||||||
result.mLength -= sizeof(UdpHeader);
|
result.mLength -= sizeof(UdpHeader);
|
||||||
|
|
||||||
/*
|
InternetAddress addr_source;
|
||||||
if (result.mLength != ntohs(udp->mDatagramLength))
|
addr_source.setIp(ip6->src_ip);
|
||||||
return PacketData(nullptr, 0);
|
addr_source.setPort(ntohs(udp->mSourcePort));
|
||||||
*/
|
|
||||||
|
|
||||||
source.setIp(ip6->src_ip);
|
InternetAddress addr_dest;
|
||||||
source.setPort(ntohs(udp->mSourcePort));
|
addr_dest.setIp(ip6->dst_ip);
|
||||||
//std::cout << source.toStdString() << " - ";
|
addr_dest.setPort(ntohs(udp->mDestinationPort));
|
||||||
|
|
||||||
destination.setIp(ip6->dst_ip);
|
return {.data = result, .source = addr_source, .dest = addr_dest};
|
||||||
destination.setPort(ntohs(udp->mDestinationPort));
|
|
||||||
//std::cout << destination.toStdString() << std::endl;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,25 +7,38 @@
|
||||||
class NetworkFrame
|
class NetworkFrame
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct PacketData
|
struct Packet
|
||||||
{
|
{
|
||||||
const uint8_t* mData;
|
const uint8_t* mData;
|
||||||
size_t mLength;
|
size_t mLength;
|
||||||
|
|
||||||
PacketData(const uint8_t* data, size_t length)
|
Packet(const uint8_t* data, size_t length)
|
||||||
:mData(data), mLength(length)
|
:mData(data), mLength(length)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
PacketData()
|
Packet()
|
||||||
:mData(nullptr), mLength(0)
|
:mData(nullptr), mLength(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
bool is_empty() const
|
||||||
|
{
|
||||||
|
return mData == nullptr || mLength == 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static PacketData GetUdpPayloadForEthernet(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
struct Payload
|
||||||
static PacketData GetUdpPayloadForIp4(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
{
|
||||||
static PacketData GetUdpPayloadForIp6(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
Packet data;
|
||||||
static PacketData GetUdpPayloadForSLL(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
InternetAddress source;
|
||||||
static PacketData GetUdpPayloadForLoopback(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
InternetAddress dest;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Payload GetUdpPayloadForEthernet(const Packet& data);
|
||||||
|
static Payload GetUdpPayloadForIp4(const Packet& data);
|
||||||
|
static Payload GetUdpPayloadForIp6(const Packet& data);
|
||||||
|
static Payload GetUdpPayloadForSLL(const Packet& data);
|
||||||
|
static Payload GetUdpPayloadForLoopback(const Packet& data);
|
||||||
|
static Payload GetUdpPayloadForRaw(const Packet& data);
|
||||||
|
|
||||||
struct EthernetHeader
|
struct EthernetHeader
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# include <asm/ioctls.h>
|
# include <asm/ioctls.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "HL_NetworkSocket.h"
|
#include "HL_NetworkSocket.h"
|
||||||
|
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||||
|
|
@ -27,7 +27,7 @@ DatagramSocket::DatagramSocket()
|
||||||
|
|
||||||
DatagramSocket::~DatagramSocket()
|
DatagramSocket::~DatagramSocket()
|
||||||
{
|
{
|
||||||
closeSocket();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::open(int family)
|
void DatagramSocket::open(int family)
|
||||||
|
|
@ -61,7 +61,7 @@ void DatagramSocket::sendDatagram(InternetAddress &dest, const void *packetData,
|
||||||
if (mHandle == INVALID_SOCKET)
|
if (mHandle == INVALID_SOCKET)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int sent = ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
/*int sent = */::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
||||||
|
|
@ -85,7 +85,7 @@ unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::closeSocket()
|
void DatagramSocket::internalClose()
|
||||||
{
|
{
|
||||||
if (mHandle != INVALID_SOCKET)
|
if (mHandle != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
|
|
@ -98,6 +98,11 @@ void DatagramSocket::closeSocket()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DatagramSocket::closeSocket()
|
||||||
|
{
|
||||||
|
internalClose();
|
||||||
|
}
|
||||||
|
|
||||||
bool DatagramSocket::isValid() const
|
bool DatagramSocket::isValid() const
|
||||||
{
|
{
|
||||||
return mHandle != INVALID_SOCKET;
|
return mHandle != INVALID_SOCKET;
|
||||||
|
|
@ -163,7 +168,7 @@ unsigned DatagramAgreggator::count()
|
||||||
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
||||||
{
|
{
|
||||||
PDatagramSocket socket = mSocketVector[index];
|
PDatagramSocket socket = mSocketVector[index];
|
||||||
return (FD_ISSET(socket->mHandle, &mReadSet) != 0);
|
return FD_ISSET(socket->mHandle, &mReadSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,12 @@ public:
|
||||||
virtual SOCKET socket() const;
|
virtual SOCKET socket() const;
|
||||||
|
|
||||||
virtual void open(int family);
|
virtual void open(int family);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int mFamily;
|
int mFamily;
|
||||||
SOCKET mHandle;
|
SOCKET mHandle;
|
||||||
int mLocalPort;
|
int mLocalPort;
|
||||||
|
void internalClose();
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
||||||
PROCESS_INFORMATION pi = { 0 };
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
char* cmdline = (char*)_alloca(cmd.size()+1);
|
char* cmdline = (char*)_alloca(cmd.size()+1);
|
||||||
strcpy(cmdline, StringHelper::replace(cmd, "/", "\\").c_str());
|
strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str());
|
||||||
|
|
||||||
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
||||||
if (! fSuccess)
|
if (! fSuccess)
|
||||||
|
|
@ -114,7 +114,7 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
memset(&pi, 0, sizeof pi);
|
memset(&pi, 0, sizeof pi);
|
||||||
|
|
||||||
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
||||||
strcpy(cmdbuffer, StringHelper::replace(cmdline, "/", "\\").c_str());
|
strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str());
|
||||||
|
|
||||||
|
|
||||||
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
||||||
|
|
@ -160,14 +160,14 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
{
|
{
|
||||||
std::string line(buf, cr - buf -1);
|
std::string line(buf, cr - buf -1);
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(StringHelper::trim(line));
|
callback(strx::trim(line));
|
||||||
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} //for
|
} //for
|
||||||
|
|
||||||
if (buf[0])
|
if (buf[0])
|
||||||
callback(StringHelper::trim(std::string(buf)));
|
callback(strx::trim(std::string(buf)));
|
||||||
|
|
||||||
char ctrlc = 3;
|
char ctrlc = 3;
|
||||||
//if (finish_flag)
|
//if (finish_flag)
|
||||||
|
|
@ -278,16 +278,16 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
}
|
}
|
||||||
while (r == sizeof(buffer) - 1);
|
while (r == sizeof(buffer) - 1);
|
||||||
|
|
||||||
if (lines.find("\n") != std::string::npos && line_callback)
|
if (lines.find('\n') != std::string::npos && line_callback)
|
||||||
{
|
{
|
||||||
std::string::size_type p = 0;
|
std::string::size_type p = 0;
|
||||||
while (p < lines.size())
|
while (p < lines.size())
|
||||||
{
|
{
|
||||||
std::string::size_type d = lines.find("\n", p);
|
std::string::size_type d = lines.find('\n', p);
|
||||||
if (d != std::string::npos)
|
if (d != std::string::npos)
|
||||||
{
|
{
|
||||||
if (line_callback)
|
if (line_callback)
|
||||||
line_callback(StringHelper::trim(lines.substr(p, d-p)));
|
line_callback(strx::trim(lines.substr(p, d-p)));
|
||||||
p = d + 1;
|
p = d + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -311,6 +311,8 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||||
|
|
||||||
pid_t OsProcess::findPid(const std::string& cmdline)
|
pid_t OsProcess::findPid(const std::string& cmdline)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -332,5 +334,6 @@ void OsProcess::killByPid(pid_t pid)
|
||||||
return;
|
return;
|
||||||
execSystem("kill -9 " + std::to_string(pid) + " &");
|
execSystem("kill -9 " + std::to_string(pid) + " &");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@
|
||||||
# include <Windows.h>
|
# include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "HL_Rtp.h"
|
#include "HL_Rtp.h"
|
||||||
#include "HL_Exception.h"
|
#include "HL_Exception.h"
|
||||||
#include "HL_String.h"
|
#include "HL_String.h"
|
||||||
|
|
@ -52,8 +56,12 @@ bool RtpHelper::isRtp(const void* buffer, size_t length)
|
||||||
if (length < 12)
|
if (length < 12)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
|
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
|
||||||
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35);
|
if (h->version != 0b10)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
unsigned char pt = h->pt;
|
||||||
|
bool rtp = ( (pt & 0x7F) >= 96 && (pt & 0x7F) <= 127) || ((pt & 0x7F) < 35);
|
||||||
return rtp;
|
return rtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,9 +70,8 @@ bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (length < 12)
|
if (length < 12)
|
||||||
return false;
|
return false;
|
||||||
unsigned char b = ((const unsigned char*)buffer)[0];
|
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
|
||||||
|
return h->version == 0b10;
|
||||||
return (b & 0xC0 ) == 128;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||||
|
|
@ -75,9 +82,17 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||||
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (isRtp(buffer, length))
|
if (isRtp(buffer, length))
|
||||||
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
|
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
|
||||||
else
|
else
|
||||||
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
|
return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
|
||||||
|
{
|
||||||
|
if (isRtp(buffer, length))
|
||||||
|
reinterpret_cast<RtpHeader*>(buffer)->ssrc = htonl(ssrc);
|
||||||
|
else
|
||||||
|
reinterpret_cast<RtcpHeader*>(buffer)->ssrc = htonl(ssrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpHelper::findPtype(const void* buffer, size_t length)
|
int RtpHelper::findPtype(const void* buffer, size_t length)
|
||||||
|
|
@ -184,69 +199,3 @@ void RtpDump::flush()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// -------------- MediaStreamId --------------------
|
|
||||||
bool MediaStreamId::operator < (const MediaStreamId& right) const
|
|
||||||
{
|
|
||||||
if (mSsrcIsId)
|
|
||||||
return std::tie(mSSRC, mSource, mDestination) < std::tie(right.mSSRC, right.mSource, right.mDestination);
|
|
||||||
else
|
|
||||||
return std::tie(mSource, mDestination) < std::tie(right.mSource, right.mDestination);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MediaStreamId::operator == (const MediaStreamId& right) const
|
|
||||||
{
|
|
||||||
if (mSsrcIsId)
|
|
||||||
return std::tie(mSSRC, mSource, mDestination) == std::tie(right.mSSRC, right.mSource, right.mDestination);
|
|
||||||
else
|
|
||||||
return std::tie(mSource, mDestination) == std::tie(right.mSource, right.mDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MediaStreamId::toString() const
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "src: " << mSource.toStdString() <<
|
|
||||||
" dst: " << mDestination.toStdString() <<
|
|
||||||
" ssrc: " << StringHelper::toHex(mSSRC);
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void writeToJson(const MediaStreamId& id, std::ostringstream& oss)
|
|
||||||
{
|
|
||||||
oss << " \"src\": \"" << id.mSource.toStdString() << "\"," << std::endl
|
|
||||||
<< " \"dst\": \"" << id.mDestination.toStdString() << "\"," << std::endl
|
|
||||||
<< " \"ssrc\": \"" << StringHelper::toHex(id.mSSRC) << "\"," << std::endl
|
|
||||||
#if !defined(USE_NULL_UUID)
|
|
||||||
<< " \"link_id\": \"" << id.mLinkId.toString() << "\"" << std::endl
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MediaStreamId::getDetectDescription() const
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "{\"event\": \"stream_detected\"," << std::endl;
|
|
||||||
writeToJson(*this, oss);
|
|
||||||
oss << "}";
|
|
||||||
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MediaStreamId::getFinishDescription() const
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "{" << std::endl
|
|
||||||
<< " \"event\": \"stream_finished\", " << std::endl;
|
|
||||||
writeToJson(*this, oss);
|
|
||||||
oss << "}";
|
|
||||||
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator << (std::ostream& output, const MediaStreamId& id)
|
|
||||||
{
|
|
||||||
return (output << id.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -10,11 +10,8 @@
|
||||||
# include "jrtplib/src/rtppacket.h"
|
# include "jrtplib/src/rtppacket.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "HL_Uuid.h"
|
#include <cstdint>
|
||||||
#include "HL_InternetAddress.h"
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Class to carry rtp/rtcp socket pair
|
// Class to carry rtp/rtcp socket pair
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|
@ -42,6 +39,7 @@ public:
|
||||||
static bool isRtpOrRtcp(const void* buffer, size_t length);
|
static bool isRtpOrRtcp(const void* buffer, size_t length);
|
||||||
static bool isRtcp(const void* buffer, size_t length);
|
static bool isRtcp(const void* buffer, size_t length);
|
||||||
static unsigned findSsrc(const void* buffer, size_t length);
|
static unsigned findSsrc(const void* buffer, size_t length);
|
||||||
|
static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
|
||||||
static int findPayloadLength(const void* buffer, size_t length);
|
static int findPayloadLength(const void* buffer, size_t length);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -72,21 +70,4 @@ public:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct MediaStreamId
|
|
||||||
{
|
|
||||||
InternetAddress mSource;
|
|
||||||
InternetAddress mDestination;
|
|
||||||
uint32_t mSSRC = 0;
|
|
||||||
bool mSsrcIsId = true;
|
|
||||||
Uuid mLinkId;
|
|
||||||
bool operator < (const MediaStreamId& s2) const;
|
|
||||||
bool operator == (const MediaStreamId& right) const;
|
|
||||||
|
|
||||||
std::string toString() const;
|
|
||||||
std::string getDetectDescription() const;
|
|
||||||
std::string getFinishDescription() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator << (std::ostream& output, const MediaStreamId& id);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
@ -127,7 +127,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||||
if (sock == INVALID_SOCKET)
|
if (sock == INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
// Return null socket
|
// Return null socket
|
||||||
PDatagramSocket result(new DatagramSocket());
|
auto result = std::make_shared<DatagramSocket>();
|
||||||
result->mLocalPort = port;
|
result->mLocalPort = port;
|
||||||
result->mFamily = family;
|
result->mFamily = family;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -170,7 +170,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
||||||
}
|
}
|
||||||
PDatagramSocket resultObject(new DatagramSocket());
|
auto resultObject = std::make_shared<DatagramSocket>();
|
||||||
resultObject->mLocalPort = testport;
|
resultObject->mLocalPort = testport;
|
||||||
resultObject->mHandle = sock;
|
resultObject->mHandle = sock;
|
||||||
if (!resultObject->setBlocking(false))
|
if (!resultObject->setBlocking(false))
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __SOCKET_HEAP_H
|
#ifndef __SOCKET_HEAP_H
|
||||||
#define __SOCKET_HEAP_H
|
#define __SOCKET_HEAP_H
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef __HELPER_STATISTICS_H
|
||||||
|
#define __HELPER_STATISTICS_H
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Average
|
||||||
|
{
|
||||||
|
int mCount = 0;
|
||||||
|
T mSum = 0;
|
||||||
|
T average() const
|
||||||
|
{
|
||||||
|
if (!mCount)
|
||||||
|
return 0;
|
||||||
|
return mSum / mCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
T value() const
|
||||||
|
{
|
||||||
|
return average();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process(T value)
|
||||||
|
{
|
||||||
|
mCount++;
|
||||||
|
mSum += value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, int minimum = 100000, int maximum = 0, int default_value = 0>
|
||||||
|
struct TestResult
|
||||||
|
{
|
||||||
|
T mMin = minimum;
|
||||||
|
T mMax = maximum;
|
||||||
|
Average<T> mAverage;
|
||||||
|
T mCurrent = default_value;
|
||||||
|
|
||||||
|
void process(T value)
|
||||||
|
{
|
||||||
|
if (mMin > value)
|
||||||
|
mMin = value;
|
||||||
|
if (mMax < value)
|
||||||
|
mMax = value;
|
||||||
|
mCurrent = value;
|
||||||
|
mAverage.process(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_initialized() const
|
||||||
|
{
|
||||||
|
return mAverage.mCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T current() const
|
||||||
|
{
|
||||||
|
if (is_initialized())
|
||||||
|
return mCurrent;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T value() const
|
||||||
|
{
|
||||||
|
return current();
|
||||||
|
}
|
||||||
|
|
||||||
|
T average() const
|
||||||
|
{
|
||||||
|
return mAverage.average();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult<T>& operator = (T value)
|
||||||
|
{
|
||||||
|
process(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T()
|
||||||
|
{
|
||||||
|
return mCurrent;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
# include <WinSock2.h>
|
# include <WinSock2.h>
|
||||||
|
|
@ -15,7 +16,7 @@
|
||||||
# include <cctype>
|
# include <cctype>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string StringHelper::extractFilename(const std::string& path)
|
std::string strx::extractFilename(const std::string& path)
|
||||||
{
|
{
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
return std::string();
|
return std::string();
|
||||||
|
|
@ -30,7 +31,7 @@ std::string StringHelper::extractFilename(const std::string& path)
|
||||||
return path.substr(p_bs + 1);
|
return path.substr(p_bs + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::appendPath(const std::string& s1, const std::string& s2)
|
std::string strx::appendPath(const std::string& s1, const std::string& s2)
|
||||||
{
|
{
|
||||||
std::string result = s1;
|
std::string result = s1;
|
||||||
if (!endsWith(result, "/") && !endsWith(result, "\\"))
|
if (!endsWith(result, "/") && !endsWith(result, "\\"))
|
||||||
|
|
@ -44,7 +45,7 @@ std::string StringHelper::appendPath(const std::string& s1, const std::string& s
|
||||||
return result + s2;
|
return result + s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::makeUtf8(const std::tstring &arg)
|
std::string strx::makeUtf8(const std::tstring &arg)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
size_t required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
|
size_t required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
|
||||||
|
|
@ -56,12 +57,12 @@ std::string StringHelper::makeUtf8(const std::tstring &arg)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::toUtf8(const std::tstring &arg)
|
std::string strx::toUtf8(const std::tstring &arg)
|
||||||
{
|
{
|
||||||
return makeUtf8(arg);
|
return makeUtf8(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tstring StringHelper::makeTstring(const std::string& arg)
|
std::tstring strx::makeTstring(const std::string& arg)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
size_t count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0);
|
size_t count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0);
|
||||||
|
|
@ -73,7 +74,7 @@ std::tstring StringHelper::makeTstring(const std::string& arg)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int StringHelper::toInt(const char *s, int defaultValue, bool* isOk)
|
int strx::toInt(const char *s, int defaultValue, bool* isOk)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
if (sscanf(s, "%d", &result) != 1)
|
if (sscanf(s, "%d", &result) != 1)
|
||||||
|
|
@ -89,7 +90,7 @@ int StringHelper::toInt(const char *s, int defaultValue, bool* isOk)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t StringHelper::toUint64(const char* s, uint64_t def, bool *isOk)
|
uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||||
{
|
{
|
||||||
uint64_t result = def;
|
uint64_t result = def;
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
|
|
@ -109,14 +110,14 @@ uint64_t StringHelper::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::toHex(unsigned int value)
|
std::string strx::toHex(unsigned int value)
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "%x", value);
|
sprintf(buffer, "%x", value);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::toHex(const void *ptr)
|
std::string strx::toHex(const void *ptr)
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr;
|
oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr;
|
||||||
|
|
@ -126,7 +127,7 @@ std::string StringHelper::toHex(const void *ptr)
|
||||||
//must be lowercase for MD5
|
//must be lowercase for MD5
|
||||||
static const char hexmap[] = "0123456789abcdef";
|
static const char hexmap[] = "0123456789abcdef";
|
||||||
|
|
||||||
std::string StringHelper::toHex(const uint8_t* input, size_t inputLength)
|
std::string strx::toHex(const uint8_t* input, size_t inputLength)
|
||||||
{
|
{
|
||||||
std::string result; result.resize(inputLength * 2);
|
std::string result; result.resize(inputLength * 2);
|
||||||
|
|
||||||
|
|
@ -147,7 +148,7 @@ std::string StringHelper::toHex(const uint8_t* input, size_t inputLength)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::prefixLines(const std::string &source, const std::string &prefix)
|
std::string strx::prefixLines(const std::string &source, const std::string &prefix)
|
||||||
{
|
{
|
||||||
// Read source line by line
|
// Read source line by line
|
||||||
std::istringstream iss(source);
|
std::istringstream iss(source);
|
||||||
|
|
@ -160,7 +161,7 @@ std::string StringHelper::prefixLines(const std::string &source, const std::stri
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::doubleToString(double value, int precision)
|
std::string strx::doubleToString(double value, int precision)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::fixed << std::setprecision(precision) << value;
|
ss << std::fixed << std::setprecision(precision) << value;
|
||||||
|
|
@ -168,7 +169,7 @@ std::string StringHelper::doubleToString(double value, int precision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char* StringHelper::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
|
const char* strx::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
return (const char*)strstr(buffer, substring);
|
return (const char*)strstr(buffer, substring);
|
||||||
|
|
@ -178,7 +179,7 @@ const char* StringHelper::findSubstring(const char* buffer, const char* substrin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StringHelper::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
|
void strx::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
|
||||||
{
|
{
|
||||||
dst.clear();
|
dst.clear();
|
||||||
std::string::size_type p = 0;
|
std::string::size_type p = 0;
|
||||||
|
|
@ -202,21 +203,21 @@ void StringHelper::split(const std::string& src, std::vector<std::string>& dst,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> StringHelper::split(const std::string& src, const std::string& delims)
|
std::vector<std::string> strx::split(const std::string& src, const std::string& delims)
|
||||||
{
|
{
|
||||||
std::vector<std::string> r;
|
std::vector<std::string> r;
|
||||||
split(src, r, delims);
|
split(src, r, delims);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::string, int> StringHelper::parseHost(const std::string& host, int defaultPort)
|
std::pair<std::string, int> strx::parseHost(const std::string& host, int defaultPort)
|
||||||
{
|
{
|
||||||
std::pair<std::string, int> result;
|
std::pair<std::string, int> result;
|
||||||
std::size_t p = host.find(':');
|
std::size_t p = host.find(':');
|
||||||
if (p != std::string::npos)
|
if (p != std::string::npos)
|
||||||
{
|
{
|
||||||
result.first = host.substr(0, p);
|
result.first = host.substr(0, p);
|
||||||
result.second = StringHelper::toInt(host.c_str() + p + 1, defaultPort);
|
result.second = strx::toInt(host.c_str() + p + 1, defaultPort);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -226,15 +227,15 @@ std::pair<std::string, int> StringHelper::parseHost(const std::string& host, int
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::string, std::string> StringHelper::parseAssignment(const std::string& s, bool trimQuotes)
|
std::pair<std::string, std::string> strx::parseAssignment(const std::string& s, bool trimQuotes)
|
||||||
{
|
{
|
||||||
std::pair<std::string, std::string> result;
|
std::pair<std::string, std::string> result;
|
||||||
|
|
||||||
std::string::size_type p = s.find('=');
|
std::string::size_type p = s.find('=');
|
||||||
if (p != std::string::npos)
|
if (p != std::string::npos)
|
||||||
{
|
{
|
||||||
result.first = StringHelper::trim(s.substr(0, p));
|
result.first = strx::trim(s.substr(0, p));
|
||||||
result.second = StringHelper::trim(s.substr(p+1));
|
result.second = strx::trim(s.substr(p+1));
|
||||||
if (trimQuotes && result.second.size() >= 2)
|
if (trimQuotes && result.second.size() >= 2)
|
||||||
{
|
{
|
||||||
if ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') ||
|
if ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') ||
|
||||||
|
|
@ -243,19 +244,19 @@ std::pair<std::string, std::string> StringHelper::parseAssignment(const std::str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
result.first = StringHelper::trim(s);
|
result.first = strx::trim(s);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::intToString(int value)
|
std::string strx::intToString(int value)
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "%d", value);
|
sprintf(buffer, "%d", value);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
float StringHelper::toFloat(const std::string &s, float v, bool* isOk)
|
float strx::toFloat(const std::string &s, float v, bool* isOk)
|
||||||
{
|
{
|
||||||
float result = 0.0;
|
float result = 0.0;
|
||||||
int code = sscanf(s.c_str(), "%f", &result);
|
int code = sscanf(s.c_str(), "%f", &result);
|
||||||
|
|
@ -274,14 +275,14 @@ float StringHelper::toFloat(const std::string &s, float v, bool* isOk)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::trim(const std::string &s)
|
std::string strx::trim(const std::string &s)
|
||||||
{
|
{
|
||||||
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; });
|
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; });
|
||||||
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }).base();
|
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }).base();
|
||||||
return (wsback <= wsfront ? std::string() : std::string(wsfront,wsback));
|
return (wsback <= wsfront ? std::string() : std::string(wsfront,wsback));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::timeToString(time_t t)
|
std::string strx::timeToString(time_t t)
|
||||||
{
|
{
|
||||||
char buffer[128] = "";
|
char buffer[128] = "";
|
||||||
struct tm lt;
|
struct tm lt;
|
||||||
|
|
@ -295,12 +296,12 @@ std::string StringHelper::timeToString(time_t t)
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::millisecondsToString(uint64_t t)
|
std::string strx::millisecondsToString(uint64_t t)
|
||||||
{
|
{
|
||||||
return timeToString(t/1000);
|
return timeToString(t/1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
int StringHelper::fromHex2Int(const std::string &s)
|
int strx::fromHex2Int(const std::string &s)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
sscanf(s.c_str(), "%x", &result);
|
sscanf(s.c_str(), "%x", &result);
|
||||||
|
|
@ -318,12 +319,12 @@ static int hex2code(char s)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hex2code(const char* s)
|
/*static int hex2code(const char* s)
|
||||||
{
|
{
|
||||||
return (hex2code(s[0]) << 4) + hex2code(s[1]);
|
return (hex2code(s[0]) << 4) + hex2code(s[1]);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
std::string StringHelper::fromHex2String(const std::string& s)
|
std::string strx::fromHex2String(const std::string& s)
|
||||||
{
|
{
|
||||||
std::string result; result.resize(s.size() / 2);
|
std::string result; result.resize(s.size() / 2);
|
||||||
const char* t = s.c_str();
|
const char* t = s.c_str();
|
||||||
|
|
@ -333,7 +334,7 @@ std::string StringHelper::fromHex2String(const std::string& s)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::replace(const std::string& s, char f, char r)
|
std::string strx::replace(const std::string& s, char f, char r)
|
||||||
{
|
{
|
||||||
std::string result(s);
|
std::string result(s);
|
||||||
for (std::string::size_type i = 0; i < result.size(); i++)
|
for (std::string::size_type i = 0; i < result.size(); i++)
|
||||||
|
|
@ -343,7 +344,7 @@ std::string StringHelper::replace(const std::string& s, char f, char r)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::replace(const std::string& s, const std::string& tmpl, const std::string& n)
|
std::string strx::replace(const std::string& s, const std::string& tmpl, const std::string& n)
|
||||||
{
|
{
|
||||||
std::string result(s);
|
std::string result(s);
|
||||||
std::string::size_type p = 0;
|
std::string::size_type p = 0;
|
||||||
|
|
@ -356,7 +357,7 @@ std::string StringHelper::replace(const std::string& s, const std::string& tmpl,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringHelper::decodeUri(const std::string& s)
|
std::string strx::decodeUri(const std::string& s)
|
||||||
{
|
{
|
||||||
std::string ret;
|
std::string ret;
|
||||||
ret.reserve(s.size());
|
ret.reserve(s.size());
|
||||||
|
|
@ -379,19 +380,19 @@ std::string StringHelper::decodeUri(const std::string& s)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StringHelper::startsWith(const std::string& s, const std::string& prefix)
|
bool strx::startsWith(const std::string& s, const std::string& prefix)
|
||||||
{
|
{
|
||||||
std::string::size_type p = s.find(prefix);
|
std::string::size_type p = s.find(prefix);
|
||||||
return p == 0;
|
return p == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StringHelper::endsWith(const std::string& s, const std::string& suffix)
|
bool strx::endsWith(const std::string& s, const std::string& suffix)
|
||||||
{
|
{
|
||||||
std::string::size_type p = s.rfind(suffix);
|
std::string::size_type p = s.rfind(suffix);
|
||||||
return (p == s.size() - suffix.size());
|
return (p == s.size() - suffix.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
int StringHelper::stringToDuration(const std::string& s)
|
int strx::stringToDuration(const std::string& s)
|
||||||
{
|
{
|
||||||
if (endsWith(s, "ms"))
|
if (endsWith(s, "ms"))
|
||||||
return std::stoi(s.substr(0, s.size()-2));
|
return std::stoi(s.substr(0, s.size()-2));
|
||||||
|
|
@ -405,92 +406,52 @@ int StringHelper::stringToDuration(const std::string& s)
|
||||||
return std::stoi(s) * 1000;
|
return std::stoi(s) * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
|
std::string strx::uppercase(const std::string& s)
|
||||||
// --------------------- XcapHelper -----------------
|
|
||||||
std::string XcapHelper::buildBuddyList(std::string listName, std::vector<std::string> buddies)
|
|
||||||
{
|
{
|
||||||
std::ostringstream result;
|
std::string r(s);
|
||||||
result << XML_HEADER <<
|
std::transform(r.begin(), r.end(), r.begin(), ::toupper);
|
||||||
"<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">" <<
|
return r;
|
||||||
"<list name=\"" << listName.c_str() << "\">";
|
|
||||||
|
|
||||||
// to test CT only!
|
|
||||||
//result << "<entry uri=\"" << "sip:dbogovych1@10.11.1.25" << "\"/>";
|
|
||||||
//result << "<entry uri=\"" << "sip:dbogovych2@10.11.1.25" << "\"/>";
|
|
||||||
|
|
||||||
for (unsigned i = 0; i<buddies.size(); i++)
|
|
||||||
{
|
|
||||||
result << "<entry uri=\"" << normalizeSipUri(buddies[i]).c_str() << "\"/>";
|
|
||||||
}
|
|
||||||
result << "</list></resource-lists>";
|
|
||||||
return result.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string XcapHelper::buildRules(std::vector<std::string> buddies)
|
std::string strx::lowercase(const std::string& s)
|
||||||
{
|
{
|
||||||
std::ostringstream result;
|
std::string r(s);
|
||||||
result << XML_HEADER <<
|
std::transform(r.begin(), r.end(), r.begin(), ::tolower);
|
||||||
"<ruleset xmlns=\"urn:ietf:params:xml:ns:common-policy\">" <<
|
return r;
|
||||||
"<rule id=\"presence_allow\">" <<
|
|
||||||
"<conditions>";
|
|
||||||
|
|
||||||
for (unsigned i = 0; i<buddies.size(); i++)
|
|
||||||
{
|
|
||||||
result << "<identity><one id=\"" <<
|
|
||||||
normalizeSipUri(buddies[i]).c_str() << "\"/></identity>";
|
|
||||||
}
|
|
||||||
result << "</conditions>" <<
|
|
||||||
"<actions>" <<
|
|
||||||
"<sub-handling xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
|
||||||
"allow" <<
|
|
||||||
"</sub-handling>" <<
|
|
||||||
"</actions>" <<
|
|
||||||
"<transformations>" <<
|
|
||||||
"<provide-devices xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
|
||||||
"<all-devices/>" <<
|
|
||||||
"</provide-devices>" <<
|
|
||||||
"<provide-persons xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
|
||||||
"<all-persons/>" <<
|
|
||||||
"</provide-persons>" <<
|
|
||||||
"<provide-services xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
|
||||||
"<all-services/>" <<
|
|
||||||
"</provide-services>" <<
|
|
||||||
"</transformations>" <<
|
|
||||||
"</rule>" <<
|
|
||||||
"</ruleset>";
|
|
||||||
return result.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string XcapHelper::buildServices(std::string serviceUri, std::string listRef)
|
std::string strx::removeQuotes(const std::string& s)
|
||||||
{
|
{
|
||||||
std::ostringstream result;
|
std::string r(s);
|
||||||
|
if (s.empty())
|
||||||
|
return s;
|
||||||
|
|
||||||
result << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl <<
|
if (r.front() == '"')
|
||||||
"<rls-services xmlns=\"urn:ietf:params:xml:ns:rls-services\"" << std::endl <<
|
r = r.substr(1);
|
||||||
"xmlns:rl=\"urn:ietf:params:xml:ns:resource-lists\"" << std::endl <<
|
|
||||||
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << std::endl <<
|
|
||||||
"<service uri=\"" << normalizeSipUri(serviceUri).c_str() << "\">" << std::endl <<
|
|
||||||
"<resource-list>" << listRef.c_str() << "</resource-list>" << std::endl <<
|
|
||||||
"<packages>" << std::endl <<
|
|
||||||
"<package>presence</package>" << std::endl <<
|
|
||||||
"</packages>" << std::endl <<
|
|
||||||
"</service>" << std::endl <<
|
|
||||||
"</rls-services>";
|
|
||||||
|
|
||||||
return result.str();
|
if (r.back() == '"')
|
||||||
|
r = r.substr(0, r.size()-1);
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string XcapHelper::normalizeSipUri(std::string uri)
|
#if defined(TARGET_WIN)
|
||||||
|
|
||||||
|
// MSVC++ lacks memmem support
|
||||||
|
const void *memmem(const void *haystack, size_t haystack_len,
|
||||||
|
const void * const needle, const size_t needle_len)
|
||||||
{
|
{
|
||||||
if (uri.length())
|
if (!haystack || !haystack_len || !needle || !needle_len)
|
||||||
{
|
return nullptr;
|
||||||
if (uri[0] == '<')
|
|
||||||
uri.erase(0,1);
|
for (const char *h = (const char*)haystack;
|
||||||
if (uri.length())
|
haystack_len >= needle_len;
|
||||||
{
|
++h, --haystack_len) {
|
||||||
if (uri[uri.length()-1] == '>')
|
if (!memcmp(h, needle, needle_len)) {
|
||||||
uri.erase(uri.length()-1, 1);
|
return h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uri;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <cstdint>
|
||||||
#include "HL_Types.h"
|
#include "HL_Types.h"
|
||||||
|
|
||||||
#ifdef TARGET_OSX
|
#ifdef TARGET_OSX
|
||||||
|
|
@ -16,7 +17,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
class StringHelper
|
class strx
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::string extractFilename(const std::string& path);
|
static std::string extractFilename(const std::string& path);
|
||||||
|
|
@ -25,6 +26,7 @@ public:
|
||||||
static std::string makeUtf8(const std::tstring& arg);
|
static std::string makeUtf8(const std::tstring& arg);
|
||||||
static std::string toUtf8(const std::tstring& arg);
|
static std::string toUtf8(const std::tstring& arg);
|
||||||
static std::tstring makeTstring(const std::string& arg);
|
static std::tstring makeTstring(const std::string& arg);
|
||||||
|
|
||||||
static int toInt(const char* s, int defaultValue, bool* isOk = nullptr);
|
static int toInt(const char* s, int defaultValue, bool* isOk = nullptr);
|
||||||
static uint64_t toUint64(const char* s, uint64_t def, bool *isOk = nullptr);
|
static uint64_t toUint64(const char* s, uint64_t def, bool *isOk = nullptr);
|
||||||
static std::string toHex(unsigned int value);
|
static std::string toHex(unsigned int value);
|
||||||
|
|
@ -33,8 +35,13 @@ public:
|
||||||
static std::string intToString(int value);
|
static std::string intToString(int value);
|
||||||
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
||||||
static std::string doubleToString(double value, int precision);
|
static std::string doubleToString(double value, int precision);
|
||||||
|
static int fromHex2Int(const std::string& s);
|
||||||
|
static std::string fromHex2String(const std::string& s);
|
||||||
|
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
|
||||||
|
|
||||||
|
|
||||||
static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength);
|
static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength);
|
||||||
|
|
||||||
static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
|
static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
|
||||||
static std::vector<std::string> split(const std::string& src, const std::string& delims = "\n");
|
static std::vector<std::string> split(const std::string& src, const std::string& delims = "\n");
|
||||||
|
|
||||||
|
|
@ -44,7 +51,7 @@ public:
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
for (const auto& i : v)
|
for (const auto& i : v)
|
||||||
{
|
{
|
||||||
if (&i != &v[0])
|
if (&i != &v.front())
|
||||||
s << delimiter;
|
s << delimiter;
|
||||||
s << i;
|
s << i;
|
||||||
}
|
}
|
||||||
|
|
@ -53,18 +60,19 @@ public:
|
||||||
|
|
||||||
static std::pair<std::string, int> parseHost(const std::string& host, int defaultPort);
|
static std::pair<std::string, int> parseHost(const std::string& host, int defaultPort);
|
||||||
static std::pair<std::string, std::string> parseAssignment(const std::string& s, bool trimQuotes = true);
|
static std::pair<std::string, std::string> parseAssignment(const std::string& s, bool trimQuotes = true);
|
||||||
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
|
|
||||||
static std::string trim(const std::string& s);
|
static std::string trim(const std::string& s);
|
||||||
static std::string timeToString(time_t t);
|
static std::string timeToString(time_t t);
|
||||||
static std::string millisecondsToString(uint64_t t);
|
static std::string millisecondsToString(uint64_t t);
|
||||||
static int fromHex2Int(const std::string& s);
|
|
||||||
static std::string fromHex2String(const std::string& s);
|
|
||||||
static std::string replace(const std::string& s, char f, char r);
|
static std::string replace(const std::string& s, char f, char r);
|
||||||
static std::string replace(const std::string& s, const std::string& tmpl, const std::string& n);
|
static std::string replace(const std::string& s, const std::string& tmpl, const std::string& n);
|
||||||
static std::string decodeUri(const std::string& s);
|
static std::string decodeUri(const std::string& s);
|
||||||
static bool startsWith(const std::string& s, const std::string& prefix);
|
static bool startsWith(const std::string& s, const std::string& prefix);
|
||||||
static bool endsWith(const std::string& s, const std::string& suffix);
|
static bool endsWith(const std::string& s, const std::string& suffix);
|
||||||
static int stringToDuration(const std::string& s);
|
static int stringToDuration(const std::string& s);
|
||||||
|
|
||||||
|
static std::string uppercase(const std::string& s);
|
||||||
|
static std::string lowercase(const std::string& s);
|
||||||
|
static std::string removeQuotes(const std::string& s);
|
||||||
};
|
};
|
||||||
|
|
||||||
class XcapHelper
|
class XcapHelper
|
||||||
|
|
@ -77,4 +85,11 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(TARGET_WIN)
|
||||||
|
|
||||||
|
// MSVC++ lacks memmem support
|
||||||
|
extern const void *memmem(const void *haystack, size_t haystack_len,
|
||||||
|
const void * const needle, const size_t needle_len);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -77,19 +77,21 @@ uint64_t ThreadHelper::getCurrentId()
|
||||||
// ------------------- TimeHelper ---------------
|
// ------------------- TimeHelper ---------------
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// Milliseconds starting from the epoch
|
||||||
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
// Seconds starting from the epoch
|
||||||
static time_t TimestampBase = time(nullptr);
|
static time_t TimestampBase = time(nullptr);
|
||||||
|
|
||||||
uint64_t TimeHelper::getTimestamp()
|
// Returns number of milliseconds starting from 01 Jan 1970 GMT
|
||||||
|
uint64_t chronox::getTimestamp()
|
||||||
{
|
{
|
||||||
time_point<steady_clock> t = steady_clock::now();
|
time_point<steady_clock> t = steady_clock::now();
|
||||||
|
|
||||||
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
||||||
|
|
||||||
return ms - TimestampStartPoint + TimestampBase * 1000;
|
return ms - TimestampStartPoint + TimestampBase * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t TimeHelper::getUptime()
|
uint64_t chronox::getUptime()
|
||||||
{
|
{
|
||||||
time_point<steady_clock> t = steady_clock::now();
|
time_point<steady_clock> t = steady_clock::now();
|
||||||
|
|
||||||
|
|
@ -98,7 +100,7 @@ uint64_t TimeHelper::getUptime()
|
||||||
return ms - TimestampStartPoint;
|
return ms - TimestampStartPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier)
|
uint32_t chronox::getDelta(uint32_t later, uint32_t earlier)
|
||||||
{
|
{
|
||||||
if (later > earlier)
|
if (later > earlier)
|
||||||
return later - earlier;
|
return later - earlier;
|
||||||
|
|
@ -109,14 +111,42 @@ uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeHelper::ExecutionTime::ExecutionTime()
|
timespec chronox::toTimespec(uint64_t milliseconds)
|
||||||
{
|
{
|
||||||
mStart = TimeHelper::getTimestamp();
|
timespec r;
|
||||||
|
r.tv_sec = milliseconds / 1000;
|
||||||
|
r.tv_nsec = milliseconds % 1000;
|
||||||
|
r.tv_nsec *= 1000 * 1000;
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t TimeHelper::ExecutionTime::getSpentTime() const
|
uint64_t chronox::toTimestamp(const timeval& ts)
|
||||||
{
|
{
|
||||||
return TimeHelper::getTimestamp() - mStart;
|
return ts.tv_sec * 1000 + ts.tv_usec / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t chronox::getDelta(const timespec& a, const timespec& b)
|
||||||
|
{
|
||||||
|
uint64_t ms_a = a.tv_sec * 1000 + a.tv_nsec / 10000000;
|
||||||
|
uint64_t ms_b = b.tv_sec * 1000 + a.tv_nsec / 10000000;
|
||||||
|
return ms_a - ms_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t chronox::getDelta(const timeval& a, const timeval& b)
|
||||||
|
{
|
||||||
|
int64_t diff_seconds = a.tv_sec - b.tv_sec;
|
||||||
|
int64_t diff_microseconds = a.tv_usec - b.tv_usec;
|
||||||
|
return diff_seconds * 1000 + diff_microseconds / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
chronox::ExecutionTime::ExecutionTime()
|
||||||
|
{
|
||||||
|
mStart = chronox::getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t chronox::ExecutionTime::getSpentTime() const
|
||||||
|
{
|
||||||
|
return chronox::getTimestamp() - mStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------- BufferQueue -----------------
|
// --------------- BufferQueue -----------------
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,11 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if defined(TARGET_WIN)
|
||||||
|
# include <WinSock2.h>
|
||||||
|
# include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef std::recursive_mutex Mutex;
|
typedef std::recursive_mutex Mutex;
|
||||||
typedef std::unique_lock<std::recursive_mutex> Lock;
|
typedef std::unique_lock<std::recursive_mutex> Lock;
|
||||||
|
|
||||||
|
|
@ -46,7 +51,7 @@ public:
|
||||||
static uint64_t getCurrentId();
|
static uint64_t getCurrentId();
|
||||||
};
|
};
|
||||||
|
|
||||||
class TimeHelper
|
class chronox
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Returns current timestamp in milliseconds
|
// Returns current timestamp in milliseconds
|
||||||
|
|
@ -59,6 +64,14 @@ public:
|
||||||
// Handles cases when clock is wrapped.
|
// Handles cases when clock is wrapped.
|
||||||
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
||||||
|
|
||||||
|
// Converts number of milliseconds starting from Epoch begin to timespec.
|
||||||
|
static timespec toTimespec(uint64_t milliseconds);
|
||||||
|
static uint64_t toTimestamp(const timeval& ts);
|
||||||
|
|
||||||
|
// Returns difference between timestamps in milliseconds
|
||||||
|
static int64_t getDelta(const timespec& a, const timespec& b);
|
||||||
|
static int64_t getDelta(const timeval& a, const timeval& b);
|
||||||
|
|
||||||
class ExecutionTime
|
class ExecutionTime
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ thread_pool::thread_pool(size_t num_of_threads, const std::string& name)
|
||||||
num_of_threads = std::thread::hardware_concurrency();
|
num_of_threads = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
for(size_t idx = 0; idx < num_of_threads; idx++)
|
for(size_t idx = 0; idx < num_of_threads; idx++)
|
||||||
this->workers.push_back(std::thread(&thread_pool::run_worker, this));
|
this->workers.emplace_back(std::thread(&thread_pool::run_worker, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new work item to the pool
|
// Add new work item to the pool
|
||||||
|
|
@ -20,6 +20,22 @@ void thread_pool::enqueue(const thread_pool::task& t)
|
||||||
this->condition.notify_one();
|
this->condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thread_pool::wait(std::chrono::milliseconds interval)
|
||||||
|
{
|
||||||
|
while (size() != 0)
|
||||||
|
std::this_thread::sleep_for(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t thread_pool::size()
|
||||||
|
{
|
||||||
|
std::unique_lock l(this->queue_mutex);
|
||||||
|
return this->tasks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t thread_pool::threads()
|
||||||
|
{
|
||||||
|
return this->workers.size();
|
||||||
|
}
|
||||||
|
|
||||||
// the destructor joins all threads
|
// the destructor joins all threads
|
||||||
thread_pool::~thread_pool()
|
thread_pool::~thread_pool()
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ class thread_pool
|
||||||
public:
|
public:
|
||||||
typedef std::function<void()> task;
|
typedef std::function<void()> task;
|
||||||
|
|
||||||
thread_pool(size_t num_of_threads, const std::string&);
|
thread_pool(size_t num_of_threads, const std::string& thread_name);
|
||||||
~thread_pool();
|
~thread_pool();
|
||||||
|
|
||||||
void enqueue(const task& task);
|
void enqueue(const task& task);
|
||||||
|
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
|
||||||
|
size_t size();
|
||||||
|
size_t threads();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// need to keep track of threads so we can join them
|
// need to keep track of threads so we can join them
|
||||||
|
|
@ -33,7 +36,7 @@ private:
|
||||||
// synchronization
|
// synchronization
|
||||||
std::mutex queue_mutex;
|
std::mutex queue_mutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
bool stop = false;
|
std::atomic_bool stop = false;
|
||||||
|
|
||||||
// thread name prefix for worker threads
|
// thread name prefix for worker threads
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "HL_Time.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <chrono>
|
||||||
|
/* return current time in milliseconds */
|
||||||
|
double now_ms(void)
|
||||||
|
{
|
||||||
|
#if defined(TARGET_WIN)
|
||||||
|
auto tp = std::chrono::steady_clock::now();
|
||||||
|
auto result = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count();
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
struct timespec res;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &res);
|
||||||
|
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare_timespec(const timespec& lhs, const timespec& rhs)
|
||||||
|
{
|
||||||
|
if (lhs.tv_sec < rhs.tv_sec)
|
||||||
|
return -1;
|
||||||
|
if (lhs.tv_sec > rhs.tv_sec)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (lhs.tv_nsec < rhs.tv_nsec)
|
||||||
|
return -1;
|
||||||
|
if (lhs.tv_nsec > rhs.tv_nsec)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator < (const timespec& lhs, const timespec& rhs)
|
||||||
|
{
|
||||||
|
return compare_timespec(lhs, rhs) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const timespec& lhs, const timespec& rhs)
|
||||||
|
{
|
||||||
|
return compare_timespec(lhs, rhs) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator > (const timespec& lhs, const timespec& rhs)
|
||||||
|
{
|
||||||
|
return compare_timespec(lhs, rhs) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_zero(const timespec& ts)
|
||||||
|
{
|
||||||
|
return !ts.tv_sec && !ts.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef __HELPER_TIME_H
|
||||||
|
#define __HELPER_TIME_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
// Return current monotonic number of milliseconds starting from some point
|
||||||
|
extern double now_ms();
|
||||||
|
|
||||||
|
// Compare the timespec.
|
||||||
|
// Returns -1 if lhs < rhs, 1 if lhs > rhs, 0 if equal
|
||||||
|
extern int compare_timespec(const timespec& lhs, const timespec& rhs);
|
||||||
|
extern bool operator < (const timespec& lhs, const timespec& rhs);
|
||||||
|
extern bool operator == (const timespec& lhs, const timespec& rhs);
|
||||||
|
extern bool operator > (const timespec& lhs, const timespec& rhs);
|
||||||
|
extern bool is_zero(const timespec& ts);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -26,4 +26,139 @@ enum SdpDirection
|
||||||
Sdp_Offer
|
Sdp_Offer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
template<
|
||||||
|
class K, class V,
|
||||||
|
class HashK = std::hash<K>, class EqK = std::equal_to<K>,
|
||||||
|
class HashV = std::hash<V>, class EqV = std::equal_to<V>
|
||||||
|
>
|
||||||
|
class BiMap {
|
||||||
|
public:
|
||||||
|
using key_type = K;
|
||||||
|
using mapped_type = V;
|
||||||
|
|
||||||
|
BiMap(const std::map<K,V>& initializers) {
|
||||||
|
for (const auto& item: initializers) {
|
||||||
|
insert(item.first, item.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert a new (key, value) pair. Returns false if either key or value already exists.
|
||||||
|
bool insert(const K& k, const V& v) {
|
||||||
|
if (contains_key(k) || contains_value(v)) return false;
|
||||||
|
auto ok = forward_.emplace(k, v);
|
||||||
|
try {
|
||||||
|
auto ov = reverse_.emplace(v, k);
|
||||||
|
if (!ov.second) { // shouldn't happen given the guard above
|
||||||
|
forward_.erase(k);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
forward_.erase(k);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return ok.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool insert(K&& k, V&& v) {
|
||||||
|
if (contains_key(k) || contains_value(v)) return false;
|
||||||
|
auto ok = forward_.emplace(std::move(k), std::move(v));
|
||||||
|
try {
|
||||||
|
auto ov = reverse_.emplace(ok.first->second, ok.first->first); // use stored refs
|
||||||
|
if (!ov.second) {
|
||||||
|
forward_.erase(ok.first);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
forward_.erase(ok.first);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return ok.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace value for existing key (and update reverse map). Returns false if value is already bound elsewhere.
|
||||||
|
bool replace_by_key(const K& k, const V& new_v) {
|
||||||
|
auto it = forward_.find(k);
|
||||||
|
if (it == forward_.end()) return false;
|
||||||
|
if (contains_value(new_v)) return false;
|
||||||
|
// remove old reverse, insert new reverse, then update forward
|
||||||
|
reverse_.erase(it->second);
|
||||||
|
reverse_.emplace(new_v, k);
|
||||||
|
it->second = new_v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace key for existing value (and update forward map). Returns false if key is already bound elsewhere.
|
||||||
|
bool replace_by_value(const V& v, const K& new_k) {
|
||||||
|
auto it = reverse_.find(v);
|
||||||
|
if (it == reverse_.end()) return false;
|
||||||
|
if (contains_key(new_k)) return false;
|
||||||
|
forward_.erase(it->second);
|
||||||
|
forward_.emplace(new_k, v);
|
||||||
|
it->second = new_k;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase by key/value. Return number erased (0 or 1).
|
||||||
|
size_t erase_key(const K& k) {
|
||||||
|
auto it = forward_.find(k);
|
||||||
|
if (it == forward_.end()) return 0;
|
||||||
|
reverse_.erase(it->second);
|
||||||
|
forward_.erase(it);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t erase_value(const V& v) {
|
||||||
|
auto it = reverse_.find(v);
|
||||||
|
if (it == reverse_.end()) return 0;
|
||||||
|
forward_.erase(it->second);
|
||||||
|
reverse_.erase(it);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup
|
||||||
|
bool contains_key(const K& k) const { return forward_.find(k) != forward_.end(); }
|
||||||
|
bool contains_value(const V& v) const { return reverse_.find(v) != reverse_.end(); }
|
||||||
|
|
||||||
|
const V* find_by_key(const K& k) const {
|
||||||
|
auto it = forward_.find(k);
|
||||||
|
return (it == forward_.end()) ? nullptr : &it->second;
|
||||||
|
}
|
||||||
|
const K* find_by_value(const V& v) const {
|
||||||
|
auto it = reverse_.find(v);
|
||||||
|
return (it == reverse_.end()) ? nullptr : &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// at() variants throw std::out_of_range on missing entries
|
||||||
|
const V& at_key(const K& k) const { return forward_.at(k); }
|
||||||
|
const K& at_value(const V& v) const { return reverse_.at(v); }
|
||||||
|
|
||||||
|
void clear() noexcept {
|
||||||
|
forward_.clear();
|
||||||
|
reverse_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept { return forward_.empty(); }
|
||||||
|
size_t size() const noexcept { return forward_.size(); }
|
||||||
|
|
||||||
|
// Reserve buckets for performance (optional)
|
||||||
|
void reserve(size_t n) {
|
||||||
|
forward_.reserve(n);
|
||||||
|
reverse_.reserve(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<K, V, HashK, EqK> forward_;
|
||||||
|
std::unordered_map<V, K, HashV, EqV> reverse_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
typedef std::chrono::steady_clock::time_point timepoint_t;
|
||||||
|
typedef std::chrono::steady_clock monoclock_t;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,57 @@
|
||||||
#include "HL_Uuid.h"
|
#include "HL_Uuid.h"
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
#include <random>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#if defined(TARGET_LINUX)
|
||||||
|
#define UUID_SYSTEM_GENERATOR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "uuid.h"
|
||||||
|
|
||||||
Uuid::Uuid()
|
Uuid::Uuid()
|
||||||
{
|
{
|
||||||
#if defined(TARGET_WIN) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
|
||||||
memset(&mUuid, 0, sizeof mUuid);
|
memset(&mUuid, 0, sizeof mUuid);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Uuid Uuid::generateOne()
|
Uuid Uuid::generateOne()
|
||||||
{
|
{
|
||||||
Uuid result;
|
Uuid result;
|
||||||
#if !defined(USE_NULL_UUID)
|
// #if defined(TARGET_LINUX)
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
// auto id = uuids::uuid_system_generator{}();
|
||||||
uuid_generate(result.mUuid);
|
// #else
|
||||||
#endif
|
std::random_device rd;
|
||||||
#if defined(TARGET_WIN)
|
auto seed_data = std::array<int, std::mt19937::state_size> {};
|
||||||
UuidCreate(&result.mUuid);
|
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
|
||||||
#endif
|
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
|
||||||
#endif
|
std::mt19937 generator(seq);
|
||||||
|
uuids::uuid_random_generator gen{generator};
|
||||||
|
|
||||||
|
auto id = gen();
|
||||||
|
// #endif
|
||||||
|
memcpy(result.mUuid, id.as_bytes().data(), id.as_bytes().size_bytes());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uuid Uuid::parse(const std::string &s)
|
Uuid Uuid::parse(const std::string &s)
|
||||||
{
|
{
|
||||||
Uuid result;
|
Uuid result;
|
||||||
#if !defined(USE_NULL_UUID)
|
auto id = uuids::uuid::from_string(s);
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
if (id)
|
||||||
uuid_parse(s.c_str(), result.mUuid);
|
memcpy(result.mUuid, id->as_bytes().data(), id->as_bytes().size_bytes());
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TARGET_WIN)
|
|
||||||
UuidFromStringA((RPC_CSTR)s.c_str(), &result.mUuid);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Uuid::toString() const
|
std::string Uuid::toString() const
|
||||||
{
|
{
|
||||||
#if defined(USE_NULL_UUID)
|
auto s = std::span<uuids::uuid::value_type, 16>{(uuids::uuid::value_type*)mUuid, 16};
|
||||||
return "UUID_disabled";
|
uuids::uuid id(s);
|
||||||
#else
|
return uuids::to_string(id);
|
||||||
char buf[64];
|
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
|
||||||
uuid_unparse_lower(mUuid, buf);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TARGET_WIN)
|
|
||||||
RPC_CSTR s = nullptr;
|
|
||||||
UuidToStringA(&mUuid, &s);
|
|
||||||
if (s)
|
|
||||||
{
|
|
||||||
strcpy(buf, (const char*)s);
|
|
||||||
RpcStringFreeA(&s);
|
|
||||||
s = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Uuid::operator < (const Uuid& right) const
|
bool Uuid::operator < (const Uuid& right) const
|
||||||
{
|
{
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
|
||||||
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
|
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TARGET_WIN)
|
|
||||||
return memcmp(&mUuid, &right.mUuid, sizeof(mUuid)) < 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,7 @@
|
||||||
#define __HL_UUID_H
|
#define __HL_UUID_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <stdint.h>
|
||||||
#if (defined(TARGET_LINUX) || defined(TARGET_OSX)) && !defined(USE_NULL_UUID)
|
|
||||||
// Please do not forget "sudo apt install uuid-dev" on Ubuntu
|
|
||||||
# include <uuid/uuid.h>
|
|
||||||
#endif
|
|
||||||
#if defined(TARGET_WIN)
|
|
||||||
# include <rpc.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class Uuid
|
class Uuid
|
||||||
{
|
{
|
||||||
|
|
@ -21,20 +14,7 @@ public:
|
||||||
bool operator < (const Uuid& right) const;
|
bool operator < (const Uuid& right) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#if defined(USE_NULL_UUID)
|
uint8_t mUuid[16];
|
||||||
unsigned char mUuid[16];
|
|
||||||
#else
|
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
|
||||||
uuid_t mUuid;
|
|
||||||
#endif
|
|
||||||
#if defined(TARGET_WIN)
|
|
||||||
UUID mUuid;
|
|
||||||
#endif
|
|
||||||
#if defined(TARGET_ANDROID)
|
|
||||||
// Stub only
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "HL_Xcap.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#ifdef TARGET_WIN
|
||||||
|
# include <WinSock2.h>
|
||||||
|
# include <Windows.h>
|
||||||
|
# include <cctype>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
|
||||||
|
// --------------------- XcapHelper -----------------
|
||||||
|
std::string XcapHelper::buildBuddyList(const std::string& listName, const std::vector<std::string>& buddies)
|
||||||
|
{
|
||||||
|
std::ostringstream result;
|
||||||
|
result << XML_HEADER <<
|
||||||
|
"<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">" <<
|
||||||
|
"<list name=\"" << listName.c_str() << "\">";
|
||||||
|
|
||||||
|
// to test CT only!
|
||||||
|
//result << "<entry uri=\"" << "sip:dbogovych1@10.11.1.25" << "\"/>";
|
||||||
|
//result << "<entry uri=\"" << "sip:dbogovych2@10.11.1.25" << "\"/>";
|
||||||
|
|
||||||
|
for (unsigned i = 0; i<buddies.size(); i++)
|
||||||
|
{
|
||||||
|
result << "<entry uri=\"" << normalizeSipUri(buddies[i]).c_str() << "\"/>";
|
||||||
|
}
|
||||||
|
result << "</list></resource-lists>";
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XcapHelper::buildRules(const std::vector<std::string>& buddies)
|
||||||
|
{
|
||||||
|
std::ostringstream result;
|
||||||
|
result << XML_HEADER <<
|
||||||
|
"<ruleset xmlns=\"urn:ietf:params:xml:ns:common-policy\">" <<
|
||||||
|
"<rule id=\"presence_allow\">" <<
|
||||||
|
"<conditions>";
|
||||||
|
|
||||||
|
for (unsigned i = 0; i<buddies.size(); i++)
|
||||||
|
{
|
||||||
|
result << "<identity><one id=\"" <<
|
||||||
|
normalizeSipUri(buddies[i]).c_str() << "\"/></identity>";
|
||||||
|
}
|
||||||
|
result << "</conditions>" <<
|
||||||
|
"<actions>" <<
|
||||||
|
"<sub-handling xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||||
|
"allow" <<
|
||||||
|
"</sub-handling>" <<
|
||||||
|
"</actions>" <<
|
||||||
|
"<transformations>" <<
|
||||||
|
"<provide-devices xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||||
|
"<all-devices/>" <<
|
||||||
|
"</provide-devices>" <<
|
||||||
|
"<provide-persons xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||||
|
"<all-persons/>" <<
|
||||||
|
"</provide-persons>" <<
|
||||||
|
"<provide-services xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||||
|
"<all-services/>" <<
|
||||||
|
"</provide-services>" <<
|
||||||
|
"</transformations>" <<
|
||||||
|
"</rule>" <<
|
||||||
|
"</ruleset>";
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XcapHelper::buildServices(const std::string& serviceUri, const std::string& listRef)
|
||||||
|
{
|
||||||
|
std::ostringstream result;
|
||||||
|
|
||||||
|
result << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl <<
|
||||||
|
"<rls-services xmlns=\"urn:ietf:params:xml:ns:rls-services\"" << std::endl <<
|
||||||
|
"xmlns:rl=\"urn:ietf:params:xml:ns:resource-lists\"" << std::endl <<
|
||||||
|
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << std::endl <<
|
||||||
|
"<service uri=\"" << normalizeSipUri(serviceUri).c_str() << "\">" << std::endl <<
|
||||||
|
"<resource-list>" << listRef.c_str() << "</resource-list>" << std::endl <<
|
||||||
|
"<packages>" << std::endl <<
|
||||||
|
"<package>presence</package>" << std::endl <<
|
||||||
|
"</packages>" << std::endl <<
|
||||||
|
"</service>" << std::endl <<
|
||||||
|
"</rls-services>";
|
||||||
|
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XcapHelper::normalizeSipUri(const std::string& uri)
|
||||||
|
{
|
||||||
|
std::string t(uri);
|
||||||
|
|
||||||
|
if (t.length())
|
||||||
|
{
|
||||||
|
if (t[0] == '<')
|
||||||
|
t.erase(0,1);
|
||||||
|
if (t.length())
|
||||||
|
{
|
||||||
|
if (t[t.length()-1] == '>')
|
||||||
|
t.erase(uri.length()-1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* Copyright(C) 2007-2024 VoIPobjects (voipobjects.com)
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef __HELPER_STRING_H
|
||||||
|
#define __HELPER_STRING_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
class XcapHelper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string buildBuddyList(const std::string& listName, const std::vector<std::string>& buddies);
|
||||||
|
static std::string buildRules(const std::vector<std::string>& buddies);
|
||||||
|
static std::string buildServices(const std::string& serviceUri, const std::string& listRef);
|
||||||
|
static std::string normalizeSipUri(const std::string& uri);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
project (media_lib)
|
project (media_lib)
|
||||||
|
|
||||||
# Rely on C++ 11
|
if (NOT DEFINED LIB_PLATFORM)
|
||||||
set (CMAKE_CXX_STANDARD 11)
|
message(FATAL_ERROR media_lib project requires LIB_PLATFORM to be set - it uses libraries from that directory)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Rely on C++ 20
|
||||||
|
set (CMAKE_CXX_STANDARD 20)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
# Produce PIC code always
|
# Produce PIC code always
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
# Set of variables to control references to codecs
|
|
||||||
set (USE_AMR_CODEC OFF CACHE BOOL "Use AMR codec. Requires libraries.")
|
|
||||||
set (USE_EVS_CODEC OFF CACHE BOOL "Use EVS codec." )
|
|
||||||
set (USE_OPUS_CODEC OFF CACHE BOOL "Use Opus codec." )
|
|
||||||
set (USE_PVQA_LIB OFF CACHE BOOL "Build with Sevana PVQA library" )
|
|
||||||
set (USE_AQUA_LIB OFF CACHE BOOL "Build with Sevana AQuA library" )
|
|
||||||
|
|
||||||
set (SOURCES
|
set (SOURCES
|
||||||
MT_Statistics.cpp
|
MT_Statistics.cpp
|
||||||
MT_WebRtc.cpp
|
MT_WebRtc.cpp
|
||||||
|
|
@ -29,6 +26,8 @@ set (SOURCES
|
||||||
MT_AudioReceiver.cpp
|
MT_AudioReceiver.cpp
|
||||||
MT_AudioCodec.cpp
|
MT_AudioCodec.cpp
|
||||||
MT_CngHelper.cpp
|
MT_CngHelper.cpp
|
||||||
|
MT_AmrCodec.cpp
|
||||||
|
MT_EvsCodec.cpp
|
||||||
|
|
||||||
MT_Statistics.h
|
MT_Statistics.h
|
||||||
MT_WebRtc.h
|
MT_WebRtc.h
|
||||||
|
|
@ -44,42 +43,55 @@ set (SOURCES
|
||||||
MT_AudioReceiver.h
|
MT_AudioReceiver.h
|
||||||
MT_AudioCodec.h
|
MT_AudioCodec.h
|
||||||
MT_CngHelper.h
|
MT_CngHelper.h
|
||||||
|
MT_AmrCodec.h
|
||||||
|
MT_EvsCodec.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(media_lib ${SOURCES})
|
||||||
|
set (LIBS_CODEC)
|
||||||
|
|
||||||
if (USE_AMR_CODEC)
|
if (USE_AMR_CODEC)
|
||||||
message("AMR NB and WB codecs will be included.")
|
include (${LIB_PLATFORM}/platform_libs.cmake)
|
||||||
add_definitions(-DUSE_AMR_CODEC)
|
message("Media: AMR NB and WB codecs will be included.")
|
||||||
set(SOURCES ${SOURCES} MT_AmrCodec.cpp MT_AmrCodec.h)
|
target_compile_definitions(media_lib PUBLIC USE_AMR_CODEC)
|
||||||
|
list (APPEND LIBS_CODEC ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_EVS_CODEC)
|
if (USE_EVS_CODEC)
|
||||||
message("EVS codec will be included.")
|
message("Media: EVS codec will be included.")
|
||||||
add_definitions (-DUSE_EVS_CODEC)
|
target_compile_definitions (media_lib PUBLIC USE_EVS_CODEC)
|
||||||
set (SOURCES ${SOURCES} MT_EvsCodec.cpp MT_EvsCodec.h)
|
list (APPEND LIBS_CODEC evs_codec)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_OPUS_CODEC)
|
if (USE_OPUS_CODEC)
|
||||||
message("Opus codec will be included.")
|
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()
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM MATCHES "Linux*" OR CMAKE_SYSTEM MATCHES "Darwin*")
|
||||||
if(CMAKE_SYSTEM MATCHES "Linux*")
|
target_compile_definitions(media_lib PUBLIC HAVE_NETINET_IN_H)
|
||||||
add_definitions(-DHAVE_NETINET_IN_H)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM MATCHES "Darwin*")
|
|
||||||
# OS X Specific flags
|
|
||||||
add_definitions(-DHAVE_NETINET_IN_H)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||||
# Windows Specific flags - MSVC expected
|
# Windows Specific flags - MSVC expected
|
||||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS -DHAVE_WINSOCK2_H
|
target_compile_definitions(media_lib PRIVATE
|
||||||
-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -DUNICODE -D_UNICODE )
|
_CRT_SECURE_NO_WARNINGS
|
||||||
|
HAVE_WINSOCK2_H
|
||||||
|
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
|
||||||
|
UNICODE
|
||||||
|
_UNICODE )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(media_lib ${SOURCES})
|
|
||||||
|
|
||||||
|
# Dependency on ice_stack - Linux build requires it
|
||||||
|
# Codec libraries as well
|
||||||
|
target_link_libraries(media_lib ice_stack ${LIBS_CODEC})
|
||||||
|
|
||||||
|
set_property(TARGET media_lib PROPERTY
|
||||||
|
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
|
||||||
target_include_directories(media_lib
|
target_include_directories(media_lib
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/
|
||||||
|
|
@ -87,9 +99,9 @@ target_include_directories(media_lib
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/opus/include/
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs
|
||||||
|
${LIB_PLATFORM}/opus/include
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(media_lib
|
target_include_directories(media_lib
|
||||||
|
|
@ -99,8 +111,5 @@ target_include_directories(media_lib
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_op
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_op
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_math
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_math
|
||||||
)
|
)
|
||||||
if (USE_RESIP_INTEGRATION)
|
|
||||||
message("USE_RESIP_INTEGRATION is turned on!")
|
|
||||||
target_compile_definitions(media_lib PUBLIC -DUSE_RESIP_INTEGRATION)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,85 +5,30 @@
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_IuUP.h"
|
#include "../helper/HL_IuUP.h"
|
||||||
|
#include "../helper/HL_Exception.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "AmrCodec"
|
#define LOG_SUBSYSTEM "AmrCodec"
|
||||||
using namespace MT;
|
using namespace MT;
|
||||||
|
|
||||||
|
|
||||||
static const uint8_t amr_block_size[16]={ 13, 14, 16, 18, 20, 21, 27, 32,
|
// Constant of AMR-NB frame lengths in bytes.
|
||||||
6 , 0 , 0 , 0 , 0 , 0 , 0 , 1 };
|
const uint8_t amrnb_framelen[9] =
|
||||||
|
{12, 13, 15, 17, 19, 20, 26, 31, 5};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant of AMR-NB frame lengths in bytes.
|
|
||||||
*/
|
|
||||||
const uint8_t amrnb_framelen[16] =
|
|
||||||
{12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant of AMR-NB frame lengths in bits.
|
|
||||||
*/
|
|
||||||
const uint16_t amrnb_framelenbits[9] =
|
const uint16_t amrnb_framelenbits[9] =
|
||||||
{95, 103, 118, 134, 148, 159, 204, 244, 39};
|
{95, 103, 118, 134, 148, 159, 204, 244, 39};
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant of AMR-NB bitrates.
|
|
||||||
*/
|
|
||||||
const uint16_t amrnb_bitrates[8] =
|
|
||||||
{4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200};
|
|
||||||
|
|
||||||
/**
|
// Constant of AMR-WB frame lengths in bytes.
|
||||||
* Constant of AMR-WB frame lengths in bytes.
|
const uint8_t amrwb_framelen[10] =
|
||||||
*/
|
{17, 23, 32, 37, 40, 46, 50, 58, 60, 5 /* SID packet */};
|
||||||
const uint8_t amrwb_framelen[16] =
|
|
||||||
{17, 23, 32, 37, 40, 46, 50, 58, 60, 5, 0, 0, 0, 0, 0, 0};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant of AMR-WB frame lengths in bits.
|
|
||||||
*/
|
|
||||||
const uint16_t amrwb_framelenbits[10] =
|
const uint16_t amrwb_framelenbits[10] =
|
||||||
{132, 177, 253, 285, 317, 365, 397, 461, 477, 40};
|
{132, 177, 253, 285, 317, 365, 397, 461, 477, 40 /* SID packet */};
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant of AMR-WB bitrates.
|
|
||||||
*/
|
|
||||||
const uint16_t amrwb_bitrates[9] =
|
|
||||||
{6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850};
|
|
||||||
|
|
||||||
|
|
||||||
// Helper routines
|
// Helper routines
|
||||||
|
|
||||||
/*static int8_t bitrateToMode(uint16_t bitrate)
|
|
||||||
{
|
|
||||||
int8_t mode = -1;
|
|
||||||
|
|
||||||
switch (bitrate)
|
|
||||||
{
|
|
||||||
// AMR NB
|
|
||||||
case 4750: mode = 0; break;
|
|
||||||
case 5150: mode = 1; break;
|
|
||||||
case 5900: mode = 2; break;
|
|
||||||
case 6700: mode = 3; break;
|
|
||||||
case 7400: mode = 4; break;
|
|
||||||
case 7950: mode = 5; break;
|
|
||||||
case 10200: mode = 6; break;
|
|
||||||
case 12200: mode = 7; break;
|
|
||||||
|
|
||||||
// AMRWB
|
|
||||||
case 6600: mode = 0; break;
|
|
||||||
case 8850: mode = 1; break;
|
|
||||||
case 12650: mode = 2; break;
|
|
||||||
case 14250: mode = 3; break;
|
|
||||||
case 15850: mode = 4; break;
|
|
||||||
case 18250: mode = 5; break;
|
|
||||||
case 19850: mode = 6; break;
|
|
||||||
case 23050: mode = 7; break;
|
|
||||||
case 23850: mode = 8; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mode;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
struct AmrPayloadInfo
|
struct AmrPayloadInfo
|
||||||
{
|
{
|
||||||
const uint8_t* mPayload;
|
const uint8_t* mPayload;
|
||||||
|
|
@ -119,12 +64,13 @@ struct AmrPayload
|
||||||
static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
||||||
{
|
{
|
||||||
AmrPayload result;
|
AmrPayload result;
|
||||||
|
|
||||||
// Do not skip packet by default; I suppose packet is good enough by default.
|
// Do not skip packet by default; I suppose packet is good enough by default.
|
||||||
result.mDiscardPacket = false;
|
result.mDiscardPacket = false;
|
||||||
|
|
||||||
// Wrap incoming data with ByteArray to make bit dequeuing easy
|
// Wrap incoming data with ByteArray to make bit dequeuing easy
|
||||||
ByteBuffer dataIn(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
ByteBuffer byte_reader(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
||||||
BitReader br(input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
BitReader bit_reader (input.mPayload, static_cast<size_t>(input.mPayloadLength));
|
||||||
|
|
||||||
// In bandwidth-efficient mode, the payload header simply consists of a
|
// In bandwidth-efficient mode, the payload header simply consists of a
|
||||||
// 4-bit codec mode request:
|
// 4-bit codec mode request:
|
||||||
|
|
@ -135,16 +81,15 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
||||||
// AMR, as defined in Table 1a in [2], or 0-8 for AMR-WB, as defined
|
// AMR, as defined in Table 1a in [2], or 0-8 for AMR-WB, as defined
|
||||||
// in Table 1a in [4]. CMR value 15 indicates that no mode request
|
// in Table 1a in [4]. CMR value 15 indicates that no mode request
|
||||||
// is present, and other values are for future use.
|
// is present, and other values are for future use.
|
||||||
result.mCodeModeRequest = static_cast<uint8_t>(br.readBits(4));
|
result.mCodeModeRequest = static_cast<uint8_t>(bit_reader.readBits(4));
|
||||||
//ICELogMedia(<< "CMR: " << result.mCodeModeRequest);
|
|
||||||
|
|
||||||
// Consume extra 4 bits for octet aligned profile
|
// Consume extra 4 bits for octet aligned profile
|
||||||
if (input.mOctetAligned)
|
if (input.mOctetAligned)
|
||||||
br.readBits(4);
|
bit_reader.readBits(4);
|
||||||
|
|
||||||
// Skip interleaving flags for now for octet aligned mode
|
// Skip interleaving flags for now for octet aligned mode
|
||||||
if (input.mInterleaving && input.mOctetAligned)
|
if (input.mInterleaving && input.mOctetAligned)
|
||||||
br.readBits(8);
|
bit_reader.readBits(8);
|
||||||
|
|
||||||
// Silence codec mode constant (it differs for wideband and narrowband codecs)
|
// Silence codec mode constant (it differs for wideband and narrowband codecs)
|
||||||
uint8_t SID_FT = input.mWideband ? 9 : 8;
|
uint8_t SID_FT = input.mWideband ? 9 : 8;
|
||||||
|
|
@ -158,105 +103,121 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
|
||||||
// F (1 bit): If set to 1, indicates that this frame is followed by
|
// F (1 bit): If set to 1, indicates that this frame is followed by
|
||||||
// another speech frame in this payload; if set to 0, indicates that
|
// another speech frame in this payload; if set to 0, indicates that
|
||||||
// this frame is the last frame in this payload.
|
// this frame is the last frame in this payload.
|
||||||
F = br.readBit();
|
F = bit_reader.readBit();
|
||||||
|
|
||||||
// FT (4 bits): Frame type index, indicating either the AMR or AMR-WB
|
// FT (4 bits): Frame type index, indicating either the AMR or AMR-WB
|
||||||
// speech coding mode or comfort noise (SID) mode of the
|
// speech coding mode or comfort noise (SID) mode of the
|
||||||
// corresponding frame carried in this payload.
|
// corresponding frame carried in this payload.
|
||||||
FT = static_cast<uint8_t>(br.readBits(4));
|
FT = static_cast<uint8_t>(bit_reader.readBits(4));
|
||||||
|
|
||||||
// Q (1 bit): Frame quality indicator. If set to 0, indicates the
|
// Q (1 bit): Frame quality indicator. If set to 0, indicates the
|
||||||
// corresponding frame is severely damaged, and the receiver should
|
// corresponding frame is severely damaged, and the receiver should
|
||||||
// set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD
|
// set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD
|
||||||
// depending on the frame type (FT).
|
// depending on the frame type (FT).
|
||||||
Q = br.readBit();
|
Q = bit_reader.readBit();
|
||||||
|
|
||||||
|
// Handle padding for octet alignment
|
||||||
|
if (input.mOctetAligned)
|
||||||
|
bit_reader.readBits(2);
|
||||||
|
|
||||||
|
AmrFrame frame;
|
||||||
|
frame.mFrameType = FT;
|
||||||
|
frame.mSTI = 0;
|
||||||
|
frame.mMode = FT < SID_FT ? FT : 0xFF;
|
||||||
|
frame.mGoodQuality = Q == 1;
|
||||||
|
frame.mTimestamp = input.mCurrentTimestamp;
|
||||||
|
result.mFrames.push_back(frame);
|
||||||
|
input.mCurrentTimestamp += input.mWideband ? 320 : 160;
|
||||||
|
}
|
||||||
|
while (F != 0);
|
||||||
|
|
||||||
|
for (size_t frameIndex=0; frameIndex < result.mFrames.size() && !result.mDiscardPacket; frameIndex++)
|
||||||
|
{
|
||||||
|
AmrFrame& f = result.mFrames[frameIndex];
|
||||||
|
|
||||||
// If receiving a ToC entry with a FT value in the range 9-14 for AMR or
|
// If receiving a ToC entry with a FT value in the range 9-14 for AMR or
|
||||||
// 10-13 for AMR-WB, the whole packet SHOULD be discarded. This is to
|
// 10-13 for AMR-WB, the whole packet SHOULD be discarded. This is to
|
||||||
// avoid the loss of data synchronization in the depacketization
|
// avoid the loss of data synchronization in the depacketization
|
||||||
// process, which can result in a huge degradation in speech quality.
|
// process, which can result in a huge degradation in speech quality.
|
||||||
if ((input.mWideband && (FT >= 10 && FT <= 13)) ||
|
bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14);
|
||||||
(!input.mWideband && (FT >= 9 && FT <= 14)))
|
// discard |= input.mWideband ? f.mFrameType >= 14 : f.mFrameType >= 15;
|
||||||
|
if (discard)
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "Discard corrupted packet");
|
|
||||||
// Discard bad packet
|
|
||||||
result.mDiscardPacket = true;
|
result.mDiscardPacket = true;
|
||||||
return result;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle padding for octet alignment
|
if (input.mWideband && f.mFrameType == 15)
|
||||||
if (input.mOctetAligned)
|
|
||||||
br.readBits(2);
|
|
||||||
|
|
||||||
AmrFrame frame;
|
|
||||||
frame.mFrameType = FT;
|
|
||||||
|
|
||||||
frame.mMode = FT < SID_FT ? FT : 0xFF;
|
|
||||||
frame.mGoodQuality = Q == 1;
|
|
||||||
frame.mTimestamp = input.mCurrentTimestamp;
|
|
||||||
|
|
||||||
result.mFrames.push_back(frame);
|
|
||||||
|
|
||||||
input.mCurrentTimestamp += input.mWideband ? 320 : 160;
|
|
||||||
}
|
|
||||||
while (F != 0);
|
|
||||||
|
|
||||||
for (size_t frameIndex=0; frameIndex < result.mFrames.size(); frameIndex++)
|
|
||||||
{
|
{
|
||||||
AmrFrame& frame = result.mFrames[frameIndex];
|
// DTX, no sense to decode the data
|
||||||
size_t bitsLength = input.mWideband ? amrwb_framelenbits[frame.mFrameType] : amrnb_framelenbits[frame.mFrameType];
|
continue;
|
||||||
size_t byteLength = input.mWideband ? amrwb_framelen[frame.mFrameType] : amrnb_framelen[frame.mFrameType];
|
}
|
||||||
ICELogMedia(<< "New AMR speech frame: frame type = " << FT <<
|
|
||||||
", mode = " << frame.mMode <<
|
if (input.mWideband && f.mFrameType == 14)
|
||||||
", timestamp = " << static_cast<int>(frame.mTimestamp) <<
|
{
|
||||||
", bits length = " << static_cast<int>(bitsLength) <<
|
// Speech lost code only
|
||||||
", byte length =" << static_cast<int>(byteLength) <<
|
continue;
|
||||||
", remaining packet length = " << static_cast<int>(dataIn.size()));
|
}
|
||||||
|
|
||||||
|
if (!f.mGoodQuality)
|
||||||
|
{
|
||||||
|
// Bad quality, frame is damaged
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bitsLength = input.mWideband ? amrwb_framelenbits[f.mFrameType] : amrnb_framelenbits[f.mFrameType];
|
||||||
|
size_t byteLength = input.mWideband ? amrwb_framelen[f.mFrameType] : amrnb_framelen[f.mFrameType];
|
||||||
|
|
||||||
if (bitsLength > 0)
|
if (bitsLength > 0)
|
||||||
{
|
{
|
||||||
if (input.mOctetAligned)
|
if (input.mOctetAligned)
|
||||||
{
|
{
|
||||||
if (dataIn.size() < byteLength)
|
if (byte_reader.size() < byteLength)
|
||||||
frame.mGoodQuality = false;
|
f.mGoodQuality = false;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// It is octet aligned scheme, so we are on byte boundary now
|
// It is octet aligned scheme, so we are on byte boundary now
|
||||||
size_t byteOffset = br.count() / 8;
|
size_t byteOffset = bit_reader.position() / 8;
|
||||||
|
|
||||||
// Copy data of AMR frame
|
// Copy data of AMR frame
|
||||||
frame.mData = std::make_shared<ByteBuffer>(input.mPayload + byteOffset, byteLength);
|
if (byteOffset + byteLength <= input.mPayloadLength)
|
||||||
|
{
|
||||||
|
f.mData = std::make_shared<ByteBuffer>();
|
||||||
|
f.mData->resize(byteLength + 1); // payload + header
|
||||||
|
memcpy(f.mData->mutableData() + 1, input.mPayload + byteOffset, byteLength);
|
||||||
|
|
||||||
|
// Add header for decoder
|
||||||
|
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ICELogError(<< "Problem parsing AMR header: octet-aligned is set, available " << int(input.mPayloadLength - byteOffset)
|
||||||
|
<< " bytes but requested " << (int)byteLength);
|
||||||
|
result.mDiscardPacket = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Allocate place for copying
|
// Allocate place for copying
|
||||||
frame.mData = std::make_shared<ByteBuffer>();
|
f.mData = std::make_shared<ByteBuffer>();
|
||||||
frame.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
|
f.mData->resize(bitsLength / 8 + ((bitsLength % 8) ? 1 : 0) + 1);
|
||||||
|
|
||||||
// Add header for decoder
|
// Add header for decoder
|
||||||
frame.mData->mutableData()[0] = (frame.mFrameType << 3) | (1 << 2);
|
f.mData->mutableData()[0] = (f.mFrameType << 3) | (1 << 2);
|
||||||
|
|
||||||
// Read bits
|
// Read bits
|
||||||
if (br.readBits(frame.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
|
if (bit_reader.readBits(f.mData->mutableData() + 1, bitsLength /*+ bitsLength*/ ) < (size_t)bitsLength)
|
||||||
frame.mGoodQuality = false;
|
f.mGoodQuality = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Padding bits are skipped
|
// Padding bits are skipped
|
||||||
|
|
||||||
/*if (br.count() / 8 != br.position() / 8 &&
|
|
||||||
br.count() / 8 != br.position() / 8 + 1)
|
|
||||||
throw std::runtime_error("Failed to parse AMR frame");*/
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static void predecodeAmrFrame(AmrFrame& frame, ByteBuffer& data)
|
|
||||||
{
|
|
||||||
// Data are already moved into
|
|
||||||
}*/
|
|
||||||
|
|
||||||
AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
|
AmrNbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
|
||||||
:mConfig(config)
|
:mConfig(config)
|
||||||
{
|
{
|
||||||
|
|
@ -278,12 +239,9 @@ int AmrNbCodec::CodecFactory::payloadType()
|
||||||
return mConfig.mPayloadType;
|
return mConfig.mPayloadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_RESIP_INTEGRATION
|
|
||||||
|
|
||||||
void AmrNbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void AmrNbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int AmrNbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
int AmrNbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
|
|
@ -295,7 +253,6 @@ void AmrNbCodec::CodecFactory::create(CodecMap& codecs)
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig));
|
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrNbCodec(mConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
PCodec AmrNbCodec::CodecFactory::create()
|
PCodec AmrNbCodec::CodecFactory::create()
|
||||||
{
|
{
|
||||||
return PCodec(new AmrNbCodec(mConfig));
|
return PCodec(new AmrNbCodec(mConfig));
|
||||||
|
|
@ -378,6 +335,9 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
|
||||||
#define AMR_BITRATE_DTX 15
|
#define AMR_BITRATE_DTX 15
|
||||||
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
|
if (mConfig.mOctetAligned)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (mConfig.mIuUP)
|
if (mConfig.mIuUP)
|
||||||
{
|
{
|
||||||
// Try to parse IuUP frame
|
// Try to parse IuUP frame
|
||||||
|
|
@ -485,7 +445,7 @@ int AmrNbCodec::plc(int lostFrames, void* output, int outputCapacity)
|
||||||
|
|
||||||
for (int i=0; i < lostFrames; i++)
|
for (int i=0; i < lostFrames; i++)
|
||||||
{
|
{
|
||||||
unsigned char buffer[32];
|
uint8_t buffer[32];
|
||||||
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
|
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
|
||||||
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
|
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
|
||||||
dataOut += L_FRAME;
|
dataOut += L_FRAME;
|
||||||
|
|
@ -519,12 +479,8 @@ int AmrWbCodec::CodecFactory::payloadType()
|
||||||
return mConfig.mPayloadType;
|
return mConfig.mPayloadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_RESIP_INTEGRATION
|
|
||||||
|
|
||||||
void AmrWbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void AmrWbCodec::CodecFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int AmrWbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
int AmrWbCodec::CodecFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
|
|
@ -536,19 +492,17 @@ void AmrWbCodec::CodecFactory::create(CodecMap& codecs)
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig));
|
codecs[payloadType()] = std::shared_ptr<Codec>(new AmrWbCodec(mConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PCodec AmrWbCodec::CodecFactory::create()
|
PCodec AmrWbCodec::CodecFactory::create()
|
||||||
{
|
{
|
||||||
return PCodec(new AmrWbCodec(mConfig));
|
return PCodec(new AmrWbCodec(mConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AmrWbStatistics MT::GAmrWbStatistics;
|
||||||
|
|
||||||
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
|
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
|
||||||
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config),
|
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config),
|
||||||
mSwitchCounter(0), mPreviousPacketLength(0)
|
mSwitchCounter(0), mPreviousPacketLength(0)
|
||||||
{
|
{
|
||||||
//mEncoderCtx = E_IF_init();
|
|
||||||
mDecoderCtx = D_IF_init();
|
mDecoderCtx = D_IF_init();
|
||||||
mCurrentDecoderTimestamp = 0;
|
mCurrentDecoderTimestamp = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -595,36 +549,16 @@ int AmrWbCodec::samplerate()
|
||||||
|
|
||||||
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
if (inputBytes % pcmLength())
|
throw Exception(ERR_NOT_IMPLEMENTED);
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Declare the data input pointer
|
|
||||||
const short *dataIn = (const short *)input;
|
|
||||||
|
|
||||||
// Declare the data output pointer
|
|
||||||
unsigned char *dataOut = (unsigned char *)output;
|
|
||||||
|
|
||||||
// Find how much RTP frames will be generated
|
|
||||||
unsigned int frames = inputBytes / pcmLength();
|
|
||||||
|
|
||||||
// Generate frames
|
|
||||||
for (unsigned int i = 0; i < frames; i++)
|
|
||||||
{
|
|
||||||
// dataOut += Encoder_Interface_Encode(mEncoderCtx, Mode::MRDTX, dataIn, dataOut, 1);
|
|
||||||
// dataIn += pcmLength() / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return frames * rtpLength();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define L_FRAME 160
|
#define L_FRAME 160
|
||||||
#define AMR_BITRATE_DTX 15
|
#define AMR_BITRATE_DTX 15
|
||||||
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
|
||||||
{
|
int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output)
|
||||||
if (mConfig.mIuUP)
|
|
||||||
{
|
{
|
||||||
IuUP::Frame frame;
|
IuUP::Frame frame;
|
||||||
if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame))
|
if (!IuUP::parse2(input.data(), input.size(), frame))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
|
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
|
||||||
|
|
@ -633,9 +567,6 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build first byte to help decoder
|
|
||||||
//ICELogDebug(<< "Decoding AMR frame length = " << frame.mPayloadSize);
|
|
||||||
|
|
||||||
// Reserve space
|
// Reserve space
|
||||||
ByteBuffer dataToDecode;
|
ByteBuffer dataToDecode;
|
||||||
dataToDecode.resize(1 + frame.mPayloadSize);
|
dataToDecode.resize(1 + frame.mPayloadSize);
|
||||||
|
|
@ -652,16 +583,17 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
||||||
|
|
||||||
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
|
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
|
||||||
|
|
||||||
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0);
|
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0);
|
||||||
return pcmLength();
|
return pcmLength();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output)
|
||||||
{
|
{
|
||||||
AmrPayloadInfo info;
|
AmrPayloadInfo info;
|
||||||
info.mCurrentTimestamp = mCurrentDecoderTimestamp;
|
info.mCurrentTimestamp = mCurrentDecoderTimestamp;
|
||||||
info.mOctetAligned = mConfig.mOctetAligned;
|
info.mOctetAligned = mConfig.mOctetAligned;
|
||||||
info.mPayload = (const uint8_t*)input;
|
info.mPayload = input.data();
|
||||||
info.mPayloadLength = inputBytes;
|
info.mPayloadLength = input.size();
|
||||||
info.mWideband = true;
|
info.mWideband = true;
|
||||||
info.mInterleaving = false;
|
info.mInterleaving = false;
|
||||||
|
|
||||||
|
|
@ -672,6 +604,7 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
GAmrWbStatistics.mNonParsed++;
|
||||||
ICELogDebug(<< "Failed to decode AMR payload");
|
ICELogDebug(<< "Failed to decode AMR payload");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -680,13 +613,17 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
||||||
|
|
||||||
// Check if packet is corrupted
|
// Check if packet is corrupted
|
||||||
if (ap.mDiscardPacket)
|
if (ap.mDiscardPacket)
|
||||||
|
{
|
||||||
|
GAmrWbStatistics.mDiscarded++;
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for output buffer capacity
|
// Check for output buffer capacity
|
||||||
if (outputCapacity < (int)ap.mFrames.size() * pcmLength())
|
if (output.size() < (int)ap.mFrames.size() * pcmLength())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
short* dataOut = (short*)output;
|
short* dataOut = (short*)output.data();
|
||||||
|
size_t dataOutSizeInBytes = 0;
|
||||||
for (AmrFrame& frame: ap.mFrames)
|
for (AmrFrame& frame: ap.mFrames)
|
||||||
{
|
{
|
||||||
memset(dataOut, 0, static_cast<size_t>(pcmLength()));
|
memset(dataOut, 0, static_cast<size_t>(pcmLength()));
|
||||||
|
|
@ -695,11 +632,22 @@ int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outp
|
||||||
{
|
{
|
||||||
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
|
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
|
||||||
dataOut += pcmLength() / 2;
|
dataOut += pcmLength() / 2;
|
||||||
|
dataOutSizeInBytes += pcmLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pcmLength() * ap.mFrames.size();
|
return dataOutSizeInBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
|
{
|
||||||
|
auto inputBuffer = std::span<const uint8_t>((uint8_t*)input, (size_t)inputBytes);
|
||||||
|
auto outputBuffer = std::span<uint8_t>((uint8_t*)output, (size_t)outputCapacity);
|
||||||
|
|
||||||
|
if (mConfig.mIuUP)
|
||||||
|
return decodeIuup(inputBuffer, outputBuffer);
|
||||||
|
else
|
||||||
|
return decodePlain(inputBuffer, outputBuffer);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -750,8 +698,6 @@ int GsmEfrCodec::GsmEfrFactory::payloadType()
|
||||||
return mPayloadType;
|
return mPayloadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_RESIP_INTEGRATION
|
|
||||||
|
|
||||||
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -767,8 +713,6 @@ void GsmEfrCodec::GsmEfrFactory::create(CodecMap& codecs)
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP));
|
codecs[payloadType()] = std::shared_ptr<Codec>(new GsmEfrCodec(mIuUP));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PCodec GsmEfrCodec::GsmEfrFactory::create()
|
PCodec GsmEfrCodec::GsmEfrFactory::create()
|
||||||
{
|
{
|
||||||
return PCodec(new GsmEfrCodec(mIuUP));
|
return PCodec(new GsmEfrCodec(mIuUP));
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
#ifndef MT_AMRCODEC_H
|
#ifndef MT_AMRCODEC_H
|
||||||
#define MT_AMRCODEC_H
|
#define MT_AMRCODEC_H
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#include "MT_Codec.h"
|
#include "MT_Codec.h"
|
||||||
#include "../helper/HL_Pointer.h"
|
#include "../helper/HL_Pointer.h"
|
||||||
|
|
||||||
|
|
@ -16,9 +18,9 @@ namespace MT
|
||||||
{
|
{
|
||||||
struct AmrCodecConfig
|
struct AmrCodecConfig
|
||||||
{
|
{
|
||||||
bool mIuUP;
|
bool mIuUP = false;
|
||||||
bool mOctetAligned;
|
bool mOctetAligned = false;
|
||||||
int mPayloadType;
|
int mPayloadType = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AmrNbCodec : public Codec
|
class AmrNbCodec : public Codec
|
||||||
|
|
@ -41,11 +43,10 @@ namespace MT
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
#ifdef USE_RESIP_INTEGRATION
|
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
void create(CodecMap& codecs) override;
|
void create(CodecMap& codecs) override;
|
||||||
#endif
|
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -66,6 +67,13 @@ namespace MT
|
||||||
int getSwitchCounter() const;
|
int getSwitchCounter() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AmrWbStatistics
|
||||||
|
{
|
||||||
|
int mDiscarded = 0;
|
||||||
|
int mNonParsed = 0;
|
||||||
|
};
|
||||||
|
extern AmrWbStatistics GAmrWbStatistics;
|
||||||
|
|
||||||
class AmrWbCodec : public Codec
|
class AmrWbCodec : public Codec
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -76,6 +84,9 @@ namespace MT
|
||||||
int mSwitchCounter;
|
int mSwitchCounter;
|
||||||
int mPreviousPacketLength;
|
int mPreviousPacketLength;
|
||||||
|
|
||||||
|
int decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output);
|
||||||
|
int decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class CodecFactory: public Factory
|
class CodecFactory: public Factory
|
||||||
{
|
{
|
||||||
|
|
@ -86,11 +97,10 @@ namespace MT
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
#ifdef USE_RESIP_INTEGRATION
|
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
void create(CodecMap& codecs) override;
|
void create(CodecMap& codecs) override;
|
||||||
#endif
|
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -128,11 +138,10 @@ namespace MT
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
#ifdef USE_RESIP_INTEGRATION
|
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
void create(CodecMap& codecs) override;
|
void create(CodecMap& codecs) override;
|
||||||
#endif
|
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
/* Copyright(C) 2007-2018 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "MT_AudioCodec.h"
|
#include "MT_AudioCodec.h"
|
||||||
#include "MT_CodecList.h"
|
#include "MT_CodecList.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
|
|
@ -58,18 +58,14 @@ int G729Codec::G729Factory::payloadType()
|
||||||
return 18;
|
return 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
void G729Codec::G729Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void G729Codec::G729Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
int G729Codec::G729Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
int G729Codec::G729Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PCodec G729Codec::G729Factory::create()
|
PCodec G729Codec::G729Factory::create()
|
||||||
{
|
{
|
||||||
return std::make_shared<G729Codec>();
|
return std::make_shared<G729Codec>();
|
||||||
|
|
@ -125,7 +121,7 @@ int G729Codec::channels()
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int SamplesPerFrame = 80;
|
// static const int SamplesPerFrame = 80;
|
||||||
int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
// Create encoder if it is not done yet
|
// Create encoder if it is not done yet
|
||||||
|
|
@ -231,7 +227,7 @@ int G729Codec::plc(int lostFrames, void* output, int outputCapacity)
|
||||||
#define OPUS_CODEC_NAME "OPUS"
|
#define OPUS_CODEC_NAME "OPUS"
|
||||||
#define OPUS_CODEC_RATE 16000
|
#define OPUS_CODEC_RATE 16000
|
||||||
#define OPUS_TARGET_BITRATE 64000
|
#define OPUS_TARGET_BITRATE 64000
|
||||||
#define OPUS_PACKET_LOSS 30
|
#define OPUS_PACKET_LOSS 10
|
||||||
#define OPUS_CODEC_COMPLEXITY 2
|
#define OPUS_CODEC_COMPLEXITY 2
|
||||||
|
|
||||||
OpusCodec::Params::Params()
|
OpusCodec::Params::Params()
|
||||||
|
|
@ -241,7 +237,6 @@ OpusCodec::Params::Params()
|
||||||
mTargetBitrate = OPUS_TARGET_BITRATE;
|
mTargetBitrate = OPUS_TARGET_BITRATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
resip::Data OpusCodec::Params::toString() const
|
resip::Data OpusCodec::Params::toString() const
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|
@ -321,10 +316,10 @@ void OpusCodec::Params::parse(const resip::Data ¶ms)
|
||||||
mStereo = paramIter->mValue == "1";
|
mStereo = paramIter->mValue == "1";
|
||||||
else
|
else
|
||||||
if (paramIter->mName == "ptime")
|
if (paramIter->mName == "ptime")
|
||||||
mPtime = StringHelper::toInt(paramIter->mValue.c_str(), 20);
|
mPtime = strx::toInt(paramIter->mValue.c_str(), 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
OpusCodec::OpusFactory::OpusFactory(int samplerate, int channels, int ptype)
|
OpusCodec::OpusFactory::OpusFactory(int samplerate, int channels, int ptype)
|
||||||
{
|
{
|
||||||
mSamplerate = samplerate;
|
mSamplerate = samplerate;
|
||||||
|
|
@ -352,7 +347,6 @@ int OpusCodec::OpusFactory::payloadType()
|
||||||
return mPType;
|
return mPType;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
void OpusCodec::OpusFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
void OpusCodec::OpusFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
|
||||||
{
|
{
|
||||||
// Put opus codec record
|
// Put opus codec record
|
||||||
|
|
@ -394,7 +388,6 @@ int OpusCodec::OpusFactory::processSdp(const resip::SdpContents::Session::Medium
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
PCodec OpusCodec::OpusFactory::create()
|
PCodec OpusCodec::OpusFactory::create()
|
||||||
{
|
{
|
||||||
|
|
@ -407,15 +400,13 @@ PCodec OpusCodec::OpusFactory::create()
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusCodec::OpusCodec(int samplerate, int channels, int ptime)
|
OpusCodec::OpusCodec(int samplerate, int channels, int ptime)
|
||||||
:mEncoderCtx(NULL), mDecoderCtx(NULL), mChannels(channels), mPTime(ptime), mSamplerate(samplerate)
|
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(channels), mPTime(ptime), mSamplerate(samplerate), mDecoderChannels(0)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
mEncoderCtx = opus_encoder_create(48000, mChannels, OPUS_APPLICATION_VOIP, &status);
|
mEncoderCtx = opus_encoder_create(mSamplerate, mChannels, OPUS_APPLICATION_VOIP, &status);
|
||||||
if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_COMPLEXITY(OPUS_CODEC_COMPLEXITY)))
|
if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_COMPLEXITY(OPUS_CODEC_COMPLEXITY)))
|
||||||
ICELogError(<< "Failed to set Opus encoder complexity");
|
ICELogError(<< "Failed to set Opus encoder complexity");
|
||||||
//if (OPUS_OK != opus_encoder_ctl(mEncoderCtx, OPUS_SET_FORCE_CHANNELS(AUDIO_CHANNELS)))
|
// Decoder creation is postponed until first packet arriving (because it may use different channel number
|
||||||
// ICELogCritical(<<"Failed to set channel number in Opus encoder");
|
|
||||||
mDecoderCtx = opus_decoder_create(48000, mChannels, &status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusCodec::applyParams(const Params ¶ms)
|
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))))
|
if (OPUS_OK != (error = opus_encoder_ctl(mEncoderCtx, OPUS_SET_PACKET_LOSS_PERC(params.mExpectedPacketLoss))))
|
||||||
ICELogError(<< "Failed to (un)set expected packet loss. Error " << opus_strerror(error));
|
ICELogError(<< "Failed to (un)set expected packet loss. Error " << opus_strerror(error));
|
||||||
|
|
||||||
mDecodeResampler.start(channels(), 48000, mSamplerate);
|
// mDecodeResampler.start(channels(), 48000, mSamplerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusCodec::~OpusCodec()
|
OpusCodec::~OpusCodec()
|
||||||
|
|
@ -441,13 +432,13 @@ OpusCodec::~OpusCodec()
|
||||||
if (mDecoderCtx)
|
if (mDecoderCtx)
|
||||||
{
|
{
|
||||||
opus_decoder_destroy(mDecoderCtx);
|
opus_decoder_destroy(mDecoderCtx);
|
||||||
mDecoderCtx = NULL;
|
mDecoderCtx = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEncoderCtx)
|
if (mEncoderCtx)
|
||||||
{
|
{
|
||||||
opus_encoder_destroy(mEncoderCtx);
|
opus_encoder_destroy(mEncoderCtx);
|
||||||
mEncoderCtx = NULL;
|
mEncoderCtx = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,39 +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 OpusCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
int nrOfChannelsInPacket = opus_packet_get_nb_channels((const unsigned char*)input);
|
int result = 0;
|
||||||
assert(nrOfChannelsInPacket == 1);
|
|
||||||
|
|
||||||
int nrOfSamplesInPacket = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char*)input, inputBytes);
|
// Examine the number of channels available in incoming packet
|
||||||
if (nrOfSamplesInPacket > 0)
|
int nr_of_channels = opus_packet_get_nb_channels((const unsigned char *) input);
|
||||||
|
|
||||||
|
// Recreate decoder if needed
|
||||||
|
if (mDecoderChannels != nr_of_channels)
|
||||||
{
|
{
|
||||||
// Send number of bytes for input and number of samples for output
|
if (mDecoderCtx)
|
||||||
int capacity = nrOfSamplesInPacket * sizeof(opus_int16) * channels();
|
{
|
||||||
opus_int16* buffer = (opus_int16*)alloca(capacity);
|
opus_decoder_destroy(mDecoderCtx);
|
||||||
int decoded = opus_decode(mDecoderCtx, (const unsigned char*)input, inputBytes, (opus_int16*)buffer, capacity / (sizeof(short) * channels()), 1);
|
mDecoderCtx = nullptr;
|
||||||
|
}
|
||||||
|
mDecoderChannels = nr_of_channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mDecoderCtx)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
mDecoderCtx = opus_decoder_create(mSamplerate, mDecoderChannels, &status);
|
||||||
|
if (status)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char *) input,
|
||||||
|
inputBytes);
|
||||||
|
if (nr_of_frames <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// We support stereo and mono here.
|
||||||
|
int buffer_capacity = nr_of_frames * sizeof(opus_int16) * nr_of_channels;
|
||||||
|
opus_int16 *buffer_decode = (opus_int16 *)alloca(buffer_capacity);
|
||||||
|
int decoded = opus_decode(mDecoderCtx,
|
||||||
|
reinterpret_cast<const unsigned char *>(input), inputBytes,
|
||||||
|
buffer_decode, nr_of_frames, 0);
|
||||||
if (decoded < 0)
|
if (decoded < 0)
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Resample 48000 to requested samplerate
|
ICELogCritical(<< "opus_decode() returned " << decoded);
|
||||||
size_t processed = 0;
|
|
||||||
return mDecodeResampler.processBuffer(buffer, decoded * sizeof(short) * channels(), processed, output, outputCapacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OpusCodec::plc(int lostFrames, void* output, int outputCapacity)
|
opus_int16 *buffer_stereo = nullptr;
|
||||||
|
int buffer_stereo_capacity = buffer_capacity * 2;
|
||||||
|
|
||||||
|
switch (nr_of_channels) {
|
||||||
|
case 1:
|
||||||
|
// Convert to stereo before
|
||||||
|
buffer_stereo = (opus_int16 *) alloca(buffer_stereo_capacity);
|
||||||
|
for (int i = 0; i < nr_of_frames; i++) {
|
||||||
|
buffer_stereo[i * 2 + 1] = buffer_decode[i];
|
||||||
|
buffer_stereo[i * 2] = buffer_decode[i];
|
||||||
|
}
|
||||||
|
assert(buffer_stereo_capacity <= outputCapacity);
|
||||||
|
memcpy(output, buffer_stereo, buffer_stereo_capacity);
|
||||||
|
result = buffer_stereo_capacity;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
assert(buffer_capacity <= outputCapacity);
|
||||||
|
memcpy(output, buffer_decode, buffer_capacity);
|
||||||
|
result = buffer_capacity;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpusCodec::plc(int lostPackets, void* output, int outputCapacity)
|
||||||
{
|
{
|
||||||
int nrOfSamplesInFrame = pcmLength() / (sizeof(short) * channels());
|
// Find how much frames do we need to produce and prefill it with silence
|
||||||
|
int frames_per_packet = (int)pcmLength() / (sizeof(opus_int16) * channels());
|
||||||
memset(output, 0, outputCapacity);
|
memset(output, 0, outputCapacity);
|
||||||
for (int i=0; i<lostFrames; i++)
|
|
||||||
{
|
|
||||||
/*int decodedSamples = */opus_decode(mDecoderCtx, NULL, 0, (opus_int16*)output + nrOfSamplesInFrame * i, nrOfSamplesInFrame, 0);
|
|
||||||
|
|
||||||
|
// Use this pointer as output
|
||||||
|
opus_int16* data_output = reinterpret_cast<opus_int16*>(output);
|
||||||
|
|
||||||
|
int nr_of_decoded_frames = 0;
|
||||||
|
|
||||||
|
// Buffer for single lost frame
|
||||||
|
opus_int16* buffer_plc = (opus_int16*)alloca(frames_per_packet * mDecoderChannels * sizeof(opus_int16));
|
||||||
|
for (int i=0; i<lostPackets; i++)
|
||||||
|
{
|
||||||
|
nr_of_decoded_frames = opus_decode(mDecoderCtx, nullptr, 0, buffer_plc, frames_per_packet, 0);
|
||||||
|
assert(nr_of_decoded_frames == frames_per_packet);
|
||||||
|
switch (mDecoderChannels)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
// Convert mono to stereo
|
||||||
|
for (int i=0; i < nr_of_decoded_frames; i++)
|
||||||
|
{
|
||||||
|
data_output[i * 2] = buffer_plc[i];
|
||||||
|
data_output[i * 2 + 1] = buffer_plc[i+1];
|
||||||
}
|
}
|
||||||
return lostFrames * pcmLength();
|
data_output += frames_per_packet * mChannels;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// Just copy data
|
||||||
|
memcpy(data_output, buffer_plc, frames_per_packet * sizeof(opus_int16) * mDecoderChannels);
|
||||||
|
data_output += frames_per_packet * mChannels;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ((char*)data_output - (char*)output) * sizeof(opus_int16);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -649,7 +714,6 @@ PCodec IlbcCodec::IlbcFactory::create()
|
||||||
return PCodec(new IlbcCodec(mPtime));
|
return PCodec(new IlbcCodec(mPtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
void IlbcCodec::IlbcFactory::create(CodecMap& codecs)
|
void IlbcCodec::IlbcFactory::create(CodecMap& codecs)
|
||||||
{
|
{
|
||||||
codecs[mPType20ms] = PCodec(create());
|
codecs[mPType20ms] = PCodec(create());
|
||||||
|
|
@ -711,8 +775,6 @@ int IlbcCodec::IlbcFactory::processSdp(const resip::SdpContents::Session::Medium
|
||||||
return pt;
|
return pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --- IsacCodec(s) ---
|
// --- IsacCodec(s) ---
|
||||||
#define ISAC_CODEC_NAME "ISAC"
|
#define ISAC_CODEC_NAME "ISAC"
|
||||||
|
|
||||||
|
|
@ -1090,8 +1152,8 @@ int GsmCodec::plc(int lostFrames, void* output, int outputCapacity)
|
||||||
|
|
||||||
G722Codec::G722Codec()
|
G722Codec::G722Codec()
|
||||||
{
|
{
|
||||||
mEncoder = g722_encode_init(NULL, 64000, 0);
|
mEncoder = g722_encode_init(nullptr, 64000, 0);
|
||||||
mDecoder = g722_decode_init(NULL, 64000, 0);
|
mDecoder = g722_decode_init(nullptr, 64000, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __AUDIO_CODEC_H
|
#ifndef __AUDIO_CODEC_H
|
||||||
#define __AUDIO_CODEC_H
|
#define __AUDIO_CODEC_H
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "MT_Codec.h"
|
#include "MT_Codec.h"
|
||||||
#include "../audio/Audio_Resampler.h"
|
#include "../audio/Audio_Resampler.h"
|
||||||
|
|
@ -46,10 +46,9 @@ public:
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
|
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
#endif
|
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
};
|
};
|
||||||
G729Codec();
|
G729Codec();
|
||||||
|
|
@ -73,7 +72,8 @@ protected:
|
||||||
OpusEncoder *mEncoderCtx;
|
OpusEncoder *mEncoderCtx;
|
||||||
OpusDecoder *mDecoderCtx;
|
OpusDecoder *mDecoderCtx;
|
||||||
int mPTime, mSamplerate, mChannels;
|
int mPTime, mSamplerate, mChannels;
|
||||||
Audio::SpeexResampler mDecodeResampler;
|
// Audio::SpeexResampler mDecodeResampler;
|
||||||
|
int mDecoderChannels;
|
||||||
public:
|
public:
|
||||||
struct Params
|
struct Params
|
||||||
{
|
{
|
||||||
|
|
@ -81,10 +81,8 @@ public:
|
||||||
int mPtime, mTargetBitrate, mExpectedPacketLoss;
|
int mPtime, mTargetBitrate, mExpectedPacketLoss;
|
||||||
|
|
||||||
Params();
|
Params();
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
resip::Data toString() const;
|
resip::Data toString() const;
|
||||||
void parse(const resip::Data& params);
|
void parse(const resip::Data& params);
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpusFactory: public Factory
|
class OpusFactory: public Factory
|
||||||
|
|
@ -102,10 +100,8 @@ public:
|
||||||
int channels() override;
|
int channels() override;
|
||||||
int samplerate() override;
|
int samplerate() override;
|
||||||
int payloadType() override;
|
int payloadType() override;
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||||
#endif
|
|
||||||
PCodec create() override;
|
PCodec create() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -145,11 +141,9 @@ public:
|
||||||
const char* name();
|
const char* name();
|
||||||
int samplerate();
|
int samplerate();
|
||||||
int payloadType();
|
int payloadType();
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
void create(CodecMap& codecs);
|
void create(CodecMap& codecs);
|
||||||
#endif
|
|
||||||
PCodec create();
|
PCodec create();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,20 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#if defined(TARGET_WIN) && !defined(NOMINMAX)
|
||||||
# define NOMINMAX
|
# define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "MT_AudioReceiver.h"
|
#include "MT_AudioReceiver.h"
|
||||||
#include "MT_AudioCodec.h"
|
#include "MT_AudioCodec.h"
|
||||||
#include "MT_CngHelper.h"
|
#include "MT_CngHelper.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
|
#include "../helper/HL_Time.h"
|
||||||
#include "../audio/Audio_Interface.h"
|
#include "../audio/Audio_Interface.h"
|
||||||
#include "../audio/Audio_Resampler.h"
|
#include "../audio/Audio_Resampler.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC)
|
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC)
|
||||||
# include "MT_AmrCodec.h"
|
# include "MT_AmrCodec.h"
|
||||||
|
|
@ -47,12 +51,22 @@ int RtpBuffer::Packet::rate() const
|
||||||
return mRate;
|
return mRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<short>& RtpBuffer::Packet::pcm() const
|
||||||
|
{
|
||||||
|
return mPcm;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<short>& RtpBuffer::Packet::pcm()
|
||||||
|
{
|
||||||
|
return mPcm;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------ RtpBuffer ----------------
|
// ------------ RtpBuffer ----------------
|
||||||
RtpBuffer::RtpBuffer(Statistics& stat)
|
RtpBuffer::RtpBuffer(Statistics& stat)
|
||||||
:mStat(stat), mSsrc(0), mHigh(RTP_BUFFER_HIGH), mLow(RTP_BUFFER_LOW), mPrebuffer(RTP_BUFFER_PREBUFFER),
|
:mStat(stat)
|
||||||
mFirstPacketWillGo(true), mReturnedCounter(0), mAddCounter(0),
|
|
||||||
mFetchedPacket(std::shared_ptr<RTPPacket>(), 0, 0)
|
|
||||||
{
|
{
|
||||||
|
if (mStat.mPacketLoss)
|
||||||
|
std::cout << "Warning: packet loss is not zero" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
RtpBuffer::~RtpBuffer()
|
RtpBuffer::~RtpBuffer()
|
||||||
|
|
@ -96,19 +110,27 @@ int RtpBuffer::getCount() const
|
||||||
return static_cast<int>(mPacketList.size());
|
return static_cast<int>(mPacketList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SequenceSort(const RtpBuffer::Packet& p1, const RtpBuffer::Packet& p2)
|
bool SequenceSort(const std::shared_ptr<RtpBuffer::Packet>& p1, const std::shared_ptr<RtpBuffer::Packet>& p2)
|
||||||
{
|
{
|
||||||
return p1.rtp()->GetExtendedSequenceNumber() < p2.rtp()->GetExtendedSequenceNumber();
|
return p1->rtp()->GetExtendedSequenceNumber() < p2->rtp()->GetExtendedSequenceNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate)
|
std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate)
|
||||||
{
|
{
|
||||||
if (!packet)
|
if (!packet)
|
||||||
return false;
|
return std::shared_ptr<Packet>();
|
||||||
|
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
|
|
||||||
// Update statistics
|
// Update statistics
|
||||||
|
if (mLastAddTime == 0.0)
|
||||||
|
mLastAddTime = now_ms();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float t = now_ms();
|
||||||
|
mStat.mPacketInterval.process(t - mLastAddTime);
|
||||||
|
mLastAddTime = t;
|
||||||
|
}
|
||||||
mStat.mSsrc = static_cast<uint16_t>(packet->GetSSRC());
|
mStat.mSsrc = static_cast<uint16_t>(packet->GetSSRC());
|
||||||
|
|
||||||
// Update jitter
|
// Update jitter
|
||||||
|
|
@ -121,15 +143,15 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
|
||||||
// New sequence number
|
// New sequence number
|
||||||
unsigned newSeqno = packet->GetExtendedSequenceNumber();
|
unsigned newSeqno = packet->GetExtendedSequenceNumber();
|
||||||
|
|
||||||
for (Packet& p: mPacketList)
|
for (std::shared_ptr<Packet>& p: mPacketList)
|
||||||
{
|
{
|
||||||
unsigned seqno = p.rtp()->GetExtendedSequenceNumber();
|
unsigned seqno = p->rtp()->GetExtendedSequenceNumber();
|
||||||
|
|
||||||
if (seqno == newSeqno)
|
if (seqno == newSeqno)
|
||||||
{
|
{
|
||||||
mStat.mDuplicatedRtp++;
|
mStat.mDuplicatedRtp++;
|
||||||
ICELogMedia(<< "Discovered duplicated packet, skipping");
|
ICELogMedia(<< "Discovered duplicated packet, skipping");
|
||||||
return false;
|
return std::shared_ptr<Packet>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seqno > maxno)
|
if (seqno > maxno)
|
||||||
|
|
@ -143,8 +165,11 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
|
||||||
|
|
||||||
if (newSeqno > minno || (available < mHigh))
|
if (newSeqno > minno || (available < mHigh))
|
||||||
{
|
{
|
||||||
Packet p(packet, timelength, rate);
|
// Insert into queue
|
||||||
|
auto p = std::make_shared<Packet>(packet, timelength, rate);
|
||||||
mPacketList.push_back(p);
|
mPacketList.push_back(p);
|
||||||
|
|
||||||
|
// Sort again
|
||||||
std::sort(mPacketList.begin(), mPacketList.end(), SequenceSort);
|
std::sort(mPacketList.begin(), mPacketList.end(), SequenceSort);
|
||||||
|
|
||||||
// Limit by max timelength
|
// Limit by max timelength
|
||||||
|
|
@ -152,21 +177,18 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
|
||||||
|
|
||||||
if (available > mHigh)
|
if (available > mHigh)
|
||||||
ICELogMedia(<< "Available " << available << "ms with limit " << mHigh << "ms");
|
ICELogMedia(<< "Available " << available << "ms with limit " << mHigh << "ms");
|
||||||
/*while (available > mHigh && mPacketList.size())
|
|
||||||
{
|
return p;
|
||||||
ICELogDebug( << "Dropping RTP packet from jitter buffer");
|
|
||||||
available -= mPacketList.front().timelength();
|
|
||||||
mPacketList.erase(mPacketList.begin());
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "Too old packet, skipping");
|
ICELogMedia(<< "Too old packet, skipping");
|
||||||
mStat.mOldRtp++;
|
mStat.mOldRtp++;
|
||||||
return false;
|
|
||||||
|
return std::shared_ptr<Packet>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return std::shared_ptr<Packet>();
|
||||||
}
|
}
|
||||||
|
|
||||||
RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
|
|
@ -179,13 +201,14 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
// See if there is enough information in buffer
|
// See if there is enough information in buffer
|
||||||
int total = findTimelength();
|
int total = findTimelength();
|
||||||
|
|
||||||
while (total > mHigh && mPacketList.size())
|
while (total > mHigh && mPacketList.size() && 0 != mHigh)
|
||||||
{
|
{
|
||||||
ICELogMedia( << "Dropping RTP packets from jitter buffer");
|
ICELogMedia( << "Dropping RTP packets from jitter buffer");
|
||||||
total -= mPacketList.front().timelength();
|
total -= mPacketList.front()->timelength();
|
||||||
|
|
||||||
// Save it as last packet however - to not confuse loss packet counter
|
// Save it as last packet however - to not confuse loss packet counter
|
||||||
mFetchedPacket = mPacketList.front();
|
mFetchedPacket = mPacketList.front();
|
||||||
|
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||||
|
|
||||||
// Erase from packet list
|
// Erase from packet list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
|
|
@ -195,65 +218,48 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total < mLow)
|
if (total < mLow)
|
||||||
|
{
|
||||||
|
// Still not prebuffered
|
||||||
result = FetchResult::NoPacket;
|
result = FetchResult::NoPacket;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mFetchedPacket.rtp())
|
// Did we fetch any packet before ?
|
||||||
|
bool is_fetched_packet = mFetchedPacket.get() != nullptr;
|
||||||
|
if (is_fetched_packet)
|
||||||
|
is_fetched_packet &= mFetchedPacket->rtp().get() != nullptr;
|
||||||
|
|
||||||
|
if (mLastSeqno.has_value())
|
||||||
{
|
{
|
||||||
if (mPacketList.empty())
|
if (mPacketList.empty())
|
||||||
{
|
{
|
||||||
result = FetchResult::NoPacket;
|
result = FetchResult::NoPacket;
|
||||||
mStat.mPacketLoss++;
|
// Don't increase counter of lost packets here; maybe it is DTX
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Current sequence number ?
|
// Current sequence number ?
|
||||||
unsigned seqno = mPacketList.front().rtp()->GetExtendedSequenceNumber();
|
uint32_t seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||||
|
|
||||||
// Gap between new packet and previous on
|
// Gap between new packet and previous on
|
||||||
int gap = seqno - mFetchedPacket.rtp()->GetSequenceNumber() - 1;
|
int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
|
||||||
gap = std::min(gap, 127);
|
gap = std::min(gap, 127);
|
||||||
if (gap > 0 && mPacketList.empty())
|
|
||||||
{
|
|
||||||
result = FetchResult::Gap;
|
|
||||||
mStat.mPacketLoss += gap;
|
|
||||||
mStat.mLoss[gap]++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (gap > 0)
|
if (gap > 0)
|
||||||
{
|
{
|
||||||
mStat.mPacketLoss += gap;
|
// std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl;
|
||||||
mStat.mLoss[gap]++;
|
mStat.mPacketLoss++;
|
||||||
|
//mStat.mLoss[gap]++;
|
||||||
|
mLastSeqno = *mLastSeqno + 1;
|
||||||
|
result = FetchResult::Gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = FetchResult::RegularPacket;
|
|
||||||
Packet& p = mPacketList.front();
|
|
||||||
rl.push_back(p.rtp());
|
|
||||||
|
|
||||||
// Maybe it is time to replay packet right now ? For case of AMR SID packets
|
|
||||||
/*if (mFetchedPacket.rtp() && gap == 0 && mFetchedPacket.timelength() >= 10 && p.timelength() >= 10)
|
|
||||||
{
|
|
||||||
int timestampDelta;
|
|
||||||
// Timestamp difference
|
|
||||||
if (p.rtp()->GetTimestamp() > mFetchedPacket.rtp()->GetTimestamp())
|
|
||||||
timestampDelta = TimeHelper::getDelta(p.rtp()->GetTimestamp(), mFetchedPacket.rtp()->GetTimestamp());
|
|
||||||
else
|
else
|
||||||
timestampDelta = TimeHelper::getDelta(mFetchedPacket.rtp()->GetTimestamp(), p.rtp()->GetTimestamp());
|
|
||||||
|
|
||||||
// Timestamp units per packet
|
|
||||||
int nrOfPackets = timestampDelta / (p.timelength() * (p.rate() / 1000));
|
|
||||||
|
|
||||||
// Add more copies of SID (most probably) packets
|
|
||||||
for (int i = 0; i < nrOfPackets - 1; i++)
|
|
||||||
{
|
{
|
||||||
//assert(false);
|
result = FetchResult::RegularPacket;
|
||||||
rl.push_back(p.rtp());
|
rl.push_back(mPacketList.front());
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Save last returned normal packet
|
// Save last returned normal packet
|
||||||
mFetchedPacket = mPacketList.front();
|
mFetchedPacket = mPacketList.front();
|
||||||
|
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||||
|
|
||||||
// Remove returned packet from the list
|
// Remove returned packet from the list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
|
|
@ -263,16 +269,17 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// See if prebuffer limit is reached
|
// See if prebuffer limit is reached
|
||||||
if (findTimelength() >= mPrebuffer)
|
if (findTimelength() >= mPrebuffer && !mPacketList.empty())
|
||||||
{
|
{
|
||||||
// Normal packet will be returned
|
// Normal packet will be returned
|
||||||
result = FetchResult::RegularPacket;
|
result = FetchResult::RegularPacket;
|
||||||
|
|
||||||
// Put it to output list
|
// Put it to output list
|
||||||
rl.push_back(mPacketList.front().rtp());
|
rl.push_back(mPacketList.front());
|
||||||
|
|
||||||
// Remember returned packet
|
// Remember returned packet
|
||||||
mFetchedPacket = mPacketList.front();
|
mFetchedPacket = mPacketList.front();
|
||||||
|
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
|
||||||
|
|
||||||
// Remove returned packet from buffer list
|
// Remove returned packet from buffer list
|
||||||
mPacketList.erase(mPacketList.begin());
|
mPacketList.erase(mPacketList.begin());
|
||||||
|
|
@ -295,7 +302,7 @@ int RtpBuffer::findTimelength()
|
||||||
{
|
{
|
||||||
int available = 0;
|
int available = 0;
|
||||||
for (unsigned i = 0; i < mPacketList.size(); i++)
|
for (unsigned i = 0; i < mPacketList.size(); i++)
|
||||||
available += mPacketList[i].timelength();
|
available += mPacketList[i]->timelength();
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,7 +328,7 @@ Receiver::~Receiver()
|
||||||
|
|
||||||
//-------------- AudioReceiver ----------------
|
//-------------- AudioReceiver ----------------
|
||||||
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
|
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
|
||||||
:Receiver(stat), mBuffer(stat), mFrameCount(0), mFailedCount(0), mCodecSettings(settings),
|
:Receiver(stat), mBuffer(stat), mCodecSettings(settings),
|
||||||
mCodecList(settings)
|
mCodecList(settings)
|
||||||
{
|
{
|
||||||
// Init resamplers
|
// Init resamplers
|
||||||
|
|
@ -331,6 +338,7 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
|
||||||
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
|
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
|
||||||
|
|
||||||
// Init codecs
|
// Init codecs
|
||||||
|
mCodecList.setSettings(settings);
|
||||||
mCodecList.fillCodecMap(mCodecMap);
|
mCodecList.fillCodecMap(mCodecMap);
|
||||||
|
|
||||||
#if defined(DUMP_DECODED)
|
#if defined(DUMP_DECODED)
|
||||||
|
|
@ -341,12 +349,6 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
|
||||||
|
|
||||||
AudioReceiver::~AudioReceiver()
|
AudioReceiver::~AudioReceiver()
|
||||||
{
|
{
|
||||||
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
|
||||||
if (mPVQA && mPvqaBuffer)
|
|
||||||
{
|
|
||||||
mStat.mPvqaMos = calculatePvqaMos(AUDIO_SAMPLERATE, mStat.mPvqaReport);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
mResampler8.stop();
|
mResampler8.stop();
|
||||||
mResampler16.stop();
|
mResampler16.stop();
|
||||||
mResampler32.stop();
|
mResampler32.stop();
|
||||||
|
|
@ -354,46 +356,110 @@ AudioReceiver::~AudioReceiver()
|
||||||
mDecodedDump.reset();
|
mDecodedDump.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update codec settings
|
||||||
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec)
|
void AudioReceiver::setCodecSettings(const CodecList::Settings& codecSettings)
|
||||||
{
|
{
|
||||||
|
if (mCodecSettings == codecSettings)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCodecSettings = codecSettings;
|
||||||
|
mCodecList.setSettings(mCodecSettings); // This builds factory list with proper payload types according to payload types in settings
|
||||||
|
|
||||||
|
// Rebuild codec map from factory list
|
||||||
|
mCodecList.fillCodecMap(mCodecMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodecList::Settings& AudioReceiver::getCodecSettings()
|
||||||
|
{
|
||||||
|
return mCodecSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t output_capacity)
|
||||||
|
{
|
||||||
|
// How much data was produced
|
||||||
|
size_t result = 0;
|
||||||
|
|
||||||
|
// Handle here regular RTP packets
|
||||||
|
// Check if payload length is ok
|
||||||
|
int tail = codec.rtpLength() ? p.GetPayloadLength() % codec.rtpLength() : 0;
|
||||||
|
|
||||||
|
if (!tail)
|
||||||
|
{
|
||||||
|
// Find number of frames
|
||||||
|
int frame_count = codec.rtpLength() ? p.GetPayloadLength() / codec.rtpLength() : 1;
|
||||||
|
int frame_length = codec.rtpLength() ? codec.rtpLength() : (int)p.GetPayloadLength();
|
||||||
|
|
||||||
|
// Save last packet time length
|
||||||
|
// mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
|
||||||
|
|
||||||
|
// Decode
|
||||||
|
|
||||||
|
for (int i=0; i < frame_count; i++)
|
||||||
|
{
|
||||||
|
auto decoded_length = codec.decode(p.GetPayloadData() + i * codec.rtpLength(),
|
||||||
|
frame_length,
|
||||||
|
output_buffer,
|
||||||
|
output_capacity);
|
||||||
|
|
||||||
|
result += decoded_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ICELogMedia(<< "RTP packet with tail.");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** detectedCodec)
|
||||||
|
{
|
||||||
|
// Estimate time length
|
||||||
|
int time_length = 0,
|
||||||
|
samplerate = 8000,
|
||||||
|
payloadLength = p->GetPayloadLength(),
|
||||||
|
ptype = p->GetPayloadType();
|
||||||
|
|
||||||
|
// ICELogInfo(<< "Adding packet No " << p->GetSequenceNumber());
|
||||||
// Increase codec counter
|
// Increase codec counter
|
||||||
mStat.mCodecCount[p->GetPayloadType()]++;
|
mStat.mCodecCount[ptype]++;
|
||||||
|
|
||||||
// Check if codec can be handled
|
// Check if codec can be handled
|
||||||
CodecMap::iterator codecIter = mCodecMap.find(p->GetPayloadType());
|
Codec* codec = nullptr;
|
||||||
|
CodecMap::iterator codecIter = mCodecMap.find(ptype);
|
||||||
if (codecIter == mCodecMap.end())
|
if (codecIter == mCodecMap.end())
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "Cannot find codec in available codecs");
|
time_length = 10;
|
||||||
return false; // Reject packet with unknown payload type
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Check if codec is created actually
|
{
|
||||||
|
// Check if codec is creating lazily
|
||||||
if (!codecIter->second)
|
if (!codecIter->second)
|
||||||
{
|
{
|
||||||
// Look for ptype
|
codecIter->second = mCodecList.createCodecByPayloadType(ptype);
|
||||||
for (int codecIndex = 0; codecIndex < mCodecList.count(); codecIndex++)
|
|
||||||
if (mCodecList.codecAt(codecIndex).payloadType() == p->GetPayloadType())
|
|
||||||
codecIter->second = mCodecList.codecAt(codecIndex).create();
|
|
||||||
}
|
}
|
||||||
|
codec = codecIter->second.get();
|
||||||
|
|
||||||
// Return pointer to codec if needed.get()
|
// Return pointer to codec if needed.get()
|
||||||
if (codec)
|
if (detectedCodec)
|
||||||
*codec = codecIter->second.get();
|
*detectedCodec = codec;
|
||||||
|
|
||||||
if (mStat.mCodecName.empty())
|
if (mStat.mCodecName.empty() && codec)
|
||||||
mStat.mCodecName = codecIter->second->name();
|
mStat.mCodecName = codec->name();
|
||||||
|
|
||||||
// Estimate time length
|
|
||||||
int time_length = 0, payloadLength = p->GetPayloadLength(), ptype = p->GetPayloadType();
|
|
||||||
|
|
||||||
if (!codecIter->second->rtpLength())
|
if (!codec)
|
||||||
time_length = codecIter->second->frameTime();
|
time_length = 10;
|
||||||
else
|
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
|
// Process jitter
|
||||||
mJitterStats.process(p.get(), codecIter->second->samplerate());
|
mJitterStats.process(p.get(), samplerate);
|
||||||
mStat.mJitter = static_cast<float>(mJitterStats.get());
|
mStat.mJitter = static_cast<float>(mJitterStats.get());
|
||||||
|
|
||||||
// Check if packet is CNG
|
// Check if packet is CNG
|
||||||
|
|
@ -405,12 +471,30 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** co
|
||||||
{
|
{
|
||||||
// It will cause statistics to report about bad RTP packet
|
// It will cause statistics to report about bad RTP packet
|
||||||
// I have to replay last packet payload here to avoid report about lost packet
|
// I have to replay last packet payload here to avoid report about lost packet
|
||||||
mBuffer.add(p, time_length, codecIter->second->samplerate());
|
mBuffer.add(p, time_length, samplerate);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue packet to buffer
|
// Queue packet to buffer
|
||||||
return mBuffer.add(p, time_length, codecIter->second->samplerate());
|
auto packet = mBuffer.add(p, time_length, samplerate).get();
|
||||||
|
|
||||||
|
if (packet)
|
||||||
|
{
|
||||||
|
// Check if early decoding configured
|
||||||
|
if (mEarlyDecode && codec)
|
||||||
|
{
|
||||||
|
// Move data to packet buffer
|
||||||
|
size_t available = decode_packet(*codec, *p, mDecodedFrame, sizeof mDecodedFrame);
|
||||||
|
if (available > 0)
|
||||||
|
{
|
||||||
|
packet->pcm().resize(available / 2);
|
||||||
|
memcpy(packet->pcm().data(), mDecodedFrame, available / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
|
void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
|
||||||
|
|
@ -424,18 +508,14 @@ void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
|
||||||
makeMonoAndResample(resample ? mCodec->samplerate() : 0,
|
makeMonoAndResample(resample ? mCodec->samplerate() : 0,
|
||||||
mCodec->channels());
|
mCodec->channels());
|
||||||
|
|
||||||
// Update PVQA with stats
|
|
||||||
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
|
||||||
updatePvqa(mResampledFrame, mResampledLength);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Send to output
|
// Send to output
|
||||||
output.add(mResampledFrame, mResampledLength);
|
output.add(mResampledFrame, mResampledLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
||||||
{
|
{
|
||||||
bool result = false;
|
DecodeResult result = DecodeResult_Skip;
|
||||||
|
bool had_decode = false;
|
||||||
|
|
||||||
// Get next packet from buffer
|
// Get next packet from buffer
|
||||||
RtpBuffer::ResultList rl;
|
RtpBuffer::ResultList rl;
|
||||||
|
|
@ -443,7 +523,7 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
||||||
switch (fr)
|
switch (fr)
|
||||||
{
|
{
|
||||||
case RtpBuffer::FetchResult::Gap:
|
case RtpBuffer::FetchResult::Gap:
|
||||||
ICELogInfo(<< "Gap detected.");
|
ICELogDebug(<< "Gap detected.");
|
||||||
|
|
||||||
mDecodedLength = mResampledLength = 0;
|
mDecodedLength = mResampledLength = 0;
|
||||||
if (mCngPacket && mCodec)
|
if (mCngPacket && mCodec)
|
||||||
|
|
@ -460,36 +540,41 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
||||||
if (options & DecodeOptions_SkipDecode)
|
if (options & DecodeOptions_SkipDecode)
|
||||||
mDecodedLength = 0;
|
mDecodedLength = 0;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame);
|
mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame);
|
||||||
|
if (!mDecodedLength)
|
||||||
|
{
|
||||||
|
// PLC is not support or failed
|
||||||
|
// So substitute the silence
|
||||||
|
size_t nr_of_samples = mCodec->frameTime() * mCodec->samplerate() / 1000 * sizeof(short);
|
||||||
|
mDecodedLength = nr_of_samples * sizeof(short);
|
||||||
|
memset(mDecodedFrame, 0, mDecodedLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDecodedLength)
|
if (mDecodedLength)
|
||||||
{
|
{
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
result = true;
|
result = DecodeResult_Ok;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RtpBuffer::FetchResult::NoPacket:
|
case RtpBuffer::FetchResult::NoPacket:
|
||||||
ICELogDebug(<< "No packet available in jitter buffer");
|
ICELogDebug(<< "No packet available in jitter buffer");
|
||||||
mFailedCount++;
|
mFailedCount++;
|
||||||
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
|
||||||
if (mResampledLength > 0)
|
|
||||||
updatePvqa(nullptr, mResampledLength);
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RtpBuffer::FetchResult::RegularPacket:
|
case RtpBuffer::FetchResult::RegularPacket:
|
||||||
mFailedCount = 0;
|
mFailedCount = 0;
|
||||||
|
for (std::shared_ptr<RtpBuffer::Packet>& p: rl)
|
||||||
for (std::shared_ptr<RTPPacket>& p: rl)
|
|
||||||
{
|
{
|
||||||
assert(p);
|
assert(p);
|
||||||
// Check if previously CNG packet was detected. Emit CNG audio here if needed.
|
// Check if previously CNG packet was detected. Emit CNG audio here if needed.
|
||||||
if (options & DecodeOptions_FillCngGap && mCngPacket && mCodec)
|
if (options & DecodeOptions_FillCngGap && mCngPacket && mCodec)
|
||||||
{
|
{
|
||||||
// Fill CNG audio is server mode is present
|
// Fill CNG audio is server mode is present
|
||||||
int units = p->GetTimestamp() - mCngPacket->GetTimestamp();
|
int units = p->rtp()->GetTimestamp() - mCngPacket->GetTimestamp();
|
||||||
int milliseconds = units / (mCodec->samplerate() / 1000);
|
int milliseconds = units / (mCodec->samplerate() / 1000);
|
||||||
if (milliseconds > mLastPacketTimeLength)
|
if (milliseconds > mLastPacketTimeLength)
|
||||||
{
|
{
|
||||||
|
|
@ -518,33 +603,41 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
||||||
if (mDecodedLength)
|
if (mDecodedLength)
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
}
|
}
|
||||||
result = true;
|
result = DecodeResult_Ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find codec
|
if (mEarlyDecode)
|
||||||
mCodec = mCodecMap[p->GetPayloadType()];
|
{
|
||||||
|
// ToDo - copy the decoded data to output buffer
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find codec by payload type
|
||||||
|
int ptype = p->rtp()->GetPayloadType();
|
||||||
|
mCodec = mCodecMap[ptype];
|
||||||
if (mCodec)
|
if (mCodec)
|
||||||
{
|
{
|
||||||
if (rate)
|
if (rate)
|
||||||
*rate = mCodec->samplerate();
|
*rate = mCodec->samplerate();
|
||||||
|
|
||||||
// Check if it is CNG packet
|
// Check if it is CNG packet
|
||||||
if ((p->GetPayloadType() == 0 || p->GetPayloadType() == 8) && p->GetPayloadLength() >= 1 && p->GetPayloadLength() <= 6)
|
if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6)
|
||||||
{
|
{
|
||||||
if (options & DecodeOptions_SkipDecode)
|
if (options & DecodeOptions_SkipDecode)
|
||||||
mDecodedLength = 0;
|
mDecodedLength = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mCngPacket = p;
|
mCngPacket = p->rtp();
|
||||||
mCngDecoder.decode3389(p->GetPayloadData(), p->GetPayloadLength());
|
mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength());
|
||||||
// Emit CNG mLastPacketLength milliseconds
|
// Emit CNG mLastPacketLength milliseconds
|
||||||
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength,
|
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength,
|
||||||
(short*)mDecodedFrame, true);
|
(short*)mDecodedFrame, true);
|
||||||
if (mDecodedLength)
|
if (mDecodedLength)
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
}
|
}
|
||||||
result = true;
|
result = DecodeResult_Ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -553,13 +646,16 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
||||||
|
|
||||||
// Handle here regular RTP packets
|
// Handle here regular RTP packets
|
||||||
// Check if payload length is ok
|
// Check if payload length is ok
|
||||||
int tail = mCodec->rtpLength() ? p->GetPayloadLength() % mCodec->rtpLength() : 0;
|
size_t payload_length = p->rtp()->GetPayloadLength();
|
||||||
|
size_t rtp_frame_length = mCodec->rtpLength();
|
||||||
|
|
||||||
|
int tail = rtp_frame_length ? payload_length % rtp_frame_length : 0;
|
||||||
|
|
||||||
if (!tail)
|
if (!tail)
|
||||||
{
|
{
|
||||||
// Find number of frames
|
// Find number of frames
|
||||||
mFrameCount = mCodec->rtpLength() ? p->GetPayloadLength() / mCodec->rtpLength() : 1;
|
mFrameCount = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() / mCodec->rtpLength() : 1;
|
||||||
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->GetPayloadLength();
|
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->rtp()->GetPayloadLength();
|
||||||
|
|
||||||
// Save last packet time length
|
// Save last packet time length
|
||||||
mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
|
mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
|
||||||
|
|
@ -571,20 +667,27 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
||||||
mDecodedLength = 0;
|
mDecodedLength = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Trigger the statistics
|
||||||
|
had_decode = true;
|
||||||
|
|
||||||
// Decode frame by frame
|
// Decode frame by frame
|
||||||
mDecodedLength = mCodec->decode(p->GetPayloadData() + i*mCodec->rtpLength(),
|
mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
|
||||||
frameLength, mDecodedFrame, sizeof mDecodedFrame);
|
frameLength, mDecodedFrame, sizeof mDecodedFrame);
|
||||||
if (mDecodedLength)
|
if (mDecodedLength > 0)
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = mFrameCount > 0;
|
result = mFrameCount > 0 ? DecodeResult_Ok : DecodeResult_Skip;
|
||||||
|
|
||||||
// Check for bitrate counter
|
// Check for bitrate counter
|
||||||
processStatisticsWithAmrCodec(mCodec.get());
|
processStatisticsWithAmrCodec(mCodec.get());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ICELogMedia(<< "RTP packet with tail.");
|
{
|
||||||
|
result = DecodeResult_BadPacket;
|
||||||
|
// ICELogMedia(<< "RTP packet with tail.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -594,6 +697,18 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (had_decode)
|
||||||
|
{
|
||||||
|
// mStat.mDecodeRequested++;
|
||||||
|
if (mLastDecodeTime == 0.0)
|
||||||
|
mLastDecodeTime = now_ms();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float t = now_ms();
|
||||||
|
mStat.mDecodingInterval.process(t - mLastDecodeTime);
|
||||||
|
mLastDecodeTime = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -639,62 +754,6 @@ Codec* AudioReceiver::findCodec(int payloadType)
|
||||||
return codecIter->second.get();
|
return codecIter->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
|
||||||
void AudioReceiver::initPvqa()
|
|
||||||
{
|
|
||||||
// Allocate space for 20 seconds audio
|
|
||||||
if (!mPvqaBuffer)
|
|
||||||
{
|
|
||||||
mPvqaBuffer = std::make_shared<Audio::DataWindow>();
|
|
||||||
mPvqaBuffer->setCapacity(Audio::Format().sizeFromTime(30000));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instantiate & open PVQA analyzer
|
|
||||||
if (!mPVQA)
|
|
||||||
{
|
|
||||||
mPVQA = std::make_shared<sevana::pvqa>();
|
|
||||||
mPVQA->open(AUDIO_SAMPLERATE, 1, PVQA_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioReceiver::updatePvqa(const void *data, int size)
|
|
||||||
{
|
|
||||||
if (!mPVQA)
|
|
||||||
initPvqa();
|
|
||||||
|
|
||||||
if (mPVQA)
|
|
||||||
{
|
|
||||||
if (data)
|
|
||||||
mPvqaBuffer->add(data, size);
|
|
||||||
else
|
|
||||||
mPvqaBuffer->addZero(size);
|
|
||||||
|
|
||||||
Audio::Format fmt;
|
|
||||||
int frames = (int)fmt.timeFromSize(mPvqaBuffer->filled()) / (PVQA_INTERVAL * 1000);
|
|
||||||
if (frames > 0)
|
|
||||||
{
|
|
||||||
int time4pvqa = (int)(frames * PVQA_INTERVAL * 1000);
|
|
||||||
int size4pvqa = (int)fmt.sizeFromTime(time4pvqa);
|
|
||||||
ICELogInfo(<< "PVQA buffer has " << time4pvqa << " milliseconds of audio.");
|
|
||||||
mPVQA->update(mPvqaBuffer->data(), size4pvqa);
|
|
||||||
mPvqaBuffer->erase(size4pvqa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float AudioReceiver::calculatePvqaMos(int rate, std::string& report)
|
|
||||||
{
|
|
||||||
if (mPVQA && mPvqaBuffer)
|
|
||||||
{
|
|
||||||
sevana::pvqa::result result;
|
|
||||||
if (mPVQA->get_result(result)) {
|
|
||||||
report = result.mReport;
|
|
||||||
return result.mMos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void AudioReceiver::processStatisticsWithAmrCodec(Codec* c)
|
void AudioReceiver::processStatisticsWithAmrCodec(Codec* c)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -8,25 +8,16 @@
|
||||||
|
|
||||||
#include "MT_Stream.h"
|
#include "MT_Stream.h"
|
||||||
#include "MT_CodecList.h"
|
#include "MT_CodecList.h"
|
||||||
#include "MT_AudioCodec.h"
|
|
||||||
#include "MT_CngHelper.h"
|
#include "MT_CngHelper.h"
|
||||||
#include "../helper/HL_Pointer.h"
|
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "../helper/HL_Optional.hpp"
|
|
||||||
|
|
||||||
#include "jrtplib/src/rtppacket.h"
|
#include "jrtplib/src/rtppacket.h"
|
||||||
#include "jrtplib/src/rtcppacket.h"
|
|
||||||
#include "jrtplib/src/rtpsourcedata.h"
|
#include "jrtplib/src/rtpsourcedata.h"
|
||||||
#include "../audio/Audio_DataWindow.h"
|
#include "../audio/Audio_DataWindow.h"
|
||||||
#include "../audio/Audio_Resampler.h"
|
#include "../audio/Audio_Resampler.h"
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY)
|
#include <optional>
|
||||||
# include "pvqa++.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
// #define DUMP_DECODED
|
|
||||||
|
|
||||||
namespace MT
|
namespace MT
|
||||||
{
|
{
|
||||||
|
|
@ -47,12 +38,17 @@ namespace MT
|
||||||
public:
|
public:
|
||||||
Packet(const std::shared_ptr<RTPPacket>& packet, int timelen, int rate);
|
Packet(const std::shared_ptr<RTPPacket>& packet, int timelen, int rate);
|
||||||
std::shared_ptr<RTPPacket> rtp() const;
|
std::shared_ptr<RTPPacket> rtp() const;
|
||||||
|
|
||||||
int timelength() const;
|
int timelength() const;
|
||||||
int rate() const;
|
int rate() const;
|
||||||
|
|
||||||
|
const std::vector<short>& pcm() const;
|
||||||
|
std::vector<short>& pcm();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<RTPPacket> mRtp;
|
std::shared_ptr<RTPPacket> mRtp;
|
||||||
int mTimelength = 0, mRate = 0;
|
int mTimelength = 0, mRate = 0;
|
||||||
|
std::vector<short> mPcm;
|
||||||
};
|
};
|
||||||
|
|
||||||
RtpBuffer(Statistics& stat);
|
RtpBuffer(Statistics& stat);
|
||||||
|
|
@ -60,35 +56,49 @@ namespace MT
|
||||||
|
|
||||||
unsigned ssrc();
|
unsigned ssrc();
|
||||||
void setSsrc(unsigned ssrc);
|
void setSsrc(unsigned ssrc);
|
||||||
|
|
||||||
void setHigh(int milliseconds);
|
void setHigh(int milliseconds);
|
||||||
int high();
|
int high();
|
||||||
|
|
||||||
void setLow(int milliseconds);
|
void setLow(int milliseconds);
|
||||||
int low();
|
int low();
|
||||||
|
|
||||||
void setPrebuffer(int milliseconds);
|
void setPrebuffer(int milliseconds);
|
||||||
int prebuffer();
|
int prebuffer();
|
||||||
|
|
||||||
int getNumberOfReturnedPackets() const;
|
int getNumberOfReturnedPackets() const;
|
||||||
int getNumberOfAddPackets() const;
|
int getNumberOfAddPackets() const;
|
||||||
|
|
||||||
int findTimelength();
|
int findTimelength();
|
||||||
int getCount() const;
|
int getCount() const;
|
||||||
// Returns false if packet was not add - maybe too old or too new or duplicate
|
|
||||||
bool add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
|
|
||||||
|
|
||||||
typedef std::vector<std::shared_ptr<RTPPacket>> ResultList;
|
// Returns false if packet was not add - maybe too old or too new or duplicate
|
||||||
|
std::shared_ptr<Packet> add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
|
||||||
|
|
||||||
|
typedef std::vector<std::shared_ptr<Packet>> ResultList;
|
||||||
typedef std::shared_ptr<ResultList> PResultList;
|
typedef std::shared_ptr<ResultList> PResultList;
|
||||||
|
|
||||||
FetchResult fetch(ResultList& rl);
|
FetchResult fetch(ResultList& rl);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned mSsrc;
|
unsigned mSsrc = 0;
|
||||||
int mHigh, mLow, mPrebuffer;
|
int mHigh = RTP_BUFFER_HIGH,
|
||||||
int mReturnedCounter, mAddCounter;
|
mLow = RTP_BUFFER_LOW,
|
||||||
|
mPrebuffer = RTP_BUFFER_PREBUFFER;
|
||||||
|
int mReturnedCounter = 0,
|
||||||
|
mAddCounter = 0;
|
||||||
|
|
||||||
mutable Mutex mGuard;
|
mutable Mutex mGuard;
|
||||||
typedef std::vector<Packet> PacketList;
|
typedef std::vector<std::shared_ptr<Packet>> PacketList;
|
||||||
PacketList mPacketList;
|
PacketList mPacketList;
|
||||||
Statistics& mStat;
|
Statistics& mStat;
|
||||||
bool mFirstPacketWillGo;
|
bool mFirstPacketWillGo = true;
|
||||||
jrtplib::RTPSourceStats mRtpStats;
|
jrtplib::RTPSourceStats mRtpStats;
|
||||||
Packet mFetchedPacket;
|
std::shared_ptr<Packet> mFetchedPacket;
|
||||||
|
std::optional<uint32_t> mLastSeqno;
|
||||||
|
|
||||||
|
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
|
||||||
|
float mLastAddTime = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Receiver
|
class Receiver
|
||||||
|
|
@ -107,6 +117,9 @@ namespace MT
|
||||||
AudioReceiver(const CodecList::Settings& codecSettings, Statistics& stat);
|
AudioReceiver(const CodecList::Settings& codecSettings, Statistics& stat);
|
||||||
~AudioReceiver();
|
~AudioReceiver();
|
||||||
|
|
||||||
|
// Update codec settings
|
||||||
|
void setCodecSettings(const CodecList::Settings& codecSettings);
|
||||||
|
CodecList::Settings& getCodecSettings();
|
||||||
// Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding.
|
// Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding.
|
||||||
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
|
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
|
||||||
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
|
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
|
||||||
|
|
@ -120,7 +133,14 @@ namespace MT
|
||||||
DecodeOptions_SkipDecode = 4
|
DecodeOptions_SkipDecode = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
bool getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr);
|
enum DecodeResult
|
||||||
|
{
|
||||||
|
DecodeResult_Ok,
|
||||||
|
DecodeResult_Skip,
|
||||||
|
DecodeResult_BadPacket
|
||||||
|
};
|
||||||
|
|
||||||
|
DecodeResult getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr);
|
||||||
|
|
||||||
// Looks for codec by payload type
|
// Looks for codec by payload type
|
||||||
Codec* findCodec(int payloadType);
|
Codec* findCodec(int payloadType);
|
||||||
|
|
@ -146,6 +166,9 @@ namespace MT
|
||||||
std::shared_ptr<jrtplib::RTPPacket> mCngPacket;
|
std::shared_ptr<jrtplib::RTPPacket> mCngPacket;
|
||||||
CngDecoder mCngDecoder;
|
CngDecoder mCngDecoder;
|
||||||
|
|
||||||
|
// Decode RTP early, do not wait for speaker callback
|
||||||
|
bool mEarlyDecode = false;
|
||||||
|
|
||||||
// Buffer to hold decoded data
|
// Buffer to hold decoded data
|
||||||
char mDecodedFrame[65536];
|
char mDecodedFrame[65536];
|
||||||
int mDecodedLength = 0;
|
int mDecodedLength = 0;
|
||||||
|
|
@ -161,27 +184,23 @@ namespace MT
|
||||||
// Last packet time length
|
// Last packet time length
|
||||||
int mLastPacketTimeLength = 0;
|
int mLastPacketTimeLength = 0;
|
||||||
|
|
||||||
int mFailedCount;
|
int mFailedCount = 0;
|
||||||
Audio::Resampler mResampler8, mResampler16,
|
Audio::Resampler mResampler8, mResampler16,
|
||||||
mResampler32, mResampler48;
|
mResampler32, mResampler48;
|
||||||
|
|
||||||
Audio::PWavFileWriter mDecodedDump;
|
Audio::PWavFileWriter mDecodedDump;
|
||||||
|
|
||||||
|
float mLastDecodeTime = 0.0; // Time last call happened to codec->decode()
|
||||||
|
|
||||||
|
float mIntervalSum = 0.0;
|
||||||
|
int mIntervalCount = 0;
|
||||||
|
|
||||||
// Zero rate will make audio mono but resampling will be skipped
|
// Zero rate will make audio mono but resampling will be skipped
|
||||||
void makeMonoAndResample(int rate, int channels);
|
void makeMonoAndResample(int rate, int channels);
|
||||||
|
|
||||||
// Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame
|
// Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame
|
||||||
void processDecoded(Audio::DataWindow& output, int options);
|
void processDecoded(Audio::DataWindow& output, int options);
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
|
||||||
std::shared_ptr<sevana::pvqa> mPVQA;
|
|
||||||
void initPvqa();
|
|
||||||
void updatePvqa(const void* data, int size);
|
|
||||||
float calculatePvqaMos(int rate, std::string& report);
|
|
||||||
|
|
||||||
std::shared_ptr<Audio::DataWindow> mPvqaBuffer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void processStatisticsWithAmrCodec(Codec* c);
|
void processStatisticsWithAmrCodec(Codec* c);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ AudioStream::~AudioStream()
|
||||||
if (mFinalStatistics)
|
if (mFinalStatistics)
|
||||||
*mFinalStatistics = mStat;
|
*mFinalStatistics = mStat;
|
||||||
|
|
||||||
ICELogInfo(<< mStat.toShortString());
|
ICELogInfo(<< mStat.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)
|
void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)
|
||||||
|
|
@ -296,9 +296,9 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||||
assert(info);
|
assert(info);
|
||||||
|
|
||||||
// Drop RTP packets if stream is not receiving now; let RTCP go
|
// Drop RTP packets if stream is not receiving now; let RTCP go
|
||||||
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtp(buffer, length))
|
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtpOrRtcp(buffer, length))
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the packet");
|
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the RT(C)P packet");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -306,21 +306,23 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||||
int receiveLength = length;
|
int receiveLength = length;
|
||||||
memcpy(mReceiveBuffer, buffer, length);
|
memcpy(mReceiveBuffer, buffer, length);
|
||||||
|
|
||||||
bool srtpResult;
|
|
||||||
if (mSrtpSession.active())
|
if (mSrtpSession.active())
|
||||||
{
|
{
|
||||||
|
bool srtpResult;
|
||||||
|
size_t srcLength = length; size_t dstLength = sizeof mSrtpDecodeBuffer;
|
||||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength);
|
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
||||||
else
|
else
|
||||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength);
|
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
||||||
if (!srtpResult)
|
if (!srtpResult)
|
||||||
{
|
{
|
||||||
ICELogError(<<"Cannot decrypt SRTP packet.");
|
ICELogError(<<"Cannot decrypt SRTP packet.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//ICELogDebug(<< "Packet no: " << RtpHelper::findPacketNo(mReceiveBuffer, receiveLength));
|
memcpy(mReceiveBuffer, mSrtpDecodeBuffer, dstLength);
|
||||||
|
receiveLength = dstLength;
|
||||||
|
}
|
||||||
|
|
||||||
switch (source.family())
|
switch (source.family())
|
||||||
{
|
{
|
||||||
|
|
@ -345,8 +347,8 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||||
mStat.mReceived += length;
|
mStat.mReceived += length;
|
||||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||||
{
|
{
|
||||||
if (!mStat.mFirstRtpTime.is_initialized())
|
if (!mStat.mFirstRtpTime)
|
||||||
mStat.mFirstRtpTime = std::chrono::system_clock::now();
|
mStat.mFirstRtpTime = std::chrono::steady_clock::now();
|
||||||
mStat.mReceivedRtp++;
|
mStat.mReceivedRtp++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -360,6 +362,7 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||||
if (packet)
|
if (packet)
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "jrtplib returned packet");
|
ICELogMedia(<< "jrtplib returned packet");
|
||||||
|
|
||||||
// Find right handler for rtp stream
|
// Find right handler for rtp stream
|
||||||
SingleAudioStream* rtpStream = nullptr;
|
SingleAudioStream* rtpStream = nullptr;
|
||||||
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());
|
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef __MT_AUDIOSTREAM_H
|
#ifndef __MT_AUDIOSTREAM_H
|
||||||
#define __MT_AUDIOSTREAM_H
|
#define __MT_AUDIOSTREAM_H
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "MT_Stream.h"
|
#include "MT_Stream.h"
|
||||||
#include "MT_NativeRtpSender.h"
|
#include "MT_NativeRtpSender.h"
|
||||||
#include "MT_SingleAudioStream.h"
|
#include "MT_SingleAudioStream.h"
|
||||||
|
|
@ -87,7 +87,8 @@ namespace MT
|
||||||
mCaptureResampler32,
|
mCaptureResampler32,
|
||||||
mCaptureResampler48;
|
mCaptureResampler48;
|
||||||
DtmfContext mDtmfContext;
|
DtmfContext mDtmfContext;
|
||||||
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE];
|
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE],
|
||||||
|
mSrtpDecodeBuffer[MAX_VALID_UDPPACKET_SIZE];
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,13 @@ Terminal::~Terminal()
|
||||||
mAudioPair.reset();
|
mAudioPair.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
PStream Terminal::createStream(int type, VariantMap& config)
|
PStream Terminal::createStream(int type, VariantMap& /*config*/)
|
||||||
{
|
{
|
||||||
PStream result;
|
PStream result;
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case Stream::Audio:
|
case Stream::Audio:
|
||||||
result = PStream(new AudioStream(MT::CodecList::Settings::DefaultSettings));
|
result = std::make_shared<AudioStream>(MT::CodecList::Settings::DefaultSettings);
|
||||||
mAudioList.add(result);
|
mAudioList.add(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ PStream Terminal::createStream(int type, VariantMap& config)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::freeStream(PStream stream)
|
void Terminal::freeStream(const PStream& stream)
|
||||||
{
|
{
|
||||||
if (AudioStream* audio = dynamic_cast<AudioStream*>(stream.get()))
|
if (AudioStream* audio = dynamic_cast<AudioStream*>(stream.get()))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,11 @@ namespace MT
|
||||||
CodecList& codeclist();
|
CodecList& codeclist();
|
||||||
|
|
||||||
PStream createStream(int type, VariantMap& config);
|
PStream createStream(int type, VariantMap& config);
|
||||||
void freeStream(PStream s);
|
void freeStream(const PStream& s);
|
||||||
|
|
||||||
Audio::PDevicePair audio();
|
Audio::PDevicePair audio();
|
||||||
void setAudio(const Audio::PDevicePair& audio);
|
void setAudio(const Audio::PDevicePair& audio);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
StreamList mAudioList;
|
StreamList mAudioList;
|
||||||
std::mutex mAudioListMutex;
|
std::mutex mAudioListMutex;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
|
|
||||||
#include "MT_CngHelper.h"
|
#include "MT_CngHelper.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ int Codec::Factory::channels()
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
void Codec::Factory::create(CodecMap& codecs)
|
void Codec::Factory::create(CodecMap& codecs)
|
||||||
{
|
{
|
||||||
codecs[payloadType()] = std::shared_ptr<Codec>(create());
|
codecs[payloadType()] = std::shared_ptr<Codec>(create());
|
||||||
|
|
@ -38,4 +36,4 @@ int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecC
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@
|
||||||
#ifndef __MT_CODEC_H
|
#ifndef __MT_CODEC_H
|
||||||
#define __MT_CODEC_H
|
#define __MT_CODEC_H
|
||||||
|
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
#include "resiprocate/resip/stack/SdpContents.hxx"
|
#include "resiprocate/resip/stack/SdpContents.hxx"
|
||||||
#endif
|
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "../helper/HL_Pointer.h"
|
#include "../helper/HL_Pointer.h"
|
||||||
|
|
@ -36,14 +34,12 @@ public:
|
||||||
virtual PCodec create() = 0;
|
virtual PCodec create() = 0;
|
||||||
|
|
||||||
virtual int channels();
|
virtual int channels();
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
typedef std::map<int, PCodec > CodecMap;
|
typedef std::map<int, PCodec > CodecMap;
|
||||||
virtual void create(CodecMap& codecs);
|
virtual void create(CodecMap& codecs);
|
||||||
virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
// Returns payload type from chosen codec if success. -1 is returned for negative result.
|
// Returns payload type from chosen codec if success. -1 is returned for negative result.
|
||||||
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||||
resip::Codec resipCodec();
|
resip::Codec resipCodec();
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
virtual ~Codec() {}
|
virtual ~Codec() {}
|
||||||
virtual const char* name() = 0;
|
virtual const char* name() = 0;
|
||||||
|
|
@ -63,8 +59,13 @@ public:
|
||||||
virtual int channels() { return 1; }
|
virtual int channels() { return 1; }
|
||||||
|
|
||||||
|
|
||||||
|
// Returns size of encoded data (RTP) in bytes
|
||||||
virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
|
virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
|
||||||
|
|
||||||
|
// Returns size of decoded data (PCM signed short) in bytes
|
||||||
virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
|
virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
|
||||||
|
|
||||||
|
// Returns size of produced data (PCM signed short) in bytes
|
||||||
virtual int plc(int lostFrames, void* output, int outputCapacity) = 0;
|
virtual int plc(int lostFrames, void* output, int outputCapacity) = 0;
|
||||||
|
|
||||||
// Returns size of codec in memory
|
// Returns size of codec in memory
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
/* Copyright(C) 2007-2021 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "MT_CodecList.h"
|
#include "MT_CodecList.h"
|
||||||
#include "MT_AudioCodec.h"
|
#include "MT_AudioCodec.h"
|
||||||
|
|
||||||
|
|
@ -22,9 +23,125 @@
|
||||||
|
|
||||||
using namespace MT;
|
using namespace MT;
|
||||||
|
|
||||||
using strx = StringHelper;
|
using strx = strx;
|
||||||
|
|
||||||
|
|
||||||
|
bool CodecList::Settings::contains(int ptype) const
|
||||||
|
{
|
||||||
|
if (ptype >= 0 && ptype < 96)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (mAmrNbOctetPayloadType.contains(ptype))
|
||||||
|
return true;
|
||||||
|
if (mAmrNbPayloadType.contains(ptype))
|
||||||
|
return true;
|
||||||
|
if (mAmrWbOctetPayloadType.contains(ptype))
|
||||||
|
return true;
|
||||||
|
if (mAmrWbPayloadType.contains(ptype))
|
||||||
|
return true;
|
||||||
|
for (const auto& s: mOpusSpec)
|
||||||
|
if (s.mPayloadType == ptype)
|
||||||
|
return true;
|
||||||
|
for (const auto& s: mEvsSpec)
|
||||||
|
if (s.mPayloadType == ptype)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (mIsac16KPayloadType == ptype || mIsac32KPayloadType == ptype)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (mIlbc20PayloadType == ptype || mIlbc30PayloadType == ptype)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CodecList::Settings::toString() const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
// oss << "wrap IuUP: " << mWrapIuUP << std::endl
|
||||||
|
// << "skip decode: " << mSkipDecode << std::endl;
|
||||||
|
|
||||||
|
if (!mAmrWbPayloadType.empty())
|
||||||
|
{
|
||||||
|
oss << "AMR WB ptype: ";
|
||||||
|
for (int ptype: mAmrWbPayloadType)
|
||||||
|
oss << ptype << " ";
|
||||||
|
}
|
||||||
|
if (!mAmrWbOctetPayloadType.empty())
|
||||||
|
{
|
||||||
|
oss << "AMR WB octet ptype: ";
|
||||||
|
for (int ptype: mAmrWbOctetPayloadType)
|
||||||
|
oss << ptype << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mAmrNbPayloadType.empty())
|
||||||
|
{
|
||||||
|
oss << "AMR ptype: ";
|
||||||
|
for (int ptype: mAmrNbPayloadType)
|
||||||
|
oss << ptype << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mAmrNbOctetPayloadType.empty())
|
||||||
|
{
|
||||||
|
oss << "AMR octet ptype: ";
|
||||||
|
for (int ptype: mAmrNbOctetPayloadType)
|
||||||
|
oss << ptype << " ";
|
||||||
|
}
|
||||||
|
if (mIsac16KPayloadType != -1)
|
||||||
|
oss << "ISAC 16Khz ptype: " << mIsac16KPayloadType << " ";
|
||||||
|
|
||||||
|
if (mIsac32KPayloadType != -1)
|
||||||
|
oss << "ISAC 32Khz ptype: " << mIsac32KPayloadType << " ";
|
||||||
|
|
||||||
|
if (mIlbc20PayloadType != -1)
|
||||||
|
oss << "iLBC 20ms ptype: " << mIlbc20PayloadType << " ";
|
||||||
|
|
||||||
|
if (mIlbc30PayloadType != -1)
|
||||||
|
oss << "iLBC 30ms ptype: " << mIlbc30PayloadType << " ";
|
||||||
|
|
||||||
|
if (mGsmFrPayloadType != -1)
|
||||||
|
oss << "GSM FR ptype: " << mGsmFrPayloadType << ", GSM FR plength: " << mGsmFrPayloadLength << " ";
|
||||||
|
|
||||||
|
if (mGsmHrPayloadType != -1)
|
||||||
|
oss << "GSM HR ptype: " << mGsmHrPayloadType << " ";
|
||||||
|
|
||||||
|
if (mGsmEfrPayloadType != -1)
|
||||||
|
oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " ";
|
||||||
|
|
||||||
|
for (auto& spec: mEvsSpec)
|
||||||
|
{
|
||||||
|
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& spec: mOpusSpec)
|
||||||
|
{
|
||||||
|
oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodecList::Settings::clear()
|
||||||
|
{
|
||||||
|
// Remove all dynamic payload type assignments
|
||||||
|
mEvsSpec.clear();
|
||||||
|
mOpusSpec.clear();
|
||||||
|
mAmrNbOctetPayloadType.clear();
|
||||||
|
mAmrNbPayloadType.clear();
|
||||||
|
mAmrWbOctetPayloadType.clear();
|
||||||
|
mAmrWbPayloadType.clear();
|
||||||
|
mIsac16KPayloadType = -1;
|
||||||
|
mIsac32KPayloadType = -1;
|
||||||
|
mIlbc20PayloadType = -1;
|
||||||
|
mIlbc30PayloadType = -1;
|
||||||
|
mGsmEfrPayloadType = -1;
|
||||||
|
mGsmFrPayloadType = -1;
|
||||||
|
mGsmHrPayloadType = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------- EvsSpec ---------------
|
|
||||||
bool CodecList::Settings::EvsSpec::isValid() const
|
bool CodecList::Settings::EvsSpec::isValid() const
|
||||||
{
|
{
|
||||||
return mPayloadType >= 96 && mPayloadType <= 127;
|
return mPayloadType >= 96 && mPayloadType <= 127;
|
||||||
|
|
@ -34,7 +151,7 @@ CodecList::Settings::EvsSpec CodecList::Settings::EvsSpec::parse(const std::stri
|
||||||
{
|
{
|
||||||
EvsSpec result;
|
EvsSpec result;
|
||||||
|
|
||||||
auto parts = strx::split(spec, "-/");
|
auto parts = strx::split(spec, "-/ ,:");
|
||||||
if (parts.size() == 3)
|
if (parts.size() == 3)
|
||||||
{
|
{
|
||||||
// Payload type number
|
// Payload type number
|
||||||
|
|
@ -79,7 +196,7 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
|
||||||
{
|
{
|
||||||
OpusSpec result;
|
OpusSpec result;
|
||||||
|
|
||||||
auto parts = strx::split(spec, "-");
|
auto parts = strx::split(spec, "-/ ,:");
|
||||||
if (parts.size() == 3)
|
if (parts.size() == 3)
|
||||||
{
|
{
|
||||||
result.mPayloadType = strx::toInt(strx::trim(parts.front()).c_str(), -1);
|
result.mPayloadType = strx::toInt(strx::trim(parts.front()).c_str(), -1);
|
||||||
|
|
@ -89,53 +206,160 @@ CodecList::Settings::OpusSpec CodecList::Settings::OpusSpec::parse(const std::st
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int findOctetMode(const char* line)
|
||||||
|
{
|
||||||
|
const char* param_name = "octet-align=";
|
||||||
|
auto p = strstr(line, param_name);
|
||||||
|
if (!p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
p += strlen(param_name);
|
||||||
|
char int_buf[8] = {0};
|
||||||
|
size_t int_buf_offset = 0;
|
||||||
|
while (*p && isdigit(*p) && int_buf_offset < sizeof(int_buf))
|
||||||
|
int_buf[int_buf_offset++] = *p++;
|
||||||
|
return atoi(int_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>& codeclist)
|
||||||
|
{
|
||||||
|
CodecList::Settings r{DefaultSettings};
|
||||||
|
|
||||||
|
for (auto& c: codeclist)
|
||||||
|
{
|
||||||
|
std::string codec_name = strx::uppercase(c.getName().c_str());
|
||||||
|
int samplerate = c.getRate();
|
||||||
|
int ptype = c.payloadType();
|
||||||
|
|
||||||
|
auto enc_params = c.encodingParameters(); // This must channels number for Opus codec
|
||||||
|
auto params = c.parameters();
|
||||||
|
|
||||||
|
// Dynamic payload type codecs only - ISAC / iLBC / Speex / etc.
|
||||||
|
if (codec_name == "OPUS")
|
||||||
|
{
|
||||||
|
// Check the parameters
|
||||||
|
int channels = strx::toInt(enc_params.c_str(), 1);
|
||||||
|
r.mOpusSpec.push_back({ptype, samplerate, channels});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (codec_name == "AMR-WB")
|
||||||
|
{
|
||||||
|
int octet_mode = findOctetMode(params.c_str());
|
||||||
|
if (octet_mode != -1)
|
||||||
|
{
|
||||||
|
if (octet_mode == 0)
|
||||||
|
r.mAmrWbPayloadType.insert(ptype);
|
||||||
|
else
|
||||||
|
if (octet_mode == 1)
|
||||||
|
r.mAmrWbOctetPayloadType.insert(ptype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (codec_name == "AMR" || codec_name == "AMR-NB")
|
||||||
|
{
|
||||||
|
int octet_mode = findOctetMode(params.c_str());
|
||||||
|
if (octet_mode != -1)
|
||||||
|
{
|
||||||
|
if (octet_mode == 0)
|
||||||
|
r.mAmrNbPayloadType.insert(ptype);
|
||||||
|
else
|
||||||
|
if (octet_mode == 1)
|
||||||
|
r.mAmrNbOctetPayloadType.insert(ptype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (codec_name == "EVS")
|
||||||
|
{
|
||||||
|
r.mEvsSpec.push_back({ptype});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CodecList::Settings::operator == (const Settings& rhs) const
|
||||||
|
{
|
||||||
|
if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType) !=
|
||||||
|
std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mAmrNbOctetPayloadType != rhs.mAmrNbOctetPayloadType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mAmrNbPayloadType != rhs.mAmrNbPayloadType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mAmrWbOctetPayloadType != rhs.mAmrWbOctetPayloadType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mAmrWbPayloadType != rhs.mAmrWbPayloadType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ToDo: compare EVS and Opus specs
|
||||||
|
if (mEvsSpec.size() != rhs.mEvsSpec.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mEvsSpec.size(); i++)
|
||||||
|
if (mEvsSpec[i] != rhs.mEvsSpec[i])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mOpusSpec.size() != rhs.mOpusSpec.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mOpusSpec.size(); i++)
|
||||||
|
if (mOpusSpec[i] != rhs.mOpusSpec[i])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
CodecList::Settings CodecList::Settings::DefaultSettings;
|
CodecList::Settings CodecList::Settings::DefaultSettings;
|
||||||
|
|
||||||
CodecList::CodecList(const Settings& settings)
|
CodecList::CodecList(const Settings& settings)
|
||||||
:mSettings(settings)
|
:mSettings(settings)
|
||||||
{
|
{
|
||||||
//mFactoryList.push_back(new OpusCodec::OpusFactory(16000, 1));
|
init(mSettings);
|
||||||
#if defined(USE_OPUS_CODEC)
|
|
||||||
if (settings.mOpusSpec.empty())
|
|
||||||
{
|
|
||||||
mFactoryList.push_back(new OpusCodec::OpusFactory(48000, 2, MT_OPUS_CODEC_PT));
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
void CodecList::init(const Settings& settings)
|
||||||
{
|
{
|
||||||
|
mFactoryList.clear();
|
||||||
|
mSettings = settings;
|
||||||
|
#if defined(USE_OPUS_CODEC)
|
||||||
for (auto spec: settings.mOpusSpec)
|
for (auto spec: settings.mOpusSpec)
|
||||||
{
|
{
|
||||||
mFactoryList.push_back(new OpusCodec::OpusFactory(spec.mRate, spec.mChannels, spec.mPayloadType));
|
mFactoryList.push_back(std::make_shared<OpusCodec::OpusFactory>(spec.mRate, spec.mChannels, spec.mPayloadType));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI)
|
#if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_RPI)
|
||||||
#if defined(USE_AMR_CODEC)
|
#if defined(USE_AMR_CODEC)
|
||||||
for (int pt: mSettings.mAmrWbPayloadType)
|
for (int pt: mSettings.mAmrWbPayloadType)
|
||||||
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt}));
|
mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
|
||||||
for (int pt: mSettings.mAmrWbOctetPayloadType)
|
for (int pt: mSettings.mAmrWbOctetPayloadType)
|
||||||
mFactoryList.push_back(new AmrWbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt}));
|
mFactoryList.push_back(std::make_shared<AmrWbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
|
||||||
|
|
||||||
for (int pt: mSettings.mAmrNbPayloadType)
|
for (int pt: mSettings.mAmrNbPayloadType)
|
||||||
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, false, pt}));
|
mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, false, pt}));
|
||||||
for (int pt: mSettings.mAmrNbOctetPayloadType)
|
for (int pt: mSettings.mAmrNbOctetPayloadType)
|
||||||
mFactoryList.push_back(new AmrNbCodec::CodecFactory({mSettings.mWrapIuUP, true, pt}));
|
mFactoryList.push_back(std::make_shared<AmrNbCodec::CodecFactory>(AmrCodecConfig{mSettings.mWrapIuUP, true, pt}));
|
||||||
|
|
||||||
mFactoryList.push_back(new GsmEfrCodec::GsmEfrFactory(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
|
if (mSettings.mGsmEfrPayloadType != -1)
|
||||||
|
mFactoryList.push_back(std::make_shared<GsmEfrCodec::GsmEfrFactory>(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
mFactoryList.push_back(std::make_shared<G711Codec::AlawFactory>());
|
||||||
|
mFactoryList.push_back(std::make_shared<G711Codec::UlawFactory>());
|
||||||
|
|
||||||
mFactoryList.push_back(new IsacCodec::IsacFactory16K(mSettings.mIsac16KPayloadType));
|
if (mSettings.mGsmFrPayloadType != -1)
|
||||||
mFactoryList.push_back(new IlbcCodec::IlbcFactory(mSettings.mIlbc20PayloadType, mSettings.mIlbc30PayloadType));
|
mFactoryList.push_back(std::make_shared<GsmCodec::GsmFactory>(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
|
||||||
mFactoryList.push_back(new G711Codec::AlawFactory());
|
mFactoryList.push_back(std::make_shared<G722Codec::G722Factory>());
|
||||||
mFactoryList.push_back(new G711Codec::UlawFactory());
|
mFactoryList.push_back(std::make_shared<G729Codec::G729Factory>());
|
||||||
|
|
||||||
mFactoryList.push_back(new GsmCodec::GsmFactory(mSettings.mGsmFrPayloadLength == 32 ? GsmCodec::Type::Bytes_32 : GsmCodec::Type::Bytes_33, mSettings.mGsmFrPayloadType));
|
|
||||||
mFactoryList.push_back(new G722Codec::G722Factory());
|
|
||||||
mFactoryList.push_back(new G729Codec::G729Factory());
|
|
||||||
#ifndef TARGET_ANDROID
|
#ifndef TARGET_ANDROID
|
||||||
mFactoryList.push_back(new GsmHrCodec::GsmHrFactory(mSettings.mGsmHrPayloadType));
|
if (mSettings.mGsmHrPayloadType != -1)
|
||||||
|
mFactoryList.push_back(std::make_shared<GsmHrCodec::GsmHrFactory>(mSettings.mGsmHrPayloadType));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(TARGET_ANDROID) && defined(USE_EVS_CODEC)
|
#if !defined(TARGET_ANDROID) && defined(USE_EVS_CODEC)
|
||||||
|
|
@ -147,15 +371,14 @@ CodecList::CodecList(const Settings& settings)
|
||||||
evs_params.ptime = 20;
|
evs_params.ptime = 20;
|
||||||
evs_params.ptype = spec.mPayloadType;
|
evs_params.ptype = spec.mPayloadType;
|
||||||
|
|
||||||
mFactoryList.push_back(new EVSCodec::EVSFactory(evs_params));
|
mFactoryList.push_back(std::make_shared<EVSCodec::EVSFactory>(evs_params));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CodecList::~CodecList()
|
CodecList::~CodecList()
|
||||||
{
|
{
|
||||||
for (FactoryList::size_type i=0; i<mFactoryList.size(); i++)
|
mFactoryList.clear();
|
||||||
delete mFactoryList[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CodecList::count() const
|
int CodecList::count() const
|
||||||
|
|
@ -180,23 +403,30 @@ int CodecList::findCodec(const std::string &name) const
|
||||||
|
|
||||||
void CodecList::fillCodecMap(CodecMap& cm)
|
void CodecList::fillCodecMap(CodecMap& cm)
|
||||||
{
|
{
|
||||||
|
cm.clear();
|
||||||
for (auto& factory: mFactoryList)
|
for (auto& factory: mFactoryList)
|
||||||
{
|
{
|
||||||
// Create codec here. Although they are not needed right now - they can be needed to find codec's info.
|
// Create codec here. Although they are not needed right now - they can be needed to find codec's info.
|
||||||
cm[factory->payloadType()] = factory->create();
|
// PCodec c = factory->create();
|
||||||
|
cm.insert({factory->payloadType(), PCodec()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PCodec CodecList::createCodecByPayloadType(int payloadType)
|
||||||
|
{
|
||||||
|
for (auto& factory: mFactoryList)
|
||||||
|
{
|
||||||
|
if (factory->payloadType() == payloadType)
|
||||||
|
return factory->create();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
CodecListPriority::CodecListPriority()
|
CodecListPriority::CodecListPriority()
|
||||||
{
|
{}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
CodecListPriority::~CodecListPriority()
|
CodecListPriority::~CodecListPriority()
|
||||||
{
|
{}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CodecListPriority::isNegativePriority(const CodecListPriority::Item& item)
|
bool CodecListPriority::isNegativePriority(const CodecListPriority::Item& item)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,14 @@
|
||||||
#ifndef __MT_CODEC_LIST_H
|
#ifndef __MT_CODEC_LIST_H
|
||||||
#define __MT_CODEC_LIST_H
|
#define __MT_CODEC_LIST_H
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#if defined(USE_RESIP_INTEGRATION)
|
|
||||||
#include "resiprocate/resip/stack/SdpContents.hxx"
|
#include "resiprocate/resip/stack/SdpContents.hxx"
|
||||||
#endif
|
|
||||||
#include "MT_Codec.h"
|
#include "MT_Codec.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <list>
|
||||||
#include "../helper/HL_VariantMap.h"
|
#include "../helper/HL_VariantMap.h"
|
||||||
|
|
||||||
#define ALL_CODECS_STRING "OPUS,ISAC,ILBC,PCMU,PCMA,G722,GSM"
|
#define ALL_CODECS_STRING "OPUS,ISAC,ILBC,PCMU,PCMA,G722,GSM"
|
||||||
|
|
@ -28,22 +29,22 @@ public:
|
||||||
bool mSkipDecode = false;
|
bool mSkipDecode = false;
|
||||||
|
|
||||||
// AMR payload types
|
// AMR payload types
|
||||||
std::set<int> mAmrWbPayloadType = { MT_AMRWB_PAYLOADTYPE };
|
std::set<int64_t> mAmrWbPayloadType = { };
|
||||||
std::set<int> mAmrNbPayloadType = { MT_AMRNB_PAYLOADTYPE };
|
std::set<int64_t> mAmrNbPayloadType = { };
|
||||||
std::set<int> mAmrWbOctetPayloadType = { MT_AMRWB_OCTET_PAYLOADTYPE };
|
std::set<int64_t> mAmrWbOctetPayloadType = { };
|
||||||
std::set<int> mAmrNbOctetPayloadType = { MT_AMRNB_OCTET_PAYLOADTYPE };
|
std::set<int64_t> mAmrNbOctetPayloadType = { };
|
||||||
|
|
||||||
bool isAmrWb(int ptype) const { return mAmrWbOctetPayloadType.count(ptype) > 0 || mAmrWbPayloadType.count(ptype) > 0; }
|
bool isAmrWb(int ptype) const { return mAmrWbOctetPayloadType.count(ptype) > 0 || mAmrWbPayloadType.count(ptype) > 0; }
|
||||||
bool isAmrNb(int ptype) const { return mAmrNbOctetPayloadType.count(ptype) > 0 || mAmrNbPayloadType.count(ptype) > 0; }
|
bool isAmrNb(int ptype) const { return mAmrNbOctetPayloadType.count(ptype) > 0 || mAmrNbPayloadType.count(ptype) > 0; }
|
||||||
|
|
||||||
int mIsac16KPayloadType = MT_ISAC16K_PAYLOADTYPE;
|
int mIsac16KPayloadType = -1;
|
||||||
int mIsac32KPayloadType = MT_ISAC32K_PAYLOADTYPE;
|
int mIsac32KPayloadType = -1;
|
||||||
int mIlbc20PayloadType = MT_ILBC20_PAYLOADTYPE;
|
int mIlbc20PayloadType = -1;
|
||||||
int mIlbc30PayloadType = MT_ILBC30_PAYLOADTYPE;
|
int mIlbc30PayloadType = -1;
|
||||||
int mGsmFrPayloadType = 3; // GSM is codec with fixed payload type. But sometimes it has to be overwritten.
|
int mGsmFrPayloadType = -1; // GSM is codec with fixed payload type. But sometimes it has to be overwritten.
|
||||||
int mGsmFrPayloadLength = 33; // Expected GSM payload length
|
int mGsmFrPayloadLength = 33; // Expected GSM payload length
|
||||||
int mGsmHrPayloadType = MT_GSMHR_PAYLOADTYPE;
|
int mGsmHrPayloadType = -1;
|
||||||
int mGsmEfrPayloadType = MT_GSMEFR_PAYLOADTYPE;
|
int mGsmEfrPayloadType = -1;
|
||||||
|
|
||||||
struct EvsSpec
|
struct EvsSpec
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +57,7 @@ public:
|
||||||
Bandwidth_FB
|
Bandwidth_FB
|
||||||
};
|
};
|
||||||
|
|
||||||
Bandwidth mBandwidth = Bandwidth_NB;
|
Bandwidth mBandwidth = Bandwidth_FB;
|
||||||
|
|
||||||
enum Encoding
|
enum Encoding
|
||||||
{
|
{
|
||||||
|
|
@ -67,6 +68,10 @@ public:
|
||||||
Encoding mEncodingType = Encoding_MIME;
|
Encoding mEncodingType = Encoding_MIME;
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
static EvsSpec parse(const std::string& spec);
|
static EvsSpec parse(const std::string& spec);
|
||||||
|
|
||||||
|
bool operator == (const EvsSpec& rhs) const { return std::tie(mPayloadType, mBandwidth, mEncodingType) == std::tie(rhs.mPayloadType, rhs.mBandwidth, rhs.mEncodingType);}
|
||||||
|
bool operator != (const EvsSpec& rhs) const { return ! (operator ==) (rhs);}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<EvsSpec> mEvsSpec;
|
std::vector<EvsSpec> mEvsSpec;
|
||||||
|
|
@ -77,26 +82,52 @@ public:
|
||||||
int mRate = -1;
|
int mRate = -1;
|
||||||
int mChannels = -1;
|
int mChannels = -1;
|
||||||
|
|
||||||
|
OpusSpec(int ptype = -1, int rate = -1, int channels = -1)
|
||||||
|
:mPayloadType(ptype), mRate(rate), mChannels(channels)
|
||||||
|
{}
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
bool operator == (const OpusSpec& rhs) const { return std::tie(mPayloadType, mRate, mChannels) == std::tie(rhs.mPayloadType, rhs.mRate, rhs.mChannels);}
|
||||||
|
bool operator != (const OpusSpec& rhs) const { return ! (operator ==) (rhs);}
|
||||||
|
|
||||||
static OpusSpec parse(const std::string& spec);
|
static OpusSpec parse(const std::string& spec);
|
||||||
};
|
};
|
||||||
std::vector<OpusSpec> mOpusSpec;
|
std::vector<OpusSpec> mOpusSpec;
|
||||||
|
|
||||||
|
// Payload type
|
||||||
|
bool contains(int ptype) const;
|
||||||
|
|
||||||
|
// Textual representation - used in logging
|
||||||
|
std::string toString() const;
|
||||||
|
void clear();
|
||||||
|
|
||||||
static Settings DefaultSettings;
|
static Settings DefaultSettings;
|
||||||
|
|
||||||
|
static Settings parseSdp(const std::list<resip::Codec>& codeclist);
|
||||||
|
|
||||||
|
bool operator == (const Settings& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
CodecList(const Settings& settings);
|
CodecList(const Settings& settings);
|
||||||
~CodecList();
|
~CodecList();
|
||||||
|
void setSettings(const Settings& settings)
|
||||||
|
{
|
||||||
|
init(settings);
|
||||||
|
}
|
||||||
|
|
||||||
int count() const;
|
int count() const;
|
||||||
Codec::Factory& codecAt(int index) const;
|
Codec::Factory& codecAt(int index) const;
|
||||||
int findCodec(const std::string& name) const;
|
int findCodec(const std::string& name) const;
|
||||||
void fillCodecMap(CodecMap& cm);
|
void fillCodecMap(CodecMap& cm);
|
||||||
|
PCodec createCodecByPayloadType(int payloadType);
|
||||||
|
void clear();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::vector<Codec::Factory*> FactoryList;
|
typedef std::vector<std::shared_ptr<Codec::Factory>> FactoryList;
|
||||||
FactoryList mFactoryList;
|
FactoryList mFactoryList;
|
||||||
Settings mSettings;
|
Settings mSettings;
|
||||||
|
|
||||||
|
void init(const Settings& settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CodecListPriority
|
class CodecListPriority
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include "MT_Dtmf.h"
|
#include "MT_Dtmf.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#ifndef MT_DTMF
|
#ifndef MT_DTMF
|
||||||
#define MT_DTMF
|
#define MT_DTMF
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../engine_config.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue