Compare commits

..

4 Commits

12152 changed files with 419446 additions and 2333370 deletions
-69
View File
@@ -1,69 +0,0 @@
# Formatting rules for rtphone's own C++ sources (src/engine, src/libs/ice, test).
# Third-party code under src/libs/ is excluded via .clang-format-ignore.
---
Language: Cpp
BasedOnStyle: LLVM
Standard: c++20
# Indentation: 4 columns, spaces only.
IndentWidth: 4
TabWidth: 4
UseTab: Never
AccessModifierOffset: -4
NamespaceIndentation: None
IndentCaseLabels: false
ColumnLimit: 120
# Allman braces, as used throughout the codebase.
BreakBeforeBraces: Allman
PointerAlignment: Left
ReferenceAlignment: Left
DerivePointerAlignment: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortBlocksOnASingleLine: Empty
# Include order matters here (platform headers, windows.h, vendored libs),
# so never reorder them.
SortIncludes: Never
IndentPPDirectives: None
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
FixNamespaceComments: true
SpaceAfterTemplateKeyword: false
AlwaysBreakTemplateDeclarations: Yes
AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments
# Objective-C++ (src/libs/ice/ICEIosSupport.mm) — same rules as C++.
---
Language: ObjC
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: Never
AccessModifierOffset: -4
NamespaceIndentation: None
IndentCaseLabels: false
ColumnLimit: 120
BreakBeforeBraces: Allman
PointerAlignment: Left
ReferenceAlignment: Left
DerivePointerAlignment: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortBlocksOnASingleLine: Empty
SortIncludes: Never
IndentPPDirectives: None
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
FixNamespaceComments: true
AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
-2
View File
@@ -1,2 +0,0 @@
src/libs/**
!src/libs/ice/ICE*
-89
View File
@@ -1,89 +0,0 @@
# Build directories
build/
build_*/
out/
bin/
lib/
Release/
Debug/
x64/
x86/
# CMake generated files
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
Makefile
*.cmake
!CMakeLists.txt
compile_commands.json
CTestTestfile.cmake
_deps/
# QtCreator
*.user
*.user.*
*.qmlproject.user
*.creator.user
.qmake.stash
# Visual Studio
*.suo
*.user
*.userosscache
*.sln.docstates
.vs/
*.vcxproj.user
*.ncb
*.sdf
*.opensdf
*.VC.db
*.VC.VC.opendb
ipch/
# Compiled object files
*.o
*.obj
*.ko
*.elf
# Precompiled headers
*.gch
*.pch
# Compiled libraries
# *.lib
# *.a
*.la
*.lo
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
# IDE caches
.cache/
.idea/
.vscode/
*.swp
*.swo
*~
# Downloaded archives
*.tar.gz
*.tar.bz2
*.tar.xz
*.zip
# OS specific
.DS_Store
Thumbs.db
# Logs and temp files
*.log
*.tmp
*.temp
+8
View File
@@ -0,0 +1,8 @@
build_exe:
script:
- mkdir -p build
- cd build
- git clone https://gitlab.com/dmytro.bogovych/libraries.git
- cmake -D LIB_PLATFORM=libraries ../src
- cmake --build .
-366
View File
@@ -1,366 +0,0 @@
# RTPhone Platform - Agent Guide
## Project Overview
RTPhone is a comprehensive real-time communication (RTC) platform developed by VoIP Objects (Sevana) that provides a complete software stack for building VoIP/SIP-based communication applications. It delivers production-ready voice communication capabilities with extensive codec support and cross-platform compatibility.
The project produces 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.
## Technology Stack
- **Language**: C++20
- **Build System**: CMake 3.20+
- **License**: Mozilla Public License Version 2.0 (see LICENSE_MPL.txt)
- **Primary Platforms**: Linux (x64, ARM/Raspberry Pi), Windows (32/64-bit), macOS, Android, iOS
## Project Structure
```
/home/anand/works/sevana/platform/rtphone/
├── src/ # Main source code
│ ├── CMakeLists.txt # Main CMake configuration
│ ├── engine/ # Core engine modules
│ │ ├── agent/ # JSON-based command interface
│ │ ├── audio/ # Cross-platform audio I/O handling
│ │ ├── endpoint/ # SIP user agent implementation
│ │ ├── helper/ # Utility functions (networking, logging, threading)
│ │ ├── media/ # Audio codec management and processing
│ │ └── engine_config.h # Compile-time configuration
│ └── libs/ # Third-party libraries
│ ├── resiprocate/ # SIP stack (git submodule)
│ ├── libsrtp/ # SRTP library (git submodule)
│ ├── libraries/ # Prebuilt platform libraries (git submodule)
│ ├── ice/ # ICE (Interactive Connectivity Establishment)
│ ├── jrtplib/ # RTP library
│ ├── opus/ # Opus codec
│ ├── webrtc/ # WebRTC components
│ ├── libg729/ # G.729 codec
│ ├── libgsm/ # GSM codec
│ ├── gsmhr/ # GSM HR codec
│ ├── g722/ # G.722 codec
│ ├── speexdsp/ # Speex DSP
│ ├── libevs/ # EVS codec (optional)
│ ├── opencore-amr/ # AMR codec (optional)
│ ├── oboe/ # Android low-latency audio
│ └── fmt/ # Format library
├── build_linux.py # Linux build script
├── build_android.py # Android build script
├── build_android.sh # Android build script (shell)
├── run_ci.sh # CI build script
└── README.txt # Human-readable README
```
## Build Instructions
### Prerequisites
- CMake 3.20+
- C++20 compatible compiler (GCC, Clang, MSVC)
- Python 3 (for build scripts)
- Ninja (recommended, used by build scripts)
- OpenSSL 1.1+ development libraries
- Platform-specific audio libraries
### Linux Build
```bash
python3 build_linux.py
```
This creates a `build_linux/` directory and outputs `librtphone.a`.
### Android Build
```bash
# Ensure ANDROID_NDK_HOME environment variable is set
export ANDROID_NDK_HOME=/path/to/android-ndk
python3 build_android.py
```
Or use the shell script:
```bash
./build_android.sh
```
This creates a `build_android/` directory with ARM64 libraries by default.
### Manual CMake Build
```bash
mkdir build && cd build
cmake ../src -G Ninja
cmake --build . -j$(nproc)
```
### Build Options
The following CMake options are available (defined in `src/CMakeLists.txt`):
- `USE_AMR_CODEC` (ON/OFF): Include AMR-NB/AMR-WB codec support. Default: ON
- `USE_EVS_CODEC` (ON/OFF): Include EVS codec support. Default: ON
- `USE_MUSL` (ON/OFF): Build with MUSL library. Default: OFF
## Module Architecture
### 1. Agent Module (`src/engine/agent/`)
Provides a JSON-based command interface for controlling the engine.
**Key Classes:**
- `AgentImpl`: Main agent implementation, processes JSON commands
- `Agent_AudioManager`: Manages audio devices and streams
**Interface Pattern:**
Commands are sent as JSON strings and responses are returned as JSON.
### 2. Endpoint Module (`src/engine/endpoint/`)
SIP user agent implementation based on reSIProcate.
**Key Classes:**
- `UserAgent` (in EP_Engine.h): Main SIP stack wrapper, handles registration, sessions, presence
- `Account` (EP_Account): SIP account management
- `Session` (EP_Session): Call/session management
- `EP_AudioProvider`: Audio data provider for sessions
- `EP_DataProvider`: Generic data provider interface
**Key Concepts:**
- Implements resiprocate handlers: `ClientRegistrationHandler`, `InviteSessionHandler`, etc.
- Supports multiple transport types: UDP, TCP, TLS
- ICE integration for NAT traversal
### 3. Media Module (`src/engine/media/`)
Audio codec management, RTP/RTCP handling, and media processing.
**Key Classes:**
- `MT::AudioCodec`: Codec management
- `MT::AudioStream`: Audio streaming
- `MT::AudioReceiver`: RTP packet receiving
- `MT::CodecList`: Codec negotiation
- `MT::SrtpHelper`: SRTP encryption
- `MT::Dtmf`: DTMF tone handling
- `MT::AmrCodec`: AMR codec wrapper
- `MT::EvsCodec`: EVS codec wrapper
### 4. Audio Module (`src/engine/audio/`)
Cross-platform audio I/O abstraction.
**Key Classes:**
- `Audio::Interface`: Audio interface abstraction
- `Audio::DevicePair`: Input/output device pair
- `Audio::Resampler`: Audio resampling
- `Audio::Mixer`: Multi-channel audio mixing
- `Audio::CoreAudio`: macOS/iOS implementation
- `Audio::DirectSound`: Windows DirectSound implementation
- `Audio::AndroidOboe`: Android Oboe implementation
- `Audio::Null`: Null/no-op implementation for testing
### 5. Helper Module (`src/engine/helper/`)
Utility classes and platform abstractions.
**Key Classes:**
- `HL::Types`: Type definitions and BiMap template
- `HL::Sync`: Threading primitives (Mutex, Event, etc.)
- `HL::ByteBuffer`: Binary data buffer
- `HL::VariantMap`: Key-value configuration storage
- `HL::Rtp`: RTP packet utilities
- `HL::IuUP`: Iu User Plane protocol (3G)
- `HL::NetworkSocket`: Network socket abstraction
- `HL::ThreadPool`: Thread pool implementation
## Code Style Guidelines
### Naming Conventions
1. **Classes**: PascalCase with module prefix
- `AgentImpl`, `UserAgent`, `MT::AudioCodec`, `HL::Sync`
2. **Files**: Prefix indicates module
- `Agent_*.cpp/h` - Agent module
- `EP_*.cpp/h` - Endpoint module
- `MT_*.cpp/h` - Media module
- `Audio_*.cpp/h` - Audio module
- `HL_*.cpp/h` - Helper module
3. **Member Variables**: Hungarian notation with `m` prefix
- `mAgentMutex`, `mSessionMap`, `mShutdown`
4. **Type Aliases**: `P` prefix for smart pointers
- `PAccount`, `PSession`, `PVariantMap`
### File Header Template
All source files must include the MPL license header:
```cpp
/* Copyright(C) 2007-YYYY VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
```
### Include Order
1. Corresponding header file (for .cpp files)
2. Project headers (using `"..."`)
3. Third-party library headers
4. Standard library headers
5. System headers
### Platform Abstraction
Use preprocessor defines for platform-specific code:
- `TARGET_WIN` - Windows
- `TARGET_LINUX` - Linux
- `TARGET_OSX` - macOS
- `TARGET_ANDROID` - Android
- `TARGET_MUSL` - MUSL libc
## Configuration
### Compile-Time Configuration (`src/engine/engine_config.h`)
Key configuration constants:
```cpp
// Audio settings
#define AUDIO_SAMPLE_WIDTH 16
#define AUDIO_CHANNELS 1
#define AUDIO_SAMPLERATE 48000
#define AUDIO_RESAMPLER_QUALITY 1
// SIP settings
#define UA_REGISTRATION_TIME 3600
#define UA_MEDIA_PORT_START 20000
#define UA_MEDIA_PORT_FINISH 30000
// Codec payload types
#define MT_AMRNB_PAYLOADTYPE 112
#define MT_AMRWB_PAYLOADTYPE 96
#define MT_EVS_PAYLOADTYPE 127
#define MT_OPUS_CODEC_PT 106
```
### Runtime Configuration
Configuration is passed via `VariantMap` objects to the UserAgent:
```cpp
enum
{
CONFIG_IPV4 = 0, // Use IP4
CONFIG_IPV6, // Use IP6
CONFIG_USERNAME, // Username
CONFIG_DOMAIN, // Domain
CONFIG_PASSWORD, // Password
CONFIG_STUNSERVER_NAME, // STUN server hostname
CONFIG_STUNSERVER_PORT, // STUN server port
CONFIG_TRANSPORT, // 0=all, 1=UDP, 2=TCP, 3=TLS
// ... see EP_Engine.h for full list
};
```
## Dependencies
### Git Submodules
The project uses Git submodules for some dependencies:
- `src/libs/resiprocate` - SIP stack (sevana branch)
- `src/libs/libsrtp` - SRTP library
- `src/libs/libraries` - Prebuilt platform libraries
To initialize:
```bash
git submodule update --init --recursive
```
### Third-Party Libraries
Prebuilt libraries are provided in `src/libs/libraries/`:
- OpenSSL 1.1 (crypto, SSL)
- Opus codec
- Opencore AMR (NB/WB)
- Boost (headers)
- PortAudio
- libevent2
## Testing
There is no dedicated test suite in the main project. Testing is typically done through:
1. Integration with the final application
2. The `run_ci.sh` script for build verification
3. Unit tests in individual library submodules (e.g., oboe)
### CI Build
```bash
./run_ci.sh
```
This configures with CMake and builds with make (2 parallel jobs).
## Security Considerations
1. **OpenSSL Integration**: Uses OpenSSL 1.1+ for TLS and certificate handling
2. **SRTP Support**: Media encryption via libsrtp
3. **Certificate Management**: Root certificates can be added at runtime via `addRootCert()`
4. **AMR/EVS Patents**: The project does NOT include patent licenses for AMR and EVS codecs. Users must acquire these independently.
## Integration Guide
### Basic Usage Pattern
1. Create `AgentImpl` instance
2. Send JSON commands via `command()` method
3. Poll for events via `waitForData()` and `read()`
4. Process JSON event responses
### Example Commands
- `config` - Configure the engine
- `start` - Start the engine
- `createAccount` - Create SIP account
- `startAccount` - Register account
- `createSession` - Create call session
- `startSession` - Make/accept call
- `waitForEvent` - Poll for events
## Development Workflow
1. **Making Changes**: Edit files in appropriate `src/engine/<module>/` directory
2. **Building**: Use `build_linux.py` for quick Linux builds
3. **Testing**: Integrate with test application or use existing CI
4. **Commits**: Follow existing commit message style (visible in git log)
## Common Tasks
### Adding a New Codec
1. Create `MT_<Codec>Codec.cpp/h` in `src/engine/media/`
2. Derive from `MT::Codec` base class
3. Register in `MT_CodecList.cpp`
4. Add to `src/CMakeLists.txt` if external library needed
### Adding Platform Audio Support
1. Create `Audio_<Platform>.cpp/h` in `src/engine/audio/`
2. Implement `Audio::Interface` methods
3. Add platform detection in `src/CMakeLists.txt`
4. Include in build conditionally
### Modifying Build Configuration
- Edit `src/CMakeLists.txt` for main build changes
- Edit `src/libs/libraries/platform_libs.cmake` for platform library paths
- Edit `src/engine/engine_config.h` for compile-time constants
## Notes
- The codebase uses C++20 features - ensure compiler compatibility
- Thread safety: Use `std::recursive_mutex` (e.g., `mAgentMutex`) for thread-safe access
- Memory management: Uses smart pointers (`std::shared_ptr`) extensively
- The JSON library is embedded in `src/libs/json/`
-79
View File
@@ -1,79 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A more detailed agent reference (style guide, JSON command list, integration notes, full module class listings) is in `AGENTS.md`. Read that for deeper context; this file only captures what is needed to be productive quickly.
## What this repo produces
A single C++20 static library, `librtphone.a`, that implements a SIP/RTP softphone stack (codecs, media transport, SIP user agent, cross-platform audio I/O). It is consumed by other applications via a JSON command interface (`AgentImpl`). There is no executable target in `src/` — only the library.
## Build and run
The Python scripts wipe and recreate their build directory each invocation, so they are clean configure+build runs, not incremental.
```bash
python3 build_linux.py # wipes build_linux/, configures with Ninja, outputs build_linux/librtphone.a
python3 build_android.py # needs ANDROID_NDK_HOME and VCPKG_ROOT; arm64-v8a, API 24
python3 build_windows.py # needs VCPKG_ROOT (defaults to C:\tools\vcpkg); VS 2022 x64
./run_ci.sh # Make-based CI build into ./build (used by Jenkins; pings Telegram on failure)
```
For incremental work, configure once and rebuild manually instead of re-running the wrapper:
```bash
mkdir -p build && cd build
cmake ../src -G Ninja -D OPUS_X86_MAY_HAVE_SSE4_1=ON
cmake --build . -j$(nproc)
```
CMake options (in `src/CMakeLists.txt`): `USE_AMR_CODEC` (default ON, force-OFF on Android), `USE_EVS_CODEC` (default ON), `USE_MUSL` (default OFF).
## Tests
There is no unit test suite for the library itself. The only first-party test is `test/rtp_decode/` — a standalone CMake project that links the library and exercises RTP decoding from a capture file. Build and run it via:
```bash
mkdir -p test/rtp_decode/build && cd test/rtp_decode/build
cmake .. && cmake --build . -j$(nproc)
./rtp_decode <args>
```
Note: `test/rtp_decode/CMakeLists.txt` does `add_subdirectory(../../src ...)`, so it rebuilds the whole library inside its own build tree. Don't expect to share artifacts with `build_linux/`.
## Submodules and external paths
The `src/libs/` directory mixes vendored sources with three git submodules: `resiprocate` (SIP stack, sevana branch), `libsrtp`, and `libraries` (prebuilt platform binaries — OpenSSL 1.1, Boost headers, etc.). After clone:
```bash
git submodule update --init --recursive
```
## Architecture: how a call flows through the modules
The five modules under `src/engine/` form a vertical stack. Understanding the flow matters more than the file list (which is in `AGENTS.md`):
1. **`agent/`** — Public API surface. Hosts apps create one `AgentImpl`, push JSON command strings into `command()`, and pull JSON event strings out via `waitForData()`/`read()`. This is the only thing external code should touch.
2. **`endpoint/`** — Wraps reSIProcate. `UserAgent` (in `EP_Engine.h`) owns the SIP transports, registrations, and session lifecycle; `EP_Account` and `EP_Session` are the SIP-side objects the agent manipulates. `EP_AudioProvider`/`EP_DataProvider` bridge SIP sessions to media streams.
3. **`media/`** (`MT::` namespace) — Codec registry (`MT_CodecList`), per-call audio pipeline (`MT_AudioStream`, `MT_AudioReceiver`), RTP send (`MT_NativeRtpSender`), SRTP (`MT_SrtpHelper`), DTMF (`MT_Dtmf`). Codec wrappers like `MT_AmrCodec`/`MT_EvsCodec` adapt vendored libraries in `src/libs/` to the `MT::Codec` base.
4. **`audio/`** (`Audio::` namespace) — Cross-platform device I/O. `Audio::Interface` is the abstraction; concrete implementations are picked at compile time: `Audio_DirectSound` (Windows), `Audio_CoreAudio` (macOS/iOS), `Audio_AndroidOboe` (Android, via `libs/oboe`), `Audio_Null` (testing). Also hosts mixing, resampling, WAV I/O, AEC integration.
5. **`helper/`** (`HL::` namespace) — Cross-cutting primitives used by every other module: `HL_Sync` (mutex/event), `HL_NetworkSocket`, `HL_Log`, `HL_VariantMap` (used as the runtime config bag passed to `UserAgent`), `HL_Rtp`, `HL_IuUP` (3G Iu-UP framing), `HL_ThreadPool`, `HL_ByteBuffer`.
ICE/STUN lives separately under `src/libs/ice/` (it is compiled directly into the library, not as a subproject) and is wired into the media path for NAT traversal.
Compile-time tunables — sample rate (48 kHz), buffer sizes, RTP/codec payload types, media port range — are all in `src/engine/engine_config.h`. Runtime configuration flows through `HL::VariantMap` keyed by the `CONFIG_*` enum in `EP_Engine.h`.
Per-stream call-quality metrics (RTT, jitter per RFC 3550 §A.8, packet-loss timeline, RFC 2833 DTMF events, network MOS) are collected in `MT::Statistics` / `MT::JitterStatistics` in `src/engine/media/MT_Statistics.{h,cpp}` and surfaced through the agent's JSON event stream.
## Conventions worth knowing before editing
- **File prefixes encode the module**: `Agent_*`, `EP_*`, `MT_*`, `Audio_*`, `HL_*`. Match this when adding files.
- **Members use `m` prefix** (`mAgentMutex`, `mSessionMap`); smart-pointer typedefs use `P` prefix (`PSession`, `PVariantMap`).
- **Platform code is gated by `TARGET_WIN` / `TARGET_LINUX` / `TARGET_OSX` / `TARGET_ANDROID` / `TARGET_MUSL`**, set in `src/CMakeLists.txt`. Don't sniff `_WIN32`/`__linux__` directly.
- **Every source file carries the MPL 2.0 header** (see top of any existing `.cpp`). New files need the same block.
- **Thread safety is via `std::recursive_mutex`** (e.g. `mAgentMutex` guards the agent's public surface). Memory uses `std::shared_ptr` extensively — prefer the existing `P*` typedefs over raw pointers.
- The codebase recently migrated to **C++20 and `std::chrono`**; avoid reintroducing older idioms or hand-rolled time math.
## Patent caveat
AMR-NB/AMR-WB and EVS sources are included but no patent licenses are bundled. If a change touches `MT_AmrCodec`/`MT_EvsCodec` or their build flags, keep in mind users are expected to license these codecs themselves — don't enable them by default in contexts where that hasn't been arranged.
-373
View File
@@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
-141
View File
@@ -1,141 +0,0 @@
# RTPhone Platform
RTPhone is a comprehensive real-time communication (RTC) platform that provides a complete software stack for building VoIP/SIP-based communication applications. Developed by VoIP Objects (Sevana), RTPhone delivers production-ready voice communication capabilities with extensive codec support and cross-platform compatibility.
## Overview
RTPhone serves as a static library (`librtphone.a`) that can be integrated into larger telephony and communication systems. It provides a JSON-based command interface for easy integration and control, making it suitable for building softphones, PBX systems, WebRTC gateways, and carrier-grade voice solutions.
## Key Features
### Audio Codec Support
RTPhone supports an extensive range of audio codecs:
**Standard Codecs:**
- G.711 (A-law/¼-law)
- G.722 (16kHz wideband)
- G.729
- GSM (Full Rate, Half Rate, Enhanced Full Rate)
- iLBC (20ms/30ms)
- ISAC (16kHz/32kHz)
**Advanced Codecs:**
- AMR-NB/AMR-WB (Adaptive Multi-Rate Narrowband/Wideband) - please be aware - there is no patents for AMR codecs usage included ! You should acquire them on your own.
- EVS (Enhanced Voice Services) - 3GPP's latest codec. Again - please be aware - there is no patents for EVS codec usage included ! You should acquire them on your own.
- Opus - Modern low-latency codec
- Speex (with acoustic echo cancellation)
**Codec Features:**
- Bandwidth-efficient and octet-aligned modes
- IuUP (Iu User Plane) protocol support for 3G networks
- Dynamic codec switching
- Packet loss concealment (PLC)
- Comfort noise generation (CNG)
### Network & Protocol Support
**SIP Features:**
- Full SIP 2.0 implementation via reSIProcate
- Multiple transport protocols (UDP, TCP, TLS)
- Registration, authentication, and session management
- SIP MESSAGE, presence, and REFER support
**Media Transport:**
- RTP/RTCP for media streaming
- SRTP for secure media
- ICE for NAT traversal with STUN/TURN support
- WebRTC integration components
- IPv4 and IPv6 support
### Cross-Platform Audio Support
- DirectSound/WMME (Windows)
- Core Audio (macOS/iOS)
- ALSA/PulseAudio (Linux)
- Oboe (Android) for low-latency audio
- PortAudio fallback support
### Audio Quality Features
- 48kHz sample rate support
- Acoustic Echo Cancellation (AEC)
- Audio resampling and format conversion
- Multi-channel audio mixing
- Perceptual Voice Quality Assessment (PVQA)
## Architecture
The platform is organized into several core modules:
- **Engine/Agent**: JSON-based command interface
- **Engine/Endpoint**: SIP user agent implementation
- **Engine/Media**: Audio codec management and processing
- **Engine/Audio**: Cross-platform audio I/O handling
- **Engine/Helper**: Utility functions (networking, logging, threading)
## Supported Platforms
- Linux (x64, ARM/Raspberry Pi)
- Windows (32/64-bit)
- macOS
- Android (with Oboe integration)
- iOS
## Building
RTPhone uses a CMake-based build system with cross-compilation support:
### Linux
```bash
python3 build_linux.py
```
### Android
```bash
python3 build_android.py
# or
./build_android.sh
```
### Dependencies
- CMake 3.10+
- OpenSSL 1.1+
- Boost libraries
- Platform-specific audio libraries
## Recent Updates
Recent development has focused on:
- AMR codec parsing and decoding improvements
- Octet-aligned mode fixes for AMR-WB
- RTP SSRC handling enhancements
- Build system optimizations
- Code modernization to C++20
## Use Cases
RTPhone is good for building:
- VoIP softphones and mobile applications
- PBX and telephony server systems
- RTP proxies
- Carrier-grade voice communication platforms
- 3GPP/IMS-compliant systems
## Security
RTPhone includes comprehensive security features:
- OpenSSL 1.1 integration for encryption
- TLS transport layer security
- SRTP media encryption
- Certificate management support
## Integration
The platform provides a developer-friendly interface with:
- Event-driven architecture
- Comprehensive logging system
- Modern C++20 codebase
For detailed integration instructions and API documentation, please refer to the source code and header files in the `/src/engine/` directory.
## License
Our source code is licensed under the MPL license. Naturally, any third-party components we use are subject to their respective licenses.
-45
View File
@@ -1,45 +0,0 @@
#!/usr/bin/python3
from pathlib import Path
import os
import multiprocessing
import shutil
# Temporary build directory
DIR_BUILD = 'build_android'
# Android NDK home directory
NDK_HOME = os.environ['ANDROID_NDK_HOME']
VCPKG_ROOT = os.environ['VCPKG_ROOT']
# CMake toolchain file
TOOLCHAIN_FILE = f'{NDK_HOME}/build/cmake/android.toolchain.cmake'
TOOLCHAIN_FILE_VCPKG=f'{VCPKG_ROOT}/scripts/buildsystems/vcpkg.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} -DCMAKE_TOOLCHAIN_FILE={TOOLCHAIN_FILE_VCPKG} '
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()
-38
View File
@@ -1,38 +0,0 @@
#!/usr/bin/env python3
from pathlib import Path
import os
import multiprocessing
import shutil
# Temporary build directory
DIR_BUILD = 'build_linux'
# This directory
DIR_THIS = Path(__file__).parent.resolve()
# Path to app
DIR_SOURCE = (DIR_THIS / '../src').resolve()
def make_build() -> Path:
if Path(DIR_BUILD).exists():
shutil.rmtree(DIR_BUILD)
os.mkdir(DIR_BUILD)
os.chdir(DIR_BUILD)
# OPUS_X86_MAY_HAVE_SSE4_1 is for clang builds
cmd = f'cmake ../src -G Ninja -D OPUS_X86_MAY_HAVE_SSE4_1=ON'
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}')
-55
View File
@@ -1,55 +0,0 @@
#!/usr/bin/env python3
# This script is just to check if the library is buildable
from pathlib import Path
import os
import multiprocessing
import shutil
# Temporary build directory
DIR_BUILD = 'build_windows'
# This directory
DIR_THIS = Path(__file__).parent.resolve()
# Path to app
DIR_SOURCE = (DIR_THIS / '../src').resolve()
CMAKE_GENERATOR = '-G "Visual Studio 17 2022" -A x64'
# Not used yet
CMAKE_BUILD_TYPE = 'Debug'
def make_build() -> Path:
if Path(DIR_BUILD).exists():
shutil.rmtree(DIR_BUILD)
os.mkdir(DIR_BUILD)
os.chdir(DIR_BUILD)
if os.environ['VCPKG_ROOT']:
vcpkg_root = os.environ['VCPKG_ROOT']
else:
vcpkg_root = 'C:\\tools\\vcpkg'
if not Path(vcpkg_root).exists():
print(f'Failed to find vcpkg (OpenSSL libraries needed)')
exit(1)
cmd = f'cmake ../src {CMAKE_GENERATOR} -D CMAKE_TOOLCHAIN_FILE="{vcpkg_root}\\scripts\\buildsystems\\vcpkg.cmake"'
print(cmd)
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) / 'Debug' / 'rtphone.lib'
if __name__ == '__main__':
p = make_build()
print (f'Built: {p}')
-123
View File
@@ -1,123 +0,0 @@
#!/usr/bin/env python3
"""
Count C/C++ files and lines in the project.
Works on both Linux and Windows.
"""
import os
import sys
from pathlib import Path
# C/C++ file extensions
CPP_EXTENSIONS = {'.c', '.cc', '.cpp', '.cxx', '.h', '.hh', '.hpp', '.hxx'}
def is_cpp_file(filepath: Path) -> bool:
"""Check if file has a C/C++ extension."""
return filepath.suffix.lower() in CPP_EXTENSIONS
def count_lines_in_file(filepath: Path) -> int:
"""Count lines in a single file."""
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
return sum(1 for _ in f)
except (IOError, OSError):
return 0
def collect_cpp_files(root_dir: Path) -> list[Path]:
"""Collect all C/C++ files recursively from root directory."""
cpp_files = []
for path in root_dir.rglob('*'):
if path.is_file() and is_cpp_file(path):
cpp_files.append(path)
return cpp_files
def get_directory_stats(root_dir: Path, cpp_files: list[Path]) -> dict[str, dict]:
"""Get statistics grouped by immediate subdirectories."""
stats = {}
# Initialize stats for immediate subdirectories
for item in root_dir.iterdir():
if item.is_dir():
stats[item.name] = {'files': 0, 'lines': 0}
# Also count files directly in root
stats['.'] = {'files': 0, 'lines': 0}
for filepath in cpp_files:
try:
# Get relative path from root
rel_path = filepath.relative_to(root_dir)
# Determine which immediate subdirectory this file belongs to
if len(rel_path.parts) == 1:
# File is directly in root
dir_key = '.'
else:
# File is in a subdirectory
dir_key = rel_path.parts[0]
if dir_key in stats:
line_count = count_lines_in_file(filepath)
stats[dir_key]['files'] += 1
stats[dir_key]['lines'] += line_count
except (ValueError, IndexError):
continue
return stats
def main():
# Use current directory as root, or accept a path argument
if len(sys.argv) > 1:
root_dir = Path(sys.argv[1]).resolve()
else:
root_dir = Path.cwd()
if not root_dir.exists():
print(f"Error: Directory '{root_dir}' does not exist.")
sys.exit(1)
print(f"Scanning C/C++ files in: {root_dir}")
print("=" * 60)
# Collect all C/C++ files
cpp_files = collect_cpp_files(root_dir)
if not cpp_files:
print("No C/C++ files found.")
sys.exit(0)
# Get directory statistics
dir_stats = get_directory_stats(root_dir, cpp_files)
# Print per-directory statistics
print("\nPer-directory C/C++ statistics:")
print("-" * 60)
print(f"{'Directory':<30} {'Files':>10} {'Lines':>15}")
print("-" * 60)
total_files = 0
total_lines = 0
# Sort directories: root first, then alphabetically
sorted_dirs = sorted(dir_stats.keys(), key=lambda x: (x != '.', x.lower()))
for dir_name in sorted_dirs:
stat = dir_stats[dir_name]
if stat['files'] > 0:
display_name = '(root)' if dir_name == '.' else dir_name
print(f"{display_name:<30} {stat['files']:>10} {stat['lines']:>15,}")
total_files += stat['files']
total_lines += stat['lines']
print("-" * 60)
print(f"{'TOTAL':<30} {total_files:>10} {total_lines:>15,}")
print("=" * 60)
if __name__ == '__main__':
main()
-23
View File
@@ -1,23 +0,0 @@
#!/bin/bash
SEND_MSG="/var/ci/conformance_ci/send_telegram_message.py"
mkdir -p build
cd build
# Configure
cmake ../src
if [ $? -ne 0 ]; then
/usr/bin/python3 $SEND_MSG "rtphone cmake failed. $BUILD_URL"
exit 1
fi
# Build
make -j2
if [ $? -ne 0 ]; then
/usr/bin/python3 $SEND_MSG "rtphone build failed. $BUILD_URL"
exit 1
fi
/usr/bin/python3 $SEND_MSG "rtphone builds ok. $BUILD_URL"
+192 -344
View File
@@ -1,381 +1,233 @@
cmake_minimum_required(VERSION 3.20)
project(rtphone) project(rtphone)
# Rely on C++ 20 cmake_minimum_required(VERSION 3.0)
set (CMAKE_CXX_STANDARD 20)
macro(configure_msvc_runtime)
if(MSVC)
# Default to statically-linked runtime.
if("${MSVC_RUNTIME}" STREQUAL "")
set(MSVC_RUNTIME "static")
endif()
# Set compiler options.
set(variables
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELWITHDEBINFO
)
if(${MSVC_RUNTIME} STREQUAL "static")
message(STATUS
"rtphone: MSVC -> forcing use of statically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
endif()
endforeach()
else()
message(STATUS
"rtphone: MSVC -> forcing use of dynamically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MT")
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
endif()
endforeach()
endif()
foreach(variable ${variables})
string(REGEX REPLACE "/Z[iI7]" ""
${variable}
"${${variable}}")
set(${variable} "${${variable}} /Zi /Oy-")
endforeach()
endif()
endmacro()
# Rely on C++ 11
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (L libs) set (rtphone_libs libs)
set (E engine) set (rtphone_engine engine)
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON) set (USE_AMR_CODEC OFF CACHE BOOL "Use AMR codec. Requires libraries.")
option (USE_EVS_CODEC "Use EVS codec." ON) set (USE_EVS_CODEC OFF CACHE BOOL "Use EVS codec." )
option (USE_MUSL "Build with MUSL library" OFF) 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 (USE_MUSL OFF CACHE BOOL "Build with MUSL 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 (CMAKE_WARN_DEPRECATED OFF)
find_package(OpenSSL REQUIRED) if (NOT DEFINED LIB_PLATFORM)
set (OPENSSL_SSL OpenSSL::SSL) set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries)
set (OPENSSL_CRYPTO OpenSSL::Crypto) endif()
message("Libraries: ${LIB_PLATFORM}")
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.0/include)
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}") message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
message ("Using OpenSSL libs: ${OPENSSL_SSL} and ${OPENSSL_CRYPTO}")
# include_directories(${OPENSSL_INCLUDE})
# Used defines for our project
set (DEFINES -DUSE_OPENSSL)
# Libraries for our project
set (LIBS_STATIC "")
set (LIBS_DYNAMIC "")
# Try to prefer static libraries anyway
set (CMAKE_FIND_LIBRARY_SUFFIXES .a .so .dylib)
# Windows-specific definitions
if (CMAKE_SYSTEM MATCHES "Windows*") if (CMAKE_SYSTEM MATCHES "Windows*")
set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS -DNOMINMAX) add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
set (TARGET_WIN ON)
endif() endif()
# Linux-specific definitions
if (CMAKE_SYSTEM MATCHES "Linux*") if (CMAKE_SYSTEM MATCHES "Linux*")
set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H) add_definitions (-DTARGET_LINUX)
set (TARGET_LINUX ON)
set (LIBS_STATIC ${LIBS_STATIC} dl)
endif() endif()
# macOS-specific definitions
if (CMAKE_SYSTEM MATCHES "Darwin*") if (CMAKE_SYSTEM MATCHES "Darwin*")
set (DEFINES ${DEFINES} -DTARGET_OSX) add_definitions (-DTARGET_OSX)
set (TARGET_OSX ON)
set (LIBS_STATIC ${LIBS_STATIC} dl)
endif() endif()
if (CMAKE_SYSTEM MATCHES "Android") if (CMAKE_SYSTEM MATCHES "Android")
# Disable opencore AMR codecs - there is no ready libraries in vcpkg right now
set (USE_AMR_CODEC OFF)
message("Adding the Oboe library") message("Adding the Oboe library")
set (OBOE_DIR libs/oboe) set (OBOE_DIR libs/oboe)
add_subdirectory (${OBOE_DIR} build_oboe) add_subdirectory (${OBOE_DIR} ./oboe)
include_directories (${OBOE_DIR}/include) include_directories (${OBOE_DIR}/include)
set (DEFINES ${DEFINES} -DTARGET_ANDROID -DHAVE_NETINET_IN_H)
set (TARGET_ANDROID ON)
set (LIBS_STATIC ${LIBS} oboe)
endif() endif()
if (USE_MUSL) if (USE_MUSL)
set (DEFINES ${DEFINES} -DTARGET_MUSL) add_definitions(-DTARGET_MUSL)
set (TARGET_MUSL ON) endif()
if (USE_AQUA_LIB)
message("Use AQuA library")
add_definitions( -DUSE_AQUA_LIBRARY )
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include)
endif()
if (USE_PVQA_LIBRARY)
message("Use PVQA libraries")
add_definitions( -DUSE_PVQA_LIBRARY )
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include
${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa++/include)
endif() endif()
set (RTPHONE_SOURCES set (RTPHONE_SOURCES
${E}/engine_config.h ${rtphone_engine}/media/MT_Statistics.cpp
${E}/media/MT_Statistics.cpp ${rtphone_engine}/media/MT_WebRtc.cpp
${E}/media/MT_WebRtc.cpp ${rtphone_engine}/media/MT_Stream.cpp
${E}/media/MT_Stream.cpp ${rtphone_engine}/media/MT_SrtpHelper.cpp
${E}/media/MT_SrtpHelper.cpp ${rtphone_engine}/media/MT_SingleAudioStream.cpp
${E}/media/MT_SingleAudioStream.cpp ${rtphone_engine}/media/MT_NativeRtpSender.cpp
${E}/media/MT_NativeRtpSender.cpp ${rtphone_engine}/media/MT_Dtmf.cpp
${E}/media/MT_Dtmf.cpp ${rtphone_engine}/media/MT_CodecList.cpp
${E}/media/MT_CodecList.cpp ${rtphone_engine}/media/MT_Codec.cpp
${E}/media/MT_Codec.cpp ${rtphone_engine}/media/MT_Box.cpp
${E}/media/MT_Box.cpp ${rtphone_engine}/media/MT_AudioStream.cpp
${E}/media/MT_AudioStream.cpp ${rtphone_engine}/media/MT_AudioReceiver.cpp
${E}/media/MT_AudioReceiver.cpp ${rtphone_engine}/media/MT_AudioCodec.cpp
${E}/media/MT_AudioCodec.cpp ${rtphone_engine}/media/MT_CngHelper.cpp
${E}/media/MT_CngHelper.cpp ${rtphone_engine}/agent/Agent_Impl.cpp
${E}/agent/Agent_Impl.cpp ${rtphone_engine}/agent/Agent_AudioManager.cpp
${E}/agent/Agent_Impl.h ${rtphone_engine}/endpoint/EP_Account.cpp
${E}/agent/Agent_AudioManager.cpp ${rtphone_engine}/endpoint/EP_AudioProvider.cpp
${E}/agent/Agent_AudioManager.h ${rtphone_engine}/endpoint/EP_DataProvider.cpp
${E}/endpoint/EP_Account.cpp ${rtphone_engine}/endpoint/EP_Engine.cpp
${E}/endpoint/EP_Account.h ${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
${E}/endpoint/EP_AudioProvider.cpp ${rtphone_engine}/endpoint/EP_Observer.cpp
${E}/endpoint/EP_AudioProvider.h ${rtphone_engine}/endpoint/EP_Session.cpp
${E}/endpoint/EP_DataProvider.cpp
${E}/endpoint/EP_DataProvider.h
${E}/endpoint/EP_Engine.cpp
${E}/endpoint/EP_Engine.h
${E}/endpoint/EP_NetworkQueue.cpp
${E}/endpoint/EP_NetworkQueue.h
${E}/endpoint/EP_Observer.cpp
${E}/endpoint/EP_Observer.h
${E}/endpoint/EP_Session.cpp
${E}/endpoint/EP_Session.h
${E}/media/MT_Statistics.h ${rtphone_engine}/media/MT_Statistics.h
${E}/media/MT_WebRtc.h ${rtphone_engine}/media/MT_WebRtc.h
${E}/media/MT_Stream.h ${rtphone_engine}/media/MT_Stream.h
${E}/media/MT_SrtpHelper.h ${rtphone_engine}/media/MT_SrtpHelper.h
${E}/media/MT_SingleAudioStream.h ${rtphone_engine}/media/MT_SingleAudioStream.h
${E}/media/MT_NativeRtpSender.h ${rtphone_engine}/media/MT_NativeRtpSender.h
${E}/media/MT_Dtmf.h ${rtphone_engine}/media/MT_Dtmf.h
${E}/media/MT_CodecList.h ${rtphone_engine}/media/MT_CodecList.h
${E}/media/MT_Codec.h ${rtphone_engine}/media/MT_Codec.h
${E}/media/MT_Box.h ${rtphone_engine}/media/MT_Box.h
${E}/media/MT_AudioStream.h ${rtphone_engine}/media/MT_AudioStream.h
${E}/media/MT_AudioReceiver.h ${rtphone_engine}/media/MT_AudioReceiver.h
${E}/media/MT_AudioCodec.h ${rtphone_engine}/media/MT_AudioCodec.h
${E}/media/MT_CngHelper.h
${E}/media/MT_Statistics.cpp ${rtphone_engine}/media/MT_CngHelper.h
${E}/media/MT_WebRtc.cpp ${rtphone_engine}/agent/Agent_Impl.h
${E}/media/MT_Stream.cpp ${rtphone_engine}/agent/Agent_AudioManager.h
${E}/media/MT_SrtpHelper.cpp ${rtphone_engine}/endpoint/EP_Account.h
${E}/media/MT_SingleAudioStream.cpp ${rtphone_engine}/endpoint/EP_AudioProvider.h
${E}/media/MT_NativeRtpSender.cpp ${rtphone_engine}/endpoint/EP_DataProvider.h
${E}/media/MT_Dtmf.cpp ${rtphone_engine}/endpoint/EP_Engine.h
${E}/media/MT_CodecList.cpp ${rtphone_engine}/endpoint/EP_NetworkQueue.h
${E}/media/MT_Codec.cpp ${rtphone_engine}/endpoint/EP_Observer.h
${E}/media/MT_Box.cpp ${rtphone_engine}/endpoint/EP_Session.h
${E}/media/MT_AudioStream.cpp
${E}/media/MT_AudioReceiver.cpp
${E}/media/MT_AudioCodec.cpp
${E}/media/MT_CngHelper.cpp
${E}/media/MT_AmrCodec.cpp
${E}/media/MT_EvsCodec.cpp
${E}/media/MT_Statistics.h
${E}/media/MT_WebRtc.h
${E}/media/MT_Stream.h
${E}/media/MT_SrtpHelper.h
${E}/media/MT_SingleAudioStream.h
${E}/media/MT_NativeRtpSender.h
${E}/media/MT_Dtmf.h
${E}/media/MT_CodecList.h
${E}/media/MT_Codec.h
${E}/media/MT_Box.h
${E}/media/MT_AudioStream.h
${E}/media/MT_AudioReceiver.h
${E}/media/MT_AudioCodec.h
${E}/media/MT_CngHelper.h
${E}/media/MT_AmrCodec.h
${E}/media/MT_EvsCodec.h
${E}/helper/HL_AsyncCommand.cpp
${E}/helper/HL_AsyncCommand.h
${E}/helper/HL_Base64.h
${E}/helper/HL_ByteBuffer.h
${E}/helper/HL_Calculator.cpp
${E}/helper/HL_Calculator.h
${E}/helper/HL_CrashRpt.cpp
${E}/helper/HL_CrashRpt.h
${E}/helper/HL_CsvReader.cpp
${E}/helper/HL_CsvReader.h
${E}/helper/HL_Epoll.cpp
${E}/helper/HL_Epoll.h
${E}/helper/HL_Exception.h
${E}/helper/HL_File.cpp
${E}/helper/HL_File.h
${E}/helper/HL_HepSupport.cpp
${E}/helper/HL_HepSupport.h
${E}/helper/HL_InternetAddress.h
${E}/helper/HL_IuUP.cpp
${E}/helper/HL_IuUP.h
${E}/helper/HL_Log.cpp
${E}/helper/HL_Log.h
${E}/helper/HL_NetworkFrame.cpp
${E}/helper/HL_NetworkFrame.h
${E}/helper/HL_NetworkSocket.cpp
${E}/helper/HL_NetworkSocket.h
${E}/helper/HL_Optional.hpp
${E}/helper/HL_OsVersion.cpp
${E}/helper/HL_OsVersion.h
${E}/helper/HL_Pointer.cpp
${E}/helper/HL_Pointer.h
${E}/helper/HL_Process.cpp
${E}/helper/HL_Process.h
${E}/helper/HL_Rtp.cpp
${E}/helper/HL_Rtp.h
${E}/helper/HL_Singletone.cpp
${E}/helper/HL_Singletone.h
${E}/helper/HL_SocketHeap.cpp
${E}/helper/HL_SocketHeap.h
${E}/helper/HL_Statistics.cpp
${E}/helper/HL_Statistics.h
${E}/helper/HL_StreamState.h
${E}/helper/HL_String.cpp
${E}/helper/HL_String.h
${E}/helper/HL_Sync.cpp
${E}/helper/HL_Sync.h
${E}/helper/HL_ThreadPool.cpp
${E}/helper/HL_ThreadPool.h
${E}/helper/HL_Time.cpp
${E}/helper/HL_Time.h
${E}/helper/HL_Types.h
${E}/helper/HL_Types.cpp
${E}/helper/HL_Usb.cpp
${E}/helper/HL_Usb.h
${E}/helper/HL_Uuid.cpp
${E}/helper/HL_Uuid.h
${E}/helper/HL_VariantMap.cpp
${E}/helper/HL_VariantMap.h
${E}/helper/HL_Xcap.cpp
${E}/helper/HL_Xcap.h
${E}/audio/Audio_Resampler.cpp
${E}/audio/Audio_Resampler.h
${E}/audio/Audio_Quality.cpp
${E}/audio/Audio_Quality.h
${E}/audio/Audio_Mixer.cpp
${E}/audio/Audio_Mixer.h
${E}/audio/Audio_Interface.cpp
${E}/audio/Audio_Interface.h
${E}/audio/Audio_Helper.cpp
${E}/audio/Audio_Helper.h
${E}/audio/Audio_DataWindow.cpp
${E}/audio/Audio_DataWindow.h
${E}/audio/Audio_DevicePair.cpp
${E}/audio/Audio_DevicePair.h
${E}/audio/Audio_Player.cpp
${E}/audio/Audio_Player.h
${E}/audio/Audio_Null.cpp
${E}/audio/Audio_Null.h
${E}/audio/Audio_CoreAudio.cpp
${E}/audio/Audio_CoreAudio.h
${E}/audio/Audio_DirectSound.cpp
${E}/audio/Audio_DirectSound.h
${E}/audio/Audio_AndroidOboe.cpp
${E}/audio/Audio_AndroidOboe.h
${E}/audio/Audio_WavFile.cpp
${E}/audio/Audio_WavFile.h
${L}/ice/hmac_sha1_impl.cpp
${L}/ice/hmac_sha1_impl.h
${L}/ice/ICEAction.h
${L}/ice/ICEAddress.cpp
${L}/ice/ICEAddress.h
${L}/ice/ICEAuthTransaction.cpp
${L}/ice/ICEAuthTransaction.h
${L}/ice/ICEBinding.cpp
${L}/ice/ICEBinding.h
${L}/ice/ICEBox.cpp
${L}/ice/ICEBox.h
${L}/ice/ICEBoxImpl.cpp
${L}/ice/ICEBoxImpl.h
${L}/ice/ICEByteBuffer.cpp
${L}/ice/ICEByteBuffer.h
${L}/ice/ICECandidate.cpp
${L}/ice/ICECandidate.h
${L}/ice/ICECandidatePair.cpp
${L}/ice/ICECandidatePair.h
${L}/ice/ICECheckList.cpp
${L}/ice/ICECheckList.h
${L}/ice/ICECRC32.cpp
${L}/ice/ICECRC32.h
${L}/ice/ICEError.cpp
${L}/ice/ICEError.h
${L}/ice/ICEEvent.h
${L}/ice/ICELog.cpp
${L}/ice/ICELog.h
${L}/ice/ICEMD5.cpp
${L}/ice/ICEMD5.h
${L}/ice/ICENetworkHelper.cpp
${L}/ice/ICENetworkHelper.h
${L}/ice/ICEPacketTimer.cpp
${L}/ice/ICEPacketTimer.h
${L}/ice/ICEPlatform.cpp
${L}/ice/ICEPlatform.h
${L}/ice/ICERelaying.cpp
${L}/ice/ICERelaying.h
${L}/ice/ICESession.cpp
${L}/ice/ICESession.h
${L}/ice/ICESHA1.cpp
${L}/ice/ICESHA1.h
${L}/ice/ICESocket.h
${L}/ice/ICEStream.cpp
${L}/ice/ICEStream.h
${L}/ice/ICEStunAttributes.cpp
${L}/ice/ICEStunAttributes.h
${L}/ice/ICEStunConfig.cpp
${L}/ice/ICEStunConfig.h
${L}/ice/ICEStunMessage.cpp
${L}/ice/ICEStunMessage.h
${L}/ice/ICEStunTransaction.cpp
${L}/ice/ICEStunTransaction.h
${L}/ice/ICESync.cpp
${L}/ice/ICESync.h
${L}/ice/ICETime.cpp
${L}/ice/ICETime.h
${L}/ice/ICETransactionList.cpp
${L}/ice/ICETransactionList.h
${L}/ice/ICETypes.h
${L}/ice/md5_impl.cpp
${L}/ice/md5_impl.h
) )
if (USE_AMR_CODEC)
add_definitions(-DUSE_AMR_CODEC)
set(RTPHONE_SOURCES ${RTPHONE_SOURCES} ${rtphone_engine}/media/MT_AmrCodec.cpp ${rtphone_engine}/media/MT_AmrCodec.h)
endif()
if (USE_EVS_CODEC)
add_definitions(-DUSE_EVS_CODEC)
set(RTPHONE_SOURCES ${RTPHONE_SOURCES} ${rtphone_engine}/media/MT_EvsCodec.cpp ${rtphone_engine}/media/MT_EvsCodec.h)
endif()
if (USE_OPUS_CODEC)
add_definitions(-DUSE_OPUS_CODEC)
endif()
add_library (rtphone STATIC ${RTPHONE_SOURCES}) add_library (rtphone STATIC ${RTPHONE_SOURCES})
add_subdirectory(${L}/resiprocate) add_subdirectory(${rtphone_libs}/resiprocate)
add_subdirectory(${L}/jrtplib/src) add_subdirectory(${rtphone_libs}/ice)
add_subdirectory(${L}/libg729) add_subdirectory(${rtphone_libs}/jrtplib/src)
add_subdirectory(${rtphone_libs}/libg729)
if (USE_EVS_CODEC) if (USE_EVS_CODEC)
add_subdirectory(${L}/libevs) add_subdirectory(${rtphone_libs}/libevs)
endif() endif()
add_subdirectory(${L}/libgsm) add_subdirectory(${rtphone_libs}/libgsm)
add_subdirectory(${L}/gsmhr) add_subdirectory(${rtphone_libs}/gsmhr)
add_subdirectory(${L}/g722) add_subdirectory(${rtphone_libs}/g722)
add_subdirectory(${L}/speexdsp) add_subdirectory(${rtphone_libs}/speexdsp)
add_subdirectory(${L}/libsrtp) add_subdirectory(${rtphone_libs}/srtp)
add_subdirectory(${L}/webrtc) add_subdirectory(${rtphone_libs}/webrtc)
add_subdirectory(${L}/opus) add_subdirectory(${rtphone_engine}/helper)
add_subdirectory(${rtphone_engine}/audio)
add_subdirectory(${rtphone_engine}/media)
# Suppose the subproject defines target "mylib" set (LIBS ice_stack jrtplib g729_codec gsm_codec
if(MSVC) gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp
# target_compile_options(opus PRIVATE /O2 /DNDEBUG) uuid)
# Optional: enable whole program optimization on MSVC
# target_compile_options(mylib PRIVATE /GL) if (CMAKE_SYSTEM MATCHES "Win*")
# target_link_options(mylib PRIVATE /LTCG) set (LIBS ${LIBS} )
else() else ()
target_compile_options(opus PRIVATE -O3 -DNDEBUG) set (LIBS ${LIBS} dl uuid)
endif ()
if (CMAKE_SYSTEM MATCHES "Android")
set (LIBS ${LIBS} oboe)
endif() endif()
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
if (USE_AMR_CODEC) if (USE_AMR_CODEC)
message("Media: AMR NB and WB codecs will be included") set (LIBS ${LIBS})
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/libs/opencore-amr build_opencore) endif (USE_AMR_CODEC)
set (OPENCORE_AMRWB opencore-amrwb)
set (OPENCORE_AMRNB opencore-amrnb)
set (DEFINES ${DEFINES} -DUSE_AMR_CODEC) target_link_libraries(rtphone
set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB}) ice_stack jrtplib g729_codec gsm_codec
endif() gsmhr_codec g722_codec srtp resiprocate
helper_lib
audio_lib
webrtc
speexdsp
uuid
${OPENSSL_SSL}
${OPENSSL_CRYPTO}
${LIBS} )
if (USE_EVS_CODEC)
message("Media: EVS codec will be included.")
set (DEFINES ${DEFINES} -DUSE_EVS_CODEC)
set (LIBS_STATIC ${LIBS_STATIC} evs_codec)
endif()
target_compile_definitions(rtphone PUBLIC ${DEFINES} )
if (TARGET_LINUX)
# PRIVATE, not PUBLIC: rtphone is a STATIC library, so these link options are
# never used to build rtphone itself and must not propagate to consumers.
# As PUBLIC they leaked into every consumer's LINK_FLAGS as an adjacent
# "-Wl,-Bstatic -Wl,-Bdynamic" pair (the wrapped libraries land in a separate
# LINK_LIBRARIES section, so nothing is actually wrapped). The trailing
# -Bdynamic forced the linker back into dynamic-search mode, which broke
# fully-static consumers (e.g. vq-core built with SERVER_STATIC_LINKING=ON:
# "attempted static link of dynamic object libz.so").
target_link_options(rtphone PRIVATE -Wl,-Bstatic)
target_compile_options(rtphone PUBLIC -Wno-deprecated -Wno-deprecated-declarations)
endif()
target_link_libraries(rtphone PUBLIC ${LIBS_STATIC})
if (TARGET_LINUX)
target_link_options(rtphone PRIVATE -Wl,-Bdynamic)
endif()
target_include_directories(rtphone target_include_directories(rtphone
PUBLIC PUBLIC
@@ -383,19 +235,15 @@ target_include_directories(rtphone
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
${CMAKE_CURRENT_SOURCE_DIR}/libs ${CMAKE_CURRENT_SOURCE_DIR}/libs
${LIB_PLATFORM}/opus/include ${LIB_PLATFORM}/opus/include
${E}/helper
${E}/audio
${E}/media
${L}
${L}/ice
PRIVATE PRIVATE
${L}/libevs/lib_com ${CMAKE_CURRENT_SOURCE_DIR}/libs/
${L}/libevs/lib_enc ${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com
${L}/libevs/lib_dec ${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc
${L}/speex/include ${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec
${L}/libs/json ${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
) )
find_package(OpenSSL REQUIRED) # For MSVC static builds
target_link_libraries(rtphone PUBLIC OpenSSL::SSL) configure_msvc_runtime()
target_link_libraries(rtphone PUBLIC OpenSSL::Crypto)
+37 -50
View File
@@ -1,28 +1,42 @@
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Agent_AudioManager.h" #include "Agent_AudioManager.h"
#include "../engine/audio/Audio_WavFile.h" #include "../engine/audio/Audio_WavFile.h"
#include "../engine/helper/HL_String.h"
#include "../engine/audio/Audio_Null.h" #include "../engine/audio/Audio_Null.h"
#include "HL_String.h"
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
#include "../engine/audio/Audio_Android.h" # include "../engine/audio/Audio_Android.h"
#endif #endif
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "AudioManager"
AudioManager::AudioManager() : mTerminal(nullptr), mAudioMonitoring(nullptr) // ---------------- AudioManager -------------
//static AudioManager GAudioManager;
AudioManager::AudioManager()
:mTerminal(nullptr)
{ {
mPlayer.setDelegate(this); mPlayer.setDelegate(this);
} }
AudioManager::~AudioManager() AudioManager::~AudioManager()
{ {
// stop(); //stop();
}
AudioManager& AudioManager::instance()
{
static std::shared_ptr<AudioManager> GAudioManager;
if (!GAudioManager)
GAudioManager = std::make_shared<AudioManager>();
return *GAudioManager;
} }
void AudioManager::setTerminal(MT::Terminal* terminal) void AudioManager::setTerminal(MT::Terminal* terminal)
@@ -35,16 +49,6 @@ MT::Terminal* AudioManager::terminal()
return mTerminal; return mTerminal;
} }
void AudioManager::setAudioMonitoring(Audio::DataConnection* monitoring)
{
mAudioMonitoring = monitoring;
}
Audio::DataConnection* AudioManager::audioMonitoring()
{
return mAudioMonitoring;
}
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard) #define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
void AudioManager::start(int usageId) void AudioManager::start(int usageId)
{ {
@@ -56,7 +60,6 @@ void AudioManager::start(int usageId)
if (mUsage.obtain(usageId) > 1) if (mUsage.obtain(usageId) > 1)
return; return;
// Maybe it is time to initialize global audio support
if (Audio::OsEngine::instance()) if (Audio::OsEngine::instance())
Audio::OsEngine::instance()->open(); Audio::OsEngine::instance()->open();
@@ -65,49 +68,43 @@ 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)
{ {
enumerator->open(Audio::myMicrophone); enumerator->open(Audio::myMicrophone);
int inputIndex = enumerator->indexOfDefaultDevice(); int inputIndex = enumerator->indexOfDefaultDevice();
// Construct default platform input device // Construct and set to terminal's audio pair input device
if (usageId != atNull) if (usageId != atNull)
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex))); mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
else else
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice()); mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
}
// Bind input to the terminal's device pair regardless of whether it was
// just constructed or externally injected via setAudioInput().
mTerminal->audio()->setInput(mAudioInput); mTerminal->audio()->setInput(mAudioInput);
}
if (!mAudioOutput) if (!mAudioOutput)
{ {
Audio::Enumerator* enumerator = Audio::Enumerator::make(usageId == atNull); Audio::Enumerator *enumerator = Audio::Enumerator::make(usageId == atNull);
enumerator->open(Audio::mySpeaker); enumerator->open(Audio::mySpeaker);
int outputIndex = enumerator->indexOfDefaultDevice(); int outputIndex = enumerator->indexOfDefaultDevice();
// Construct default platform output device // Construct and set terminal's audio pair output device
if (usageId != atNull) if (usageId != atNull)
{ {
if (outputIndex >= enumerator->count()) if (outputIndex >= enumerator->count())
outputIndex = 0; outputIndex = 0;
mAudioOutput = Audio::POutputDevice(Audio::OutputDevice::make(enumerator->idAt(outputIndex))); mAudioOutput = Audio::POutputDevice(
Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
} }
else else
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice()); mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
}
mTerminal->audio()->setOutput(mAudioOutput); mTerminal->audio()->setOutput(mAudioOutput);
} }
}
// Open audio // Open audio
if (mAudioInput) if (mAudioInput)
@@ -137,7 +134,7 @@ void AudioManager::stop(int usageId)
{ {
LOCK_MANAGER; LOCK_MANAGER;
ICELogInfo(<< "Stop main audio with usage id " << usageId); ICELogInfo( << "Stop main audio with usage id " << usageId);
if (mTerminal) if (mTerminal)
{ {
if (mTerminal->audio()) if (mTerminal->audio())
@@ -156,24 +153,12 @@ void AudioManager::stop(int usageId)
} }
} }
void AudioManager::setAudioInput(Audio::PInputDevice input)
{
LOCK_MANAGER;
mAudioInput = std::move(input);
}
void AudioManager::setAudioOutput(Audio::POutputDevice output)
{
LOCK_MANAGER;
mAudioOutput = std::move(output);
}
void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit) void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit)
{ {
// Check if file exists // Check if file exists
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>(); Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
#ifdef TARGET_WIN #ifdef TARGET_WIN
r->open(strx::makeTstring(path)); r->open(StringHelper::makeTstring(path));
#else #else
r->open(path); r->open(path);
#endif #endif
@@ -194,13 +179,15 @@ void AudioManager::stopPlayFile(int usageId)
mPlayer.release(usageId); mPlayer.release(usageId);
} }
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item) {} void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item)
{
}
void AudioManager::process() void AudioManager::process()
{ {
mPlayer.releasePlayed(); mPlayer.releasePlayed();
std::vector<int> ids; std::vector<int> ids;
mTerminal->audio()->player().retrieveUsageIds(ids); mTerminal->audio()->player().retrieveUsageIds(ids);
for (int id : ids) for (unsigned i=0; i<ids.size(); i++)
stop(id); stop(ids[i]);
} }
+6 -13
View File
@@ -8,7 +8,11 @@
#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"
enum enum
@@ -30,13 +34,13 @@ enum
#define AudioSessionCoeff 64 #define AudioSessionCoeff 64
class AudioManager : public Audio::Player::EndOfAudioDelegate class AudioManager: public Audio::Player::EndOfAudioDelegate
{ {
public: public:
AudioManager(); AudioManager();
virtual ~AudioManager(); virtual ~AudioManager();
// static AudioManager& instance(); static AudioManager& instance();
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application // Enforces to close audio devices. Used to shutdown AudioManager on exit from application
void close(); void close();
@@ -45,19 +49,10 @@ 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);
// Inject a custom input device. Must be called before start(): when set,
// start() skips construction of the default platform microphone. Pass an
// empty pointer to clear the override.
void setAudioInput(Audio::PInputDevice input);
void setAudioOutput(Audio::POutputDevice output);
enum AudioTarget enum AudioTarget
{ {
atNull, atNull,
@@ -74,7 +69,6 @@ public:
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0); void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
void stopPlayFile(int usageId); void stopPlayFile(int usageId);
void onFilePlayed(Audio::Player::PlaylistItem& item); void onFilePlayed(Audio::Player::PlaylistItem& item);
// Must be called from main loop to release used audio devices // Must be called from main loop to release used audio devices
@@ -85,7 +79,6 @@ protected:
Audio::POutputDevice mAudioOutput; Audio::POutputDevice mAudioOutput;
Audio::Player mPlayer; Audio::Player mPlayer;
MT::Terminal* mTerminal; MT::Terminal* mTerminal;
Audio::DataConnection* mAudioMonitoring;
std::map<int, int> UsageMap; std::map<int, int> UsageMap;
UsageCounter mUsage; UsageCounter mUsage;
+262 -156
View File
@@ -3,12 +3,17 @@
#include "helper/HL_String.h" #include "helper/HL_String.h"
#include "helper/HL_StreamState.h" #include "helper/HL_StreamState.h"
#include "helper/HL_VariantMap.h" #include "helper/HL_VariantMap.h"
// #include "helper/HL_CsvReader.h" #include "helper/HL_CsvReader.h"
// #include "helper/HL_Base64.h" #include "helper/HL_Base64.h"
#include "media/MT_CodecList.h" #include <fstream>
#include "audio/Audio_Null.h"
// #include <fstream>
#if defined(USE_PVQA_LIBRARY)
# include "pvqa++.h"
#endif
#if defined(USE_AQUA_LIBRARY)
# include "aqua++.h"
#endif
const std::string Status_Ok = "ok"; const std::string Status_Ok = "ok";
const std::string Status_SessionNotFound = "session not found"; const std::string Status_SessionNotFound = "session not found";
@@ -17,11 +22,11 @@ const std::string Status_FailedToOpenFile = "failed to open file";
const std::string Status_NoActiveProvider = "no active provider"; const std::string Status_NoActiveProvider = "no active provider";
const std::string Status_NoMediaAction = "no valid media action"; const std::string Status_NoMediaAction = "no valid media action";
const std::string Status_NoCommand = "no valid command"; const std::string Status_NoCommand = "no valid command";
const std::string Status_NoAudioManager = "no audio manager";
#define LOG_SUBSYSTEM "agent" #define LOG_SUBSYSTEM "Agent"
AgentImpl::AgentImpl() : mShutdown(false), mEventListChangeCondVar() AgentImpl::AgentImpl()
:mShutdown(false), mEventListChangeCondVar()
{ {
#if defined(TARGET_ANDROID) || defined(TARGET_WIN) #if defined(TARGET_ANDROID) || defined(TARGET_WIN)
ice::GLogger.useDebugWindow(true); ice::GLogger.useDebugWindow(true);
@@ -34,24 +39,6 @@ AgentImpl::~AgentImpl()
stopAgentAndThread(); stopAgentAndThread();
} }
// Get access to internal audio manager. Value can be nullptr.
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
{
return mAudioManager;
}
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
{
mAudioMonitoring = monitoring;
if (mAudioManager)
mAudioManager->setAudioMonitoring(monitoring);
}
Audio::DataConnection* AgentImpl::monitoring() const
{
return mAudioMonitoring;
}
void AgentImpl::run() void AgentImpl::run()
{ {
while (!mShutdown) while (!mShutdown)
@@ -77,71 +64,82 @@ std::string AgentImpl::command(const std::string& command)
return ""; return "";
std::string cmd = d["command"].asString(); std::string cmd = d["command"].asString();
if (cmd != "wait_for_event" && cmd != "agent_add_root_cert") if (cmd != "wait_for_event")
{ {
ICELogInfo(<< command); ICELogInfo(<< command);
} }
if (cmd == "config") if (cmd == "config")
processConfig(d, answer); processConfig(d, answer);
else if (cmd == "start") else
if (cmd == "start")
processStart(d, answer); processStart(d, answer);
else if (cmd == "stop") else
if (cmd == "stop")
processStop(d, answer); processStop(d, answer);
else if (cmd == "account_create") else
if (cmd == "account_create")
processCreateAccount(d, answer); processCreateAccount(d, answer);
else if (cmd == "account_start") else
if (cmd == "account_start")
processStartAccount(d, answer); processStartAccount(d, answer);
else if (cmd == "account_setuserinfo") else
if (cmd == "account_setuserinfo")
processSetUserInfoToAccount(d, answer); processSetUserInfoToAccount(d, answer);
else if (cmd == "session_create") else
{ if (cmd == "session_create")
// For Bugsnag test
// int* v = nullptr;
// *v = 0;
processCreateSession(d, answer); processCreateSession(d, answer);
} else
else if (cmd == "session_start") if (cmd == "session_start")
processStartSession(d, answer); processStartSession(d, answer);
else if (cmd == "session_stop") else
if (cmd == "session_stop")
processStopSession(d, answer); processStopSession(d, answer);
else if (cmd == "session_accept") else
if (cmd == "session_accept")
processAcceptSession(d, answer); processAcceptSession(d, answer);
else if (cmd == "session_destroy") else
if (cmd == "session_destroy")
processDestroySession(d, answer); processDestroySession(d, answer);
else if (cmd == "session_use_stream") else
if (cmd == "session_use_stream")
processUseStreamForSession(d, answer); processUseStreamForSession(d, answer);
else if (cmd == "wait_for_event") else
if (cmd == "wait_for_event")
processWaitForEvent(d, answer); processWaitForEvent(d, answer);
else if (cmd == "session_get_media_stats") else
if (cmd == "session_get_media_stats")
processGetMediaStats(d, answer); processGetMediaStats(d, answer);
else if (cmd == "agent_network_changed") else
if (cmd == "agent_network_changed")
processNetworkChanged(d, answer); processNetworkChanged(d, answer);
else if (cmd == "agent_add_root_cert") else
if (cmd == "agent_add_root_cert")
processAddRootCert(d, answer); processAddRootCert(d, answer);
else if (cmd == "detach_log") else
if (cmd == "detach_log")
{ {
GLogger.closeFile(); GLogger.closeFile();
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
else if (cmd == "attach_log") else
if (cmd == "attach_log")
{ {
GLogger.openFile(); GLogger.openFile();
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
else if (cmd == "log_message") else
if (cmd == "log_message")
processLogMessage(d, answer); processLogMessage(d, answer);
else else
{ {
answer["status"] = Status_NoCommand; answer["status"] = Status_NoCommand;
} }
} }
catch (std::exception& e) catch(std::exception& e)
{ {
answer["status"] = e.what(); answer["status"] = e.what();
} }
std::string result = answer.toStyledString(); return answer.toStyledString();
return result;
} }
bool AgentImpl::waitForData(int /*milliseconds*/) bool AgentImpl::waitForData(int /*milliseconds*/)
@@ -154,18 +152,32 @@ std::string AgentImpl::read()
return ""; return "";
} }
void AgentImpl::processConfig(JsonCpp::Value& d, JsonCpp::Value& answer) void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
#if !defined(TARGET_ANDROID) && defined(USE_PVQA_LIBRARY)
// It works for desktop OSes only
// Because Android requires special initializing procedure (valid JNI environment context)
std::string pvqaLicense = d["pvqa-license"].asString(), pvqaConfig = d["pvqa-config"].asString();
if (!pvqaLicense.empty() && !pvqaConfig.empty())
sevana::pvqa::initialize(pvqaLicense, pvqaConfig);
#endif
#if !defined(TARGET_ANDROID) && defined(USE_AQUA_LIBRARY)
std::string aquaLicense = d["aqua-license"].asString();
if (!aquaLicense.empty())
sevana::aqua::initialize(aquaLicense.c_str(), aquaLicense.size());
#endif
std::string transport = d["transport"].asString(); std::string transport = d["transport"].asString();
config()[CONFIG_TRANSPORT] = config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
(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;
@@ -174,17 +186,15 @@ void AgentImpl::processConfig(JsonCpp::Value& d, JsonCpp::Value& answer)
config()[CONFIG_MULTIPLEXING] = true; config()[CONFIG_MULTIPLEXING] = true;
config()[CONFIG_DISPLAYNAME] = "Voip quality tester"; config()[CONFIG_DISPLAYNAME] = "Voip quality tester";
config()[CONFIG_USERAGENT] = d["useragent"].asString();
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() || transport == "tls"; 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)
@@ -200,25 +210,28 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value& answer)
SocketHeap::instance().start(); SocketHeap::instance().start();
// Initialize terminal // Initialize terminal
auto settings = MT::CodecList::Settings::getClientSettings(); MT::CodecList::Settings settings;
mTerminal = std::make_shared<MT::Terminal>(settings); mTerminal = std::make_shared<MT::Terminal>(settings);
// Enable/disable codecs // Enable/disable codecs
PVariantMap priorityConfig = std::make_shared<VariantMap>(); PVariantMap priorityConfig = std::make_shared<VariantMap>();
MT::CodecList& cl = mTerminal->codeclist(); MT::CodecList& cl = mTerminal->codeclist();
for (int i = 0; i < cl.count(); i++) for (int i=0; i<cl.count(); i++)
priorityConfig->at(i) = i; priorityConfig->at(i) = i;
// Disable dynamic payload codec types - commented for now
/*if (cl.codecAt(i).payloadType() < 96)
priorityConfig->at(i) = i;
else
priorityConfig->at(i) = -1;*/
config()[CONFIG_CODEC_PRIORITY] = priorityConfig; config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
// Enable audio // Enable audio
mAudioManager = std::make_shared<AudioManager>(); mAudioManager = std::make_shared<AudioManager>();
mAudioManager->setTerminal(mTerminal.get()); mAudioManager->setTerminal(mTerminal.get());
if (mAudioMonitoring)
mAudioManager->setAudioMonitoring(mAudioMonitoring);
// Do not start audio manager here. Start right before call. // Do not start here. Start right before call.
// Initialize endpoint // Initialize endpoint
start(); start();
@@ -235,7 +248,7 @@ void AgentImpl::processStop(JsonCpp::Value& /*request*/, JsonCpp::Value& answer)
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processCreateAccount(JsonCpp::Value& d, JsonCpp::Value& answer) void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
PVariantMap c = std::make_shared<VariantMap>(); PVariantMap c = std::make_shared<VariantMap>();
@@ -243,11 +256,9 @@ void AgentImpl::processCreateAccount(JsonCpp::Value& d, JsonCpp::Value& answer)
(*c)[CONFIG_USERNAME] = d["username"].asString(); (*c)[CONFIG_USERNAME] = d["username"].asString();
(*c)[CONFIG_PASSWORD] = d["password"].asString(); (*c)[CONFIG_PASSWORD] = d["password"].asString();
(*c)[CONFIG_DOMAIN] = d["domain"].asString(); (*c)[CONFIG_DOMAIN] = d["domain"].asString();
if (d.isMember("domain_port"))
(*c)[CONFIG_DOMAINPORT] = d["domain_port"].asInt();
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool(); (*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
auto nameAndPort = strx::parseHost(d["stun_server"].asString(), 3478); auto nameAndPort = StringHelper::parseHost(d["stun_server"].asString(), 3478);
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first; (*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second; (*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
@@ -271,7 +282,7 @@ void AgentImpl::processStartAccount(JsonCpp::Value& request, JsonCpp::Value& ans
answer["status"] = Status_AccountNotFound; answer["status"] = Status_AccountNotFound;
} }
void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map // Locate account in map
@@ -281,7 +292,7 @@ void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Va
Account::UserInfo info; Account::UserInfo info;
JsonCpp::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k : keys) for (const std::string& k: keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
accountIter->second->setUserInfo(info); accountIter->second->setUserInfo(info);
@@ -291,7 +302,7 @@ void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Va
answer["status"] = Status_AccountNotFound; answer["status"] = Status_AccountNotFound;
} }
void AgentImpl::processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processCreateSession(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto accountIter = mAccountMap.find(request["account_id"].asInt()); auto accountIter = mAccountMap.find(request["account_id"].asInt());
@@ -315,15 +326,10 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
{ {
// Agent was not started // Agent was not started
ICELogError(<< "No audio manager installed."); ICELogError(<< "No audio manager installed.");
answer["status"] = Status_NoAudioManager; answer["status"] = "Audio manager not started. Most probably agent is not started.";
return; return;
} }
if (request["use_null_mic"].asBool())
mAudioManager->setAudioInput(std::make_shared<Audio::NullInputDevice>());
if (request["use_null_spk"].asBool())
mAudioManager->setAudioOutput(std::make_shared<Audio::NullOutputDevice>());
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull); mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
auto sessionIter = mSessionMap.find(request["session_id"].asInt()); auto sessionIter = mSessionMap.find(request["session_id"].asInt());
@@ -332,10 +338,9 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
// Ensure audio provider is here // Ensure audio provider is here
PSession session = sessionIter->second; PSession session = sessionIter->second;
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) | audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
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 = {
@@ -358,10 +363,9 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
{ "specp", "32"} { "specp", "32"}
}; };
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on // 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";
-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())
@@ -374,7 +378,6 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
mAquaMap[sessionIter->first] = qc; mAquaMap[sessionIter->first] = qc;
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get()); dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
#endif #endif
*/
// TODO: support SRTP via StreamState::Srtp option in audio provider state // TODO: support SRTP via StreamState::Srtp option in audio provider state
@@ -382,7 +385,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
Session::UserHeaders info; Session::UserHeaders info;
JsonCpp::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k : keys) for (const std::string& k: keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
session->setUserHeaders(info); session->setUserHeaders(info);
@@ -416,19 +419,8 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto sessionIter = mSessionMap.find(request["session_id"].asInt()); auto sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
{
if (!mAudioManager)
{
ICELogError(<< "No audio manager installed.");
answer["status"] = Status_NoAudioManager;
}
else
{ {
// Ensure audio manager is here // Ensure audio manager is here
if (request["use_null_mic"].asBool())
mAudioManager->setAudioInput(std::make_shared<Audio::NullInputDevice>());
if (request["use_null_spk"].asBool())
mAudioManager->setAudioOutput(std::make_shared<Audio::NullOutputDevice>());
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull); mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
// Accept session on SIP level // Accept session on SIP level
@@ -438,7 +430,7 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
Session::UserHeaders info; Session::UserHeaders info;
JsonCpp::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k : keys) for (const std::string& k: keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
session->setUserHeaders(info); session->setUserHeaders(info);
@@ -447,9 +439,9 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
}
else else
answer["status"] = Status_SessionNotFound; answer["status"] = Status_SessionNotFound;
} }
void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer)
@@ -460,26 +452,23 @@ 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;
} }
void AgentImpl::processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
// Deliberately does NOT take mAgentMutex: events are produced by the worker std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// thread inside process(), which needs mAgentMutex. Holding it here would
// stall all SIP/media processing for the whole timeout and guarantee that
// the awaited event can never arrive during the wait.
int timeout = 0; //int x = 0;
if (request.isMember("timeout")) //int y = 1/x;
timeout = request["timeout"].asInt();
int timeout = request["timeout"].asInt();
std::unique_lock<std::mutex> eventLock(mEventListMutex); std::unique_lock<std::mutex> eventLock(mEventListMutex);
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout), if (mEventList.empty())
[this]() { return !mEventList.empty(); }); mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout));
if (!mEventList.empty()) if (!mEventList.empty())
{ {
@@ -489,6 +478,41 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& ans
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
#if defined(USE_PVQA_LIBRARY)
static JsonCpp::Value CsvReportToJson(const std::string& report)
{
JsonCpp::Value detectorValues;
std::istringstream iss(report);
CsvReader reader(iss);
std::vector<std::string> cells;
if (reader.readLine(cells))
{
JsonCpp::Value detectorNames;
for (size_t nameIndex = 0; nameIndex < cells.size(); nameIndex++)
detectorNames[static_cast<int>(nameIndex)] = StringHelper::trim(cells[nameIndex]);
// Put first line name of columns
detectorValues[0] = detectorNames;
int rowIndex = 1;
while (reader.readLine(cells))
{
// Skip last column for now
JsonCpp::Value row;
for (size_t valueIndex = 0; valueIndex < cells.size(); valueIndex++)
{
bool isFloat = true;
float v = StringHelper::toFloat(cells[valueIndex], 0.0, &isFloat);
if (isFloat)
row[static_cast<int>(valueIndex)] = static_cast<double>(v);
else
row[static_cast<int>(valueIndex)] = cells[valueIndex];
}
detectorValues[rowIndex++] = row;
}
}
return detectorValues;
}
#endif
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
@@ -499,13 +523,25 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
{ {
PSession session = sessionIter->second; PSession session = sessionIter->second;
VariantMap result; VariantMap result;
session->getSessionInfo(Session::InfoOptions::Detailed, result); bool includePvqa = request["include_pvqa"].asBool();
#if defined(USE_AQUA_LIBRARY)
bool includeAqua = request["include_aqua"].asBool();
std::string aquaReference = request["aqua_reference_audio"].asString();
#endif
session->getSessionInfo(includePvqa ? Session::InfoOptions::Detailed : Session::InfoOptions::Standard,
result);
if (result.exists(SessionInfo_AudioCodec)) 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 (result.exists(SessionInfo_LostRtp)) #if defined(USE_PVQA_LIBRARY)
if (result.exists(SessionInfo_PvqaMos))
answer["pvqa_mos"] = result[SessionInfo_PvqaMos].asFloat();
if (result.exists(SessionInfo_PvqaReport))
answer["pvqa_report"] = CsvReportToJson(result[SessionInfo_PvqaReport].asStdString());
#endif
if (result.exists(SessionInfo_PacketLoss))
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt(); answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
if (result.exists(SessionInfo_DroppedRtp)) if (result.exists(SessionInfo_DroppedRtp))
answer["rtp_dropped"] = result[SessionInfo_DroppedRtp].asInt(); answer["rtp_dropped"] = result[SessionInfo_DroppedRtp].asInt();
@@ -514,9 +550,6 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt(); answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt();
if (result.exists(SessionInfo_ReceivedRtp)) if (result.exists(SessionInfo_ReceivedRtp))
answer["rtp_received"] = result[SessionInfo_ReceivedRtp].asInt(); answer["rtp_received"] = result[SessionInfo_ReceivedRtp].asInt();
// Raw byte total; consumers divide by rtp_received for the average packet size.
if (result.exists(SessionInfo_ReceivedRtpTraffic))
answer["rtp_received_bytes"] = result[SessionInfo_ReceivedRtpTraffic].asInt();
if (result.exists(SessionInfo_Duration)) if (result.exists(SessionInfo_Duration))
answer["duration"] = result[SessionInfo_Duration].asInt(); answer["duration"] = result[SessionInfo_Duration].asInt();
if (result.exists(SessionInfo_Jitter)) if (result.exists(SessionInfo_Jitter))
@@ -525,13 +558,79 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
answer["rtt"] = result[SessionInfo_Rtt].asFloat(); answer["rtt"] = result[SessionInfo_Rtt].asFloat();
if (result.exists(SessionInfo_BitrateSwitchCounter)) if (result.exists(SessionInfo_BitrateSwitchCounter))
answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt(); answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt();
if (result.exists(SessionInfo_CngCounter))
answer["cng_counter"] = result[SessionInfo_CngCounter].asInt();
if (result.exists(SessionInfo_SSRC)) if (result.exists(SessionInfo_SSRC))
answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt(); answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt();
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
@@ -552,24 +651,24 @@ void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp
std::string pem = request["cert"].asString(); std::string pem = request["cert"].asString();
std::string::size_type pb = 0, pe = 0; std::string::size_type pb = 0, pe = 0;
while (pb != std::string::npos && pe != std::string::npos)
{
pb = pem.find(BeginCertificate, pb);
pe = pem.find(EndCertificate, pe);
if (pb != std::string::npos && pe != std::string::npos && pe > pb) for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe);
pb != std::string::npos && pe != std::string::npos;
pb = pem.find(BeginCertificate, pb + BeginCertificate.size()), pe = pem.find(EndCertificate, pe + EndCertificate.size()))
{ {
std::string cert = pem.substr(pb, pe - pb + EndCertificate.size()); // Get single certificate
std::string cert = pem.substr(pb, pe + EndCertificate.size());
//int size = cert.size();
addRootCert(ByteBuffer(cert.c_str(), cert.size())); addRootCert(ByteBuffer(cert.c_str(), cert.size()));
pb = ++pe; // Delete processed part
} pem.erase(0, pe + EndCertificate.size());
} }
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processLogMessage(JsonCpp::Value &request, JsonCpp::Value &answer)
{ {
int level = request["level"].asInt(); int level = request["level"].asInt();
std::string message = request["message"].asString(); std::string message = request["message"].asString();
@@ -588,8 +687,7 @@ void AgentImpl::stopAgentAndThread()
stop(); stop();
} }
catch (...) catch (...)
{ {}
}
// Stop worker thread // Stop worker thread
if (mThread) if (mThread)
@@ -633,8 +731,8 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
std::string actionText = request["media_action"].asString(), std::string actionText = request["media_action"].asString(),
directionText = request["media_direction"].asString(); directionText = request["media_direction"].asString();
MT::Stream::MediaDirection direction = MT::Stream::MediaDirection direction = directionText == "incoming" ? MT::Stream::MediaDirection::Incoming
directionText == "incoming" ? MT::Stream::MediaDirection::Incoming : MT::Stream::MediaDirection::Outgoing; : MT::Stream::MediaDirection::Outgoing;
std::string path = request["path"].asString(); std::string path = request["path"].asString();
// Try to open file // Try to open file
@@ -652,7 +750,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
else else
{ {
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>(); Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
if (!reader->open(strx::makeTstring(path))) if (!reader->open(StringHelper::makeTstring(path)))
answer["status"] = Status_FailedToOpenFile; answer["status"] = Status_FailedToOpenFile;
else else
{ {
@@ -661,7 +759,8 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
} }
} }
} }
else if (actionText == "write") else
if (actionText == "write")
{ {
if (path.empty()) if (path.empty())
{ {
@@ -672,7 +771,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
else else
{ {
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>(); Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS)) if (!writer->open(StringHelper::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
answer["status"] = Status_FailedToOpenFile; answer["status"] = Status_FailedToOpenFile;
else else
{ {
@@ -681,44 +780,51 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
} }
} }
} }
else if (actionText == "mirror") else
if (actionText == "mirror")
{ {
prov->setupMirror(request["enable"].asBool()); prov->setupMirror(request["enable"].asBool());
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
else else
answer["status"] = Status_NoCommand; answer["status"] = Status_AccountNotFound;
} }
else else
answer["status"] = Status_NoMediaAction; answer["status"] = Status_NoMediaAction;
} }
} }
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, #if defined(USE_AQUA_LIBRARY)
void* userTag) void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
{ {
/*switch (direction) /* if (mIncomingAudioDump && direction == MT::Stream::MediaDirection::Incoming)
mIncomingAudioDump->write(data, length);
if (mOutgoingAudioDump && direction == MT::Stream::MediaDirection::Outgoing)
mOutgoingAudioDump->write(data, length);*/
// User tag points to accumulator object which includes
// auto* sa = reinterpret_cast<sevana::aqua*>(userTag);
switch (direction)
{ {
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break; case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break; case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
}*/ }
} }
#endif
// Called on new incoming session; providers shoukld // Called on new incoming session; providers shoukld
#define EVENT_WITH_NAME(X) \ #define EVENT_WITH_NAME(X) JsonCpp::Value v; v["event_name"] = X;
JsonCpp::Value v; \
v["event_name"] = X;
PDataProvider AgentImpl::onProviderNeeded(const std::string& name) PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
{ {
assert(mTerminal);
EVENT_WITH_NAME("provider_needed"); EVENT_WITH_NAME("provider_needed");
v["provider_name"] = name; v["provider_name"] = name;
addEvent(v); addEvent(v);
return std::make_shared<AudioProvider>(*this, *mTerminal); return PDataProvider(new AudioProvider(*this, *mTerminal));
} }
// Called on new session offer // Called on new session offer
@@ -740,7 +846,6 @@ void AgentImpl::onSessionTerminated(PSession s, int responsecode, int reason)
if (mOutgoingAudioDump) if (mOutgoingAudioDump)
mOutgoingAudioDump->close(); mOutgoingAudioDump->close();
*/ */
if (mAudioManager)
mAudioManager->stop(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull); mAudioManager->stop(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
// Gather statistics before // Gather statistics before
EVENT_WITH_NAME("session_terminated"); EVENT_WITH_NAME("session_terminated");
@@ -846,7 +951,8 @@ void AgentImpl::onCheckFinished(PSession s, const char* description)
} }
// Called when log message must be recorded // Called when log message must be recorded
void AgentImpl::onLog(const char* /*msg*/) {} void AgentImpl::onLog(const char* /*msg*/)
{}
// Called when problem with SIP connection(s) detected // Called when problem with SIP connection(s) detected
void AgentImpl::onSipConnectionFailed() void AgentImpl::onSipConnectionFailed()
@@ -863,12 +969,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
+22 -12
View File
@@ -13,10 +13,18 @@
#include "Agent_AudioManager.h" #include "Agent_AudioManager.h"
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#include <atomic> #if defined(USE_AQUA_LIBRARY)
# include "aqua++.h"
#endif
#if defined(USE_PVQA_LIBRARY)
# include "pvqa++.h"
#endif
class AgentImpl : public UserAgent, public MT::Stream::MediaObserver class AgentImpl: public UserAgent
#if defined(USE_AQUA_LIBRARY)
, public MT::Stream::MediaObserver
#endif
{ {
protected: protected:
std::recursive_mutex mAgentMutex; std::recursive_mutex mAgentMutex;
@@ -31,12 +39,19 @@ protected:
typedef std::map<int, PSession> SessionMap; typedef std::map<int, PSession> SessionMap;
SessionMap mSessionMap; SessionMap mSessionMap;
#if defined(USE_AQUA_LIBRARY)
// Keys are the same as used in mSessionMap
typedef std::map<int, std::shared_ptr<sevana::aqua>> AquaMap;
AquaMap mAquaMap;
ByteBuffer mAquaIncoming, mAquaOutgoing;
void closeAqua(int sessionId);
#endif
std::shared_ptr<std::thread> mThread; std::shared_ptr<std::thread> mThread;
std::atomic<bool> mShutdown; volatile bool mShutdown;
std::shared_ptr<MT::Terminal> mTerminal; std::shared_ptr<MT::Terminal> mTerminal;
std::shared_ptr<AudioManager> mAudioManager; std::shared_ptr<AudioManager> mAudioManager;
Audio::DataConnection* mAudioMonitoring = nullptr; //Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump;
void run(); void run();
void addEvent(const JsonCpp::Value& v); void addEvent(const JsonCpp::Value& v);
@@ -67,12 +82,6 @@ public:
bool waitForData(int milliseconds); bool waitForData(int milliseconds);
std::string read(); std::string read();
// Get access to internal audio manager. Value can be nullptr.
const std::shared_ptr<AudioManager>& audioManager() const;
void setAudioMonitoring(Audio::DataConnection* monitoring);
Audio::DataConnection* monitoring() const;
// UserAgent overrides // UserAgent overrides
// Called on new incoming session; providers shoukld // Called on new incoming session; providers shoukld
PDataProvider onProviderNeeded(const std::string& name) override; PDataProvider onProviderNeeded(const std::string& name) override;
@@ -122,9 +131,10 @@ public:
// Called when problem with SIP connection(s) detected // Called when problem with SIP connection(s) detected
void onSipConnectionFailed() override; void onSipConnectionFailed() override;
#if defined(USE_AQUA_LIBRARY)
// Called on incoming & outgoing audio for voice sessions // Called on incoming & outgoing audio for voice sessions
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
void* userTag) override; #endif
}; };
#endif #endif
+15 -4
View File
@@ -1,16 +1,27 @@
#include "Agent_Interface.h" #include "Agent_Interface.h"
#include "Agent_Impl.h" #include "Agent_Impl.h"
Agent::Agent() : mContext(new AgentImpl()); Agent::Agent()
:mContext(new AgentImpl());
{ {
} }
Agent::~Agent() {} Agent::~Agent()
{
void Agent::write(const std::string& command) {} }
void Agent::write(const std::string& command)
{
}
bool Agent::waitForData(int milliseconds) bool Agent::waitForData(int milliseconds)
{ {
return false; return false;
} }
std::string Agent::read() {} std::string Agent::read()
{
}
+94 -89
View File
@@ -14,9 +14,11 @@ using namespace Audio;
// -------------------- AndroidEnumerator ----------------------------- // -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator() {} AndroidEnumerator::AndroidEnumerator()
{}
AndroidEnumerator::~AndroidEnumerator() {} AndroidEnumerator::~AndroidEnumerator()
{}
int AndroidEnumerator::indexOfDefaultDevice() int AndroidEnumerator::indexOfDefaultDevice()
{ {
@@ -38,21 +40,26 @@ std::string AndroidEnumerator::nameAt(int index)
return "Audio"; return "Audio";
} }
void AndroidEnumerator::open(int direction) {} void AndroidEnumerator::open(int direction)
{}
void AndroidEnumerator::close() {} void AndroidEnumerator::close()
{}
// ----------------------- // -----------------------
OpenSLEngine::OpenSLEngine() {} OpenSLEngine::OpenSLEngine()
{}
OpenSLEngine::~OpenSLEngine() {} OpenSLEngine::~OpenSLEngine()
{}
void OpenSLEngine::open() void OpenSLEngine::open()
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
if (++mUsageCounter == 1) if (++mUsageCounter == 1)
internalOpen(); internalOpen();
} }
void OpenSLEngine::close() void OpenSLEngine::close()
@@ -65,9 +72,7 @@ void OpenSLEngine::close()
internalClose(); internalClose();
} }
#define CHECK_OPENSLES_ERROR \ #define CHECK_OPENSLES_ERROR if (resultCode != SL_RESULT_SUCCESS) throw Exception(ERR_OPENSLES, (int)resultCode)
if (resultCode != SL_RESULT_SUCCESS) \
throw Exception(ERR_OPENSLES, (int)resultCode)
void OpenSLEngine::internalOpen() void OpenSLEngine::internalOpen()
{ {
@@ -113,15 +118,25 @@ OpenSLEngine& OpenSLEngine::instance()
} }
// --------------- Input implementation ---------------- // --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId) {} AndroidInputDevice::AndroidInputDevice(int devId)
{}
AndroidInputDevice::~AndroidInputDevice() {} AndroidInputDevice::~AndroidInputDevice()
{}
static int RateToProbe[12][2] = { static int RateToProbe[12][2] = {
{SL_SAMPLINGRATE_16, 16000}, {SL_SAMPLINGRATE_8, 8000}, {SL_SAMPLINGRATE_32, 32000}, { SL_SAMPLINGRATE_16, 16000 },
{SL_SAMPLINGRATE_44_1, 44100}, {SL_SAMPLINGRATE_11_025, 10025}, {SL_SAMPLINGRATE_22_05, 22050}, { SL_SAMPLINGRATE_8, 8000 },
{SL_SAMPLINGRATE_24, 24000}, {SL_SAMPLINGRATE_48, 48000}, {SL_SAMPLINGRATE_64, 64000}, { SL_SAMPLINGRATE_32, 32000 },
{SL_SAMPLINGRATE_88_2, 88200}, {SL_SAMPLINGRATE_96, 96000}, {SL_SAMPLINGRATE_192, 192000}}; { SL_SAMPLINGRATE_44_1, 44100 },
{ SL_SAMPLINGRATE_11_025, 10025 },
{ SL_SAMPLINGRATE_22_05, 22050 },
{ SL_SAMPLINGRATE_24, 24000 },
{ SL_SAMPLINGRATE_48, 48000 },
{ SL_SAMPLINGRATE_64, 64000 },
{ SL_SAMPLINGRATE_88_2, 88200 },
{ SL_SAMPLINGRATE_96, 96000 },
{ SL_SAMPLINGRATE_192, 192000} };
bool AndroidInputDevice::open() bool AndroidInputDevice::open()
{ {
@@ -143,7 +158,7 @@ bool AndroidInputDevice::open()
if (!opened) if (!opened)
internalClose(); internalClose();
} }
catch (...) catch(...)
{ {
opened = false; opened = false;
internalClose(); internalClose();
@@ -177,7 +192,8 @@ bool AndroidInputDevice::fakeMode()
return false; return false;
} }
void AndroidInputDevice::setFakeMode(bool fakemode) {} void AndroidInputDevice::setFakeMode(bool fakemode)
{}
int AndroidInputDevice::readBuffer(void* buffer) int AndroidInputDevice::readBuffer(void* buffer)
{ {
@@ -190,13 +206,7 @@ int AndroidInputDevice::readBuffer(void* buffer)
return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE); return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE);
} }
#define CHECK_SL_INTERFACE(INTF, ERR) \ #define CHECK_SL_INTERFACE(INTF, ERR) {if (!INTF) throw Exception(ERR_OPENSLES, ERR); if (!(*INTF)) throw Exception(ERR_OPENSLES, ERR);}
{ \
if (!INTF) \
throw Exception(ERR_OPENSLES, ERR); \
if (!(*INTF)) \
throw Exception(ERR_OPENSLES, ERR); \
}
void AndroidInputDevice::internalOpen(int rateCode, int rate) void AndroidInputDevice::internalOpen(int rateCode, int rate)
{ {
@@ -204,37 +214,33 @@ void AndroidInputDevice::internalOpen(int rateCode, int rate)
SLuint32 nrOfChannels = 1; SLuint32 nrOfChannels = 1;
// Prepare audio source // Prepare audio source
SLDataLocator_IODevice devDescription = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SLDataLocator_IODevice devDescription = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL}; SLDataSource audioSource = { &devDescription, NULL };
SLDataSource audioSource = {&devDescription, NULL};
// Source flags // Source flags
SLuint32 speakersFlags = SLuint32 speakersFlags = nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Buffer queue // Buffer queue
SLDataLocator_AndroidSimpleBufferQueue queueDescription = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; SLDataLocator_AndroidSimpleBufferQueue queueDescription = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
// Audio format // Audio format
SLDataFormat_PCM formatDescription = {SL_DATAFORMAT_PCM, nrOfChannels, SLDataFormat_PCM formatDescription = { SL_DATAFORMAT_PCM, nrOfChannels, (SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16,
(SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags, SL_BYTEORDER_LITTLEENDIAN };
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags,
SL_BYTEORDER_LITTLEENDIAN};
SLDataSink audioSink = {&queueDescription, &formatDescription}; SLDataSink audioSink = { &queueDescription, &formatDescription };
// Create recorder // Create recorder
// Do not forget about RECORD_AUDIO permission // Do not forget about RECORD_AUDIO permission
const SLInterfaceID interfacesList[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION}; const SLInterfaceID interfacesList[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
const SLboolean interfacesRequirements[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; const SLboolean interfacesRequirements[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
// Get access to OpenSL engine // Get access to OpenSL engine
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine(); SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
CHECK_SL_INTERFACE(engine_interface, -1); CHECK_SL_INTERFACE(engine_interface, -1);
resultCode = (*engine_interface) resultCode = (*engine_interface)->CreateAudioRecorder(
->CreateAudioRecorder(OpenSLEngine::instance().getNativeEngine(), &mRecorderObject, &audioSource, OpenSLEngine::instance().getNativeEngine(),
&audioSink, 2, interfacesList, interfacesRequirements); &mRecorderObject, &audioSource, &audioSink, 2, interfacesList, interfacesRequirements);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderObject, -2); CHECK_SL_INTERFACE(mRecorderObject, -2);
@@ -252,8 +258,7 @@ void AndroidInputDevice::internalOpen(int rateCode, int rate)
CHECK_SL_INTERFACE(mRecorderInterface, -3); CHECK_SL_INTERFACE(mRecorderInterface, -3);
// Now buffer queue interface... // Now buffer queue interface...
resultCode = resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
(*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4); CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
@@ -271,9 +276,8 @@ void AndroidInputDevice::internalOpen(int rateCode, int rate)
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Setup buffers // Setup buffers
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++) for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
(*mRecorderBufferInterface) (*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
// Start finally // Start finally
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING); resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
@@ -298,7 +302,8 @@ void AndroidInputDevice::internalClose()
{ {
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state); (*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
SyncHelper::delay(1); SyncHelper::delay(1);
} while (state == SL_RECORDSTATE_RECORDING); }
while (state == SL_RECORDSTATE_RECORDING);
} }
(*mRecorderObject)->Destroy(mRecorderObject); (*mRecorderObject)->Destroy(mRecorderObject);
} }
@@ -326,8 +331,9 @@ void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{ {
char* resampled = (char*)alloca(Format().sizeFromTime(10)); char* resampled = (char*)alloca(Format().sizeFromTime(10));
int processed = 0; int processed = 0;
int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled, int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled, Format().sizeFromTime(10));
Format().sizeFromTime(10)); if (outlen > 0) mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10)); if (outlen > 0)
mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10));
mDeviceRateCache.erase(tenMsSize); mDeviceRateCache.erase(tenMsSize);
} }
@@ -340,30 +346,27 @@ void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
} }
*/ */
// Re-enqueue used buffer // Re-enqueue used buffer
(*mRecorderBufferInterface) (*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
mRecorderBufferIndex++; mRecorderBufferIndex++;
mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT; mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT;
} }
void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context) void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{ {
try try
{ {
if (context) if (context)
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq); reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
} }
catch (...) catch(...)
{ {}
}
} }
// ------------ AndroidOutputDevice ----------------- // ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId) AndroidOutputDevice::AndroidOutputDevice(int devId)
{ {
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this)); ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
} }
AndroidOutputDevice::~AndroidOutputDevice() AndroidOutputDevice::~AndroidOutputDevice()
{ {
ICELogDebug(<< "Deleting AndroidOutputDevice."); ICELogDebug(<< "Deleting AndroidOutputDevice.");
@@ -383,7 +386,7 @@ bool AndroidOutputDevice::open()
mDeviceRate = RateToProbe[rateIndex][1]; mDeviceRate = RateToProbe[rateIndex][1];
ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex); ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex);
} }
catch (...) catch(...)
{ {
opened = false; opened = false;
} }
@@ -410,7 +413,9 @@ bool AndroidOutputDevice::fakeMode()
return false; return false;
} }
void AndroidOutputDevice::setFakeMode(bool fakemode) {} void AndroidOutputDevice::setFakeMode(bool fakemode)
{
}
void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice) void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
{ {
@@ -420,12 +425,12 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
SLuint32 channels = 1; SLuint32 channels = 1;
// Configure audio source // Configure audio source
SLDataLocator_AndroidSimpleBufferQueue queue_desc = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; SLDataLocator_AndroidSimpleBufferQueue queue_desc = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
const SLInterfaceID interfacesList[] = {SL_IID_VOLUME}; const SLInterfaceID interfacesList[] = { SL_IID_VOLUME };
const SLboolean interfaceRequirements[] = {SL_BOOLEAN_FALSE}; const SLboolean interfaceRequirements[] = { SL_BOOLEAN_FALSE };
resultCode = (*OpenSLEngine::instance().getNativeEngine()) resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateOutputMix(
->CreateOutputMix(OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList, OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
interfaceRequirements); interfaceRequirements);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
@@ -434,28 +439,30 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Prepare mixer configuration // Prepare mixer configuration
SLuint32 speakers = channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER; SLuint32 speakers =
channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Describe audio format // Describe audio format
SLDataFormat_PCM pcm_format = { SLDataFormat_PCM pcm_format = {SL_DATAFORMAT_PCM, channels, (SLuint32) rateId,
SL_DATAFORMAT_PCM, channels, (SLuint32)rateId, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, speakers, SL_BYTEORDER_LITTLEENDIAN}; speakers, SL_BYTEORDER_LITTLEENDIAN};
// Describe audio source - buffers + audio format // Describe audio source - buffers + audio format
SLDataSource audio_source = {&queue_desc, &pcm_format}; SLDataSource audio_source = { &queue_desc, &pcm_format };
// Describe audio sink // Describe audio sink
SLDataLocator_OutputMix mixer_desc = {SL_DATALOCATOR_OUTPUTMIX, mMixer}; SLDataLocator_OutputMix mixer_desc = { SL_DATALOCATOR_OUTPUTMIX, mMixer };
SLDataSink audio_sink = {&mixer_desc, NULL}; SLDataSink audio_sink = { &mixer_desc, NULL };
// Create player instance // Create player instance
const SLInterfaceID playerInterfaces[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME, const SLInterfaceID playerInterfaces[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
SL_IID_ANDROIDCONFIGURATION}; SL_IID_VOLUME,
const SLboolean playerInterfacesReqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; SL_IID_ANDROIDCONFIGURATION };
const SLboolean playerInterfacesReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
resultCode = (*OpenSLEngine::instance().getNativeEngine()) resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateAudioPlayer(
->CreateAudioPlayer(OpenSLEngine::instance().getNativeEngine(), &mPlayer, &audio_source, OpenSLEngine::instance().getNativeEngine(), &mPlayer,
&audio_sink, 3, playerInterfaces, playerInterfacesReqs); &audio_source, &audio_sink, 3, playerInterfaces, playerInterfacesReqs);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Get android config interface // Get android config interface
@@ -464,8 +471,8 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
if (resultCode == SL_RESULT_SUCCESS) if (resultCode == SL_RESULT_SUCCESS)
{ {
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA; SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
resultCode = (*mAndroidConfig) resultCode = (*mAndroidConfig)->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE,
->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); &streamType, sizeof(SLint32));
if (resultCode != SL_RESULT_SUCCESS) if (resultCode != SL_RESULT_SUCCESS)
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode); ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
} }
@@ -481,7 +488,8 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Get the buffer queue interface // Get the buffer queue interface
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mBufferQueue); resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&mBufferQueue);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Setup callback // Setup callback
@@ -494,7 +502,8 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
mBufferIndex = 0; mBufferIndex = 0;
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize, (SLuint32)mBufferSize); (*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize,
(SLuint32)mBufferSize);
// Set the player's state to playing // Set the player's state to playing
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING); resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
@@ -511,15 +520,12 @@ void AndroidOutputDevice::internalClose()
{ {
mInShutdown = true; mInShutdown = true;
ICELogInfo(<< "Stop player"); ICELogInfo(<< "Stop player");
if (mPlayerControl) if (mPlayerControl) {
{ if (*mPlayerControl) {
if (*mPlayerControl)
{
SLuint32 state = SL_PLAYSTATE_PLAYING; SLuint32 state = SL_PLAYSTATE_PLAYING;
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED); (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
while (state != SL_PLAYSTATE_STOPPED) while (state != SL_PLAYSTATE_STOPPED) {
{
(*mPlayerControl)->GetPlayState(mPlayerControl, &state); (*mPlayerControl)->GetPlayState(mPlayerControl, &state);
SyncHelper::delay(1); SyncHelper::delay(1);
} }
@@ -585,9 +591,8 @@ void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void*
{ {
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq); reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
} }
catch (...) catch(...)
{ {}
}
} }
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
+22 -21
View File
@@ -25,9 +25,9 @@
namespace Audio namespace Audio
{ {
class AndroidEnumerator : public Enumerator class AndroidEnumerator: public Enumerator
{ {
public: public:
AndroidEnumerator(); AndroidEnumerator();
~AndroidEnumerator(); ~AndroidEnumerator();
@@ -39,12 +39,12 @@ public:
int idAt(int index); int idAt(int index);
int indexOfDefaultDevice(); int indexOfDefaultDevice();
protected: protected:
}; };
class AndroidInputDevice : public InputDevice class AndroidInputDevice: public InputDevice
{ {
public: public:
AndroidInputDevice(int devId); AndroidInputDevice(int devId);
~AndroidInputDevice(); ~AndroidInputDevice();
@@ -57,7 +57,7 @@ public:
int readBuffer(void* buffer); int readBuffer(void* buffer);
bool active() const; bool active() const;
protected: protected:
bool mActive = false; bool mActive = false;
SLObjectItf mRecorderObject = nullptr; SLObjectItf mRecorderObject = nullptr;
SLRecordItf mRecorderInterface = nullptr; SLRecordItf mRecorderInterface = nullptr;
@@ -77,11 +77,11 @@ protected:
void internalClose(); void internalClose();
void handleCallback(SLAndroidSimpleBufferQueueItf bq); void handleCallback(SLAndroidSimpleBufferQueueItf bq);
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context); static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
}; };
class AndroidOutputDevice : public OutputDevice class AndroidOutputDevice: public OutputDevice
{ {
public: public:
AndroidOutputDevice(int devId); AndroidOutputDevice(int devId);
~AndroidOutputDevice(); ~AndroidOutputDevice();
@@ -92,7 +92,7 @@ public:
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
protected: protected:
std::mutex mMutex; std::mutex mMutex;
int mDeviceRate = 0; int mDeviceRate = 0;
SLObjectItf mMixer = nullptr; SLObjectItf mMixer = nullptr;
@@ -111,11 +111,12 @@ protected:
void handleCallback(SLAndroidSimpleBufferQueueItf bq); void handleCallback(SLAndroidSimpleBufferQueueItf bq);
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context); static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
};
class OpenSLEngine : public OsEngine };
{
public: class OpenSLEngine: public OsEngine
{
public:
OpenSLEngine(); OpenSLEngine();
~OpenSLEngine(); ~OpenSLEngine();
@@ -129,7 +130,7 @@ public:
static OpenSLEngine& instance(); static OpenSLEngine& instance();
protected: protected:
std::mutex mMutex; std::mutex mMutex;
int mUsageCounter = 0; int mUsageCounter = 0;
SLObjectItf mEngineObject = nullptr; SLObjectItf mEngineObject = nullptr;
@@ -137,8 +138,8 @@ protected:
void internalOpen(); void internalOpen();
void internalClose(); void internalClose();
}; };
} // namespace Audio }
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
+29 -80
View File
@@ -4,23 +4,24 @@
#include <mutex> #include <mutex>
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <algorithm>
#include "../helper/HL_String.h" #include "../helper/HL_String.h"
#include "../helper/HL_Time.h" #include "../helper/HL_Time.h"
#ifdef TARGET_ANDROID #ifdef TARGET_ANDROID
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "Audio"
using namespace Audio; using namespace Audio;
// -------------------- AndroidEnumerator ----------------------------- // -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator() {} AndroidEnumerator::AndroidEnumerator()
{}
AndroidEnumerator::~AndroidEnumerator() {} AndroidEnumerator::~AndroidEnumerator()
{}
int AndroidEnumerator::indexOfDefaultDevice() int AndroidEnumerator::indexOfDefaultDevice()
{ {
@@ -42,12 +43,15 @@ std::string AndroidEnumerator::nameAt(int index)
return "Audio"; return "Audio";
} }
void AndroidEnumerator::open(int direction) {} void AndroidEnumerator::open(int direction)
{}
void AndroidEnumerator::close() {} void AndroidEnumerator::close()
{}
// --------------- Input implementation ---------------- // --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId) {} AndroidInputDevice::AndroidInputDevice(int devId)
{}
AndroidInputDevice::~AndroidInputDevice() AndroidInputDevice::~AndroidInputDevice()
{ {
@@ -89,14 +93,13 @@ void AndroidInputDevice::close()
if (mRecordingStream != nullptr) if (mRecordingStream != nullptr)
{ {
mRecordingStream->close(); mRecordingStream->close();
delete mRecordingStream; delete mRecordingStream; mRecordingStream = nullptr;
mRecordingStream = nullptr;
} }
mActive = false; mActive = false;
} }
oboe::DataCallbackResult AndroidInputDevice::onAudioReady(oboe::AudioStream* audioStream, void* audioData, oboe::DataCallbackResult
int32_t numFrames) AndroidInputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
@@ -122,7 +125,8 @@ bool AndroidInputDevice::fakeMode()
return false; return false;
} }
void AndroidInputDevice::setFakeMode(bool fakemode) {} void AndroidInputDevice::setFakeMode(bool fakemode)
{}
int AndroidInputDevice::readBuffer(void* buffer) int AndroidInputDevice::readBuffer(void* buffer)
{ {
@@ -132,14 +136,11 @@ int AndroidInputDevice::readBuffer(void* buffer)
// ------------ AndroidOutputDevice ----------------- // ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId) AndroidOutputDevice::AndroidOutputDevice(int devId)
{ {
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this)); ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
} }
AndroidOutputDevice::~AndroidOutputDevice() AndroidOutputDevice::~AndroidOutputDevice()
{ {
ICELogDebug(<< "Deleting AndroidOutputDevice."); ICELogDebug(<< "Deleting AndroidOutputDevice.");
// Mark shutdown before closing so a disconnect callback racing with teardown
// does not resurrect the stream via onErrorAfterClose()'s restart.
mInShutdown = true;
close(); close();
} }
@@ -150,7 +151,6 @@ bool AndroidOutputDevice::open()
if (mActive) if (mActive)
return true; return true;
mInShutdown = false;
mRequestedFrames = 0; mRequestedFrames = 0;
mStartTime = 0.0; mStartTime = 0.0;
mEndTime = 0.0; mEndTime = 0.0;
@@ -161,20 +161,16 @@ bool AndroidOutputDevice::open()
builder.setSharingMode(oboe::SharingMode::Exclusive); builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setFormat(oboe::AudioFormat::I16); builder.setFormat(oboe::AudioFormat::I16);
builder.setChannelCount(oboe::ChannelCount::Mono); builder.setChannelCount(oboe::ChannelCount::Mono);
// Route through the platform voice-call path: correct device selection/volume
// and platform voice tuning for a softphone.
builder.setUsage(oboe::Usage::VoiceCommunication);
builder.setContentType(oboe::ContentType::Speech);
// builder.setDataCallback(this); // builder.setDataCallback(this);
builder.setCallback(this); builder.setCallback(this);
// builder.setErrorCallback(this) //builder.setErrorCallback(this)
oboe::Result rescode = builder.openStream(&mPlayingStream); oboe::Result rescode = builder.openStream(&mPlayingStream);
if (rescode != oboe::Result::OK) if (rescode != oboe::Result::OK)
return false; return false;
mDeviceRate = mPlayingStream->getSampleRate(); mDeviceRate = mPlayingStream->getSampleRate();
ICELogInfo(<< "Output opened with rate " << mDeviceRate); ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
mActive = true; mActive = true;
rescode = mPlayingStream->requestStart(); rescode = mPlayingStream->requestStart();
@@ -182,18 +178,7 @@ bool AndroidOutputDevice::open()
{ {
close(); close();
mActive = false; mActive = false;
return mActive;
} }
// Latch the burst size and start from a two-burst buffer. onAudioReady() grows
// this on XRuns (up to a cap) so we keep low latency when the device can sustain
// it and trade a little latency for glitch-free playback when it can't.
mBurstFrames = mPlayingStream->getFramesPerBurst();
mXRunLast = 0;
mHeartbeatLast = 0.0f;
if (mBurstFrames > 0)
mPlayingStream->setBufferSizeInFrames(mBurstFrames * 2);
return mActive; return mActive;
} }
@@ -206,8 +191,7 @@ void AndroidOutputDevice::close()
if (mPlayingStream != nullptr) if (mPlayingStream != nullptr)
{ {
mPlayingStream->close(); mPlayingStream->close();
delete mPlayingStream; delete mPlayingStream; mPlayingStream = nullptr;
mPlayingStream = nullptr;
} }
mEndTime = now_ms(); mEndTime = now_ms();
mActive = false; mActive = false;
@@ -226,10 +210,11 @@ bool AndroidOutputDevice::fakeMode()
return false; return false;
} }
void AndroidOutputDevice::setFakeMode(bool /*fakemode*/) {} void AndroidOutputDevice::setFakeMode(bool /*fakemode*/)
{
}
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream* audioStream, void* audioData, oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
int32_t numFrames)
{ {
if (mInShutdown) if (mInShutdown)
return oboe::DataCallbackResult::Stop; return oboe::DataCallbackResult::Stop;
@@ -247,50 +232,14 @@ oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream* au
} }
mRequestedFrames += numFrames; mRequestedFrames += numFrames;
// Adaptive buffer sizing: on new XRuns (device-side underruns, i.e. we missed a
// callback deadline), grow the buffer one burst at a time up to a cap. Both calls
// are documented as safe from within the data callback.
auto xrun = audioStream->getXRunCount();
if (xrun && xrun.value() > mXRunLast)
{
mXRunLast = xrun.value();
if (mBurstFrames > 0)
{
int32_t cap = mBurstFrames * 8;
int32_t cur = audioStream->getBufferSizeInFrames();
int32_t next = std::min(cur + mBurstFrames, cap);
if (next > cur)
audioStream->setBufferSizeInFrames(next);
}
}
// Device-side heartbeat (Step 0): surfaces XRuns/buffer growth so device glitches
// can be told apart from network/jitter impairments.
float t = now_ms();
if (mHeartbeatLast == 0.0f)
mHeartbeatLast = t;
else if (t - mHeartbeatLast >= 5000.0f)
{
ICELogInfo(<< "[spk-heartbeat] xruns=" << mXRunLast << " bufFrames=" << audioStream->getBufferSizeInFrames()
<< " burst=" << mBurstFrames << " rate=" << mDeviceRate);
mHeartbeatLast = t;
}
return oboe::DataCallbackResult::Continue; return oboe::DataCallbackResult::Continue;
} }
// Disconnect recovery: on a route change (headset/BT plug/unplug) AAudio tears the // TODO - special case https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
// stream down and calls this on its own thread after the stream is closed. Rebuild void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result) {
// on the new default route so audio doesn't silently die mid-call. if (result == oboe::Result::ErrorDisconnected) {
// See https://github.com/google/oboe/blob/master/docs/notes/disconnect.md // LOGI("Restarting AudioStream after disconnect");
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream* stream, oboe::Result result) // soundEngine.restart(); // please check oboe samples for soundEngine.restart(); call
{
if (result == oboe::Result::ErrorDisconnected && !mInShutdown)
{
ICELogInfo(<< "Output stream disconnected; restarting on the new route");
// close() and open() each take mMutex internally; this callback holds none.
close();
open();
} }
} }
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
+20 -24
View File
@@ -25,9 +25,9 @@
namespace Audio namespace Audio
{ {
class AndroidEnumerator : public Enumerator class AndroidEnumerator: public Enumerator
{ {
public: public:
AndroidEnumerator(); AndroidEnumerator();
~AndroidEnumerator(); ~AndroidEnumerator();
@@ -39,12 +39,12 @@ public:
int idAt(int index); int idAt(int index);
int indexOfDefaultDevice(); int indexOfDefaultDevice();
protected: protected:
}; };
class AndroidInputDevice : public InputDevice, public oboe::AudioStreamCallback class AndroidInputDevice: public InputDevice, public oboe::AudioStreamCallback
{ {
public: public:
AndroidInputDevice(int devId); AndroidInputDevice(int devId);
~AndroidInputDevice(); ~AndroidInputDevice();
@@ -57,10 +57,11 @@ public:
int readBuffer(void* buffer); int readBuffer(void* buffer);
bool active() const; bool active() const;
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames); oboe::DataCallbackResult
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
protected: protected:
bool mActive = false; bool mActive = false;
oboe::AudioStream* mRecordingStream = nullptr; oboe::AudioStream* mRecordingStream = nullptr;
PResampler mResampler; PResampler mResampler;
@@ -71,11 +72,11 @@ protected:
std::condition_variable mDataCondVar; std::condition_variable mDataCondVar;
int mRecorderBufferIndex; int mRecorderBufferIndex;
std::mutex mMutex; std::mutex mMutex;
}; };
class AndroidOutputDevice : public OutputDevice, public oboe::AudioStreamCallback class AndroidOutputDevice: public OutputDevice, public oboe::AudioStreamCallback
{ {
public: public:
AndroidOutputDevice(int devId); AndroidOutputDevice(int devId);
~AndroidOutputDevice(); ~AndroidOutputDevice();
@@ -86,10 +87,10 @@ public:
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames); oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
void onErrorAfterClose(oboe::AudioStream* stream, oboe::Result result); void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result);
protected: protected:
std::mutex mMutex; std::mutex mMutex;
int mDeviceRate = 0; int mDeviceRate = 0;
oboe::AudioStream* mPlayingStream = nullptr; oboe::AudioStream* mPlayingStream = nullptr;
@@ -98,15 +99,10 @@ protected:
bool mInShutdown = false; bool mInShutdown = false;
bool mActive = false; bool mActive = false;
// Adaptive buffer sizing + device-side diagnostics (Step 0 / #5).
int32_t mBurstFrames = 0; // frames per burst, latched at open()
int32_t mXRunLast = 0; // last observed cumulative XRun count
float mHeartbeatLast = 0.0f; // now_ms() of last device heartbeat log
// Statistics // Statistics
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0; float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
}; };
} // namespace Audio }
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
+150 -85
View File
@@ -7,13 +7,13 @@
#include "Audio_CoreAudio.h" #include "Audio_CoreAudio.h"
#include "../Helper/HL_Log.h" #include "../Helper/HL_Log.h"
// #include <qdebug.h> //#include <qdebug.h>
#include <AudioUnit/AudioUnit.h> #include <AudioUnit/AudioUnit.h>
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
using namespace Audio; using namespace Audio;
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "CoreAudio"
enum enum
{ {
@@ -23,11 +23,12 @@ enum
static inline short Float32ToInt16(Float32 v) static inline short Float32ToInt16(Float32 v)
{ {
// assert (v <= 1.0); //assert (v <= 1.0);
int r = int(v * 32768); int r = int(v * 32768);
if (r >= 32768) if (r >= 32768)
return (short)32767; return (short)32767;
else if (r < -32768) else
if (r < -32768)
return (short)-32768; return (short)-32768;
else else
return (short)r; return (short)r;
@@ -42,7 +43,7 @@ static inline Float32 Int16ToFloat32(short v)
static inline Float32 StereoToMono(bool interleaved, Float32* buffer, int samples, int index, int channels) static inline Float32 StereoToMono(bool interleaved, Float32* buffer, int samples, int index, int channels)
{ {
Float32 sum = 0; Float32 sum = 0;
for (int i = 0; i < channels; i++) for (int i = 0; i<channels; i++)
{ {
if (!interleaved) if (!interleaved)
sum += buffer[index * channels + i]; sum += buffer[index * channels + i];
@@ -54,7 +55,7 @@ static inline Float32 StereoToMono(bool interleaved, Float32* buffer, int sample
static inline void MonoToStereo(bool interleaved, Float32 sample, Float32* buffer, int samples, int index, int channels) static inline void MonoToStereo(bool interleaved, Float32 sample, Float32* buffer, int samples, int index, int channels)
{ {
for (int i = 0; i < channels; i++) for (int i=0; i<channels; i++)
{ {
if (!interleaved) if (!interleaved)
buffer[index * channels + i] = sample; buffer[index * channels + i] = sample;
@@ -63,7 +64,7 @@ static inline void MonoToStereo(bool interleaved, Float32 sample, Float32* buffe
} }
} }
static void propertyListenerCallback(void* inUserData, AudioQueueRef queueObject, AudioQueuePropertyID propertyID) static void propertyListenerCallback(void *inUserData, AudioQueueRef queueObject, AudioQueuePropertyID propertyID)
{ {
// AudioPlayer *player = (AudioPlayer *) inUserData; // AudioPlayer *player = (AudioPlayer *) inUserData;
// gets a reference to the playback object // gets a reference to the playback object
@@ -72,7 +73,10 @@ static void propertyListenerCallback(void* inUserData, AudioQueueRef queueObject
} }
CoreAudioUnit::CoreAudioUnit() : mUnit(0) {} CoreAudioUnit::CoreAudioUnit()
:mUnit(0)
{
}
void CoreAudioUnit::open(bool voice) void CoreAudioUnit::open(bool voice)
{ {
@@ -80,7 +84,9 @@ void CoreAudioUnit::open(bool voice)
#ifdef TARGET_IOS #ifdef TARGET_IOS
UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
/* We want to be able to open playback and recording streams */ /* We want to be able to open playback and recording streams */
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
sizeof(audioCategory),
&audioCategory);
if (ostatus != kAudioSessionNoError) if (ostatus != kAudioSessionNoError)
{ {
ICELogError(<< "Cannot set audio session to PlaybackAndRecord category, error" << ostatus); ICELogError(<< "Cannot set audio session to PlaybackAndRecord category, error" << ostatus);
@@ -95,8 +101,7 @@ void CoreAudioUnit::open(bool voice)
#ifdef TARGET_IOS #ifdef TARGET_IOS
desc.componentSubType = voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_RemoteIO; desc.componentSubType = voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_RemoteIO;
#else #else
desc.componentSubType = desc.componentSubType = kAudioUnitSubType_HALOutput;//voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_DefaultOutput;
kAudioUnitSubType_HALOutput; // voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_DefaultOutput;
#endif #endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0; desc.componentFlags = 0;
@@ -148,8 +153,12 @@ AudioStreamBasicDescription CoreAudioUnit::getFormat(int scope, int bus)
void CoreAudioUnit::setFormat(AudioStreamBasicDescription& format, int scope, int bus) void CoreAudioUnit::setFormat(AudioStreamBasicDescription& format, int scope, int bus)
{ {
OSStatus ostatus = OSStatus ostatus = AudioUnitSetProperty(mUnit,
AudioUnitSetProperty(mUnit, kAudioUnitProperty_StreamFormat, scope, bus, &format, sizeof(format)); kAudioUnitProperty_StreamFormat,
scope,
bus,
&format,
sizeof(format));
if (ostatus != noErr) if (ostatus != noErr)
{ {
ICELogError(<< "Cannot set stream format, error " << int(ostatus)); ICELogError(<< "Cannot set stream format, error " << int(ostatus));
@@ -172,20 +181,28 @@ bool CoreAudioUnit::getEnabled(int scope, int bus)
void CoreAudioUnit::setEnabled(bool enabled, int scope, int bus) void CoreAudioUnit::setEnabled(bool enabled, int scope, int bus)
{ {
UInt32 enable = enabled ? 1 : 0; UInt32 enable = enabled ? 1 : 0;
OSStatus ostatus = OSStatus ostatus = AudioUnitSetProperty(mUnit,
AudioUnitSetProperty(mUnit, kAudioOutputUnitProperty_EnableIO, scope, bus, &enable, sizeof(enable)); kAudioOutputUnitProperty_EnableIO,
scope,
bus,
&enable,
sizeof(enable));
if (ostatus != noErr) if (ostatus != noErr)
{ {
ICELogError(<< "Cannot enable input on audio device , error " << int(ostatus)); ICELogError(<< "Cannot enable input on audio device , error " << int(ostatus));
// throw AudioException(ERR_COREAUDIO, ostatus); //throw AudioException(ERR_COREAUDIO, ostatus);
} }
} }
void CoreAudioUnit::makeCurrent(AudioDeviceID deviceId, int scope, int bus) void CoreAudioUnit::makeCurrent(AudioDeviceID deviceId, int scope, int bus)
{ {
OSStatus ostatus = OSStatus ostatus = AudioUnitSetProperty(mUnit,
AudioUnitSetProperty(mUnit, kAudioOutputUnitProperty_CurrentDevice, scope, bus, &deviceId, sizeof(deviceId)); kAudioOutputUnitProperty_CurrentDevice,
scope,
bus,
&deviceId,
sizeof(deviceId));
if (ostatus != noErr) if (ostatus != noErr)
{ {
ICELogError(<< "Cannot make device " << int(deviceId) << " current, error " << ostatus); ICELogError(<< "Cannot make device " << int(deviceId) << " current, error " << ostatus);
@@ -196,7 +213,12 @@ void CoreAudioUnit::makeCurrent(AudioDeviceID deviceId, int scope, int bus)
void CoreAudioUnit::setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus) void CoreAudioUnit::setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus)
{ {
OSStatus ostatus = AudioUnitSetProperty(mUnit, callbackType, scope, bus, &cb, sizeof(cb)); OSStatus ostatus = AudioUnitSetProperty(mUnit,
callbackType,
scope,
bus,
&cb,
sizeof(cb));
if (ostatus != noErr) if (ostatus != noErr)
{ {
ICELogError(<< "Cannot set callback pointer, error " << int(ostatus)); ICELogError(<< "Cannot set callback pointer, error " << int(ostatus));
@@ -208,8 +230,7 @@ void CoreAudioUnit::setBufferFrameSizeInMilliseconds(int ms)
{ {
#ifdef TARGET_IOS #ifdef TARGET_IOS
Float32 preferredBufferSize = Float32(ms) / 1000; // in seconds Float32 preferredBufferSize = Float32(ms) / 1000; // in seconds
OSStatus ostatus = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, OSStatus ostatus = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredBufferSize), &preferredBufferSize);
sizeof(preferredBufferSize), &preferredBufferSize);
if (ostatus != noErr) if (ostatus != noErr)
{ {
ICELogError(<< "Cannot set audio buffer length to " << ms << " milliseconds"); ICELogError(<< "Cannot set audio buffer length to " << ms << " milliseconds");
@@ -224,8 +245,12 @@ int CoreAudioUnit::getBufferFrameSize()
{ {
UInt32 bufsize = 0; UInt32 bufsize = 0;
UInt32 size = sizeof(UInt32); UInt32 size = sizeof(UInt32);
OSStatus ostatus = AudioUnitGetProperty(mUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, OSStatus ostatus = AudioUnitGetProperty(mUnit,
Bus_Speaker, &bufsize, &size); kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
Bus_Speaker,
&bufsize,
&size);
if (ostatus != noErr) if (ostatus != noErr)
{ {
ICELogError(<< "Cannot obtain input buffer size , error " << int(ostatus)); ICELogError(<< "Cannot obtain input buffer size , error " << int(ostatus));
@@ -249,9 +274,12 @@ AudioUnit CoreAudioUnit::getHandle()
return mUnit; return mUnit;
} }
OSStatus MacDevice::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, OSStatus MacDevice::outputCallback( void *inRefCon,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioUnitRenderActionFlags *ioActionFlags,
AudioBufferList* ioData) const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData )
{ {
MacDevice* d = reinterpret_cast<MacDevice*>(inRefCon); MacDevice* d = reinterpret_cast<MacDevice*>(inRefCon);
if (!d) if (!d)
@@ -265,18 +293,20 @@ OSStatus MacDevice::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* i
AudioBuffer& ab = ioData->mBuffers[0]; AudioBuffer& ab = ioData->mBuffers[0];
if (ab.mNumberChannels == 1) if (ab.mNumberChannels == 1)
{ {
ICELogMedia(<< "CoreAudio output callback for mono " << (int)ab.mDataByteSize << " bytes"); ICELogMedia(<< "CoreAudio output callback for mono " << (int)ab.mDataByteSize <<
" bytes");
memset(ab.mData, 0, ab.mDataByteSize); memset(ab.mData, 0, ab.mDataByteSize);
d->provideAudioToSpeaker(ab.mNumberChannels, ab.mData, ab.mDataByteSize); d->provideAudioToSpeaker(ab.mNumberChannels, ab.mData, ab.mDataByteSize);
for (int i = 1; i < ioData->mNumberBuffers; i++) for (int i=1; i<ioData->mNumberBuffers; i++)
memcpy(ioData->mBuffers[i].mData, ab.mData, ab.mDataByteSize); memcpy(ioData->mBuffers[i].mData, ab.mData, ab.mDataByteSize);
} }
else else
{ {
ICELogMedia(<< "CoreAudio output callback for stereo " << (int)ab.mDataByteSize << " bytes") ICELogMedia(<< "CoreAudio output callback for stereo " << (int)ab.mDataByteSize <<
" bytes")
// Iterate requested buffers // Iterate requested buffers
for (unsigned i = 0; i < ioData->mNumberBuffers; i++) for (unsigned i=0; i<ioData->mNumberBuffers; i++)
{ {
unsigned channels = ioData->mBuffers[i].mNumberChannels; unsigned channels = ioData->mBuffers[i].mNumberChannels;
short* dataPtr = (short*)ioData->mBuffers[i].mData; short* dataPtr = (short*)ioData->mBuffers[i].mData;
@@ -295,25 +325,32 @@ OSStatus MacDevice::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* i
static char GlobalInputBuffer[AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE]; static char GlobalInputBuffer[AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE];
OSStatus MacDevice::inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, OSStatus MacDevice::inputCallback(void *inRefCon,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioUnitRenderActionFlags *ioActionFlags,
AudioBufferList* ioData) const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{ {
// ICELogDebug(<< "CoreAudio input callback"); //ICELogDebug(<< "CoreAudio input callback");
MacDevice* d = reinterpret_cast<MacDevice*>(inRefCon); MacDevice* d = reinterpret_cast<MacDevice*>(inRefCon);
if (!d) if (!d)
return 0; return 0;
OSStatus ostatus; OSStatus ostatus;
AudioBuffer& b = d->mInputBufferList->mBuffers[0]; AudioBuffer& b = d->mInputBufferList->mBuffers[0];
// b.mDataByteSize = 65536; //b.mDataByteSize = 65536;
b.mNumberChannels = d->mStreamFormat.mChannelsPerFrame; b.mNumberChannels = d->mStreamFormat.mChannelsPerFrame;
b.mData = NULL; b.mData = NULL;
b.mDataByteSize = inNumberFrames * d->mStreamFormat.mChannelsPerFrame; b.mDataByteSize = inNumberFrames * d->mStreamFormat.mChannelsPerFrame;
// Render the unit to get input data // Render the unit to get input data
ostatus = AudioUnitRender(d->mAudioUnit.getHandle(), ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ostatus = AudioUnitRender(d->mAudioUnit.getHandle(),
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
d->mInputBufferList); d->mInputBufferList);
if (ostatus != noErr) if (ostatus != noErr)
@@ -328,7 +365,10 @@ OSStatus MacDevice::inputCallback(void* inRefCon, AudioUnitRenderActionFlags*
} }
#ifdef TARGET_IOS #ifdef TARGET_IOS
void MacDevice::propListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inData) void MacDevice::propListener(void *inClientData,
AudioSessionPropertyID inID,
UInt32 inDataSize,
const void * inData)
{ {
MacDevice* d = reinterpret_cast<MacDevice*>(inClientData); MacDevice* d = reinterpret_cast<MacDevice*>(inClientData);
CFDictionaryRef routeDictionary; CFDictionaryRef routeDictionary;
@@ -348,9 +388,10 @@ void MacDevice::propListener(void* inClientData, AudioSessionPropertyID inID, UI
} }
// Audio route changed. Nothing to do in this implementation. // Audio route changed. Nothing to do in this implementation.
} }
void MacDevice::interruptionListener(void* inClientData, UInt32 inInterruption) void MacDevice::interruptionListener(void *inClientData, UInt32 inInterruption)
{ {
MacDevice* d = reinterpret_cast<MacDevice*>(inClientData); MacDevice* d = reinterpret_cast<MacDevice*>(inClientData);
@@ -368,16 +409,19 @@ void MacDevice::interruptionListener(void* inClientData, UInt32 inInterruption)
*/ */
/* Make sure we set the correct audio category before restarting */ /* Make sure we set the correct audio category before restarting */
audioCategory = kAudioSessionCategory_PlayAndRecord; audioCategory = kAudioSessionCategory_PlayAndRecord;
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
sizeof(audioCategory),
&audioCategory);
if (ostatus != kAudioSessionNoError) if (ostatus != kAudioSessionNoError)
{ {
ICELogError(<< "Cannot set the audio session category, error " << ostatus); ICELogError(<<"Cannot set the audio session category, error " << ostatus);
} }
// Start stream // Start stream
d->startStream(); d->startStream();
} }
else if (inInterruption == kAudioSessionBeginInterruption) else
if (inInterruption == kAudioSessionBeginInterruption)
{ {
d->stopStream(); d->stopStream();
} }
@@ -385,25 +429,28 @@ void MacDevice::interruptionListener(void* inClientData, UInt32 inInterruption)
#endif #endif
MacDevice::MacDevice(int devId) MacDevice::MacDevice(int devId)
: mDeviceId(devId), mCapture(false), mRender(false), mActive(false), mConnection(nullptr), mUsageCount(0) :mDeviceId(devId), mCapture(false), mRender(false), mActive(false),
mConnection(nullptr), mUsageCount(0)
{ {
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE); mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE);
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE); mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE);
} }
MacDevice::~MacDevice() {} MacDevice::~MacDevice()
{
}
DataConnection* MacDevice::connection() DataConnection* MacDevice::connection()
{ {
return mConnection; return mConnection;
} }
void MacDevice::setConnection(DataConnection* c) void MacDevice::setConnection(DataConnection *c)
{ {
mConnection = c; mConnection = c;
} }
void MacDevice::provideAudioToSpeaker(int channels, void* buffer, int length) void MacDevice::provideAudioToSpeaker(int channels, void *buffer, int length)
{ {
if (!mConnection) if (!mConnection)
return; return;
@@ -412,7 +459,7 @@ void MacDevice::provideAudioToSpeaker(int channels, void* buffer, int length)
return; return;
} }
void MacDevice::obtainAudioFromMic(int channels, const void* buffer, int length) void MacDevice::obtainAudioFromMic(int channels, const void *buffer, int length)
{ {
if (!mConnection) if (!mConnection)
return; return;
@@ -492,13 +539,13 @@ bool MacDevice::createUnit(bool voice)
mAudioUnit.open(voice); mAudioUnit.open(voice);
// if (mCapture != mAudioUnit.getEnabled(kAudioUnitScope_Input, Bus_Microphone)) //if (mCapture != mAudioUnit.getEnabled(kAudioUnitScope_Input, Bus_Microphone))
mAudioUnit.setEnabled(mCapture, kAudioUnitScope_Input, Bus_Microphone); mAudioUnit.setEnabled(mCapture, kAudioUnitScope_Input, Bus_Microphone);
// mAudioUnit.setEnabled(mCapture, kAudioUnitScope_Output, Bus_Microphone); //mAudioUnit.setEnabled(mCapture, kAudioUnitScope_Output, Bus_Microphone);
// if (mRender != mAudioUnit.getEnabled(kAudioUnitScope_Output, Bus_Speaker)) //if (mRender != mAudioUnit.getEnabled(kAudioUnitScope_Output, Bus_Speaker))
mAudioUnit.setEnabled(mRender, kAudioUnitScope_Output, Bus_Speaker); mAudioUnit.setEnabled(mRender, kAudioUnitScope_Output, Bus_Speaker);
// mAudioUnit.setEnabled(mRender, kAudioUnitScope_Input, Bus_Speaker); //mAudioUnit.setEnabled(mRender, kAudioUnitScope_Input, Bus_Speaker);
#ifdef TARGET_OSX #ifdef TARGET_OSX
@@ -535,9 +582,8 @@ bool MacDevice::createUnit(bool voice)
// Start resample // Start resample
mRenderResampler.start(mStreamFormat.mChannelsPerFrame, AUDIO_SAMPLERATE, mStreamFormat.mSampleRate); mRenderResampler.start(mStreamFormat.mChannelsPerFrame, AUDIO_SAMPLERATE, mStreamFormat.mSampleRate);
// Set current render format - it is format required by unit from application; scope is Input and bus is 0 // Set current render format - it is format required by unit from application; scope is Input and bus is 0 (speaker)
// (speaker) //mAudioUnit.setFormat(mRenderInputFormat, kAudioUnitScope_Input, Bus_Speaker);
// mAudioUnit.setFormat(mRenderInputFormat, kAudioUnitScope_Input, Bus_Speaker);
// Configure callback // Configure callback
AURenderCallbackStruct cb; AURenderCallbackStruct cb;
@@ -551,10 +597,9 @@ bool MacDevice::createUnit(bool voice)
AURenderCallbackStruct cb; AURenderCallbackStruct cb;
cb.inputProc = inputCallback; cb.inputProc = inputCallback;
cb.inputProcRefCon = this; cb.inputProcRefCon = this;
mAudioUnit.setCallback(cb, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, mAudioUnit.setCallback(cb, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, Bus_Microphone); //!!!
Bus_Microphone); //!!!
#ifdef TARGET_OSX #ifdef TARGET_OSX
AudioBuffer* ab; AudioBuffer *ab;
UInt32 size, bufsize; UInt32 size, bufsize;
mAudioUnit.setBufferFrameSizeInMilliseconds(COREAUDIO_BUFFER_TIME); mAudioUnit.setBufferFrameSizeInMilliseconds(COREAUDIO_BUFFER_TIME);
@@ -570,11 +615,11 @@ bool MacDevice::createUnit(bool voice)
mInputBufferList->mNumberBuffers = 1; mInputBufferList->mNumberBuffers = 1;
ab = &mInputBufferList->mBuffers[0]; ab = &mInputBufferList->mBuffers[0];
ab->mNumberChannels = mStreamFormat.mChannelsPerFrame; ab->mNumberChannels = mStreamFormat.mChannelsPerFrame;
ab->mDataByteSize = 0; // bufsize * ab->mNumberChannels * mCaptureOutputFormat.mBitsPerChannel / 8 * 8; ab->mDataByteSize = 0;//bufsize * ab->mNumberChannels * mCaptureOutputFormat.mBitsPerChannel / 8 * 8;
ab->mData = NULL; // malloc(ab->mDataByteSize); ab->mData = NULL;//malloc(ab->mDataByteSize);
if (!ab->mData) if (!ab->mData)
{ {
// ICELogError(<< "No memory for capture buffer"); //ICELogError(<< "No memory for capture buffer");
} }
#endif #endif
#ifdef TARGET_IOS #ifdef TARGET_IOS
@@ -617,9 +662,8 @@ void MacDevice::startStream()
} }
// Share audio chain // Share audio chain
// UInt32 allowMixing = YES; //UInt32 allowMixing = YES;
// AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof (allowMixing), //AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof (allowMixing), &allowMixing);
// &allowMixing);
// Activate audio chain // Activate audio chain
AudioSessionSetActive(true); AudioSessionSetActive(true);
@@ -677,9 +721,15 @@ protected:
MacDeviceList* MacDeviceList::mInstance = NULL; MacDeviceList* MacDeviceList::mInstance = NULL;
MacDeviceList::MacDeviceList() {} MacDeviceList::MacDeviceList()
{
MacDeviceList::~MacDeviceList() {} }
MacDeviceList::~MacDeviceList()
{
}
MacDeviceList& MacDeviceList::instance() MacDeviceList& MacDeviceList::instance()
{ {
@@ -691,7 +741,7 @@ MacDeviceList& MacDeviceList::instance()
PMacDevice MacDeviceList::findDevice(int devId) PMacDevice MacDeviceList::findDevice(int devId)
{ {
Lock l(mGuard); Lock l(mGuard);
for (unsigned i = 0; i < mDeviceList.size(); i++) for (unsigned i=0; i<mDeviceList.size(); i++)
{ {
PMacDevice& d = mDeviceList[i]; PMacDevice& d = mDeviceList[i];
if (d->getId() == devId) if (d->getId() == devId)
@@ -704,7 +754,8 @@ PMacDevice MacDeviceList::findDevice(int devId)
} }
// Share list of opened devices // Share list of opened devices
MacInputDevice::MacInputDevice(int devId) : InputDevice() MacInputDevice::MacInputDevice(int devId)
:InputDevice()
{ {
// Look for MacDevice // Look for MacDevice
mDevice = MacDeviceList::instance().findDevice(devId); mDevice = MacDeviceList::instance().findDevice(devId);
@@ -733,7 +784,8 @@ Format MacInputDevice::getFormat()
return mDevice->getFormat(); return mDevice->getFormat();
} }
MacOutputDevice::MacOutputDevice(int devId) : OutputDevice() MacOutputDevice::MacOutputDevice(int devId)
:OutputDevice()
{ {
// Look for MacDevice // Look for MacDevice
mDevice = MacDeviceList::instance().findDevice(devId); mDevice = MacDeviceList::instance().findDevice(devId);
@@ -762,9 +814,16 @@ Format MacOutputDevice::getFormat()
return mDevice->getFormat(); return mDevice->getFormat();
} }
MacEnumerator::MacEnumerator() : mDefaultInput(0), mDefaultOutput(0) {} MacEnumerator::MacEnumerator()
:mDefaultInput(0), mDefaultOutput(0)
{
MacEnumerator::~MacEnumerator() {} }
MacEnumerator::~MacEnumerator()
{
}
void MacEnumerator::open(int direction) void MacEnumerator::open(int direction)
{ {
@@ -782,7 +841,8 @@ void MacEnumerator::open(int direction)
addr.mSelector = kAudioHardwarePropertyDevices; addr.mSelector = kAudioHardwarePropertyDevices;
addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mScope = kAudioObjectPropertyScopeGlobal;
addr.mElement = kAudioObjectPropertyElementMaster; addr.mElement = kAudioObjectPropertyElementMaster;
osstatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &devSize); osstatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
0, NULL, &devSize);
if (osstatus != noErr) if (osstatus != noErr)
{ {
devSize = 0; devSize = 0;
@@ -794,11 +854,11 @@ void MacEnumerator::open(int direction)
deviceIds.resize(devCount); deviceIds.resize(devCount);
// Get actual list // Get actual list
osstatus = osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &devSize, (void*)&deviceIds.front()); 0, NULL, &devSize, (void *)&deviceIds.front());
if (osstatus != noErr) if (osstatus != noErr)
return; return;
for (unsigned i = 0; i < deviceIds.size(); i++) for (unsigned i=0; i<deviceIds.size(); i++)
{ {
DeviceInfo di; DeviceInfo di;
di.mId = deviceIds[i]; di.mId = deviceIds[i];
@@ -827,12 +887,13 @@ void MacEnumerator::open(int direction)
addr.mElement = kAudioObjectPropertyElementMaster; addr.mElement = kAudioObjectPropertyElementMaster;
size = sizeof(devId); size = sizeof(devId);
osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void*)&devId); osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&addr, 0, NULL, &size, (void *)&devId);
if (osstatus == noErr) if (osstatus == noErr)
{ {
std::vector<DeviceInfo>::iterator r = std::find_if( std::vector<DeviceInfo>::iterator r = std::find_if(mDeviceList.begin(), mDeviceList.end(),
mDeviceList.begin(), mDeviceList.end(), [devId](const DeviceInfo& di) { return di.mId == devId; }); [devId] (const DeviceInfo& di) { return di.mId == devId;});
if (r != mDeviceList.end()) if (r != mDeviceList.end())
mDefaultInput = r - mDeviceList.begin(); mDefaultInput = r - mDeviceList.begin();
} }
@@ -842,11 +903,13 @@ void MacEnumerator::open(int direction)
if (direction == mySpeaker) if (direction == mySpeaker)
{ {
addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void*)&devId); osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&addr, 0, NULL,
&size, (void *)&devId);
if (osstatus == noErr) if (osstatus == noErr)
{ {
std::vector<DeviceInfo>::iterator r = std::find_if( std::vector<DeviceInfo>::iterator r = std::find_if(mDeviceList.begin(), mDeviceList.end(),
mDeviceList.begin(), mDeviceList.end(), [devId](const DeviceInfo& di) { return di.mId == devId; }); [devId](const DeviceInfo& di) { return di.mId == devId;});
if (r != mDeviceList.end()) if (r != mDeviceList.end())
mDefaultOutput = r - mDeviceList.begin(); mDefaultOutput = r - mDeviceList.begin();
} }
@@ -855,7 +918,7 @@ void MacEnumerator::open(int direction)
#endif #endif
} }
void MacEnumerator::getInfo(DeviceInfo& di) void MacEnumerator::getInfo(DeviceInfo &di)
{ {
UInt32 size; UInt32 size;
OSStatus osstatus; OSStatus osstatus;
@@ -867,7 +930,7 @@ void MacEnumerator::getInfo(DeviceInfo& di)
addr.mElement = kAudioObjectPropertyElementMaster; addr.mElement = kAudioObjectPropertyElementMaster;
char name[256]; char name[256];
size = sizeof(name); size = sizeof(name);
AudioObjectGetPropertyData(di.mId, &addr, 0, NULL, &size, (void*)name); AudioObjectGetPropertyData(di.mId, &addr, 0, NULL, &size, (void *)name);
di.mName = name; di.mName = name;
// Get the number of input channels // Get the number of input channels
@@ -921,8 +984,7 @@ void MacEnumerator::getInfo(DeviceInfo& di)
di.mOutputCount += buf->mBuffers[idx].mNumberChannels; di.mOutputCount += buf->mBuffers[idx].mNumberChannels;
} }
} }
free(buf); free(buf); buf = NULL;
buf = NULL;
} }
/* Get default sample rate */ /* Get default sample rate */
@@ -930,7 +992,7 @@ void MacEnumerator::getInfo(DeviceInfo& di)
addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mScope = kAudioObjectPropertyScopeGlobal;
size = sizeof(Float64); size = sizeof(Float64);
Float64 sampleRate; Float64 sampleRate;
osstatus = AudioObjectGetPropertyData(di.mId, &addr, 0, NULL, &size, &sampleRate); osstatus = AudioObjectGetPropertyData (di.mId, &addr, 0, NULL, &size, &sampleRate);
if (osstatus == noErr) if (osstatus == noErr)
di.mDefaultRate = int(sampleRate); di.mDefaultRate = int(sampleRate);
@@ -947,7 +1009,10 @@ void MacEnumerator::getInfo(DeviceInfo& di)
} }
} }
void MacEnumerator::close() {} void MacEnumerator::close()
{
}
int MacEnumerator::count() int MacEnumerator::count()
{ {
+32 -25
View File
@@ -25,16 +25,18 @@
namespace Audio namespace Audio
{ {
class AudioException : public Exception class AudioException: public Exception
{ {
public: public:
AudioException(int code, OSStatus subcode) : Exception(code, int(subcode)) {} AudioException(int code, OSStatus subcode)
:Exception(code, int(subcode))
{}
}; };
// #ifndef AudioDeviceID //#ifndef AudioDeviceID
// # define AudioDeviceID unsigned //# define AudioDeviceID unsigned
// #endif //#endif
class MacEnumerator : public Enumerator class MacEnumerator: public Enumerator
{ {
public: public:
MacEnumerator(); MacEnumerator();
@@ -57,11 +59,7 @@ protected:
bool mCanChangeInputVolume; bool mCanChangeInputVolume;
int mInputCount, mOutputCount; int mInputCount, mOutputCount;
int mDefaultRate; int mDefaultRate;
DeviceInfo() DeviceInfo(): mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0), mDefaultRate(16000) {}
: mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0),
mDefaultRate(16000)
{
}
}; };
std::vector<DeviceInfo> mDeviceList; std::vector<DeviceInfo> mDeviceList;
unsigned mDefaultInput, mDefaultOutput; unsigned mDefaultInput, mDefaultOutput;
@@ -119,8 +117,7 @@ protected:
CoreAudioUnit mAudioUnit; CoreAudioUnit mAudioUnit;
AudioComponent mComponent; AudioComponent mComponent;
AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat, AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat, mStreamFormat;
mStreamFormat;
AudioBufferList* mInputBufferList; AudioBufferList* mInputBufferList;
DataConnection* mConnection; DataConnection* mConnection;
SpeexResampler mCaptureResampler, mRenderResampler; SpeexResampler mCaptureResampler, mRenderResampler;
@@ -133,22 +130,32 @@ protected:
void setupStreamFormat(); void setupStreamFormat();
bool createResampleUnit(AudioStreamBasicDescription format); bool createResampleUnit(AudioStreamBasicDescription format);
static OSStatus outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, static OSStatus outputCallback( void *inRefCon,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioUnitRenderActionFlags *ioActionFlags,
AudioBufferList* ioData); const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData );
static OSStatus inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, static OSStatus inputCallback(void *inRefCon,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioUnitRenderActionFlags *ioActionFlags,
AudioBufferList* ioData); const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
#ifdef TARGET_IOS #ifdef TARGET_IOS
static void propListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inData); static void propListener(void *inClientData,
static void interruptionListener(void* inClientData, UInt32 inInterruption); AudioSessionPropertyID inID,
UInt32 inDataSize,
const void * inData);
static void interruptionListener(void *inClientData, UInt32 inInterruption);
#endif #endif
}; };
typedef std::shared_ptr<MacDevice> PMacDevice; typedef std::shared_ptr<MacDevice> PMacDevice;
class MacInputDevice : public InputDevice class MacInputDevice: public InputDevice
{ {
public: public:
MacInputDevice(int devId); MacInputDevice(int devId);
@@ -161,12 +168,12 @@ public:
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
int readBuffer(void* buffer); int readBuffer(void* buffer);
protected: protected:
PMacDevice mDevice; PMacDevice mDevice;
}; };
class MacOutputDevice : public OutputDevice class MacOutputDevice: public OutputDevice
{ {
public: public:
MacOutputDevice(int devId); MacOutputDevice(int devId);
@@ -183,7 +190,7 @@ protected:
PMacDevice mDevice; PMacDevice mDevice;
}; };
} // namespace Audio }
#endif // TARGET_OSX #endif // TARGET_OSX
+23 -54
View File
@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2018 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,40 +8,30 @@
using namespace Audio; using namespace Audio;
DataWindow::DataWindow() {} DataWindow::DataWindow()
{
mFilled = 0;
mData = NULL;
mCapacity = 0;
}
DataWindow::~DataWindow() DataWindow::~DataWindow()
{ {
if (mData) if (mData)
{
free(mData); free(mData);
mData = nullptr;
}
} }
void DataWindow::setCapacity(size_t capacity) void DataWindow::setCapacity(int capacity)
{ {
Lock l(mMutex); Lock l(mMutex);
int tail = capacity - mCapacity;
// The window only ever grows; a smaller request keeps the current buffer.
if (capacity <= mCapacity)
return;
size_t tail = capacity - mCapacity;
char* buffer = mData;
mData = (char*)realloc(mData, capacity); mData = (char*)realloc(mData, capacity);
if (!mData)
{
// Realloc failed
mData = buffer;
throw std::bad_alloc();
}
if (tail > 0) if (tail > 0)
memset(mData + mCapacity, 0, tail); memset(mData + mCapacity, 0, tail);
mCapacity = capacity; mCapacity = capacity;
} }
void DataWindow::addZero(size_t length) void DataWindow::addZero(int length)
{ {
Lock l(mMutex); Lock l(mMutex);
@@ -60,7 +50,7 @@ void DataWindow::addZero(size_t length)
} }
void DataWindow::add(const void* data, size_t length) void DataWindow::add(const void* data, int length)
{ {
Lock l(mMutex); Lock l(mMutex);
@@ -94,7 +84,7 @@ void DataWindow::add(short sample)
add(&sample, sizeof sample); add(&sample, sizeof sample);
} }
void DataWindow::erase(size_t length) void DataWindow::erase(int length)
{ {
Lock l(mMutex); Lock l(mMutex);
if (length > mFilled) if (length > mFilled)
@@ -120,21 +110,21 @@ void DataWindow::clear()
mFilled = 0; mFilled = 0;
} }
short DataWindow::shortAt(size_t index) const short DataWindow::shortAt(int index) const
{ {
Lock l(mMutex); Lock l(mMutex);
assert(index < mFilled / 2); assert(index < mFilled / 2);
return ((short*)mData)[index]; return ((short*)mData)[index];
} }
void DataWindow::setShortAt(short value, size_t index) void DataWindow::setShortAt(short value, int index)
{ {
Lock l(mMutex); Lock l(mMutex);
assert(index < mFilled / 2); assert(index < mFilled / 2);
((short*)mData)[index] = value; ((short*)mData)[index] = value;
} }
size_t DataWindow::read(void* buffer, size_t length) int DataWindow::read(void* buffer, int length)
{ {
Lock l(mMutex); Lock l(mMutex);
if (length > mFilled) if (length > mFilled)
@@ -144,33 +134,31 @@ size_t DataWindow::read(void* buffer, size_t length)
if (buffer) if (buffer)
memcpy(buffer, mData, length); memcpy(buffer, mData, length);
if (length < mFilled) if (length < mFilled)
memmove(mData, mData + length, mFilled - length); memmove(mData, mData+length, mFilled - length);
mFilled -= length; mFilled -= length;
} }
return length; return length;
} }
size_t DataWindow::filled() const int DataWindow::filled() const
{ {
Lock l(mMutex); Lock l(mMutex);
return mFilled; return mFilled;
} }
void DataWindow::setFilled(size_t filled) void DataWindow::setFilled(int filled)
{ {
Lock l(mMutex); Lock l(mMutex);
if (filled > mCapacity)
throw std::bad_alloc();
mFilled = filled; mFilled = filled;
} }
size_t DataWindow::capacity() const int DataWindow::capacity() const
{ {
Lock l(mMutex); Lock l(mMutex);
return mCapacity; return mCapacity;
} }
void DataWindow::zero(size_t length) void DataWindow::zero(int length)
{ {
Lock l(mMutex); Lock l(mMutex);
assert(length <= mCapacity); assert(length <= mCapacity);
@@ -178,34 +166,15 @@ void DataWindow::zero(size_t length)
memset(mData, 0, mFilled); memset(mData, 0, mFilled);
} }
size_t DataWindow::moveTo(DataWindow& dst, size_t size)
{
Lock l(mMutex);
size_t avail = std::min(size, (size_t)filled());
if (avail != 0)
{
dst.add(mData, avail);
erase(avail);
}
return avail;
}
std::chrono::milliseconds DataWindow::getTimeLength(const Audio::Format& fmt) const
{
Lock l(mMutex);
return std::chrono::milliseconds(mFilled / sizeof(short) / fmt.channels() / (fmt.rate() / 1000));
}
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src) void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
{ {
Lock lockDst(dst.mMutex), lockSrc(src.mMutex); Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
dst.setCapacity(src.filled() * 2); dst.setCapacity(src.filled()*2);
short* input = (short*)src.mutableData(); short* input = (short*)src.mutableData();
short* output = (short*)dst.mutableData(); short* output = (short*)dst.mutableData();
for (int i = 0; i < src.filled() / 2; i++) for (int i=0; i<src.filled()/2; i++)
output[i * 2] = output[i * 2 + 1] = input[i]; output[i*2] = output[i*2+1] = input[i];
dst.mFilled = src.filled() * 2; dst.mFilled = src.filled() * 2;
} }
+20 -24
View File
@@ -8,44 +8,40 @@
#include "../helper/HL_ByteBuffer.h" #include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include "Audio_Interface.h"
namespace Audio namespace Audio
{ {
class DataWindow class DataWindow
{ {
public: public:
DataWindow(); DataWindow();
~DataWindow(); ~DataWindow();
void setCapacity(size_t capacity); void setCapacity(int capacity);
size_t capacity() const; int capacity() const;
void addZero(size_t length); void addZero(int length);
void add(const void* data, size_t length); void add(const void* data, int length);
void add(short sample); void add(short sample);
size_t read(void* buffer, size_t length); int read(void* buffer, int length);
void erase(size_t length); void erase(int length = -1);
const char* data() const; const char* data() const;
char* mutableData(); char* mutableData();
size_t filled() const; int filled() const;
void setFilled(size_t filled); void setFilled(int filled);
void clear(); void clear();
short shortAt(size_t index) const; short shortAt(int index) const;
void setShortAt(short value, size_t index); void setShortAt(short value, int index);
void zero(size_t length); void zero(int length);
size_t moveTo(DataWindow& dst, size_t size /* in bytes*/);
std::chrono::milliseconds getTimeLength(const Format& fmt) const;
static void makeStereoFromMono(DataWindow& dst, DataWindow& src); static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
protected: protected:
mutable Mutex mMutex; mutable Mutex mMutex;
char* mData = nullptr; char* mData;
size_t mFilled = 0; int mFilled;
size_t mCapacity = 0; int mCapacity;
}; };
} // namespace Audio }
#endif #endif
+22 -68
View File
@@ -1,21 +1,20 @@
/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define NOMINMAX
#include "Audio_DevicePair.h" #include "Audio_DevicePair.h"
#include <algorithm> #include <algorithm>
#include <assert.h> #include <assert.h>
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "Audio"
using namespace Audio; using namespace Audio;
// --- DevicePair --- // --- DevicePair ---
DevicePair::DevicePair() DevicePair::DevicePair(bool aec, bool agc)
: mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false), :mConfig(NULL), mDelegate(NULL), mAec(aec), mAgc(agc), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS)
mAecFilter(AUDIO_MIC_BUFFER_LENGTH * 10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS),
mMonitoring(nullptr)
{ {
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1)); mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1)); mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
@@ -29,50 +28,26 @@ DevicePair::~DevicePair()
if (mInput) if (mInput)
{ {
if (mInput->connection() == this) if (mInput->connection() == this)
mInput->setConnection(nullptr); mInput->setConnection(NULL);
mInput.reset(); mInput.reset();
} }
if (mOutput) if (mOutput)
{ {
if (mOutput->connection() == this) if (mOutput->connection() == this)
mOutput->setConnection(nullptr); mOutput->setConnection(NULL);
mOutput.reset(); mOutput.reset();
} }
} }
DevicePair& DevicePair::setAec(bool aec)
{
mAec = aec;
return *this;
}
bool DevicePair::aec()
{
return mAec;
}
DevicePair& DevicePair::setAgc(bool agc)
{
mAgc = agc;
return *this;
}
bool DevicePair::agc()
{
return mAgc;
}
VariantMap* DevicePair::config() VariantMap* DevicePair::config()
{ {
return mConfig; return mConfig;
} }
DevicePair& DevicePair::setConfig(VariantMap* config) void DevicePair::setConfig(VariantMap* config)
{ {
mConfig = config; mConfig = config;
return *this;
} }
PInputDevice DevicePair::input() PInputDevice DevicePair::input()
@@ -80,17 +55,15 @@ PInputDevice DevicePair::input()
return mInput; return mInput;
} }
DevicePair& DevicePair::setInput(PInputDevice input) void DevicePair::setInput(PInputDevice input)
{ {
if (mInput == input) if (mInput == input)
return *this; return;
mInput = input; mInput = input;
mInput->setConnection(this); mInput->setConnection(this);
if (mDelegate) if (mDelegate)
mDelegate->deviceChanged(this); mDelegate->deviceChanged(this);
return *this;
} }
POutputDevice DevicePair::output() POutputDevice DevicePair::output()
@@ -98,17 +71,14 @@ POutputDevice DevicePair::output()
return mOutput; return mOutput;
} }
DevicePair& DevicePair::setOutput(POutputDevice output) void DevicePair::setOutput(POutputDevice output)
{ {
if (output == mOutput) if (output == mOutput)
return *this; return;
mOutput = output; mOutput = output;
mOutput->setConnection(this); mOutput->setConnection(this);
if (mDelegate) if (mDelegate)
mDelegate->deviceChanged(this); mDelegate->deviceChanged(this);
return *this;
} }
bool DevicePair::start() bool DevicePair::start()
@@ -118,7 +88,6 @@ bool DevicePair::start()
result = mInput->open(); result = mInput->open();
if (mOutput && result) if (mOutput && result)
result &= mOutput->open(); result &= mOutput->open();
return result; return result;
} }
@@ -130,10 +99,9 @@ void DevicePair::stop()
mOutput->close(); mOutput->close();
} }
DevicePair& DevicePair::setDelegate(Delegate* dc) void DevicePair::setDelegate(Delegate* dc)
{ {
mDelegate = dc; mDelegate = dc;
return *this;
} }
DevicePair::Delegate* DevicePair::delegate() DevicePair::Delegate* DevicePair::delegate()
@@ -141,17 +109,6 @@ DevicePair::Delegate* DevicePair::delegate()
return mDelegate; return mDelegate;
} }
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
{
mMonitoring = monitoring;
return *this;
}
DataConnection* DevicePair::monitoring()
{
return mMonitoring;
}
Player& DevicePair::player() Player& DevicePair::player()
{ {
return mPlayer; return mPlayer;
@@ -177,6 +134,7 @@ void DevicePair::onMicData(const Format& f, const void* buffer, int length)
for (int blockIndex = 0; blockIndex < blocks; blockIndex++) for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
{ {
size_t wasProcessed = 0; size_t wasProcessed = 0;
size_t wasProduced = mMicResampler.resample(f.mRate, // Source rate size_t wasProduced = mMicResampler.resample(f.mRate, // Source rate
@@ -193,11 +151,12 @@ void DevicePair::onMicData(const Format& f, const void* buffer, int length)
mInputBuffer.erase((int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH)); mInputBuffer.erase((int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
} }
} }
void DevicePair::onSpkData(const Format& f, void* buffer, int length) void DevicePair::onSpkData(const Format& f, void* buffer, int length)
{ {
// ICELogMedia(<< "Audio::DevicePair::onSpkData() begin"); //ICELogMedia(<< "Audio::DevicePair::onSpkData() begin");
#ifdef DUMP_NATIVEOUTPUT #ifdef DUMP_NATIVEOUTPUT
if (!mNativeOutputDump) if (!mNativeOutputDump)
{ {
@@ -214,6 +173,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
int required = length - mOutputNativeData.filled(); int required = length - mOutputNativeData.filled();
if (required > 0) if (required > 0)
{ {
// Find how much blocks must be received from RTP/decoder side // Find how much blocks must be received from RTP/decoder side
int nativeBufferSize = (int)nativeFormat.sizeFromTime(AUDIO_SPK_BUFFER_LENGTH); int nativeBufferSize = (int)nativeFormat.sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
int blocks = required / nativeBufferSize; int blocks = required / nativeBufferSize;
@@ -225,7 +185,6 @@ 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());
@@ -238,10 +197,8 @@ 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(Format().mRate, mOutput10msBuffer.data(), size_t wasProduced = mSpkResampler.resample(nativeFormat.mRate, mOutput10msBuffer.data(), mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
mOutput10msBuffer.capacity(), wasProcessed, f.mRate, mOutputNativeData.mutableData() + mOutputNativeData.filled(), mOutputNativeData.capacity() - mOutputNativeData.filled());
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);
@@ -257,20 +214,17 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
mOutputNativeData.read(buffer, length); mOutputNativeData.read(buffer, length);
// Send data to monitoring if needed #define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
if (mMonitoring)
mMonitoring->onSpkData(f, buffer, length);
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
// AEC filter wants frames. // AEC filter wants frames.
if (mAec) if (mAec)
{ {
int nrOfFrames = mAecSpkBuffer.filled() / AEC_FRAME_SIZE; int nrOfFrames = mAecSpkBuffer.filled() / AEC_FRAME_SIZE;
for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++) for (int frameIndex=0; frameIndex < nrOfFrames; frameIndex++)
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)
+17 -21
View File
@@ -11,50 +11,47 @@
#include "Audio_Resampler.h" #include "Audio_Resampler.h"
#include "Audio_DataWindow.h" #include "Audio_DataWindow.h"
// #define DUMP_NATIVEOUTPUT //#define DUMP_NATIVEOUTPUT
// #define DUMP_NATIVEINPUT //#define DUMP_NATIVEINPUT
namespace Audio namespace Audio
{ {
class DevicePair : protected DataConnection class DevicePair: protected DataConnection
{ {
public: public:
class Delegate : public DataConnection class Delegate: public DataConnection
{ {
public: public:
virtual void deviceChanged(DevicePair* dpair) = 0; virtual void deviceChanged(DevicePair* dpair) = 0;
}; };
DevicePair(); DevicePair(bool aec = true, bool agc = true);
virtual ~DevicePair(); virtual ~DevicePair();
DevicePair& setAec(bool aec); void setAec(bool aec);
bool aec(); bool aec();
DevicePair& setAgc(bool agc); void setAgc(bool agc);
bool agc(); bool agc();
VariantMap* config(); VariantMap* config();
DevicePair& setConfig(VariantMap* config); void setConfig(VariantMap* config);
PInputDevice input(); PInputDevice input();
DevicePair& setInput(PInputDevice input); void setInput(PInputDevice input);
POutputDevice output(); POutputDevice output();
DevicePair& setOutput(POutputDevice output); void setOutput(POutputDevice output);
bool start(); bool start();
void stop(); void stop();
DevicePair& setDelegate(Delegate* dc); void setDelegate(Delegate* dc);
Delegate* delegate(); Delegate* delegate();
DevicePair& setMonitoring(DataConnection* monitoring);
DataConnection* monitoring();
Player& player(); Player& player();
protected: protected:
VariantMap* mConfig; VariantMap* mConfig;
PInputDevice mInput; PInputDevice mInput;
POutputDevice mOutput; POutputDevice mOutput;
@@ -66,7 +63,6 @@ protected:
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;
@@ -77,9 +73,9 @@ protected:
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 processMicData(const Format& f, void* buffer, int length); void processMicData(const Format& f, void* buffer, int length);
}; };
typedef std::shared_ptr<DevicePair> PDevicePair; typedef std::shared_ptr<DevicePair> PDevicePair;
} // namespace Audio }
#endif #endif
+133 -116
View File
@@ -22,7 +22,7 @@
#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) #define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) #define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "DirectSound"
using namespace Audio; using namespace Audio;
@@ -39,14 +39,14 @@ public:
{ {
HINSTANCE mInstance; HINSTANCE mInstance;
HRESULT(WINAPI* DirectSoundCreate8)(LPGUID, LPDIRECTSOUND8*, LPUNKNOWN); HRESULT (WINAPI *DirectSoundCreate8)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN);
HRESULT(WINAPI* DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
HRESULT(WINAPI* DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID); HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
HRESULT(WINAPI* DirectSoundCaptureCreate8)(LPGUID, LPDIRECTSOUNDCAPTURE8*, LPUNKNOWN); HRESULT (WINAPI *DirectSoundCaptureCreate8)(LPGUID, LPDIRECTSOUNDCAPTURE8* , LPUNKNOWN);
HRESULT(WINAPI* DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
HRESULT(WINAPI* DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID); HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
HRESULT(WINAPI* GetDeviceID)(LPCGUID src, LPGUID dst); HRESULT (WINAPI *GetDeviceID)(LPCGUID src, LPGUID dst);
} mRoutines; } mRoutines;
protected: protected:
@@ -57,11 +57,14 @@ protected:
DSoundInit gDSoundInit; DSoundInit gDSoundInit;
DSoundInit::DSoundInit() : mRefCount(0) {} DSoundInit::DSoundInit()
:mRefCount(0)
{
}
DSoundInit::~DSoundInit() DSoundInit::~DSoundInit()
{ {
// Unload(); //Unload();
} }
void DSoundInit::load() void DSoundInit::load()
@@ -74,21 +77,16 @@ void DSoundInit::load()
hr = ::CoInitialize(NULL); hr = ::CoInitialize(NULL);
// load the DirectSound DLL //load the DirectSound DLL
mRoutines.mInstance = ::LoadLibraryW(L"dsound.dll"); mRoutines.mInstance = ::LoadLibraryW(L"dsound.dll");
if (!mRoutines.mInstance) if (!mRoutines.mInstance)
throw std::logic_error("Cannot load dsound.dll"); throw std::logic_error("Cannot load dsound.dll");
mRoutines.DirectSoundCaptureCreate8 = mRoutines.DirectSoundCaptureCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureCreate8");
(HRESULT(WINAPI*)(LPGUID, LPDIRECTSOUNDCAPTURE8*, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, mRoutines.DirectSoundCaptureEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureEnumerateW");
"DirectSoundCaptureCreate8"); mRoutines.DirectSoundCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCreate8");
mRoutines.DirectSoundCaptureEnumerateW = (HRESULT(WINAPI*)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress( mRoutines.DirectSoundEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW");
mRoutines.mInstance, "DirectSoundCaptureEnumerateW"); mRoutines.GetDeviceID = (HRESULT (WINAPI*) (LPCGUID, LPGUID)) GetProcAddress(mRoutines.mInstance, "GetDeviceID");
mRoutines.DirectSoundCreate8 = (HRESULT(WINAPI*)(LPGUID, LPDIRECTSOUND8*, LPUNKNOWN))::GetProcAddress(
mRoutines.mInstance, "DirectSoundCreate8");
mRoutines.DirectSoundEnumerateW =
(HRESULT(WINAPI*)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW");
mRoutines.GetDeviceID = (HRESULT(WINAPI*)(LPCGUID, LPGUID))GetProcAddress(mRoutines.mInstance, "GetDeviceID");
} }
} }
@@ -108,7 +106,10 @@ void DSoundInit::unload()
} }
// --------------- VistaEnumerator --------------------- // --------------- VistaEnumerator ---------------------
VistaEnumerator::VistaEnumerator() : mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture) {} VistaEnumerator::VistaEnumerator()
:mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture)
{
}
VistaEnumerator::~VistaEnumerator() VistaEnumerator::~VistaEnumerator()
{ {
@@ -122,8 +123,10 @@ void VistaEnumerator::open(int direction)
mDirection = (direction == myMicrophone) ? eCapture : eRender; mDirection = (direction == myMicrophone) ? eCapture : eRender;
HRESULT hr = HRESULT hr = CoCreateInstance(
CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mEnumerator); CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&mEnumerator);
if (!mEnumerator) if (!mEnumerator)
return; return;
@@ -149,7 +152,7 @@ void VistaEnumerator::close()
if (mDefaultDevice) if (mDefaultDevice)
{ {
// mDefaultDevice->Release(); //mDefaultDevice->Release();
mDefaultDevice = NULL; mDefaultDevice = NULL;
} }
@@ -159,7 +162,7 @@ void VistaEnumerator::close()
mEnumerator = NULL; mEnumerator = NULL;
} }
} }
catch (...) catch(...)
{ {
} }
} }
@@ -182,7 +185,7 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
if (mmres != MMSYSERR_NOERROR) if (mmres != MMSYSERR_NOERROR)
return NULL; return NULL;
id = (WCHAR*)_alloca(idSize * sizeof(WCHAR)); id = (WCHAR*)_alloca(idSize*sizeof(WCHAR));
mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize); mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize);
} }
else else
@@ -192,7 +195,7 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
if (mmres != MMSYSERR_NOERROR) if (mmres != MMSYSERR_NOERROR)
return NULL; return NULL;
id = (WCHAR*)_alloca(idSize * sizeof(WCHAR)); id = (WCHAR*)_alloca(idSize*sizeof(WCHAR));
mmres = waveOutMessage((HWAVEOUT)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize); mmres = waveOutMessage((HWAVEOUT)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize);
} }
@@ -210,7 +213,7 @@ void VistaEnumerator::enumerate()
mNameList.clear(); mNameList.clear();
int res = (int)count(); int res = (int)count();
for (int i = 0; i < res; i++) for (int i=0; i<res; i++)
{ {
IMMDevice* dev = mapIndexToInterface(i); IMMDevice* dev = mapIndexToInterface(i);
if (dev) if (dev)
@@ -255,23 +258,26 @@ int VistaEnumerator::indexOfDefaultDevice()
if (mDirection == mySpeaker) if (mDirection == mySpeaker)
{ {
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
else else
{ {
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
return devID; return devID;
} }
// -------------- XpEnumerator --------------- // -------------- XpEnumerator ---------------
XpEnumerator::XpEnumerator() : mDirection(-1) {} XpEnumerator::XpEnumerator()
:mDirection(-1)
{
}
XpEnumerator::~XpEnumerator() {} XpEnumerator::~XpEnumerator()
{
}
void XpEnumerator::open(int direction) void XpEnumerator::open(int direction)
{ {
@@ -279,7 +285,7 @@ void XpEnumerator::open(int direction)
if (direction == myMicrophone) if (direction == myMicrophone)
{ {
int count = waveInGetNumDevs(); int count = waveInGetNumDevs();
for (int i = 0; i < count; i++) for (int i=0; i<count; i++)
{ {
WAVEINCAPSW caps; WAVEINCAPSW caps;
if (waveInGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR) if (waveInGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR)
@@ -291,7 +297,7 @@ void XpEnumerator::open(int direction)
else else
{ {
int count = waveOutGetNumDevs(); int count = waveOutGetNumDevs();
for (int i = 0; i < count; i++) for (int i=0; i<count; i++)
{ {
WAVEOUTCAPSW caps; WAVEOUTCAPSW caps;
if (waveOutGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR) if (waveOutGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR)
@@ -302,7 +308,9 @@ void XpEnumerator::open(int direction)
} }
} }
void XpEnumerator::close() {} void XpEnumerator::close()
{
}
int XpEnumerator::count() int XpEnumerator::count()
{ {
@@ -325,14 +333,12 @@ int XpEnumerator::indexOfDefaultDevice()
if (mDirection == mySpeaker) if (mDirection == mySpeaker)
{ {
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
else else
{ {
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
return devID; return devID;
@@ -340,12 +346,11 @@ int XpEnumerator::indexOfDefaultDevice()
// -------- DSoundInputDevice --------------- // -------- DSoundInputDevice ---------------
DSoundInputDevice::DSoundInputDevice(GUID deviceId) DSoundInputDevice::DSoundInputDevice(GUID deviceId)
: mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE), :mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE), mEnableDenoiser(true),
mEnableDenoiser(true), mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT) mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT)
#ifdef AUDIO_DUMPINPUT #ifdef AUDIO_DUMPINPUT
, ,mDump(AUDIO_SAMPLERATE)
mDump(AUDIO_SAMPLERATE) #endif
#endif
{ {
gDSoundInit.load(); gDSoundInit.load();
@@ -354,7 +359,7 @@ DSoundInputDevice::DSoundInputDevice(GUID deviceId)
mDevice = NULL; mDevice = NULL;
mBuffer = NULL; mBuffer = NULL;
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
for (unsigned i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++) for (unsigned i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
{ {
mEventArray[i].dwOffset = (i + 1) * AUDIO_MIC_BUFFER_SIZE - 1; mEventArray[i].dwOffset = (i + 1) * AUDIO_MIC_BUFFER_SIZE - 1;
mEventSignals[i] = mEventArray[i].hEventNotify = ::CreateEvent(NULL, TRUE, FALSE, NULL); mEventSignals[i] = mEventArray[i].hEventNotify = ::CreateEvent(NULL, TRUE, FALSE, NULL);
@@ -366,7 +371,7 @@ DSoundInputDevice::~DSoundInputDevice()
{ {
close(); close();
::CloseHandle(mShutdownSignal); ::CloseHandle(mShutdownSignal);
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++) for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
::CloseHandle(mEventArray[i].hEventNotify); ::CloseHandle(mEventArray[i].hEventNotify);
gDSoundInit.unload(); gDSoundInit.unload();
@@ -384,12 +389,13 @@ bool DSoundInputDevice::isSimulate() const
void DSoundInputDevice::openDevice() void DSoundInputDevice::openDevice()
{ {
ICELogInfo(<< "Open DirectSound audio input.")::CoInitialize(NULL); ICELogInfo(<< "Open DirectSound audio input.")
::CoInitialize(NULL);
Lock l(mGuard); Lock l(mGuard);
// Ensure if GUID is not null // Ensure if GUID is not null
if (IsEqualGUID(mGUID, GUID_NULL)) if (IsEqualGUID(mGUID, GUID_NULL))
{ {
setSimulate(true); setSimulate( true );
return; return;
} }
@@ -398,28 +404,24 @@ void DSoundInputDevice::openDevice()
mDump.open(L"audioinput.wav"); mDump.open(L"audioinput.wav");
#endif #endif
mNextBuffer = 0; mNextBuffer = 0; mDevice = NULL; IUnknown* unk = NULL; mBuffer = NULL;
mDevice = NULL;
IUnknown* unk = NULL;
mBuffer = NULL;
DSoundHelper::checkComResult(gDSoundInit.mRoutines.DirectSoundCaptureCreate8(&mGUID, &mDevice, NULL)); DSoundHelper::checkComResult(gDSoundInit.mRoutines.DirectSoundCaptureCreate8(&mGUID, &mDevice, NULL));
WAVEFORMATEX wfx; WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(wfx)); memset(&wfx, 0, sizeof(wfx));
// wfx.cbSize = sizeof(wfx); //wfx.cbSize = sizeof(wfx);
wfx.nChannels = AUDIO_CHANNELS; wfx.nChannels = AUDIO_CHANNELS;
wfx.nSamplesPerSec = AUDIO_SAMPLERATE; wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
wfx.wBitsPerSample = 16; wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;;
;
wfx.nAvgBytesPerSec = AUDIO_SAMPLERATE * 2 * AUDIO_CHANNELS; wfx.nAvgBytesPerSec = AUDIO_SAMPLERATE * 2 * AUDIO_CHANNELS;
wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.wFormatTag = WAVE_FORMAT_PCM;
DSCBUFFERDESC dsbd; DSCBUFFERDESC dsbd;
ZeroMemory(&dsbd, sizeof(dsbd)); ZeroMemory(&dsbd, sizeof(dsbd));
dsbd.dwSize = sizeof(DSCBUFFERDESC); dsbd.dwSize = sizeof(DSCBUFFERDESC);
dsbd.dwFlags = 0; // DSBCAPS_CTRLPOSITIONNOTIFY; dsbd.dwFlags = 0;//DSBCAPS_CTRLPOSITIONNOTIFY;
dsbd.dwBufferBytes = AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE; dsbd.dwBufferBytes = AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE;
dsbd.lpwfxFormat = &wfx; dsbd.lpwfxFormat = &wfx;
dsbd.dwFXCount = 0; dsbd.dwFXCount = 0;
@@ -432,7 +434,7 @@ void DSoundInputDevice::openDevice()
DSoundHelper::checkComResult(mNotifications->SetNotificationPositions(AUDIO_MIC_BUFFER_COUNT, mEventArray)); DSoundHelper::checkComResult(mNotifications->SetNotificationPositions(AUDIO_MIC_BUFFER_COUNT, mEventArray));
DSoundHelper::checkComResult(mBuffer->Start(DSCBSTART_LOOPING)); DSoundHelper::checkComResult(mBuffer->Start(DSCBSTART_LOOPING));
dscb->Release(); dscb->Release();
setSimulate(false); setSimulate( false );
} }
bool DSoundInputDevice::open() bool DSoundInputDevice::open()
@@ -450,7 +452,7 @@ bool DSoundInputDevice::open()
void DSoundInputDevice::closeDevice() void DSoundInputDevice::closeDevice()
{ {
ICELogInfo(<< "Close DirectSound audio input"); ICELogInfo(<<"Close DirectSound audio input");
Lock l(mGuard); Lock l(mGuard);
#ifdef AUDIO_DUMPINPUT #ifdef AUDIO_DUMPINPUT
@@ -503,7 +505,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
// Ensure device exists // Ensure device exists
if (!mDevice) if (!mDevice)
{ {
setSimulate(true); setSimulate( true );
return false; return false;
} }
@@ -522,10 +524,9 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
try try
{ {
if (::WaitForSingleObject(mEventArray[mNextBuffer].hEventNotify, if (::WaitForSingleObject(mEventArray[mNextBuffer].hEventNotify, AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_LENGTH * 4) != WAIT_OBJECT_0)
AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_LENGTH * 4) != WAIT_OBJECT_0)
{ {
setSimulate(true); setSimulate( true );
return false; return false;
} }
@@ -533,7 +534,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
if (::WaitForMultipleObjects(AUDIO_MIC_BUFFER_COUNT, mEventSignals, TRUE, 0) != WAIT_TIMEOUT) if (::WaitForMultipleObjects(AUDIO_MIC_BUFFER_COUNT, mEventSignals, TRUE, 0) != WAIT_TIMEOUT)
{ {
// Possible overflow. Consider current buffer resulting. Reset ALL events. // Possible overflow. Consider current buffer resulting. Reset ALL events.
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++) for (int i = 0; i<AUDIO_MIC_BUFFER_COUNT; i++)
ResetEvent(mEventArray[i].hEventNotify); ResetEvent(mEventArray[i].hEventNotify);
} }
else else
@@ -542,12 +543,11 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
// Find the buffer start offset // Find the buffer start offset
mReadOffset = mNextBuffer * AUDIO_MIC_BUFFER_SIZE; mReadOffset = mNextBuffer * AUDIO_MIC_BUFFER_SIZE;
// increase the buffer's index //increase the buffer's index
if (++mNextBuffer == AUDIO_MIC_BUFFER_COUNT) if (++mNextBuffer == AUDIO_MIC_BUFFER_COUNT)
mNextBuffer = 0; mNextBuffer = 0;
LPVOID ptr1 = NULL, ptr2 = NULL; LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0;
DWORD len1 = 0, len2 = 0;
DSoundHelper::checkComResult(mBuffer->Lock(mReadOffset, AUDIO_MIC_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0)); DSoundHelper::checkComResult(mBuffer->Lock(mReadOffset, AUDIO_MIC_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
// Copy&Enqueue captured data to mQueue // Copy&Enqueue captured data to mQueue
@@ -574,9 +574,9 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
return true; return true;
} }
catch (...) catch(...)
{ {
setSimulate(true); setSimulate( true );
} }
return false; return false;
} }
@@ -585,7 +585,8 @@ void DSoundInputDevice::setSimulate(bool s)
{ {
if (!mSimulate && s) if (!mSimulate && s)
mNullAudio.start(); mNullAudio.start();
else if (mSimulate && !s) else
if (mSimulate && !s)
mNullAudio.stop(); mNullAudio.stop();
mSimulate = s; mSimulate = s;
@@ -598,7 +599,7 @@ Format DSoundInputDevice::getFormat()
int DSoundInputDevice::readBuffer(void* buffer) int DSoundInputDevice::readBuffer(void* buffer)
{ {
// Lock lock(mGuard); //Lock lock(mGuard);
if (mRefCount <= 0 || isSimulate()) if (mRefCount <= 0 || isSimulate())
return 0; return 0;
@@ -641,8 +642,10 @@ void DSoundInputDevice::threadProc(void* arg)
DSoundOutputDevice::DSoundOutputDevice(GUID deviceId) DSoundOutputDevice::DSoundOutputDevice(GUID deviceId)
: mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL), mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0), :mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL),
mThreadHandle(0), mSimulate(false), mGUID(deviceId), mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT) mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0),
mThreadHandle(0), mSimulate(false), mGUID(deviceId),
mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT)
{ {
gDSoundInit.load(); gDSoundInit.load();
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -697,7 +700,7 @@ void DSoundOutputDevice::openDevice()
ICELogInfo(<< "Open DirectSound audio output"); ICELogInfo(<< "Open DirectSound audio output");
if (IsEqualGUID(mGUID, GUID_NULL)) if (IsEqualGUID(mGUID, GUID_NULL))
{ {
setSimulate(true); setSimulate( true );
return; return;
} }
@@ -726,10 +729,10 @@ void DSoundOutputDevice::openDevice()
dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0; dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = NULL; //&wfx; dsbd.lpwfxFormat = NULL;//&wfx;
dsbd.guid3DAlgorithm = DS3DALG_DEFAULT; dsbd.guid3DAlgorithm = DS3DALG_DEFAULT;
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mPrimaryBuffer, NULL)); DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mPrimaryBuffer, NULL ));
DSBCAPS caps; DSBCAPS caps;
caps.dwSize = sizeof(caps); caps.dwSize = sizeof(caps);
caps.dwFlags = 0; caps.dwFlags = 0;
@@ -748,16 +751,14 @@ void DSoundOutputDevice::openDevice()
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL)); DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL));
// Fill the buffer with silence // Fill the buffer with silence
LPVOID ptr1 = NULL, ptr2 = NULL; LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0;
DWORD len1 = 0, len2 = 0; DSoundHelper::checkComResult(mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0));
DSoundHelper::checkComResult(
mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0));
if (len1 && ptr1) if (len1 && ptr1)
memset(ptr1, 0, len1); memset(ptr1, 0, len1);
if (len2 && ptr2) if (len2 && ptr2)
memset(ptr2, 0, len2); memset(ptr2, 0, len2);
DSoundHelper::checkComResult(mBuffer->Unlock(ptr1, len1, ptr2, len2)); DSoundHelper::checkComResult(mBuffer->Unlock(ptr1, len1, ptr2, len2));
DSoundHelper::checkComResult(mBuffer->Play(0, 0, DSBPLAY_LOOPING)); DSoundHelper::checkComResult(mBuffer->Play(0,0,DSBPLAY_LOOPING));
mBuffer->GetCurrentPosition(NULL, &mWriteCursor); mBuffer->GetCurrentPosition(NULL, &mWriteCursor);
} }
@@ -803,9 +804,8 @@ bool DSoundOutputDevice::getMediaFrame()
if (mConnection) if (mConnection)
mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame); mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame);
} }
catch (...) catch(...)
{ {}
}
return true; return true;
} }
@@ -833,8 +833,7 @@ bool DSoundOutputDevice::process()
offset %= mBufferSize; offset %= mBufferSize;
// See what we can write // See what we can write
LPVOID ptr1 = NULL, ptr2 = NULL; LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0;
DWORD len1 = 0, len2 = 0;
DSoundHelper::checkComResult(mBuffer->Lock(offset, AUDIO_SPK_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0)); DSoundHelper::checkComResult(mBuffer->Lock(offset, AUDIO_SPK_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
assert(ptr2 == NULL); assert(ptr2 == NULL);
@@ -885,12 +884,12 @@ void DSoundOutputDevice::threadProc(void* arg)
impl->restoreBuffer(); impl->restoreBuffer();
impl->process(); impl->process();
} }
catch (const Exception& e) catch(const Exception& e)
{ {
ICELogError(<< "DirectSound output failed with code = " << e.code() << ", subcode = " << e.subcode()); ICELogError(<< "DirectSound output failed with code = " << e.code() << ", subcode = " << e.subcode());
impl->setSimulate(true); impl->setSimulate(true);
} }
catch (...) catch(...)
{ {
ICELogError(<< "DirectSound output failed due to unexpected exception."); ICELogError(<< "DirectSound output failed due to unexpected exception.");
impl->setSimulate(true); impl->setSimulate(true);
@@ -926,9 +925,9 @@ bool DSoundOutputDevice::closing()
return false; return false;
} }
typedef WINUSERAPI HRESULT(WINAPI* LPFNDLLGETCLASSOBJECT)(const CLSID&, const IID&, void**); typedef WINUSERAPI HRESULT (WINAPI *LPFNDLLGETCLASSOBJECT) (const CLSID &, const IID &, void **);
HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET* ppKsPropertySet) HRESULT DirectSoundPrivateCreate (OUT LPKSPROPERTYSET * ppKsPropertySet)
{ {
HMODULE hLibDsound = NULL; HMODULE hLibDsound = NULL;
LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL; LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL;
@@ -939,48 +938,49 @@ HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET* ppKsPropertySet
// Load dsound.dll // Load dsound.dll
hLibDsound = LoadLibrary(TEXT("dsound.dll")); hLibDsound = LoadLibrary(TEXT("dsound.dll"));
if (!hLibDsound) if(!hLibDsound)
{ {
hr = DSERR_GENERIC; hr = DSERR_GENERIC;
} }
// Find DllGetClassObject // Find DllGetClassObject
if (SUCCEEDED(hr)) if(SUCCEEDED(hr))
{ {
pfnDllGetClassObject = (LPFNDLLGETCLASSOBJECT)GetProcAddress(hLibDsound, "DllGetClassObject"); pfnDllGetClassObject =
(LPFNDLLGETCLASSOBJECT)GetProcAddress ( hLibDsound, "DllGetClassObject" );
if (!pfnDllGetClassObject) if(!pfnDllGetClassObject)
{ {
hr = DSERR_GENERIC; hr = DSERR_GENERIC;
} }
} }
// Create a class factory object // Create a class factory object
if (SUCCEEDED(hr)) if(SUCCEEDED(hr))
{ {
hr = pfnDllGetClassObject(CLSID_DirectSoundPrivate, IID_IClassFactory, (LPVOID*)&pClassFactory); hr = pfnDllGetClassObject (CLSID_DirectSoundPrivate, IID_IClassFactory, (LPVOID *)&pClassFactory );
} }
// Create the DirectSoundPrivate object and query for an IKsPropertySet // Create the DirectSoundPrivate object and query for an IKsPropertySet
// interface // interface
if (SUCCEEDED(hr)) if(SUCCEEDED(hr))
{ {
hr = pClassFactory->CreateInstance(NULL, IID_IKsPropertySet, (LPVOID*)&pKsPropertySet); hr = pClassFactory->CreateInstance ( NULL, IID_IKsPropertySet, (LPVOID *)&pKsPropertySet );
} }
// Release the class factory // Release the class factory
if (pClassFactory) if(pClassFactory)
{ {
pClassFactory->Release(); pClassFactory->Release();
} }
// Handle final success or failure // Handle final success or failure
if (SUCCEEDED(hr)) if(SUCCEEDED(hr))
{ {
*ppKsPropertySet = pKsPropertySet; *ppKsPropertySet = pKsPropertySet;
} }
else if (pKsPropertySet) else if(pKsPropertySet)
{ {
pKsPropertySet->Release(); pKsPropertySet->Release();
} }
@@ -990,7 +990,7 @@ HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET* ppKsPropertySet
return hr; return hr;
} }
BOOL GetInfoFromDSoundGUID(GUID i_sGUID, int& dwWaveID) BOOL GetInfoFromDSoundGUID( GUID i_sGUID, int &dwWaveID)
{ {
LPKSPROPERTYSET pKsPropertySet = NULL; LPKSPROPERTYSET pKsPropertySet = NULL;
HRESULT hr; HRESULT hr;
@@ -999,34 +999,45 @@ BOOL GetInfoFromDSoundGUID(GUID i_sGUID, int& dwWaveID)
PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL; PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL;
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription; DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription;
memset(&sDirectSoundDeviceDescription, 0, sizeof(sDirectSoundDeviceDescription)); memset(&sDirectSoundDeviceDescription,0,sizeof(sDirectSoundDeviceDescription));
hr = DirectSoundPrivateCreate(&pKsPropertySet); hr = DirectSoundPrivateCreate( &pKsPropertySet );
if (SUCCEEDED(hr)) if(SUCCEEDED(hr))
{ {
ULONG ulBytesReturned = 0; ULONG ulBytesReturned = 0;
sDirectSoundDeviceDescription.DeviceId = i_sGUID; sDirectSoundDeviceDescription.DeviceId = i_sGUID;
// On the first call the final size is unknown so pass the size of the struct in order to receive // On the first call the final size is unknown so pass the size of the struct in order to receive
// "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings. // "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings.
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0, hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
&sDirectSoundDeviceDescription, sizeof(sDirectSoundDeviceDescription), DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
&ulBytesReturned); NULL,
0,
&sDirectSoundDeviceDescription,
sizeof(sDirectSoundDeviceDescription),
&ulBytesReturned
);
if (ulBytesReturned) if (ulBytesReturned)
{ {
// On the first call it notifies us of the required amount of memory in order to receive the strings. // On the first call it notifies us of the required amount of memory in order to receive the strings.
// Allocate the required memory, the strings will be pointed to the memory space directly after the struct. // Allocate the required memory, the strings will be pointed to the memory space directly after the struct.
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA) new BYTE[ulBytesReturned]; psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA)new BYTE[ulBytesReturned];
*psDirectSoundDeviceDescription = sDirectSoundDeviceDescription; *psDirectSoundDeviceDescription = sDirectSoundDeviceDescription;
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0, hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
psDirectSoundDeviceDescription, ulBytesReturned, &ulBytesReturned); DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
NULL,
0,
psDirectSoundDeviceDescription,
ulBytesReturned,
&ulBytesReturned
);
dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId; dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId;
/*Description = psDirectSoundDeviceDescription->Description; /*Description = psDirectSoundDeviceDescription->Description;
Module = psDirectSoundDeviceDescription->Module; Module = psDirectSoundDeviceDescription->Module;
Interface = psDirectSoundDeviceDescription->Interface;*/ Interface = psDirectSoundDeviceDescription->Interface;*/
delete[] psDirectSoundDeviceDescription; delete [] psDirectSoundDeviceDescription;
retval = TRUE; retval = TRUE;
} }
@@ -1042,10 +1053,16 @@ struct EnumResult
GUID mGuid; GUID mGuid;
}; };
BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext) BOOL CALLBACK DSEnumCallback(
LPGUID lpGuid,
LPCTSTR lpcstrDescription,
LPCTSTR lpcstrModule,
LPVOID lpContext
)
{ {
if (lpGuid) if (lpGuid)
{ {
int devId = -1; int devId = -1;
GetInfoFromDSoundGUID(*lpGuid, devId); GetInfoFromDSoundGUID(*lpGuid, devId);
EnumResult* er = (EnumResult*)lpContext; EnumResult* er = (EnumResult*)lpContext;
+9 -9
View File
@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -6,7 +6,7 @@
#ifndef __AUDIO_DSOUND_H #ifndef __AUDIO_DSOUND_H
#define __AUDIO_DSOUND_H #define __AUDIO_DSOUND_H
#include "../engine_config.h" #include "../config.h"
#include <winsock2.h> #include <winsock2.h>
#include <windows.h> #include <windows.h>
@@ -22,7 +22,7 @@
#include <EndpointVolume.h> #include <EndpointVolume.h>
#include <MMDeviceAPI.h> #include <MMDeviceAPI.h>
#if defined(_MSC_VER) #if defined(_MSC_VER)
#include <Functiondiscoverykeys_devpkey.h> # include <Functiondiscoverykeys_devpkey.h>
#endif #endif
#include <vector> #include <vector>
#include <string> #include <string>
@@ -31,7 +31,7 @@
namespace Audio namespace Audio
{ {
class VistaEnumerator : public Enumerator class VistaEnumerator: public Enumerator
{ {
public: public:
VistaEnumerator(); VistaEnumerator();
@@ -56,7 +56,7 @@ protected:
IMMDevice* mapIndexToInterface(int index); IMMDevice* mapIndexToInterface(int index);
}; };
class XpEnumerator : public Enumerator class XpEnumerator: public Enumerator
{ {
public: public:
XpEnumerator(); XpEnumerator();
@@ -83,10 +83,10 @@ public:
}; };
#if !defined(_MSC_VER) #if !defined(_MSC_VER)
typedef struct IDirectSoundNotify8* LPDIRECTSOUNDNOTIFY8; typedef struct IDirectSoundNotify8 *LPDIRECTSOUNDNOTIFY8;
#endif #endif
class DSoundInputDevice : public InputDevice class DSoundInputDevice: public InputDevice
{ {
public: public:
DSoundInputDevice(GUID deviceId); DSoundInputDevice(GUID deviceId);
@@ -136,7 +136,7 @@ protected:
static void threadProc(void* arg); static void threadProc(void* arg);
}; };
class DSoundOutputDevice : public OutputDevice class DSoundOutputDevice: public OutputDevice
{ {
public: public:
DSoundOutputDevice(GUID deviceId); DSoundOutputDevice(GUID deviceId);
@@ -182,6 +182,6 @@ protected:
static void threadProc(void* arg); static void threadProc(void* arg);
}; };
} // namespace Audio }
#endif #endif
+14 -10
View File
@@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef TARGET_WIN #ifdef TARGET_WIN
#include <WinSock2.h> # include <WinSock2.h>
#endif #endif
#include <assert.h> #include <assert.h>
@@ -29,6 +29,10 @@ TimeSource::TimeSource(int quantTime, int nrOfQuants)
mTailTime = 0; mTailTime = 0;
} }
TimeSource::~TimeSource()
{
}
void TimeSource::start() void TimeSource::start()
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
@@ -39,7 +43,9 @@ void TimeSource::start()
#endif #endif
} }
void TimeSource::stop() {} void TimeSource::stop()
{
}
unsigned TimeSource::time() unsigned TimeSource::time()
{ {
@@ -48,15 +54,15 @@ unsigned TimeSource::time()
if (!QueryPerformanceCounter(&c)) if (!QueryPerformanceCounter(&c))
throw Exception(ERR_QPC, GetLastError()); throw Exception(ERR_QPC, GetLastError());
// find the f //find the f
double f = (double)mFreq.QuadPart / 1000.0; double f = (double)mFreq.QuadPart / 1000.0;
// find the difference //find the difference
unsigned __int64 diff = c.QuadPart - mCounter.QuadPart; unsigned __int64 diff = c.QuadPart - mCounter.QuadPart;
mCounter.QuadPart = c.QuadPart; mCounter.QuadPart = c.QuadPart;
diff = (unsigned __int64)((double)diff / f + 0.5); // get ms diff = (unsigned __int64)((double)diff / f + 0.5); //get ms
diff += mTailTime; diff += mTailTime;
if (diff > mDepthTime) if (diff > mDepthTime)
@@ -66,8 +72,8 @@ unsigned TimeSource::time()
} }
else else
{ {
mTailTime = (unsigned)(diff % (unsigned __int64)mQuantTime); mTailTime = (unsigned )(diff % (unsigned __int64)mQuantTime);
unsigned int t = (unsigned)(diff / (unsigned __int64)mQuantTime); unsigned int t = (unsigned )(diff / (unsigned __int64)mQuantTime);
return t * mQuantTime; return t * mQuantTime;
} }
#endif #endif
@@ -96,13 +102,11 @@ unsigned TimeSource::time()
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
assert(0); assert(0);
#endif #endif
return 0;
} }
// --- StubTimer --- // --- StubTimer ---
StubTimer::StubTimer(int bufferTime, int bufferCount) StubTimer::StubTimer(int bufferTime, int bufferCount)
: mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false), :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);
+17 -17
View File
@@ -10,15 +10,15 @@
#include <EndpointVolume.h> #include <EndpointVolume.h>
#include <MMDeviceAPI.h> #include <MMDeviceAPI.h>
#if defined(_MSC_VER) #if defined(_MSC_VER)
#include <Functiondiscoverykeys_devpkey.h> # include <Functiondiscoverykeys_devpkey.h>
#endif #endif
#endif #endif
#if defined(TARGET_OSX) || defined(TARGET_IOS) #if defined(TARGET_OSX) || defined(TARGET_IOS)
#include <AudioUnit/AudioUnit.h> # include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioConverter.h> # include <AudioToolbox/AudioConverter.h>
#include <AudioToolbox/AudioServices.h> # include <AudioToolbox/AudioServices.h>
#include <mach/mach_time.h> # include <mach/mach_time.h>
#endif #endif
#include <vector> #include <vector>
@@ -27,9 +27,9 @@
namespace Audio namespace Audio
{ {
class TimeSource class TimeSource
{ {
protected: protected:
#ifdef TARGET_WIN #ifdef TARGET_WIN
LARGE_INTEGER mCounter; /// Current value from QPC. LARGE_INTEGER mCounter; /// Current value from QPC.
LARGE_INTEGER mFreq; /// Current frequency from QPC. LARGE_INTEGER mFreq; /// Current frequency from QPC.
@@ -45,25 +45,25 @@ protected:
unsigned mDepthTime; /// Number of available time quants. unsigned mDepthTime; /// Number of available time quants.
unsigned mTailTime; /// Not-accounted milliseconds. unsigned mTailTime; /// Not-accounted milliseconds.
public: public:
TimeSource(int quantTime, int nrOfQuants); TimeSource(int quantTime, int nrOfQuants);
~TimeSource() = default; ~TimeSource();
void start(); void start();
void stop(); void stop();
unsigned time(); unsigned time();
}; };
class StubTimer class StubTimer
{ {
public: public:
StubTimer(int bufferTime, int bufferCount); StubTimer(int bufferTime, int bufferCount);
~StubTimer(); ~StubTimer();
void start(); void start();
void stop(); void stop();
void waitForBuffer(); void waitForBuffer();
protected: protected:
unsigned mBufferTime; unsigned mBufferTime;
unsigned mBufferCount; unsigned mBufferCount;
unsigned mCurrentTime; unsigned mCurrentTime;
@@ -72,7 +72,7 @@ protected:
HANDLE mStubSignal; HANDLE mStubSignal;
#endif #endif
bool mActive; bool mActive;
}; };
} // namespace Audio }
#endif #endif
+38 -20
View File
@@ -7,16 +7,16 @@
#include "../helper/HL_OsVersion.h" #include "../helper/HL_OsVersion.h"
#if !defined(USE_NULL_AUDIO) #if !defined(USE_NULL_AUDIO)
#ifdef TARGET_WIN # ifdef TARGET_WIN
#include "Audio_Wmme.h" # include "Audio_Wmme.h"
#include "Audio_DirectSound.h" # include "Audio_DirectSound.h"
#endif # endif
#ifdef TARGET_OSX # ifdef TARGET_OSX
#include "Audio_CoreAudio.h" # include "Audio_CoreAudio.h"
#endif # endif
#ifdef TARGET_ANDROID # ifdef TARGET_ANDROID
#include "Audio_Android.h" # include "Audio_Android.h"
#endif # endif
#endif #endif
#include "Audio_Helper.h" #include "Audio_Helper.h"
@@ -24,9 +24,14 @@
using namespace Audio; using namespace Audio;
Device::Device() : mConnection(nullptr) {} Device::Device()
:mConnection(nullptr)
{
}
Device::~Device() {} Device::~Device()
{
}
void Device::setConnection(DataConnection* connection) void Device::setConnection(DataConnection* connection)
@@ -39,9 +44,13 @@ DataConnection* Device::connection()
return mConnection; return mConnection;
} }
InputDevice::InputDevice() {} InputDevice::InputDevice()
{
}
InputDevice::~InputDevice() {} InputDevice::~InputDevice()
{
}
InputDevice* InputDevice::make(int devId) InputDevice* InputDevice::make(int devId)
{ {
@@ -62,9 +71,13 @@ InputDevice* InputDevice::make(int devId)
return nullptr; return nullptr;
} }
OutputDevice::OutputDevice() {} OutputDevice::OutputDevice()
{
}
OutputDevice::~OutputDevice() {} OutputDevice::~OutputDevice()
{
}
OutputDevice* OutputDevice::make(int devId) OutputDevice* OutputDevice::make(int devId)
{ {
@@ -72,7 +85,7 @@ OutputDevice* OutputDevice::make(int devId)
return new NullOutputDevice(); return new NullOutputDevice();
#else #else
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
// return new WmmeOutputDevice(index); //return new WmmeOutputDevice(index);
return new DSoundOutputDevice(DSoundHelper::deviceId2Guid(devId, false)); return new DSoundOutputDevice(DSoundHelper::deviceId2Guid(devId, false));
#endif #endif
#ifdef TARGET_OSX #ifdef TARGET_OSX
@@ -87,9 +100,13 @@ OutputDevice* OutputDevice::make(int devId)
// --- Enumerator --- // --- Enumerator ---
Enumerator::Enumerator() {} Enumerator::Enumerator()
{
}
Enumerator::~Enumerator() {} Enumerator::~Enumerator()
{
}
int Enumerator::nameToIndex(const std::tstring& name) int Enumerator::nameToIndex(const std::tstring& name)
{ {
@@ -102,6 +119,7 @@ int Enumerator::nameToIndex(const std::tstring& name)
Enumerator* Enumerator::make(bool useNull) Enumerator* Enumerator::make(bool useNull)
{ {
if (useNull) if (useNull)
return new NullEnumerator(); return new NullEnumerator();
#ifndef USE_NULL_AUDIO #ifndef USE_NULL_AUDIO
@@ -129,7 +147,7 @@ OsEngine* OsEngine::instance()
#ifdef TARGET_ANDROID #ifdef TARGET_ANDROID
return nullptr; // As we use Oboe library for now return nullptr; // As we use Oboe library for now
// return &OpenSLEngine::instance(); //return &OpenSLEngine::instance();
#endif #endif
return nullptr; return nullptr;
+53 -51
View File
@@ -7,7 +7,7 @@
#define __AUDIO_INTERFACE_H #define __AUDIO_INTERFACE_H
#include <string> #include <string>
#include "../engine_config.h" #include "../config.h"
#include "../helper/HL_Types.h" #include "../helper/HL_Types.h"
#include "../helper/HL_VariantMap.h" #include "../helper/HL_VariantMap.h"
#include "../helper/HL_Pointer.h" #include "../helper/HL_Pointer.h"
@@ -16,57 +16,60 @@
namespace Audio namespace Audio
{ {
enum enum
{ {
myMicrophone = 1, myMicrophone = 1,
mySpeaker = 2 mySpeaker = 2
}; };
struct Format struct Format
{ {
int mRate; int mRate;
int mChannels; int mChannels;
Format() : mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS) {} Format()
:mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS)
{}
Format(int rate, int channels) : mRate(rate), mChannels(channels) {} Format(int rate, int channels)
:mRate(rate), mChannels(channels)
{}
size_t samplesFromSize(size_t length) const { return length / 2 / mChannels; } size_t samplesFromSize(size_t length) const
{
return length / 2 / mChannels;
}
// Returns milliseconds // Returns milliseconds
float timeFromSize(size_t length) const { return float(samplesFromSize(length) / (mRate / 1000.0)); } float timeFromSize(size_t length) const
{
return float(samplesFromSize(length) / (mRate / 1000.0));
}
float sizeFromTime(size_t milliseconds) const { return float((milliseconds * mRate) / 500.0 * mChannels); } float sizeFromTime(size_t milliseconds) const
{
size_t sizeFromTime(std::chrono::milliseconds ms) const { return sizeFromTime(ms.count()); } return float((milliseconds * mRate) / 500.0 * mChannels);
}
std::string toString() std::string toString()
{ {
char buffer[64]; char buffer[64];
std::snprintf(buffer, sizeof(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; } class DataConnection
{
bool operator!=(const Format& rhs) const { return mRate != rhs.mRate || mChannels != rhs.mChannels; } public:
int rate() const { return mRate; }
int channels() const { return mChannels; }
};
class DataConnection
{
public:
virtual void onMicData(const Format& format, const void* buffer, int length) = 0; virtual void onMicData(const Format& format, const void* buffer, int length) = 0;
virtual void onSpkData(const Format& format, void* buffer, int length) = 0; virtual void onSpkData(const Format& format, void* buffer, int length) = 0;
}; };
class Device class Device
{ {
public: public:
Device(); Device();
virtual ~Device(); virtual ~Device();
@@ -76,35 +79,34 @@ public:
virtual bool open() = 0; virtual bool open() = 0;
virtual void close() = 0; virtual void close() = 0;
virtual Format getFormat() = 0; virtual Format getFormat() = 0;
protected:
protected:
DataConnection* mConnection; DataConnection* mConnection;
}; };
class InputDevice : public Device class InputDevice: public Device
{ {
public: public:
InputDevice(); InputDevice();
virtual ~InputDevice(); virtual ~InputDevice();
static InputDevice* make(int devId); static InputDevice* make(int devId);
}; };
typedef std::shared_ptr<InputDevice> PInputDevice; typedef std::shared_ptr<InputDevice> PInputDevice;
class OutputDevice : public Device class OutputDevice: public Device
{ {
public: public:
OutputDevice(); OutputDevice();
virtual ~OutputDevice(); virtual ~OutputDevice();
static OutputDevice* make(int devId); static OutputDevice* make(int devId);
}; };
typedef std::shared_ptr<OutputDevice> POutputDevice; typedef std::shared_ptr<OutputDevice> POutputDevice;
class Enumerator class Enumerator
{ {
public: public:
Enumerator(); Enumerator();
virtual ~Enumerator(); virtual ~Enumerator();
int nameToIndex(const std::tstring& name); int nameToIndex(const std::tstring& name);
@@ -118,16 +120,16 @@ public:
virtual int indexOfDefaultDevice() = 0; virtual int indexOfDefaultDevice() = 0;
static Enumerator* make(bool useNull = false); static Enumerator* make(bool useNull = false);
}; };
class OsEngine class OsEngine
{ {
public: public:
virtual void open() = 0; virtual void open() = 0;
virtual void close() = 0; virtual void close() = 0;
static OsEngine* instance(); static OsEngine* instance();
};
}; };
}; // namespace Audio
#endif #endif
+36 -49
View File
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../engine_config.h" #include "../config.h"
#include "../helper/HL_Exception.h" #include "../helper/HL_Exception.h"
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
@@ -12,7 +12,7 @@
#include "Audio_Mixer.h" #include "Audio_Mixer.h"
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "Mixer"
using namespace Audio; using namespace Audio;
Mixer::Stream::Stream() Mixer::Stream::Stream()
@@ -28,7 +28,9 @@ Mixer::Stream::Stream()
mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT); mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT);
} }
Mixer::Stream::~Stream() {} Mixer::Stream::~Stream()
{
}
void Mixer::Stream::setSsrc(unsigned ssrc) void Mixer::Stream::setSsrc(unsigned ssrc)
{ {
@@ -71,9 +73,7 @@ void Mixer::Stream::addPcm(int rate, const void* input, int length)
if (mTempBuffer.size() < outputSize) if (mTempBuffer.size() < outputSize)
mTempBuffer.resize(outputSize); mTempBuffer.resize(outputSize);
Resampler* resampler = (rate == 8000) Resampler* resampler = (rate == 8000) ? &mResampler8 : ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
? &mResampler8
: ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
size_t inputProcessed = 0; size_t inputProcessed = 0;
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize); resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize);
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length // inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length
@@ -88,12 +88,13 @@ Mixer::Mixer()
mOutput.setCapacity(32768); mOutput.setCapacity(32768);
} }
Mixer::~Mixer() {} Mixer::~Mixer()
{
}
void Mixer::unregisterChannel(void* channel) void Mixer::unregisterChannel(void* channel)
{ {
Lock l(mMutex); for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
{ {
Stream& c = mChannelList[i]; Stream& c = mChannelList[i];
if (c.active() && c.context() == channel) if (c.active() && c.context() == channel)
@@ -107,8 +108,7 @@ void Mixer::unregisterChannel(void* channel)
void Mixer::clear(void* context, unsigned ssrc) void Mixer::clear(void* context, unsigned ssrc)
{ {
Lock l(mMutex); for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
{ {
Stream& c = mChannelList[i]; Stream& c = mChannelList[i];
if (c.active() && c.context() == context && c.ssrc() == ssrc) if (c.active() && c.context() == context && c.ssrc() == ssrc)
@@ -125,7 +125,7 @@ Mixer::Stream* Mixer::allocateChannel(void* context, unsigned ssrc)
// Allocate new channel // Allocate new channel
Lock l(mMutex); Lock l(mMutex);
Stream* channel; Stream* channel;
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++) for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT;i++)
{ {
channel = &mChannelList[i]; channel = &mChannelList[i];
if (!channel->active()) if (!channel->active())
@@ -141,17 +141,18 @@ Mixer::Stream* Mixer::allocateChannel(void* context, unsigned ssrc)
return NULL; return NULL;
} }
void Mixer::addPcm(void* context, unsigned ssrc, const void* inputData, int inputLength, int inputRate, bool fadeOut) void Mixer::addPcm(void* context, unsigned ssrc,
const void* inputData, int inputLength,
int inputRate, bool fadeOut)
{ {
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000); assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
Lock l(mMutex);
int i; int i;
// Locate a channel // Locate a channel
Stream* channel = NULL; Stream* channel = NULL;
for (i = 0; i < AUDIO_MIX_CHANNEL_COUNT && !channel; i++) for (i=0; i<AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
{ {
Stream& c = mChannelList[i]; Stream& c = mChannelList[i];
if (c.active() && c.context() == context && c.ssrc() == ssrc) if (c.active() && c.context() == context && c.ssrc() == ssrc)
@@ -171,13 +172,12 @@ void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate,
{ {
assert(rate == 8000 || rate == 16000 || rate == 32000 || rate == 48000); assert(rate == 8000 || rate == 16000 || rate == 32000 || rate == 48000);
Lock l(mMutex);
int i; int i;
// Locate a channel // Locate a channel
Stream* channel = NULL; Stream* channel = NULL;
for (i = 0; i < AUDIO_MIX_CHANNEL_COUNT && !channel; i++) for (i=0; i<AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
{ {
Stream& c = mChannelList[i]; Stream& c = mChannelList[i];
if (c.active() && c.context() == context && c.ssrc() == ssrc) if (c.active() && c.context() == context && c.ssrc() == ssrc)
@@ -191,13 +191,11 @@ void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate,
} }
channel->addPcm(rate, w.data(), w.filled()); channel->addPcm(rate, w.data(), w.filled());
// ICELogSpecial(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes"); //ICELogSpecial(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes");
} }
void Mixer::mix() void Mixer::mix()
{ {
Lock l(mMutex);
// Current sample // Current sample
int sample = 0; int sample = 0;
@@ -213,7 +211,7 @@ void Mixer::mix()
// Build active channel map // Build active channel map
Stream* channelList[AUDIO_MIX_CHANNEL_COUNT]; Stream* channelList[AUDIO_MIX_CHANNEL_COUNT];
int activeCounter = 0; int activeCounter = 0;
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++) for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
if (mChannelList[i].active()) if (mChannelList[i].active())
channelList[activeCounter++] = &mChannelList[i]; channelList[activeCounter++] = &mChannelList[i];
@@ -235,9 +233,10 @@ void Mixer::mix()
// Erase copied audio samples // 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());
} }
else if (activeCounter == 2) else
if (activeCounter == 2)
{ {
Stream& audio1 = *channelList[0]; Stream& audio1 = *channelList[0];
Stream& audio2 = *channelList[1]; Stream& audio2 = *channelList[1];
@@ -252,7 +251,7 @@ void Mixer::mix()
available = maxsize - filled; available = maxsize - filled;
short sample = 0; short sample = 0;
for (int i = 0; i < available; i++) for (int i=0; i<available; i++)
{ {
short sample1 = filled1 > i ? audio1.data().shortAt(i) : 0; short sample1 = filled1 > i ? audio1.data().shortAt(i) : 0;
short sample2 = filled2 > i ? audio2.data().shortAt(i) : 0; short sample2 = filled2 > i ? audio2.data().shortAt(i) : 0;
@@ -260,8 +259,8 @@ void Mixer::mix()
mOutput.add(sample); mOutput.add(sample);
} }
audio1.data().erase(available * 2); audio1.data().erase(available*2);
audio2.data().erase(available * 2); audio2.data().erase(available*2);
} }
else else
{ {
@@ -270,7 +269,7 @@ void Mixer::mix()
sample = 0; sample = 0;
sourceCounter = 0; sourceCounter = 0;
processed = 0; processed = 0;
for (int i = 0; i < activeCounter; i++) for (int i=0; i<activeCounter; i++)
{ {
Stream& audio = *channelList[i]; Stream& audio = *channelList[i];
processed++; processed++;
@@ -296,13 +295,14 @@ void Mixer::mix()
mOutput.add(outputBuffer, outputCounter * 2); mOutput.add(outputBuffer, outputCounter * 2);
outputCounter = 0; outputCounter = 0;
} }
} while (sourceCounter); }
while (sourceCounter);
processed = 0; processed = 0;
for (int i = 0; i < activeCounter; i++) for (int i=0; i<activeCounter; i++)
{ {
Stream& audio = *channelList[i]; Stream& audio = *channelList[i];
audio.data().erase(sampleCounter * 2); audio.data().erase(sampleCounter*2);
} }
} }
} }
@@ -310,41 +310,28 @@ void Mixer::mix()
int Mixer::getPcm(void* outputData, int outputLength) int Mixer::getPcm(void* outputData, int outputLength)
{ {
Lock l(mMutex);
if (mOutput.filled() < outputLength) if (mOutput.filled() < outputLength)
mix(); mix();
// ICELogSpecial(<<"Mixer has " << mOutput.filled() << " available bytes"); //ICELogSpecial(<<"Mixer has " << mOutput.filled() << " available bytes");
memset(outputData, 0, outputLength); memset(outputData, 0, outputLength);
return mOutput.read(outputData, outputLength); return mOutput.read(outputData, outputLength);
} }
int Mixer::mixAndGetPcm(Audio::DataWindow& output) int Mixer::mixAndGetPcm(Audio::DataWindow& output)
{ {
Lock l(mMutex);
// Mix // Mix
mix(); mix();
size_t avail = mOutput.filled(); // Set output space
if (!avail) output.setCapacity(mOutput.filled());
{
output.setFilled(0);
return 0;
}
// Make sure output has enough space (setCapacity only ever grows the window) // Read mixed data to output
if (output.capacity() < avail) return mOutput.read(output.mutableData(), output.capacity());
output.setCapacity(avail);
// Read mixed data to output and publish the real byte count
size_t got = mOutput.read(output.mutableData(), avail);
output.setFilled(got);
return static_cast<int>(got);
} }
int Mixer::available() int Mixer::available()
{ {
return mOutput.filled(); return mOutput.filled();
} }
+11 -8
View File
@@ -6,7 +6,7 @@
#ifndef _RX_MIXER_H #ifndef _RX_MIXER_H
#define _RX_MIXER_H #define _RX_MIXER_H
#include "../engine_config.h" #include "../config.h"
#include "../helper/HL_ByteBuffer.h" #include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include "Audio_Resampler.h" #include "Audio_Resampler.h"
@@ -16,14 +16,17 @@
namespace Audio namespace Audio
{ {
class Mixer class Mixer
{ {
protected: protected:
class Stream class Stream
{ {
protected: protected:
DataWindow mData; DataWindow mData;
Resampler mResampler8, mResampler16, mResampler32, mResampler48; Resampler mResampler8,
mResampler16,
mResampler32,
mResampler48;
bool mActive; bool mActive;
void* mContext; void* mContext;
unsigned mSSRC; unsigned mSSRC;
@@ -52,7 +55,7 @@ protected:
void mix(); void mix();
Stream* allocateChannel(void* context, unsigned ssrc); Stream* allocateChannel(void* context, unsigned ssrc);
public: public:
Mixer(); Mixer();
~Mixer(); ~Mixer();
@@ -63,7 +66,7 @@ public:
int getPcm(void* outputData, int outputLength); int getPcm(void* outputData, int outputLength);
int mixAndGetPcm(Audio::DataWindow& output); int mixAndGetPcm(Audio::DataWindow& output);
int available(); int available();
}; };
} // namespace Audio } //end of namespace
#endif #endif
+40 -49
View File
@@ -1,14 +1,12 @@
#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 "audio"
using namespace Audio; using namespace Audio;
using namespace std::chrono_literals;
NullTimer::NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name) NullTimer::NullTimer(int interval, Delegate *delegate, const char* name)
: mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name) :mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
{ {
start(); start();
} }
@@ -33,70 +31,62 @@ void NullTimer::stop()
void NullTimer::run() void NullTimer::run()
{ {
mTail = 0us; mTail = 0;
while (!mShutdown) while (!mShutdown)
{ {
// Get current timestamp // Get current timestamp
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now(); std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
while (mTail >= mInterval) while (mTail >= mInterval * 1000)
{ {
if (mDelegate) if (mDelegate)
mDelegate->onTimerSignal(*this); mDelegate->onTimerSignal(*this);
mTail -= mInterval; mTail -= mInterval * 1000;
} }
// Sleep for mInterval - mTail milliseconds // Sleep for mInterval - mTail milliseconds
std::this_thread::sleep_for(mInterval - mTail); std::this_thread::sleep_for(std::chrono::microseconds(mInterval * 1000 - mTail));
mTail = mTail += (int)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp).count();
mTail + std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp);
} }
} }
// --------------------- NullInputDevice ------------------------- // --------------------- NullInputDevice -------------------------
NullInputDevice::NullInputDevice() : mBuffer(nullptr) {} NullInputDevice::NullInputDevice()
:mBuffer(nullptr)
{
}
NullInputDevice::~NullInputDevice() NullInputDevice::~NullInputDevice()
{ {
internalClose(); close();
} }
bool NullInputDevice::open() bool NullInputDevice::open()
{ {
ICELogInfo(<< "Starting NullInputDevice for " << AUDIO_MIC_BUFFER_LENGTH << "ms buffers");
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE); mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE); memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
mTimeCounter = 0; mTimeCounter = 0; mDataCounter = 0;
mDataCounter = 0;
// Creation of timer starts it also. So first onTimerSignal can come even before open() returns. // Creation of timer starts it also. So first onTimerSignal can come even before open() returns.
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_MIC_BUFFER_LENGTH), this, "null_mic"); mTimer = std::make_shared<NullTimer>(AUDIO_MIC_BUFFER_LENGTH, this, "NullMicrophoneThread");
return true; return true;
} }
void NullInputDevice::internalClose() void NullInputDevice::close()
{ {
ICELogInfo(<< "Stopping NullInputDevice");
mTimer.reset(); mTimer.reset();
if (mBuffer) if (mBuffer)
{ {
free(mBuffer); free(mBuffer);
mBuffer = nullptr; mBuffer = nullptr;
} }
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);
return Format();
return {}; // Return library-define default format
} }
void NullInputDevice::onTimerSignal(NullTimer& timer) void NullInputDevice::onTimerSignal(NullTimer& timer)
@@ -108,44 +98,40 @@ void NullInputDevice::onTimerSignal(NullTimer& timer)
} }
// --------------------- NullOutputDevice -------------------------- // --------------------- NullOutputDevice --------------------------
NullOutputDevice::NullOutputDevice() : mBuffer(nullptr) {} NullOutputDevice::NullOutputDevice()
:mBuffer(nullptr)
{
}
NullOutputDevice::~NullOutputDevice() NullOutputDevice::~NullOutputDevice()
{ {
internalClose(); close();
} }
bool NullOutputDevice::open() bool NullOutputDevice::open()
{ {
mTimeCounter = 0; mTimeCounter = 0; mDataCounter = 0;
mDataCounter = 0;
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE); mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
// Creation of timer starts it also. So first onSpkData() can come before open() returns even. // Creation of timer starts it also. So first onSpkData() can come before open() returns even.
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_SPK_BUFFER_LENGTH), this, "null_spk"); mTimer = std::make_shared<NullTimer>(AUDIO_SPK_BUFFER_LENGTH, this, "NullSpeakerThread");
return true; return true;
} }
void NullOutputDevice::internalClose()
{
mTimer.reset();
free(mBuffer);
mBuffer = nullptr;
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
}
void NullOutputDevice::close() void NullOutputDevice::close()
{ {
internalClose(); mTimer.reset();
free(mBuffer); mBuffer = nullptr;
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
} }
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);
return Format(); return Format();
} }
void NullOutputDevice::onTimerSignal(NullTimer& timer) void NullOutputDevice::onTimerSignal(NullTimer &timer)
{ {
mTimeCounter += AUDIO_SPK_BUFFER_LENGTH; mTimeCounter += AUDIO_SPK_BUFFER_LENGTH;
mDataCounter += AUDIO_SPK_BUFFER_SIZE; mDataCounter += AUDIO_SPK_BUFFER_SIZE;
@@ -154,13 +140,17 @@ void NullOutputDevice::onTimerSignal(NullTimer& timer)
} }
// ---------------------- NullEnumerator -------------------------- // ---------------------- NullEnumerator --------------------------
NullEnumerator::NullEnumerator() {} NullEnumerator::NullEnumerator()
{}
NullEnumerator::~NullEnumerator() {} NullEnumerator::~NullEnumerator()
{}
void NullEnumerator::open(int direction) {} void NullEnumerator::open(int direction)
{}
void NullEnumerator::close() {} void NullEnumerator::close()
{}
int NullEnumerator::count() int NullEnumerator::count()
{ {
@@ -185,3 +175,4 @@ int NullEnumerator::indexOfDefaultDevice()
{ {
return 0; return 0;
} }
+26 -33
View File
@@ -2,47 +2,43 @@
#define __AUDIO_NULL_H #define __AUDIO_NULL_H
#include <thread> #include <thread>
#include <chrono>
#include "Audio_Interface.h" #include "Audio_Interface.h"
namespace Audio namespace Audio
{ {
class NullTimer class NullTimer
{ {
public: public:
class Delegate class Delegate
{ {
public: public:
virtual void onTimerSignal(NullTimer& timer) = 0; virtual void onTimerSignal(NullTimer& timer) = 0;
}; };
protected: protected:
std::thread mWorkerThread; std::thread mWorkerThread;
std::atomic_bool mShutdown = {false}; volatile bool mShutdown;
Delegate* mDelegate = nullptr; Delegate* mDelegate;
std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds int mInterval, // Interval - wanted number of milliseconds
std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink mTail; // Number of milliseconds that can be sent immediately to sink
std::string mThreadName; std::string mThreadName;
void start(); void start();
void stop(); void stop();
void run(); void run();
public:
public:
/* Interval is in milliseconds. */ /* Interval is in milliseconds. */
NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name = nullptr); NullTimer(int interval, Delegate* delegate, const char* name = nullptr);
~NullTimer(); ~NullTimer();
}; };
class NullInputDevice : public InputDevice, public NullTimer::Delegate class NullInputDevice: public InputDevice, public NullTimer::Delegate
{ {
protected: protected:
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();
@@ -51,18 +47,15 @@ public:
Format getFormat() override; Format getFormat() override;
void onTimerSignal(NullTimer& timer) override; void onTimerSignal(NullTimer& timer) override;
}; };
class NullOutputDevice : public OutputDevice, public NullTimer::Delegate class NullOutputDevice: public OutputDevice, public NullTimer::Delegate
{ {
protected: protected:
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;
public:
void internalClose();
public:
NullOutputDevice(); NullOutputDevice();
virtual ~NullOutputDevice(); virtual ~NullOutputDevice();
@@ -71,11 +64,11 @@ public:
Format getFormat() override; Format getFormat() override;
void onTimerSignal(NullTimer& timer) override; void onTimerSignal(NullTimer& timer) override;
}; };
class NullEnumerator : public Enumerator class NullEnumerator: public Enumerator
{ {
public: public:
NullEnumerator(); NullEnumerator();
~NullEnumerator(); ~NullEnumerator();
@@ -86,8 +79,8 @@ public:
std::tstring nameAt(int index) override; std::tstring nameAt(int index) override;
int idAt(int index) override; int idAt(int index) override;
int indexOfDefaultDevice() override; int indexOfDefaultDevice() override;
};
} // namespace Audio };
}
#endif #endif
+12 -7
View File
@@ -7,13 +7,18 @@
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "Player"
using namespace Audio; using namespace Audio;
// -------------- Player ----------- // -------------- Player -----------
Player::Player() : mDelegate(nullptr), mPlayedTime(0) {} Player::Player()
:mDelegate(nullptr), mPlayedTime(0)
{
}
Player::~Player() {} Player::~Player()
{
}
void Player::setDelegate(EndOfAudioDelegate* d) void Player::setDelegate(EndOfAudioDelegate* d)
{ {
@@ -63,15 +68,15 @@ void Player::onSpkData(const Format& f, void* buffer, int length)
continue; continue;
} }
int wasread = item.mFile->read((char*)buffer + produced, length - produced); int wasread = item.mFile->read((char*)buffer+produced, length-produced);
mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND; mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND;
produced += wasread; produced += wasread;
if (wasread < length - produced) if (wasread < length-produced)
{ {
if (item.mLoop) if (item.mLoop)
{ {
item.mFile->rewind(); item.mFile->rewind();
wasread = item.mFile->read((char*)buffer + produced, (length - produced)); wasread = item.mFile->read((char*)buffer+produced, (length - produced));
mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND; mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND;
produced += wasread; produced += wasread;
} }
@@ -119,7 +124,7 @@ void Player::release(int usage)
if (!usageIter->second) if (!usageIter->second)
mUsage.erase(usageIter); mUsage.erase(usageIter);
for (unsigned i = 0; i < mPlaylist.size(); i++) for (unsigned i=0; i<mPlaylist.size(); i++)
if (mPlaylist[i].mUsageId == usage) if (mPlaylist[i].mUsageId == usage)
mPlaylist.erase(mPlaylist.begin() + i); mPlaylist.erase(mPlaylist.begin() + i);
+7 -8
View File
@@ -16,11 +16,10 @@
namespace Audio namespace Audio
{ {
class Player : public DataConnection class Player: public DataConnection
{ {
friend class DevicePair; friend class DevicePair;
public:
public:
struct PlaylistItem struct PlaylistItem
{ {
PWavFileReader mFile; PWavFileReader mFile;
@@ -36,7 +35,7 @@ public:
virtual void onFilePlayed(PlaylistItem& item) = 0; virtual void onFilePlayed(PlaylistItem& item) = 0;
}; };
protected: protected:
typedef std::map<int, int> UsageMap; typedef std::map<int, int> UsageMap;
Audio::POutputDevice mOutput; Audio::POutputDevice mOutput;
UsageMap mUsage; // References map UsageMap mUsage; // References map
@@ -52,7 +51,7 @@ protected:
void onFilePlayed(); void onFilePlayed();
void obtain(int usageId); void obtain(int usageId);
public: public:
Player(); Player();
~Player(); ~Player();
@@ -67,6 +66,6 @@ public:
void clear(); void clear();
int releasePlayed(); int releasePlayed();
void retrieveUsageIds(std::vector<int>& ids); void retrieveUsageIds(std::vector<int>& ids);
}; };
} // namespace Audio }
#endif #endif
+33 -32
View File
@@ -3,29 +3,26 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../engine_config.h" #include "../config.h"
#include "Audio_Quality.h" #include "Audio_Quality.h"
#include "../helper/HL_Exception.h" #include "../helper/HL_Exception.h"
#include "../helper/HL_Types.h" #include "../helper/HL_Types.h"
#include "speex/speex_preprocess.h" #include "speex/speex_preprocess.h"
#ifdef WIN32 #ifdef WIN32
#include <malloc.h> # include <malloc.h>
#endif #endif
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
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;
for (int i = 0; i < channels; i++) for (int i=0; i<channels; i++)
{ {
Channel c; Channel c;
float level = DefaultLevel; float level = DefaultLevel;
@@ -34,7 +31,8 @@ AgcFilter::AgcFilter(int channels)
c.mIgain = 65536; c.mIgain = 65536;
if (level > 1.0f) if (level > 1.0f)
level = 1.0f; level = 1.0f;
else if (level < 0.5f) else
if (level < 0.5f)
level = 0.5f; level = 0.5f;
c.mIpeak = (int)(SHRT_MAX * level * 65536); c.mIpeak = (int)(SHRT_MAX * level * 65536);
@@ -44,11 +42,13 @@ AgcFilter::AgcFilter(int channels)
} }
} }
AgcFilter::~AgcFilter() {} AgcFilter::~AgcFilter()
void AgcFilter::process(void* pcm, int length)
{ {
for (size_t i = 0; i < mChannelList.size(); i++) }
void AgcFilter::process(void *pcm, int length)
{
for (size_t i=0; i<mChannelList.size(); i++)
processChannel((short*)pcm, length / (sizeof(short) * mChannelList.size()), i); processChannel((short*)pcm, length / (sizeof(short) * mChannelList.size()), i);
} }
@@ -56,7 +56,7 @@ void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex)
{ {
int i; int i;
for (i = 0; i < nrOfSamples; i++) for(i=0; i<nrOfSamples; i++)
{ {
long gain_new; long gain_new;
int sample; int sample;
@@ -65,14 +65,14 @@ void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex)
/* get the abs of buffer[i] */ /* get the abs of buffer[i] */
sample = pcm[sampleIndex]; sample = pcm[sampleIndex];
sample = (sample < 0 ? -(sample) : sample); sample = (sample < 0 ? -(sample):sample);
if (sample > (int)channel.mSampleMax) if(sample > (int)channel.mSampleMax)
{ {
/* update the max */ /* update the max */
channel.mSampleMax = (unsigned int)sample; channel.mSampleMax = (unsigned int)sample;
} }
channel.mCounter++; channel.mCounter ++;
/* Will we get an overflow with the current gain factor? */ /* Will we get an overflow with the current gain factor? */
if (((sample * channel.mIgain) >> 16) > channel.mIpeak) if (((sample * channel.mIgain) >> 16) > channel.mIpeak)
@@ -80,7 +80,7 @@ void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex)
/* Yes: Calculate new gain. */ /* Yes: Calculate new gain. */
channel.mIgain = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16; channel.mIgain = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16;
channel.mSilenceCounter = 0; channel.mSilenceCounter = 0;
pcm[sampleIndex] = (short)((pcm[sampleIndex] * channel.mIgain) >> 16); pcm[sampleIndex] = (short) ((pcm[sampleIndex] * channel.mIgain) >> 16);
continue; continue;
} }
@@ -109,23 +109,23 @@ void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex)
channel.mCounter = 0; channel.mCounter = 0;
channel.mSampleMax = 1; channel.mSampleMax = 1;
} }
pcm[sampleIndex] = (short)((pcm[sampleIndex] * channel.mIgain) >> 16); pcm[sampleIndex] = (short) ((pcm[sampleIndex] * channel.mIgain) >> 16);
} }
} }
// --- AecFilter --- // --- AecFilter ---
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
#include "speex/speex_echo.h" # include "speex/speex_echo.h"
#include "Audio_Interface.h" #include "Audio_Interface.h"
#if !defined(TARGET_WIN) #if !defined(TARGET_WIN)
#include <alloca.h> # include <alloca.h>
#endif #endif
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
#include "aec/echo_cancellation.h" # include "aec/echo_cancellation.h"
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
@@ -136,12 +136,12 @@ static void CheckWRACode(unsigned errorcode)
} }
#endif #endif
AecFilter::AecFilter(int tailTime, int frameTime, int rate) : mCtx(nullptr), mFrameTime(frameTime), mRate(rate) AecFilter::AecFilter(int tailTime, int frameTime, int rate)
:mCtx(nullptr), mFrameTime(frameTime), mRate(rate)
{ {
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
if (AUDIO_CHANNELS == 2) if (AUDIO_CHANNELS == 2)
mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS, mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS, AUDIO_CHANNELS );
AUDIO_CHANNELS);
else else
mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000)); mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000));
int tmp = rate; int tmp = rate;
@@ -159,7 +159,7 @@ AecFilter::~AecFilter()
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
if (mCtx) if (mCtx)
{ {
// speex_echo_state_destroy((SpeexEchoState*)mCtx); //speex_echo_state_destroy((SpeexEchoState*)mCtx);
mCtx = nullptr; mCtx = nullptr;
} }
#endif #endif
@@ -170,7 +170,7 @@ AecFilter::~AecFilter()
#endif #endif
} }
void AecFilter::fromMic(void* data) void AecFilter::fromMic(void *data)
{ {
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
short* output = (short*)alloca(Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH)); short* output = (short*)alloca(Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
@@ -180,13 +180,12 @@ void AecFilter::fromMic(void* data)
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
short* inputframe = (short*)ALLOCA(framesize); short* inputframe = (short*)ALLOCA(framesize);
memcpy(inputframe, (char*)data + framesize * i, framesize); memcpy(inputframe, (char*)data+framesize*i, framesize);
CheckWRACode(WebRtcAec_Process(mCtx, (short*)inputframe, NULL, (short*)data + framesize / 2 * i, NULL, CheckWRACode(WebRtcAec_Process(mCtx, (short*)inputframe, NULL, (short*)data+framesize/2*i, NULL, mFrameTime * mRate / 1000, 0,0));
mFrameTime * mRate / 1000, 0, 0));
#endif #endif
} }
void AecFilter::toSpeaker(void* data) void AecFilter::toSpeaker(void *data)
{ {
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
speex_echo_playback((SpeexEchoState*)mCtx, (short*)data); speex_echo_playback((SpeexEchoState*)mCtx, (short*)data);
@@ -203,9 +202,10 @@ int AecFilter::frametime()
} }
DenoiseFilter::DenoiseFilter(int rate) : mRate(rate) DenoiseFilter::DenoiseFilter(int rate)
:mRate(rate)
{ {
mCtx = speex_preprocess_state_init(mRate / 100, mRate); mCtx = speex_preprocess_state_init(mRate/100, mRate);
} }
DenoiseFilter::~DenoiseFilter() DenoiseFilter::~DenoiseFilter()
@@ -221,7 +221,7 @@ void DenoiseFilter::fromMic(void* data, int timelength)
// Process by 10-ms blocks // Process by 10-ms blocks
spx_int16_t* in = (spx_int16_t*)data; spx_int16_t* in = (spx_int16_t*)data;
for (int blockIndex = 0; blockIndex < timelength / 10; blockIndex++) for (int blockIndex=0; blockIndex<timelength/10; blockIndex++)
{ {
spx_int16_t* block = in + blockIndex * (mRate / 100) * AUDIO_CHANNELS; spx_int16_t* block = in + blockIndex * (mRate / 100) * AUDIO_CHANNELS;
speex_preprocess_run((SpeexPreprocessState*)mCtx, block); speex_preprocess_run((SpeexPreprocessState*)mCtx, block);
@@ -232,3 +232,4 @@ int DenoiseFilter::rate()
{ {
return mRate; return mRate;
} }
+17 -18
View File
@@ -5,15 +5,15 @@
#ifndef __AUDIO_QUALITY_H #ifndef __AUDIO_QUALITY_H
#define __AUDIO_QUALITY_H #define __AUDIO_QUALITY_H
#include "../engine_config.h" #include "../config.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include <vector> #include <vector>
namespace Audio namespace Audio
{ {
class AgcFilter class AgcFilter
{ {
protected: protected:
struct Channel struct Channel
{ {
unsigned int mSampleMax; unsigned int mSampleMax;
@@ -24,17 +24,16 @@ protected:
}; };
std::vector<Channel> mChannelList; std::vector<Channel> mChannelList;
void processChannel(short* pcm, int nrOfSamples, int channelIndex); void processChannel(short* pcm, int nrOfSamples, int channelIndex);
public:
public:
AgcFilter(int channels); AgcFilter(int channels);
~AgcFilter(); ~AgcFilter();
void process(void* pcm, int length); void process(void* pcm, int length);
}; };
class AecFilter class AecFilter
{ {
public: public:
AecFilter(int tailTime, int frameTime, int rate); AecFilter(int tailTime, int frameTime, int rate);
~AecFilter(); ~AecFilter();
@@ -43,28 +42,28 @@ public:
void fromMic(void* data); void fromMic(void* data);
int frametime(); int frametime();
protected: protected:
void* mCtx; /// The echo canceller context's pointer. void* mCtx; /// The echo canceller context's pointer.
Mutex mGuard; /// Mutex to protect this instance. Mutex mGuard; /// Mutex to protect this instance.
int mFrameTime; /// Duration of single audio frame (in milliseconds) int mFrameTime; /// Duration of single audio frame (in milliseconds)
int mRate; int mRate;
}; };
class DenoiseFilter class DenoiseFilter
{ {
public: public:
DenoiseFilter(int rate); DenoiseFilter(int rate);
~DenoiseFilter(); ~DenoiseFilter();
void fromMic(void* data, int timelength); void fromMic(void* data, int timelength);
int rate(); int rate();
protected: protected:
Mutex mGuard; /// Mutex to protect this instance. Mutex mGuard; /// Mutex to protect this instance.
void* mCtx; /// The denoiser context pointer. void* mCtx; /// The denoiser context pointer.
int mRate; /// Duration of single audio frame (in milliseconds) int mRate; /// Duration of single audio frame (in milliseconds)
}; };
} // namespace Audio }
#endif #endif
+55 -51
View File
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../engine_config.h" #include "../config.h"
#include "Audio_Resampler.h" #include "Audio_Resampler.h"
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
@@ -17,7 +17,10 @@ namespace Audio
{ {
SpeexResampler::SpeexResampler() {} SpeexResampler::SpeexResampler()
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0)
{
}
void SpeexResampler::start(int channels, int sourceRate, int destRate) void SpeexResampler::start(int channels, int sourceRate, int destRate)
{ {
@@ -34,8 +37,8 @@ void SpeexResampler::start(int channels, int sourceRate, int destRate)
if (sourceRate != destRate) if (sourceRate != destRate)
{ {
// Defer context creation until first request // Defer context creation until first request
// mContext = speex_resampler_init(channels, sourceRate, destRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode); //mContext = speex_resampler_init(channels, sourceRate, destRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
// assert(mContext != NULL); //assert(mContext != NULL);
} }
} }
@@ -48,35 +51,28 @@ void SpeexResampler::stop()
} }
} }
bool SpeexResampler::isOpened() const
{
return mContext != nullptr;
}
SpeexResampler::~SpeexResampler() SpeexResampler::~SpeexResampler()
{ {
stop(); stop();
} }
size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed, void* dest, size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed,
size_t destCapacity) void* dest, size_t destCapacity)
{ {
assert(mSourceRate != 0 && mDestRate != 0); assert(mSourceRate != 0 && mDestRate != 0);
if (mDestRate == mSourceRate) if (mDestRate == mSourceRate)
{ {
// Pass-through, but never write past the caller's buffer: clamp to its
// capacity instead of trusting sourceLength (which is caller/file driven).
assert(destCapacity >= sourceLength); assert(destCapacity >= sourceLength);
size_t copied = std::min(sourceLength, destCapacity); memcpy(dest, src, sourceLength);
memcpy(dest, src, copied); sourceProcessed = sourceLength;
sourceProcessed = copied; return sourceLength;
return copied;
} }
if (!mContext) if (!mContext)
{ {
mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode); mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate,
AUDIO_RESAMPLER_QUALITY, &mErrorCode);
if (!mContext) if (!mContext)
return 0; return 0;
} }
@@ -101,9 +97,11 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
unsigned inLen = sourceLength / (sizeof(short) * mChannels); unsigned inLen = sourceLength / (sizeof(short) * mChannels);
outLen /= sizeof(short) * mChannels; outLen /= sizeof(short) * mChannels;
assert(mContext != NULL); assert(mContext != NULL);
spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen), out_len = static_cast<spx_uint32_t>(outLen); spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen),
out_len = static_cast<spx_uint32_t>(outLen);
int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState*)mContext, (spx_int16_t*)src, &in_len, int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState *)mContext,
(spx_int16_t*)src, &in_len,
(spx_int16_t*)dest, &out_len); (spx_int16_t*)dest, &out_len);
assert(speexCode == RESAMPLER_ERR_SUCCESS); assert(speexCode == RESAMPLER_ERR_SUCCESS);
@@ -115,25 +113,24 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
return outLen * sizeof(short) * mChannels; return outLen * sizeof(short) * mChannels;
} }
int SpeexResampler::sourceRate() const int SpeexResampler::sourceRate()
{ {
return mSourceRate; return mSourceRate;
} }
int SpeexResampler::destRate() const int SpeexResampler::destRate()
{ {
return mDestRate; return mDestRate;
} }
size_t SpeexResampler::getDestLength(size_t sourceLen) const size_t SpeexResampler::getDestLength(size_t sourceLen)
{ {
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f); return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f) / 2 * 2;
} }
size_t SpeexResampler::getSourceLength(size_t destLen) const size_t SpeexResampler::getSourceLength(size_t destLen)
{ {
// Here we want to get 'destLen' number of samples return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f) / 2 * 2;
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f);
} }
// Returns instance + speex resampler size in bytes // Returns instance + speex resampler size in bytes
@@ -143,27 +140,27 @@ size_t SpeexResampler::getSize() const
} }
// -------------------------- ChannelConverter -------------------- // -------------------------- ChannelConverter --------------------
int ChannelConverter::stereoToMono(const void* source, int sourceLength, void* dest, int destLength) int ChannelConverter::stereoToMono(const void *source, int sourceLength, void *dest, int destLength)
{ {
assert(destLength == sourceLength / 2); assert(destLength == sourceLength / 2);
const short* input = (const short*)source; const short* input = (const short*)source;
short* output = (short*)dest; short* output = (short*)dest;
for (int sampleIndex = 0; sampleIndex < destLength / 2; sampleIndex++) for (int sampleIndex = 0; sampleIndex < destLength/2; sampleIndex++)
{ {
output[sampleIndex] = (input[sampleIndex * 2] + input[sampleIndex * 2 + 1]) >> 1; output[sampleIndex] = (input[sampleIndex*2] + input[sampleIndex*2+1]) >> 1;
} }
return sourceLength / 2; return sourceLength / 2;
} }
int ChannelConverter::monoToStereo(const void* source, int sourceLength, void* dest, int destLength) int ChannelConverter::monoToStereo(const void *source, int sourceLength, void *dest, int destLength)
{ {
assert(destLength == sourceLength * 2); assert (destLength == sourceLength * 2);
const short* input = (const short*)source; const short* input = (const short*)source;
short* output = (short*)dest; short* output = (short*)dest;
// Convert starting from the end of buffer to allow inplace conversion // Convert starting from the end of buffer to allow inplace conversion
for (int sampleIndex = sourceLength / 2 - 1; sampleIndex >= 0; sampleIndex--) for (int sampleIndex = sourceLength/2 - 1; sampleIndex >= 0; sampleIndex--)
{ {
output[2 * sampleIndex] = output[2 * sampleIndex + 1] = input[sampleIndex]; output[2*sampleIndex] = output[2*sampleIndex+1] = input[sampleIndex];
} }
return sourceLength * 2; return sourceLength * 2;
} }
@@ -180,14 +177,13 @@ Resampler48kTo16k::~Resampler48kTo16k()
WebRtcSpl_ResetResample48khzTo16khz(&mContext); WebRtcSpl_ResetResample48khzTo16khz(&mContext);
} }
int Resampler48kTo16k::process(const void* source, int sourceLen, void* dest, int destLen) int Resampler48kTo16k::process(const void *source, int sourceLen, void *dest, int destLen)
{ {
const short* input = (const short*)source; const short* input = (const short*)source; int inputLen = sourceLen / 2;
int inputLen = sourceLen / 2; short* output = (short*)dest; //int outputCapacity = destLen / 2;
short* output = (short*)dest; // int outputCapacity = destLen / 2;
assert(inputLen % 480 == 0); assert(inputLen % 480 == 0);
int frames = inputLen / 480; int frames = inputLen / 480;
for (int i = 0; i < frames; i++) for (int i=0; i<frames; i++)
WebRtcSpl_Resample48khzTo16khz(input + i * 480, output + i * 160, &mContext, mTemp); WebRtcSpl_Resample48khzTo16khz(input + i * 480, output + i * 160, &mContext, mTemp);
return sourceLen / 3; return sourceLen / 3;
@@ -204,14 +200,13 @@ Resampler16kto48k::~Resampler16kto48k()
WebRtcSpl_ResetResample16khzTo48khz(&mContext); WebRtcSpl_ResetResample16khzTo48khz(&mContext);
} }
int Resampler16kto48k::process(const void* source, int sourceLen, void* dest, int destLen) int Resampler16kto48k::process(const void *source, int sourceLen, void *dest, int destLen)
{ {
const WebRtc_Word16* input = (const WebRtc_Word16*)source; const WebRtc_Word16* input = (const WebRtc_Word16*)source; int inputLen = sourceLen / 2;
int inputLen = sourceLen / 2; WebRtc_Word16* output = (WebRtc_Word16*)dest; //int outputCapacity = destLen / 2;
WebRtc_Word16* output = (WebRtc_Word16*)dest; // int outputCapacity = destLen / 2;
assert(inputLen % 160 == 0); assert(inputLen % 160 == 0);
int frames = inputLen / 160; int frames = inputLen / 160;
for (int i = 0; i < frames; i++) for (int i=0; i<frames; i++)
WebRtcSpl_Resample16khzTo48khz(input + i * 160, output + i * 480, &mContext, mTemp); WebRtcSpl_Resample16khzTo48khz(input + i * 160, output + i * 480, &mContext, mTemp);
return sourceLen * 3; return sourceLen * 3;
@@ -220,12 +215,18 @@ int Resampler16kto48k::process(const void* source, int sourceLen, void* dest, in
#endif #endif
// ---------------- UniversalResampler ------------------- // ---------------- UniversalResampler -------------------
UniversalResampler::UniversalResampler() {} UniversalResampler::UniversalResampler()
{
UniversalResampler::~UniversalResampler() {} }
size_t UniversalResampler::resample(int sourceRate, const void* sourceBuffer, size_t sourceLength, UniversalResampler::~UniversalResampler()
size_t& sourceProcessed, int destRate, void* destBuffer, size_t destCapacity) {
}
size_t UniversalResampler::resample(int sourceRate, const void *sourceBuffer, size_t sourceLength,
size_t& sourceProcessed, int destRate, void *destBuffer, size_t destCapacity)
{ {
assert(destBuffer && sourceBuffer); assert(destBuffer && sourceBuffer);
size_t result; size_t result;
@@ -244,7 +245,10 @@ size_t UniversalResampler::resample(int sourceRate, const void* sourceBuffer, si
return result; return result;
} }
void UniversalResampler::preload() {} void UniversalResampler::preload()
{
}
size_t UniversalResampler::getDestLength(int sourceRate, int destRate, size_t sourceLength) size_t UniversalResampler::getDestLength(int sourceRate, int destRate, size_t sourceLength)
{ {
@@ -269,7 +273,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
PResampler r; PResampler r;
if (resamplerIter == mResamplerMap.end()) if (resamplerIter == mResamplerMap.end())
{ {
r = std::make_shared<Resampler>(); r = PResampler(new Resampler());
r->start(AUDIO_CHANNELS, sourceRate, destRate); r->start(AUDIO_CHANNELS, sourceRate, destRate);
mResamplerMap[RatePair(sourceRate, destRate)] = r; mResamplerMap[RatePair(sourceRate, destRate)] = r;
} }
@@ -278,4 +282,4 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
return r; return r;
} }
} // namespace Audio } // end of namespace
+44 -45
View File
@@ -7,7 +7,7 @@
#define __AUDIO_RESAMPLER_H #define __AUDIO_RESAMPLER_H
#ifdef USE_WEBRTC_RESAMPLER #ifdef USE_WEBRTC_RESAMPLER
#include "signal_processing_library/signal_processing_library.h" # include "signal_processing_library/signal_processing_library.h"
#endif #endif
#include <vector> #include <vector>
@@ -16,47 +16,47 @@
namespace Audio namespace Audio
{ {
class SpeexResampler class SpeexResampler
{ {
public: public:
SpeexResampler(); SpeexResampler();
~SpeexResampler(); ~SpeexResampler();
void start(int channels, int sourceRate, int destRate); void start(int channels, int sourceRate, int destRate);
void stop(); void stop();
bool isOpened() const; size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed,
void* dest, size_t destCapacity);
size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed, void* dest, int sourceRate();
size_t destCapacity); int destRate();
int sourceRate() const; size_t getDestLength(size_t sourceLen);
int destRate() const; size_t getSourceLength(size_t destLen);
size_t getDestLength(size_t sourceLen) const;
size_t getSourceLength(size_t destLen) const;
// Returns instance + speex encoder size in bytes // Returns instance + speex encoder size in bytes
size_t getSize() const; size_t getSize() const;
protected: protected:
void* mContext = nullptr; void* mContext;
int mErrorCode = 0; int mErrorCode;
int mSourceRate = 0, mDestRate = 0, mChannels = 0; int mSourceRate,
short mLastSample = 0; mDestRate,
}; mChannels;
short mLastSample;
};
typedef SpeexResampler Resampler; typedef SpeexResampler Resampler;
typedef std::shared_ptr<Resampler> PResampler; typedef std::shared_ptr<Resampler> PResampler;
class ChannelConverter class ChannelConverter
{ {
public: public:
static int stereoToMono(const void* source, int sourceLength, void* dest, int destLength); static int stereoToMono(const void* source, int sourceLength, void* dest, int destLength);
static int monoToStereo(const void* source, int sourceLength, void* dest, int destLength); static int monoToStereo(const void* source, int sourceLength, void* dest, int destLength);
}; };
// Operates with AUDIO_CHANNELS number of channels // Operates with AUDIO_CHANNELS number of channels
class UniversalResampler class UniversalResampler
{ {
public: public:
UniversalResampler(); UniversalResampler();
~UniversalResampler(); ~UniversalResampler();
@@ -65,41 +65,40 @@ public:
size_t getDestLength(int sourceRate, int destRate, size_t sourceLength); size_t getDestLength(int sourceRate, int destRate, size_t sourceLength);
size_t getSourceLength(int sourceRate, int destRate, size_t destLength); size_t getSourceLength(int sourceRate, int destRate, size_t destLength);
protected: protected:
typedef std::pair<int, int> RatePair; typedef std::pair<int, int> RatePair;
typedef std::map<RatePair, PResampler> ResamplerMap; typedef std::map<RatePair, PResampler> ResamplerMap;
ResamplerMap mResamplerMap; ResamplerMap mResamplerMap;
PResampler findResampler(int sourceRate, int destRate); PResampler findResampler(int sourceRate, int destRate);
void preload(); void preload();
}; };
#ifdef USE_WEBRTC_RESAMPLER #ifdef USE_WEBRTC_RESAMPLER
// n*10 milliseconds buffers required! // n*10 milliseconds buffers required!
class Resampler48kTo16k class Resampler48kTo16k
{ {
public: public:
Resampler48kTo16k(); Resampler48kTo16k();
~Resampler48kTo16k(); ~Resampler48kTo16k();
int process(const void* source, int sourceLen, void* dest, int destLen); int process(const void* source, int sourceLen, void* dest, int destLen);
protected:
protected:
WebRtc_Word32 mTemp[496]; WebRtc_Word32 mTemp[496];
WebRtcSpl_State48khzTo16khz mContext; WebRtcSpl_State48khzTo16khz mContext;
}; };
class Resampler16kto48k class Resampler16kto48k
{ {
public: public:
Resampler16kto48k(); Resampler16kto48k();
~Resampler16kto48k(); ~Resampler16kto48k();
int process(const void* source, int sourceLen, void* dest, int destLen); int process(const void* source, int sourceLen, void* dest, int destLen);
protected: protected:
WebRtc_Word32 mTemp[336]; WebRtc_Word32 mTemp[336];
WebRtcSpl_State16khzTo48khz mContext; WebRtcSpl_State16khzTo48khz mContext;
}; };
#endif #endif
} // namespace Audio } // end of namespace
#endif #endif
+154 -192
View File
@@ -7,20 +7,18 @@
#include "helper/HL_Exception.h" #include "helper/HL_Exception.h"
#include "helper/HL_String.h" #include "helper/HL_String.h"
#include "helper/HL_Log.h" #include "helper/HL_Log.h"
#include "../engine_config.h" #include "../config.h"
#include <memory.h> #include <memory.h>
#include <assert.h>
#ifndef WORD #ifndef WORD
#define WORD unsigned short # define WORD unsigned short
#endif #endif
#ifndef DWORD #ifndef DWORD
#define DWORD unsigned int # define DWORD unsigned int
#endif #endif
typedef struct typedef struct {
{
WORD wFormatTag; WORD wFormatTag;
WORD nChannels; WORD nChannels;
DWORD nSamplesPerSec; DWORD nSamplesPerSec;
@@ -28,67 +26,64 @@ typedef struct
WORD nBlockAlign; WORD nBlockAlign;
WORD wBitsPerSample; WORD wBitsPerSample;
WORD cbSize; WORD cbSize;
} WaveFormatEx; }
WaveFormatEx;
#define WAVE_FORMAT_PCM 1 #define WAVE_FORMAT_PCM 1
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "WavFileReader"
#define LOCK std::unique_lock<std::recursive_mutex> lock(mFileMtx); #define LOCK std::unique_lock<std::recursive_mutex> lock(mFileMtx);
using namespace Audio; using namespace Audio;
// ---------------------- WavFileReader ------------------------- // ---------------------- WavFileReader -------------------------
WavFileReader::WavFileReader() : mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0) WavFileReader::WavFileReader()
:mHandle(nullptr), mRate(0), mLastError(0)
{ {
mDataOffset = 0; mDataOffset = 0;
} }
WavFileReader::~WavFileReader() {} WavFileReader::~WavFileReader()
{
}
#define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED); #define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED);
std::string WavFileReader::readChunk() std::string WavFileReader::readChunk()
{ {
char name[5] = {0}; char name[5];
readBuffer(name, 4); if (fread(name, 1, 4, mHandle) != 4)
THROW_READERROR;
name[4] = 0;
std::string result = name; std::string result = name;
uint32_t size = 0; unsigned size;
readBuffer(&size, 4); if (fread(&size, 4, 1, mHandle) != 1)
THROW_READERROR;
if (result == "data") if (result == "fact")
mDataLength = size; fread(&mDataLength, 4, 1, mHandle);
else else
// Skip the chunk body; RIFF chunks are word-aligned, so odd sizes carry a pad byte if (result != "data")
mInput->seekg(std::streamoff(size + (size & 1)), std::ios_base::cur); fseek(mHandle, size, SEEK_CUR);
else
mDataLength = size;
return result; return result;
} }
void WavFileReader::readBuffer(void* buffer, size_t sz) bool WavFileReader::open(const std::tstring& filename)
{
auto p = mInput->tellg();
mInput->read(reinterpret_cast<char*>(buffer), sz);
if (mInput->tellg() - p != sz)
throw Exception(ERR_WAVFILE_FAILED);
}
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
{
auto p = mInput->tellg();
mInput->read(reinterpret_cast<char*>(buffer), sz);
return mInput->tellg() - p;
}
bool WavFileReader::open(const std::filesystem::path& p)
{ {
LOCK; LOCK;
try try
{ {
mPath = p; #ifdef WIN32
mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in); mHandle = _wfopen(filename.c_str(), L"rb");
if (!mInput->is_open()) #else
mHandle = fopen(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;
@@ -102,72 +97,76 @@ bool WavFileReader::open(const std::filesystem::path& p)
// Read the .WAV header // Read the .WAV header
char riff[4]; char riff[4];
readBuffer(riff, sizeof riff); if (fread(riff, 4, 1, mHandle) < 1)
THROW_READERROR;
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F')) if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
THROW_READERROR; THROW_READERROR;
// Read the file size // Read the file size
uint32_t filesize = 0; unsigned int filesize = 0;
readBuffer(&filesize, sizeof(filesize)); if (fread(&filesize, 4, 1, mHandle) < 1)
THROW_READERROR;
char wavefmt[9] = {0}; char wavefmt[9];
readBuffer(wavefmt, 8); if (fread(wavefmt, 8, 1, mHandle) < 1)
THROW_READERROR;
wavefmt[8] = 0;
if (strcmp(wavefmt, "WAVEfmt ") != 0) if (strcmp(wavefmt, "WAVEfmt ") != 0)
THROW_READERROR; THROW_READERROR;
uint32_t fmtSize = 0; unsigned fmtSize = 0;
readBuffer(&fmtSize, sizeof(fmtSize)); if (fread(&fmtSize, 4, 1, mHandle) < 1)
THROW_READERROR;
auto fmtStart = mInput->tellg(); unsigned fmtStart = ftell(mHandle);
uint16_t formattag = 0; unsigned short formattag = 0;
readBuffer(&formattag, sizeof(formattag)); if (fread(&formattag, 2, 1, mHandle) < 1)
THROW_READERROR;
if (formattag != 1 /*WAVE_FORMAT_PCM*/) if (formattag != 1/*WAVE_FORMAT_PCM*/)
THROW_READERROR; THROW_READERROR;
mChannels = 0; mChannels = 0;
readBuffer(&mChannels, sizeof(mChannels)); if (fread(&mChannels, 2, 1, mHandle) < 1)
THROW_READERROR;
mSamplerate = 0; mRate = 0;
readBuffer(&mSamplerate, sizeof(mSamplerate)); if (fread(&mRate, 4, 1, mHandle) < 1)
THROW_READERROR;
uint32_t avgbytespersec = 0; unsigned int avgbytespersec = 0;
readBuffer(&avgbytespersec, sizeof(avgbytespersec)); if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
THROW_READERROR;
uint16_t blockalign = 0; unsigned short blockalign = 0;
readBuffer(&blockalign, sizeof(blockalign)); if (fread(&blockalign, 2, 1, mHandle) < 1)
THROW_READERROR;
mBits = 0; mBits = 0;
readBuffer(&mBits, sizeof(mBits)); if (fread(&mBits, 2, 1, mHandle) < 1)
// Only 16-bit PCM is supported: the read path feeds the data
// directly into a 16-bit resampler.
if (mBits != 16)
THROW_READERROR; THROW_READERROR;
// The read path is mono-only: it divides byte counts by AUDIO_CHANNELS if (mBits !=8 && mBits != 16)
// and starts the resampler with AUDIO_CHANNELS. Reject anything else -
// a multi-channel file would make read() copy more source bytes than the
// caller's (mono-sized) output buffer can hold. Also reject a zero rate,
// which would divide by zero in readRaw() / poison the resampler ratio.
if (mChannels != AUDIO_CHANNELS || mSamplerate == 0)
THROW_READERROR; THROW_READERROR;
// Look for the chunk 'data' // Read the "chunk"
mInput->seekg(fmtStart + std::streampos(fmtSize)); fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
//unsigned pos = ftell(mHandle);
mDataLength = 0; mDataLength = 0;
while (readChunk() != "data") while (readChunk() != "data")
; ;
mDataOffset = mInput->tellg(); mFileName = filename;
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE); mDataOffset = ftell(mHandle);
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
} }
catch (...) catch(...)
{ {
mInput.reset(); fclose(mHandle); mHandle = nullptr;
mLastError = static_cast<unsigned>(-1); mLastError = static_cast<unsigned>(-1);
} }
return isOpened(); return isOpened();
@@ -176,118 +175,78 @@ bool WavFileReader::open(const std::filesystem::path& p)
void WavFileReader::close() void WavFileReader::close()
{ {
LOCK; LOCK;
mInput.reset();
if (nullptr != mHandle)
fclose(mHandle);
mHandle = nullptr;
} }
int WavFileReader::samplerate() const int WavFileReader::rate() const
{ {
return mSamplerate; return mRate;
} }
int WavFileReader::channels() const unsigned WavFileReader::read(void* buffer, unsigned bytes)
{
return mChannels;
}
size_t WavFileReader::read(void* buffer, size_t bytes)
{ {
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2; return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
} }
size_t WavFileReader::readRaw(void* buffer, size_t bytes) unsigned WavFileReader::read(short* buffer, unsigned samples)
{
return readRaw((short*)buffer, bytes / channels() / sizeof(short)) * channels() * sizeof(short);
}
size_t WavFileReader::read(short* buffer, size_t samples)
{ {
LOCK; LOCK;
if (!mInput) if (!mHandle)
return 0; return 0;
// Get number of samples that must be read from source file // Get number of samples that must be read from source file
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8; int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
bool useHeap = requiredBytes > sizeof mTempBuffer; void* temp = alloca(requiredBytes);
void* temp;
if (useHeap)
temp = malloc(requiredBytes);
else
temp = mTempBuffer;
memset(temp, 0, requiredBytes); memset(temp, 0, requiredBytes);
// Find required size of input buffer // Find required size of input buffer
if (mDataLength) if (mDataLength)
{ {
auto filePosition = mInput->tellg(); unsigned filePosition = ftell(mHandle);
// Check how much data we can read // Check how much data we can read
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset; unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0; requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
} }
size_t readBytes = tryReadBuffer(temp, requiredBytes); /*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
size_t processedBytes = 0; size_t processedBytes = 0;
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes, buffer, samples * 2 * AUDIO_CHANNELS); size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
buffer, samples * 2 * AUDIO_CHANNELS);
if (useHeap)
free(temp);
return result / 2 / AUDIO_CHANNELS; 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
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset;
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
}
size_t readBytes = tryReadBuffer(buffer, requiredBytes);
return readBytes / channels() / sizeof(short);
}
bool WavFileReader::isOpened() bool WavFileReader::isOpened()
{ {
LOCK; LOCK;
if (!mInput)
return false; return (mHandle != 0);
return mInput->is_open();
} }
void WavFileReader::rewind() void WavFileReader::rewind()
{ {
LOCK; LOCK;
if (mInput)
mInput->seekg(mDataOffset); if (mHandle)
fseek(mHandle, mDataOffset, SEEK_SET);
} }
std::filesystem::path WavFileReader::path() const std::tstring WavFileReader::filename() const
{ {
LOCK; LOCK;
return mPath;
return mFileName;
} }
size_t WavFileReader::size() const unsigned WavFileReader::size() const
{ {
LOCK; LOCK;
return mDataLength; return mDataLength;
} }
@@ -300,7 +259,10 @@ unsigned WavFileReader::lastError() const
#define BITS_PER_CHANNEL 16 #define BITS_PER_CHANNEL 16
WavFileWriter::WavFileWriter() : mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0) {} WavFileWriter::WavFileWriter()
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
{
}
WavFileWriter::~WavFileWriter() WavFileWriter::~WavFileWriter()
{ {
@@ -313,76 +275,68 @@ void WavFileWriter::checkWriteResult(int result)
throw Exception(ERR_WAVFILE_FAILED, errno); throw Exception(ERR_WAVFILE_FAILED, errno);
} }
void WavFileWriter::writeBuffer(const void* buffer, size_t sz) bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
{
if (!mOutput)
return;
auto p = mOutput->tellp();
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
if (mOutput->tellp() - p != sz)
throw Exception(ERR_WAVFILE_FAILED);
}
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
{ {
LOCK; LOCK;
close(); close();
mSamplerate = samplerate;
mRate = rate;
mChannels = channels; mChannels = channels;
mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc); #ifdef WIN32
if (!mOutput->is_open()) mHandle = _wfopen(filename.c_str(), L"wb");
#else
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "wb");
#endif
if (nullptr == mHandle)
{ {
int errorcode = errno; ICELogError(<< "Failed to create .wav file: filename = " << StringHelper::makeUtf8(filename) << " , error = " << errno);
ICELogError(<< "Failed to create .wav file: filename = " << p << " , error = " << errorcode);
mOutput.reset();
return false; return false;
} }
// Write the .WAV header // Write the .WAV header
const char* riff = "RIFF"; const char* riff = "RIFF";
writeBuffer(riff, 4); checkWriteResult( fwrite(riff, 4, 1, mHandle) );
// Write the file size // Write the file size
uint32_t filesize = 0; unsigned int filesize = 0;
writeBuffer(&filesize, sizeof filesize); checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
const char* wavefmt = "WAVEfmt "; const char* wavefmt = "WAVEfmt ";
writeBuffer(wavefmt, 8); checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
// Set the format description // Set the format description
uint32_t dwFmtSize = 16; /*= 16L*/ DWORD dwFmtSize = 16; /*= 16L*/;
; 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;
writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag)); checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
format.nChannels = mChannels; format.nChannels = mChannels;
writeBuffer(&format.nChannels, sizeof(format.nChannels)); checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
format.nSamplesPerSec = mSamplerate; format.nSamplesPerSec = mRate;
writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec)); checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
format.nAvgBytesPerSec = mSamplerate * 2 * mChannels; format.nAvgBytesPerSec = mRate * 2 * mChannels;
writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec)); checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
format.nBlockAlign = 2 * mChannels; format.nBlockAlign = 2 * mChannels;
writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign)); checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
format.wBitsPerSample = BITS_PER_CHANNEL; format.wBitsPerSample = BITS_PER_CHANNEL;
writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample)); checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
const char* data = "data"; const char* data = "data";
writeBuffer(data, 4); checkWriteResult( fwrite(data, 4, 1, mHandle));
mPath = p; mFileName = filename;
mWritten = 0; mWritten = 0;
mLengthOffset = mOutput->tellp(); mLengthOffset = ftell(mHandle);
writeBuffer(&mWritten, sizeof mWritten); checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
return isOpened(); return isOpened();
} }
@@ -390,43 +344,51 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
void WavFileWriter::close() void WavFileWriter::close()
{ {
LOCK; LOCK;
mOutput.reset();
if (mHandle)
{
fclose(mHandle);
mHandle = nullptr;
}
} }
size_t WavFileWriter::write(const void* buffer, size_t bytes) size_t WavFileWriter::write(const void* buffer, size_t bytes)
{ {
LOCK; LOCK;
if (!mOutput) if (!mHandle)
return 0; return 0;
// Seek the end of file - here new data will be written // Seek the end of file
mOutput->seekp(0, std::ios_base::end); fseek(mHandle, 0, SEEK_END);
mWritten += bytes; mWritten += bytes;
// Write the data // Write the data
writeBuffer(buffer, bytes); fwrite(buffer, bytes, 1, mHandle);
// Write file length // Write file length
mOutput->seekp(4, std::ios_base::beg); fseek(mHandle, 4, SEEK_SET);
uint32_t fl = mWritten + 36; int32_t fl = mWritten + 36;
writeBuffer(&fl, sizeof(fl)); fwrite(&fl, sizeof(fl), 1, mHandle);
// Write data length // Write data length
mOutput->seekp(mLengthOffset, std::ios_base::beg); fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
writeBuffer(&mWritten, sizeof(mWritten)); checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
return bytes; return bytes;
} }
bool WavFileWriter::isOpened() const bool WavFileWriter::isOpened()
{ {
LOCK; LOCK;
return mOutput && mOutput->is_open();
return (mHandle != nullptr);
} }
std::filesystem::path WavFileWriter::path() const std::tstring WavFileWriter::filename()
{ {
LOCK; LOCK;
return mPath;
return mFileName;
} }
+37 -47
View File
@@ -12,85 +12,75 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <filesystem>
#include <fstream>
namespace Audio namespace Audio
{ {
class WavFileReader class WavFileReader
{ {
protected: protected:
uint16_t mChannels = 0; FILE* mHandle;
uint16_t mBits = 0; short mChannels;
int mSamplerate = 0; short mBits;
std::filesystem::path mPath; int mRate;
std::tstring mFileName;
mutable std::recursive_mutex mFileMtx; mutable std::recursive_mutex mFileMtx;
size_t mDataOffset = 0; unsigned mDataOffset;
size_t mDataLength = 0; unsigned mDataLength;
Resampler mResampler; Resampler mResampler;
unsigned mLastError = 0; unsigned mLastError;
std::unique_ptr<std::ifstream> mInput;
uint8_t mTempBuffer[16384];
std::string readChunk(); std::string readChunk();
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read public:
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
public:
WavFileReader(); WavFileReader();
~WavFileReader(); ~WavFileReader();
bool open(const std::filesystem::path& p); bool open(const std::tstring& filename);
void close(); void close();
bool isOpened(); bool isOpened();
void rewind(); void rewind();
int samplerate() const; int rate() const;
int channels() const;
// This method returns number of read bytes // This method returns number of read bytes
size_t read(void* buffer, size_t bytes); unsigned read(void* buffer, unsigned bytes);
size_t readRaw(void* buffer, size_t bytes);
// This method returns number of read samples // This method returns number of read samples
size_t read(short* buffer, size_t samples); unsigned read(short* buffer, unsigned samples);
size_t readRaw(short* buffer, size_t samples); std::tstring filename() const;
unsigned size() const;
std::filesystem::path path() const;
size_t size() const;
unsigned lastError() const; unsigned lastError() const;
}; };
typedef std::shared_ptr<WavFileReader> PWavFileReader; typedef std::shared_ptr<WavFileReader> PWavFileReader;
class WavFileWriter class WavFileWriter
{ {
protected: protected:
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file. FILE* mHandle; /// Handle of audio file.
std::filesystem::path mPath; /// Path to requested audio file. std::tstring mFileName; /// Path to requested audio file.
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance. std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
size_t mWritten = 0; /// Amount of written data (in bytes) int mWritten; /// Amount of written data (in bytes)
size_t mLengthOffset = 0; /// Position of length field. int mLengthOffset; /// Position of length field.
int mSamplerate = 0, mChannels = 0; int mRate,
mChannels;
void checkWriteResult(int result); void checkWriteResult(int result);
void writeBuffer(const void* buffer, size_t sz);
public: public:
WavFileWriter(); WavFileWriter();
~WavFileWriter(); ~WavFileWriter();
bool open(const std::filesystem::path& p, int samplerate, int channels); bool open(const std::tstring& filename, int rate, int channels);
void close(); void close();
bool isOpened() const; bool isOpened();
size_t write(const void* buffer, size_t bytes); size_t write(const void* buffer, size_t bytes);
std::filesystem::path path() const; std::tstring filename();
}; };
typedef std::shared_ptr<WavFileWriter> PWavFileWriter; typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
} // namespace Audio }
#endif #endif
+31 -30
View File
@@ -44,6 +44,7 @@ WmmeInputDevice::Buffer::~Buffer()
GlobalUnlock(mHeaderHandle); GlobalUnlock(mHeaderHandle);
GlobalFree(mHeaderHandle); GlobalFree(mHeaderHandle);
} }
} }
bool WmmeInputDevice::Buffer::prepare(HWAVEIN device) bool WmmeInputDevice::Buffer::prepare(HWAVEIN device)
@@ -54,7 +55,7 @@ bool WmmeInputDevice::Buffer::prepare(HWAVEIN device)
mHeader->lpData = (LPSTR)mData; mHeader->lpData = (LPSTR)mData;
resCode = waveInPrepareHeader(device, mHeader, sizeof *mHeader); resCode = waveInPrepareHeader(device, mHeader, sizeof *mHeader);
// if (resCode != MMSYSERR_NOERROR) //if (resCode != MMSYSERR_NOERROR)
// LogCritical("Audio", << "Failed to prepare source header. Error code " << resCode << "."); // LogCritical("Audio", << "Failed to prepare source header. Error code " << resCode << ".");
return resCode == MMSYSERR_NOERROR; return resCode == MMSYSERR_NOERROR;
@@ -65,7 +66,7 @@ bool WmmeInputDevice::Buffer::unprepare(HWAVEIN device)
if (mHeader->dwFlags & WHDR_PREPARED) if (mHeader->dwFlags & WHDR_PREPARED)
{ {
MMRESULT resCode = waveInUnprepareHeader(device, mHeader, sizeof *mHeader); MMRESULT resCode = waveInUnprepareHeader(device, mHeader, sizeof *mHeader);
// if (resCode != MMSYSERR_NOERROR) //if (resCode != MMSYSERR_NOERROR)
// LogCritical("Audio", << "Failed to unprepare source header. Error code " << resCode << "."); // LogCritical("Audio", << "Failed to unprepare source header. Error code " << resCode << ".");
return resCode == MMSYSERR_NOERROR; return resCode == MMSYSERR_NOERROR;
} }
@@ -80,7 +81,7 @@ bool WmmeInputDevice::Buffer::isFinished()
bool WmmeInputDevice::Buffer::addToDevice(HWAVEIN device) bool WmmeInputDevice::Buffer::addToDevice(HWAVEIN device)
{ {
MMRESULT resCode = waveInAddBuffer(device, mHeader, sizeof(*mHeader)); MMRESULT resCode = waveInAddBuffer(device, mHeader, sizeof(*mHeader));
// if (resCode != MMSYSERR_NOERROR) //if (resCode != MMSYSERR_NOERROR)
// LogCritical("Audio", << "Failed to add buffer to source audio device. Error code is " << resCode << "."); // LogCritical("Audio", << "Failed to add buffer to source audio device. Error code is " << resCode << ".");
return resCode == MMSYSERR_NOERROR; return resCode == MMSYSERR_NOERROR;
} }
@@ -92,8 +93,8 @@ void* WmmeInputDevice::Buffer::data()
WmmeInputDevice::WmmeInputDevice(int deviceId) WmmeInputDevice::WmmeInputDevice(int deviceId)
: mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false), mBufferIndex(0), mDeviceIndex(deviceId), :mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false),
mThreadHandle(0) mBufferIndex(0), mDeviceIndex(deviceId), mThreadHandle(0)
{ {
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -112,11 +113,10 @@ bool WmmeInputDevice::fakeMode()
return mFakeMode; return mFakeMode;
} }
void CALLBACK WmmeInputDevice::callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, void CALLBACK WmmeInputDevice::callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
DWORD_PTR dwParam2)
{ {
WmmeInputDevice* impl; WmmeInputDevice* impl;
switch (uMsg) switch(uMsg)
{ {
case WIM_DATA: case WIM_DATA:
impl = (WmmeInputDevice*)dwInstance; impl = (WmmeInputDevice*)dwInstance;
@@ -147,8 +147,7 @@ void WmmeInputDevice::openDevice()
// Open wavein // Open wavein
MMRESULT mmres = MMRESULT mmres = waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mmres != MMSYSERR_NOERROR) if (mmres != MMSYSERR_NOERROR)
{ {
mFakeMode = true; mFakeMode = true;
@@ -159,13 +158,13 @@ void WmmeInputDevice::openDevice()
// Create the buffers for running // Create the buffers for running
mBufferIndex = 0; mBufferIndex = 0;
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++) for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].prepare(mDevHandle); mBufferList[i].prepare(mDevHandle);
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++) for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].addToDevice(mDevHandle); mBufferList[i].addToDevice(mDevHandle);
/*mmres = */ waveInStart(mDevHandle); /*mmres = */waveInStart(mDevHandle);
} }
bool WmmeInputDevice::open() bool WmmeInputDevice::open()
@@ -191,7 +190,7 @@ void WmmeInputDevice::closeDevice()
} }
// Close buffers // Close buffers
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++) for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].unprepare(mDevHandle); mBufferList[i].unprepare(mDevHandle);
// Close device // Close device
@@ -217,6 +216,7 @@ void WmmeInputDevice::close()
::SetEvent(mShutdownSignal); ::SetEvent(mShutdownSignal);
::WaitForSingleObject(mThreadHandle, INFINITE); ::WaitForSingleObject(mThreadHandle, INFINITE);
mThreadHandle = 0; mThreadHandle = 0;
} }
bool WmmeInputDevice::tryReadBuffer(void* buffer) bool WmmeInputDevice::tryReadBuffer(void* buffer)
@@ -244,7 +244,7 @@ void WmmeInputDevice::setFakeMode(bool fakeMode)
int WmmeInputDevice::readBuffer(void* buffer) int WmmeInputDevice::readBuffer(void* buffer)
{ {
// Lock lock(mGuard); //Lock lock(mGuard);
if (mRefCount <= 0 || mFakeMode) if (mRefCount <= 0 || mFakeMode)
return 0; return 0;
@@ -287,7 +287,8 @@ void WmmeInputDevice::threadProc(void* arg)
} }
// --- WmmeOutputDevice --- // --- WmmeOutputDevice ---
WmmeOutputDevice::Buffer::Buffer() : mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL) WmmeOutputDevice::Buffer::Buffer()
:mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL)
{ {
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE); mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
if (!mHeaderHandle) if (!mHeaderHandle)
@@ -340,8 +341,8 @@ bool WmmeOutputDevice::Buffer::write(HWAVEOUT device)
} }
WmmeOutputDevice::WmmeOutputDevice(int index) WmmeOutputDevice::WmmeOutputDevice(int index)
: mDevice(NULL), mDeviceIndex(index), mPlayedTime(0), mPlayedCount(0), mBufferIndex(0), mThreadHandle(NULL), :mDevice(NULL), mDeviceIndex(index), mPlayedTime(0), mPlayedCount(0), mBufferIndex(0), mThreadHandle(NULL),
mFailed(false), mShutdownMarker(false) mFailed(false), mShutdownMarker(false)
{ {
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -353,8 +354,7 @@ WmmeOutputDevice::~WmmeOutputDevice()
close(); close();
// Destroy used signals // Destroy used signals
CloseHandle(mDoneSignal); CloseHandle(mDoneSignal); CloseHandle(mShutdownSignal);
CloseHandle(mShutdownSignal);
} }
bool WmmeOutputDevice::open() bool WmmeOutputDevice::open()
@@ -395,10 +395,10 @@ void WmmeOutputDevice::openDevice()
throw Exception(ERR_WMME_FAILED, mmres); throw Exception(ERR_WMME_FAILED, mmres);
// Prebuffer silence // Prebuffer silence
for (unsigned i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++) for (unsigned i=0; i<AUDIO_SPK_BUFFER_COUNT; i++)
{ {
// bool dumb = false; //bool dumb = false;
// mCallback(mBufferList[i].mData, SPK_BUFFER_SIZE, dumb, dumb); //mCallback(mBufferList[i].mData, SPK_BUFFER_SIZE, dumb, dumb);
memset(mBufferList[i].mData, 0, AUDIO_SPK_BUFFER_SIZE); memset(mBufferList[i].mData, 0, AUDIO_SPK_BUFFER_SIZE);
mBufferList[i].prepare(mDevice); mBufferList[i].prepare(mDevice);
mBufferList[i].write(mDevice); mBufferList[i].write(mDevice);
@@ -430,12 +430,13 @@ bool WmmeOutputDevice::areBuffersFinished()
{ {
Lock l(mGuard); Lock l(mGuard);
bool result = true; bool result = true;
for (unsigned i = 0; i < AUDIO_SPK_BUFFER_COUNT && result; i++) for (unsigned i=0; i<AUDIO_SPK_BUFFER_COUNT && result; i++)
{ {
bool finished = mBufferList[i].mHeader->dwFlags & WHDR_DONE || !mBufferList[i].mHeader->dwFlags; bool finished = mBufferList[i].mHeader->dwFlags & WHDR_DONE ||
!mBufferList[i].mHeader->dwFlags;
if (finished) if (finished)
{ {
/* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED) /* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED)
mBufferList[i].Unprepare(mDevice); */ mBufferList[i].Unprepare(mDevice); */
} }
result &= finished; result &= finished;
@@ -464,7 +465,7 @@ void WmmeOutputDevice::threadProc(void* arg)
WaitForSingleObject(impl->mDoneSignal, 500); WaitForSingleObject(impl->mDoneSignal, 500);
// Iterate buffers to find played // Iterate buffers to find played
for (i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++) for (i=0; i<AUDIO_SPK_BUFFER_COUNT; i++)
{ {
index = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT; index = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
Buffer& buffer = impl->mBufferList[index]; Buffer& buffer = impl->mBufferList[index];
@@ -487,7 +488,8 @@ void WmmeOutputDevice::threadProc(void* arg)
exitCount++; exitCount++;
} }
impl->mBufferIndex = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT; impl->mBufferIndex = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
} while (!exitSignal || exitCount < AUDIO_SPK_BUFFER_COUNT); }
while (!exitSignal || exitCount < AUDIO_SPK_BUFFER_COUNT);
impl->closeDevice(); impl->closeDevice();
} }
@@ -538,8 +540,7 @@ bool WmmeOutputDevice::closing()
return mClosing; return mClosing;
} }
void CALLBACK WmmeOutputDevice::callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, void CALLBACK WmmeOutputDevice::callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
DWORD_PTR dwParam2)
{ {
WmmeOutputDevice* impl; WmmeOutputDevice* impl;
+19 -19
View File
@@ -8,7 +8,7 @@
#ifdef TARGET_WIN #ifdef TARGET_WIN
#include "../engine_config.h" #include "../config.h"
#include <winsock2.h> #include <winsock2.h>
#include <windows.h> #include <windows.h>
@@ -30,9 +30,9 @@
namespace Audio namespace Audio
{ {
class WmmeInputDevice : public InputDevice class WmmeInputDevice: public InputDevice
{ {
public: public:
WmmeInputDevice(int index); WmmeInputDevice(int index);
~WmmeInputDevice(); ~WmmeInputDevice();
@@ -45,7 +45,7 @@ public:
int readBuffer(void* buffer); int readBuffer(void* buffer);
HWAVEIN handle(); HWAVEIN handle();
protected: protected:
class Buffer class Buffer
{ {
public: public:
@@ -79,14 +79,13 @@ protected:
void openDevice(); void openDevice();
void closeDevice(); void closeDevice();
static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
DWORD_PTR dwParam2);
static void threadProc(void* arg); static void threadProc(void* arg);
}; };
class WmmeOutputDevice : public OutputDevice class WmmeOutputDevice: public OutputDevice
{ {
public: public:
WmmeOutputDevice(int index); WmmeOutputDevice(int index);
~WmmeOutputDevice(); ~WmmeOutputDevice();
@@ -99,18 +98,16 @@ public:
bool fakeMode(); bool fakeMode();
bool closing(); bool closing();
protected: protected:
class Buffer class Buffer
{ {
friend class WmmeOutputDevice; friend class WmmeOutputDevice;
public: public:
Buffer(); Buffer();
~Buffer(); ~Buffer();
bool prepare(HWAVEOUT device); bool prepare(HWAVEOUT device);
bool unprepare(HWAVEOUT device); bool unprepare(HWAVEOUT device);
bool write(HWAVEOUT device); bool write(HWAVEOUT device);
protected: protected:
WAVEHDR* mHeader; WAVEHDR* mHeader;
void* mData; void* mData;
@@ -124,7 +121,9 @@ protected:
Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT]; Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT];
unsigned mPlayedTime; /// Amount of played time in milliseconds unsigned mPlayedTime; /// Amount of played time in milliseconds
bool mClosing; bool mClosing;
HANDLE mDoneSignal, mShutdownSignal, mThreadHandle; HANDLE mDoneSignal,
mShutdownSignal,
mThreadHandle;
volatile bool mShutdownMarker; volatile bool mShutdownMarker;
volatile LONG mPlayedCount; volatile LONG mPlayedCount;
@@ -135,13 +134,14 @@ protected:
void closeDevice(); void closeDevice();
bool areBuffersFinished(); bool areBuffersFinished();
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
DWORD_PTR dwParam2);
static void threadProc(void* arg); static void threadProc(void* arg);
}; };
} // namespace Audio
}
#endif #endif
+1
View File
@@ -1 +1,2 @@
#include "Audio_iOS.h" #include "Audio_iOS.h"
+4 -2
View File
@@ -1,19 +1,21 @@
#ifndef __AUDIO_IOS #ifndef __AUDIO_IOS
#define __AUDIO_IOS #define __AUDIO_IOS
class IosInputDevice : public InputDevice class IosInputDevice: public InputDevice
{ {
protected: protected:
public: public:
IosInputDevice(); IosInputDevice();
~IosInputDevice(); ~IosInputDevice();
void open(); void open();
void close(); void close();
}; };
class IosOutputDevice : public OutputDevice class IosOutputDevice: public OutputDevice
{ {
protected: protected:
public: public:
+1 -16
View File
@@ -1,42 +1,27 @@
project (audio_lib) project (audio_lib)
# Rely on C++ 11 # Rely on C++ 11
set (CMAKE_CXX_STANDARD 20) set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set (AUDIOLIB_SOURCES set (AUDIOLIB_SOURCES
Audio_Resampler.cpp Audio_Resampler.cpp
Audio_Resampler.h
Audio_Quality.cpp Audio_Quality.cpp
Audio_Quality.h
Audio_Mixer.cpp Audio_Mixer.cpp
Audio_Mixer.h
Audio_Interface.cpp Audio_Interface.cpp
Audio_Interface.h
Audio_Helper.cpp Audio_Helper.cpp
Audio_Helper.h
Audio_DataWindow.cpp Audio_DataWindow.cpp
Audio_DataWindow.h
Audio_DevicePair.cpp Audio_DevicePair.cpp
Audio_DevicePair.h
Audio_Player.cpp Audio_Player.cpp
Audio_Player.h
Audio_Null.cpp Audio_Null.cpp
Audio_Null.h
Audio_CoreAudio.cpp Audio_CoreAudio.cpp
Audio_CoreAudio.h
Audio_DirectSound.cpp Audio_DirectSound.cpp
Audio_DirectSound.h
Audio_AndroidOboe.cpp
Audio_AndroidOboe.h
Audio_WavFile.cpp Audio_WavFile.cpp
Audio_WavFile.h
) )
add_library(audio_lib ${AUDIOLIB_SOURCES}) add_library(audio_lib ${AUDIOLIB_SOURCES})
set_property(TARGET audio_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
## ##
target_include_directories(audio_lib target_include_directories(audio_lib
+113
View File
@@ -0,0 +1,113 @@
/* Copyright(C) 2007-2020 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __TOOLKIT_CONFIG_H
#define __TOOLKIT_CONFIG_H
#define USE_SPEEX_AEC
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
//#define USE_WEBRTC_AEC
#define USER
#define AUDIO_SAMPLE_WIDTH 16
#define AUDIO_CHANNELS 1
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
#define AUDIO_SAMPLERATE 8000
#define AUDIO_MIC_BUFFER_COUNT 16
#define AUDIO_MIC_BUFFER_LENGTH 10
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_SPK_BUFFER_COUNT 16
#define AUDIO_SPK_BUFFER_LENGTH 10
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_MIX_CHANNEL_COUNT 16
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
#define AUDIO_RESAMPLER_QUALITY 1
#define AEC_FRAME_TIME 10
#define AEC_TAIL_TIME 160
// Defined these two lines to get dumping of audio input/output
//#define AUDIO_DUMPINPUT
//#define AUDIO_DUMPOUTPUT
#define UA_REGISTRATION_TIME 3600
#define UA_MEDIA_PORT_START 20000
#define UA_MEDIA_PORT_FINISH 30000
#define UA_MAX_UDP_PACKET_SIZE 576
#define UA_PUBLICATION_ID "314"
#define MT_SAMPLERATE AUDIO_SAMPLERATE
#define MT_MAXAUDIOFRAME 1440
#define MT_MAXRTPPACKET 1500
#define MT_DTMF_END_PACKETS 3
#define RTP_BUFFER_HIGH 24480
#define RTP_BUFFER_LOW 10
#define RTP_BUFFER_PREBUFFER 80
#define RTP_DECODED_CAPACITY 2048
#define DEFAULT_SUBSCRIPTION_TIME 1200
#define DEFAULT_SUBSCRIPTION_REFRESHTIME 500
#define PRESENCE_IN_REG_HEADER "PresenceInReg"
// Maximum UDP packet length
#define MAX_UDPPACKET_SIZE 65535
#define MAX_VALID_UDPPACKET_SIZE 2048
// AMR codec defines - it requires USE_AMR_CODEC defined
// #define USE_AMR_CODEC
#define MT_AMRNB_PAYLOADTYPE 112
#define MT_AMRNB_CODECNAME "amr"
#define MT_AMRNB_OCTET_PAYLOADTYPE 113
#define MT_AMRWB_PAYLOADTYPE 96
#define MT_AMRWB_CODECNAME "amr-wb"
#define MT_AMRWB_OCTET_PAYLOADTYPE 97
#define MT_GSMEFR_PAYLOADTYPE 126
#define MT_GSMEFR_CODECNAME "GERAN-EFR"
#define MT_EVS_PAYLOADTYPE 127
#define MT_EVS_CODECNAME "EVS"
// OPUS codec defines
// #define USE_OPUS_CODEC
#define MT_OPUS_CODEC_PT 106
// ILBC codec defines
#define MT_ILBC20_PAYLOADTYPE -1
#define MT_ILBC30_PAYLOADTYPE -1
// ISAC codec defines
#define MT_ISAC16K_PAYLOADTYPE -1
#define MT_ISAC32K_PAYLOADTYPE -1
// GSM HR payload type
#define MT_GSMHR_PAYLOADTYPE -1
// Mirror buffer capacity
#define MT_MIRROR_CAPACITY 32768
// Mirror buffer readiness threshold - 50 milliseconds
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
# define TEXT(X) X
#endif
// In milliseconds
#define MT_SEVANA_FRAME_TIME 680
#endif
+88 -83
View File
@@ -10,15 +10,15 @@
#include <resip/stack/Pidf.hxx> #include <resip/stack/Pidf.hxx>
#include <resip/stack/PlainContents.hxx> #include <resip/stack/PlainContents.hxx>
#define LOG_SUBSYSTEM "engine" #define LOG_SUBSYSTEM "Account"
#define CONFIG(X) mConfig->at(X) #define CONFIG(X) mConfig->at(X)
#define CONFIG_EXISTS(X) mConfig->exists(X) #define CONFIG_EXISTS(X) mConfig->exists(X)
// #define MODIFY_VIA_BEHIND_NAT //#define MODIFY_VIA_BEHIND_NAT
// NAT decorator // NAT decorator
class NATDecorator : public resip::MessageDecorator class NATDecorator: public resip::MessageDecorator
{ {
protected: protected:
UserAgent& mUserAgent; UserAgent& mUserAgent;
@@ -35,26 +35,29 @@ public:
NATDecorator(UserAgent& endpoint); NATDecorator(UserAgent& endpoint);
virtual ~NATDecorator(); virtual ~NATDecorator();
virtual void decorateMessage(resip::SipMessage& msg, const resip::Tuple& source, const resip::Tuple& destination, virtual void decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId);
const resip::Data& sigcompId);
virtual void rollbackMessage(resip::SipMessage& msg); virtual void rollbackMessage(resip::SipMessage& msg);
virtual MessageDecorator* clone() const; virtual MessageDecorator* clone() const;
}; };
NATDecorator::NATDecorator(UserAgent& ua) : mUserAgent(ua), mViaPort(0), mContactsPort(0) {} NATDecorator::NATDecorator(UserAgent& ua)
:mUserAgent(ua), mViaPort(0), mContactsPort(0)
{
}
NATDecorator::~NATDecorator() {} NATDecorator::~NATDecorator()
{
}
void NATDecorator::decorateMessage(resip::SipMessage& msg, const resip::Tuple& source, const resip::Tuple& destination, void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId)
const resip::Data& sigcompId)
{ {
// Make a copy to allow rollback // Make a copy to allow rollback
mMessage = msg; mMessage = msg;
std::stringstream dump; std::stringstream dump;
mMessage.encode(dump); mMessage.encode(dump);
// ICELogDebug(<< "Decorating message: \n" << dump.str()); //ICELogDebug(<< "Decorating message: \n" << dump.str());
// Check From: header and find the account // Check From: header and find the account
resip::NameAddr from; resip::NameAddr from;
@@ -98,11 +101,11 @@ void NATDecorator::decorateMessage(resip::SipMessage& msg, const resip::Tuple& s
uri.port() = account->mExternalAddress.port(); uri.port() = account->mExternalAddress.port();
if (account->mConfig->at(CONFIG_SIPS).asBool()) if (account->mConfig->at(CONFIG_SIPS).asBool())
{ {
// uri.scheme() = "sips"; //uri.scheme() = "sips";
// uri.param(resip::p_transport) = "tls"; //uri.param(resip::p_transport) = "tls";
} }
// uri.scheme() = account->mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip"; //uri.scheme() = account->mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
} }
} }
} }
@@ -138,7 +141,7 @@ void NATDecorator::rollbackMessage(resip::SipMessage& msg)
{ {
uri.host() = mContactsHost; uri.host() = mContactsHost;
uri.port() = mContactsPort; uri.port() = mContactsPort;
// uri.scheme() = mContactsScheme; //uri.scheme() = mContactsScheme;
} }
} }
} }
@@ -150,16 +153,19 @@ resip::MessageDecorator* NATDecorator::clone() const
} }
Account::Account(PVariantMap config, UserAgent& agent) Account::Account(PVariantMap config, UserAgent& agent)
: mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None), mRegistration(NULL) :mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
mRegistration(NULL)
{ {
mProfile = std::make_shared<resip::UserProfile>(agent.mProfile); mProfile = resip::SharedPtr<resip::UserProfile>(new resip::UserProfile(agent.mProfile));
mId = Account::generateId(); mId = Account::generateId();
setup(*config); setup(*config);
} }
Account::~Account() {} Account::~Account()
{
}
void Account::setup(VariantMap& config) void Account::setup(VariantMap &config)
{ {
// Credentials // Credentials
@@ -170,8 +176,8 @@ void Account::setup(VariantMap& config)
mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()), mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()),
resip::Data(config[CONFIG_USERNAME].asStdString()), resip::Data(config[CONFIG_USERNAME].asStdString()),
resip::Data(config[CONFIG_PASSWORD].asStdString())); resip::Data(config[CONFIG_PASSWORD].asStdString()));
ICELogInfo(<< "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() << ", username to " ICELogInfo( << "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() <<
<< config[CONFIG_USERNAME].asStdString()); ", username to " << config[CONFIG_USERNAME].asStdString());
// Proxy // Proxy
mProfile->unsetOutboundProxy(); mProfile->unsetOutboundProxy();
@@ -195,7 +201,7 @@ void Account::setup(VariantMap& config)
} }
// NAT decorator // NAT decorator
mProfile->setOutboundDecorator(std::make_shared<NATDecorator>(mAgent)); mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent)));
// Rinstance // Rinstance
if (config.exists(CONFIG_INSTANCE_ID)) if (config.exists(CONFIG_INSTANCE_ID))
@@ -228,7 +234,7 @@ void Account::setup(VariantMap& config)
if (config[CONFIG_DOMAINPORT].asInt() != 0) if (config[CONFIG_DOMAINPORT].asInt() != 0)
from.uri().port() = config[CONFIG_DOMAINPORT].asInt(); from.uri().port() = config[CONFIG_DOMAINPORT].asInt();
else else
from.uri().port(); // = 5060; from.uri().port();// = 5060;
from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString()); from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString());
from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString()); from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString());
@@ -260,12 +266,10 @@ void Account::start()
// Create registration // Create registration
mRegistration = new ResipSession(*mAgent.mDum); mRegistration = new ResipSession(*mAgent.mDum);
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
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())) regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
.push_back(resip::StringCategory(iter->second.c_str()));
mRegistrationState = RegistrationState::Registering; mRegistrationState = RegistrationState::Registering;
@@ -274,10 +278,10 @@ void Account::start()
// Check if STUN IP is required // Check if STUN IP is required
bool noStunServerIp = !CONFIG_EXISTS(CONFIG_STUNSERVER_IP); bool noStunServerIp = !CONFIG_EXISTS(CONFIG_STUNSERVER_IP);
// bool hasStunServerName = !CONFIG(CONFIG_STUNSERVER_NAME).asStdString().empty(); //bool hasStunServerName = !CONFIG(CONFIG_STUNSERVER_NAME).asStdString().empty();
if (noStunServerIp) if (noStunServerIp)
{ {
ICELogInfo(<< "No STUN server name or IP is not specified. Has to resolve/discover STUN server IP."); ICELogInfo(<<"No STUN server name or IP is not specified. Has to resolve/discover STUN server IP.");
mRefreshStunServerIpTimer.start(CONFIG(CONFIG_DNS_CACHE_TIME).asInt() * 1000); mRefreshStunServerIpTimer.start(CONFIG(CONFIG_DNS_CACHE_TIME).asInt() * 1000);
mRefreshStunServerIpTimer.isTimeToSend(); mRefreshStunServerIpTimer.isTimeToSend();
queryStunServerIp(); queryStunServerIp();
@@ -301,7 +305,8 @@ void Account::stop()
mRegistrationHandle->removeAll(); mRegistrationHandle->removeAll();
mRegistrationHandle = resip::ClientRegistrationHandle(); mRegistrationHandle = resip::ClientRegistrationHandle();
} }
else if (mRegistration) else
if (mRegistration)
{ {
mRegistration->end(); mRegistration->end();
} }
@@ -356,8 +361,7 @@ void Account::publishPresence(bool online, const std::string& content, int secon
if (mPublication.isValid()) if (mPublication.isValid())
mPublication->update(&p); mPublication->update(&p);
else else
mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p, mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p, resip::Symbols::Presence, seconds));
resip::Symbols::Presence, seconds));
} }
void Account::stopPublish() void Account::stopPublish()
@@ -376,15 +380,15 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
observer->mSessionId = observer->mSession->sessionId(); observer->mSessionId = observer->mSession->sessionId();
observer->mPeer = target; observer->mPeer = target;
std::shared_ptr<resip::SipMessage> msg; resip::SharedPtr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME; int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME)) if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt(); expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME)) if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt(); refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt();
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile, resip::Data(package), expires, msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile,
refresh, observer->mSession); resip::Data(package), expires, refresh, observer->mSession);
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY); msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
mAgent.mClientObserverMap[observer->mSessionId] = observer; mAgent.mClientObserverMap[observer->mSessionId] = observer;
@@ -405,14 +409,12 @@ int Account::sendMsg(const std::string& peer, const void* ptr, unsigned length,
resip::Mime type; resip::Mime type;
std::string::size_type p = mime.find('/'); std::string::size_type p = mime.find('/');
if (p != std::string::npos) if (p != std::string::npos)
type = resip::Mime(resip::Data(mime.substr(0, p)), resip::Data(mime.substr(p + 1))); type = resip::Mime(resip::Data(mime.substr(0, p)), resip::Data(mime.substr(p+1)));
else else
type = resip::Mime(resip::Data(mime), resip::Data()); type = resip::Mime(resip::Data(mime), resip::Data());
resip::ClientPagerMessageHandle msgHandle = resip::ClientPagerMessageHandle msgHandle = mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s);
mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s); unique_ptr<resip::Contents> contentPtr(new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)),type));
unique_ptr<resip::Contents> contentPtr(
new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)), type));
int result = s->sessionId(); int result = s->sessionId();
msgHandle->page(std::move(contentPtr)); msgHandle->page(std::move(contentPtr));
@@ -449,13 +451,12 @@ resip::NameAddr Account::contact(SecureScheme ss)
void Account::queryStunServerIp() void Account::queryStunServerIp()
{ {
ICELogInfo(<< "Looking for STUN/TURN server IP"); ICELogInfo(<<"Looking for STUN/TURN server IP");
if (!mConfig->exists(CONFIG_STUNSERVER_NAME)) if (!mConfig->exists(CONFIG_STUNSERVER_NAME))
{ {
// Send request to find STUN or TURN service // Send request to find STUN or TURN service
std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." + std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." + mConfig->at(CONFIG_DOMAIN).asStdString();
mConfig->at(CONFIG_DOMAIN).asStdString();
// Start lookup // Start lookup
mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this); mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this);
@@ -474,7 +475,7 @@ void Account::queryStunServerIp()
} }
} }
void Account::prepareIceStack(Session* session, ice::AgentRole icerole) void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
{ {
ice::ServerConfig config; ice::ServerConfig config;
ice::NetworkAddress addr; ice::NetworkAddress addr;
@@ -494,10 +495,10 @@ void Account::prepareIceStack(Session* session, ice::AgentRole icerole)
config.mUseIPv4 = mAgent.config()[CONFIG_IPV4].asBool(); config.mUseIPv4 = mAgent.config()[CONFIG_IPV4].asBool();
config.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool(); config.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool();
// config.mDetectNetworkChange = true; //config.mDetectNetworkChange = true;
// config.mNetworkCheckInterval = 5000; //config.mNetworkCheckInterval = 5000;
session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config)); session->mIceStack = resip::SharedPtr<ice::Stack>(ice::Stack::makeICEBox(config));
session->mIceStack->setEventHandler(session, this); session->mIceStack->setEventHandler(session, this);
session->mIceStack->setRole(icerole); session->mIceStack->setRole(icerole);
} }
@@ -508,7 +509,7 @@ void Account::process()
queryStunServerIp(); queryStunServerIp();
} }
void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response) void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
{ {
// Save registration handle // Save registration handle
mRegistrationHandle = h; mRegistrationHandle = h;
@@ -521,7 +522,7 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
const resip::Via& via = response.header(resip::h_Vias).front(); const resip::Via& via = response.header(resip::h_Vias).front();
// Get the sent host // Get the sent host
const resip::Data& sentHost = via.sentHost(); // response.header(h_Contacts).front().uri().host(); const resip::Data& sentHost = via.sentHost();//response.header(h_Contacts).front().uri().host();
// Get the sentPort // Get the sentPort
int sentPort = via.sentPort(); int sentPort = via.sentPort();
@@ -551,14 +552,14 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip())); mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
} }
mUsedTransport = response.getReceivedTransportTuple().getType(); const resip::Transport* transport = response.getReceivedTransport();
// bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS; mUsedTransport = transport->transport();
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
// Retry registration for stream based transport too // Retry registration for stream based transport too
if ((hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
mConfig->at(CONFIG_EXTERNALIP).asBool())
{ {
// mRegistrationHandle->requestRefresh(); //mRegistrationHandle->requestRefresh();
// Unregister at first // Unregister at first
mRegistrationHandle->removeAll(); mRegistrationHandle->removeAll();
mRegistrationState = RegistrationState::Reregistering; mRegistrationState = RegistrationState::Reregistering;
@@ -570,12 +571,12 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
mAgent.onAccountStart(mAgent.getAccount(this)); mAgent.onAccountStart(mAgent.getAccount(this));
} }
void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response) void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
{ {
// Check if this unregistering is a part of rport pr // Check if this unregistering is a part of rport pr
if (mRegistrationState == RegistrationState::Reregistering) if (mRegistrationState == RegistrationState::Reregistering)
{ {
// if (/*this->mUseExternalIP && */response.getSource().getType() == resip::UDP) //if (/*this->mUseExternalIP && */response.getSource().getType() == resip::UDP)
{ {
resip::Uri hostport(contact(SecureScheme::TlsOnly).uri()); resip::Uri hostport(contact(SecureScheme::TlsOnly).uri());
hostport.host() = resip::Data(mExternalAddress.ip()); hostport.host() = resip::Data(mExternalAddress.ip());
@@ -585,24 +586,19 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
const char* transportName = nullptr; const char* transportName = nullptr;
switch (mUsedTransport) switch (mUsedTransport)
{ {
case resip::TCP: case resip::TCP: transportName = "tcp"; break;
transportName = "tcp"; case resip::TLS: transportName = "tls"; break;
break;
case resip::TLS:
transportName = "tls";
break;
} }
hostport.param(resip::p_transport) = resip::Data(transportName); hostport.param(resip::p_transport) = resip::Data(transportName);
} }
mProfile->setOverrideHostAndPort(hostport); mProfile->setOverrideHostAndPort(hostport);
// mProfile->setDefaultFrom(from); //mProfile->setDefaultFrom(from);
} }
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt()); mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME); resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++) for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
regmessage->header(resip::ExtensionHeader(iter->first.c_str())) regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
.push_back(resip::StringCategory(iter->second.c_str()));
mAgent.mDum->send(regmessage); mAgent.mDum->send(regmessage);
return; return;
@@ -630,17 +626,16 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
if (result.status == 0) if (result.status == 0)
{ {
resip::Data foundAddress = result.records.front().host(); resip::Data foundAddress = result.records.front().host();
ICELogInfo(<< "Success to resolve STUN/TURN address to " << foundAddress.c_str()); ICELogInfo( << "Success to resolve STUN/TURN address to " << foundAddress.c_str());
mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str()); mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str());
// Here the IP address of STUN/TURN server is found. If account is registered already - it means account is // Here the IP address of STUN/TURN server is found. If account is registered already - it means account is ready.
// ready.
if (mRegistrationState == RegistrationState::Registered) if (mRegistrationState == RegistrationState::Registered)
mAgent.onAccountStart(mAgent.getAccount(this)); mAgent.onAccountStart(mAgent.getAccount(this));
} }
else else
{ {
ICELogError(<< "Failed to resolve STUN or TURN server IP address."); ICELogError( << "Failed to resolve STUN or TURN server IP address.");
if (mRegistrationState == RegistrationState::Registered) if (mRegistrationState == RegistrationState::Registered)
{ {
int startCode = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString().empty() ? 0 : 503; int startCode = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString().empty() ? 0 : 503;
@@ -649,7 +644,10 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
} }
} }
void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&) {} void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&)
{
}
void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result) void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
{ {
@@ -657,14 +655,14 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
{ {
// Find lowest priority // Find lowest priority
int priority = 0x7FFFFFFF; int priority = 0x7FFFFFFF;
for (size_t i = 0; i < result.records.size(); i++) for (size_t i=0; i<result.records.size(); i++)
if (result.records[i].priority() < priority) if (result.records[i].priority() < priority)
priority = result.records[i].priority(); priority = result.records[i].priority();
size_t index = 0; size_t index = 0;
int weight = 0; int weight = 0;
for (size_t i = 0; i < result.records.size(); i++) for (size_t i=0; i<result.records.size(); i++)
{ {
if (result.records[i].priority() == priority && result.records[i].weight() >= weight) if (result.records[i].priority() == priority && result.records[i].weight() >= weight)
{ {
@@ -677,15 +675,15 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
const char* host = result.records[index].target().c_str(); const char* host = result.records[index].target().c_str();
ICELogInfo(<< "Success to find STUN/TURN server on " << result.records[index].target().c_str() << ":" ICELogInfo( << "Success to find STUN/TURN server on " << result.records[index].target().c_str() <<
<< (int)result.records[index].port()); ":" << (int)result.records[index].port());
if (inet_addr(host) == INADDR_NONE) if (inet_addr(host) == INADDR_NONE)
{ {
// Try to resolve domain name now // Try to resolve domain name now
mAgent.mStack->getDnsStub().lookup<resip::RR_A>(result.records[index].target(), this); mAgent.mStack->getDnsStub().lookup<resip::RR_A>(result.records[index].target(), this);
// mStack->getDnsStub().lookup<resip::RR_AAAA>(result.records[index].target(), this); //mStack->getDnsStub().lookup<resip::RR_AAAA>(result.records[index].target(), this);
} }
else else
{ {
@@ -694,16 +692,23 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
} }
else else
{ {
ICELogError(<< "Failed to find STUN or TURN service for specified domain."); ICELogError( << "Failed to find STUN or TURN service for specified domain.");
// mAgent::shutdown(); //mAgent::shutdown();
} }
} }
void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&) {} void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&)
{
void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&) {} }
bool Account::isResponsibleFor(const resip::NameAddr& addr) void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&)
{
}
bool Account::isResponsibleFor(const resip::NameAddr &addr)
{ {
std::string user = addr.uri().user().c_str(); std::string user = addr.uri().user().c_str();
std::string domain = addr.uri().host().c_str(); std::string domain = addr.uri().host().c_str();
@@ -720,7 +725,7 @@ bool Account::isResponsibleFor(const resip::NameAddr& addr)
return false; return false;
} }
void Account::setUserInfo(const UserInfo& info) void Account::setUserInfo(const UserInfo &info)
{ {
mUserInfo = info; mUserInfo = info;
if (mRegistrationHandle.isValid()) if (mRegistrationHandle.isValid())
@@ -735,8 +740,8 @@ Account::UserInfo Account::getUserInfo() const
return mUserInfo; return mUserInfo;
} }
std::atomic_int Account::IdGenerator; resip::AtomicCounter Account::IdGenerator;
int Account::generateId() int Account::generateId()
{ {
return ++IdGenerator; return IdGenerator.increment();
} }
+6 -7
View File
@@ -22,11 +22,10 @@
class UserAgent; class UserAgent;
class Session; class Session;
class Account : public resip::DnsResultSink class Account: public resip::DnsResultSink
{ {
friend class UserAgent; friend class UserAgent;
friend class NATDecorator; friend class NATDecorator;
public: public:
Account(PVariantMap config, UserAgent& agent); Account(PVariantMap config, UserAgent& agent);
~Account(); ~Account();
@@ -66,7 +65,7 @@ public:
void setup(VariantMap& config); void setup(VariantMap& config);
/* Returns corresponding resiprocate profile */ /* Returns corresponding resiprocate profile */
std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; } resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; }
typedef std::map<std::string, std::string> UserInfo; typedef std::map<std::string, std::string> UserInfo;
void setUserInfo(const UserInfo& info); void setUserInfo(const UserInfo& info);
@@ -84,7 +83,7 @@ protected:
RegistrationState mRegistrationState; RegistrationState mRegistrationState;
ice::NetworkAddress mExternalAddress; ice::NetworkAddress mExternalAddress;
std::shared_ptr<resip::UserProfile> mProfile; resip::SharedPtr<resip::UserProfile> mProfile;
UserAgent& mAgent; UserAgent& mAgent;
bool mPresenceOnline; bool mPresenceOnline;
std::string mPresenceContent; std::string mPresenceContent;
@@ -136,7 +135,7 @@ protected:
#pragma endregion #pragma endregion
static int generateId(); static int generateId();
static std::atomic_int IdGenerator; static resip::AtomicCounter IdGenerator;
}; };
typedef std::shared_ptr<Account> PAccount; typedef std::shared_ptr<Account> PAccount;
+56 -53
View File
@@ -14,10 +14,11 @@
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
#include "../helper/HL_String.h" #include "../helper/HL_String.h"
#define LOG_SUBSYSTEM "engine" #define LOG_SUBSYSTEM "AudioProvider"
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal) AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
: mUserAgent(agent), mTerminal(terminal), mState(0), mRemoteTelephoneCodec(0), mRemoteNoSdp(false) :mUserAgent(agent), mTerminal(terminal), mState(0),
mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
{ {
mActive = mfActive; mActive = mfActive;
mRemoteState = msSendRecv; mRemoteState = msSendRecv;
@@ -25,11 +26,12 @@ AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY)) if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap()); mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
mSrtpSuite = SRTP_NONE; mSrtpSuite = SRTP_NONE;
setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending);
(int)StreamState::Sending);
} }
AudioProvider::~AudioProvider() {} AudioProvider::~AudioProvider()
{
}
std::string AudioProvider::streamName() std::string AudioProvider::streamName()
{ {
@@ -53,7 +55,7 @@ void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
mActiveStream->setDestination(addr); mActiveStream->setDestination(addr);
} }
void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag) void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer, void* userTag)
{ {
mMediaObserver = observer; mMediaObserver = observer;
mMediaObserverTag = userTag; mMediaObserverTag = userTag;
@@ -62,21 +64,20 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver* observer,
} }
// Processes incoming data // Processes incoming data
void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source)
{ {
if (!mActiveStream) if (!mActiveStream)
return; return;
if (RtpHelper::isRtpOrRtcp(dataBuffer, dataSize)) if (RtpHelper::isRtpOrRtcp(dataBuffer, dataSize))
{ {
ICELogMedia(<< "Adding new data to stream processing"); ICELogMedia(<<"Adding new data to stream processing");
mActiveStream->dataArrived(s, dataBuffer, dataSize, source); mActiveStream->dataArrived(s, dataBuffer, dataSize, source);
} }
} }
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer, void AudioProvider::sendData(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size)
unsigned int size)
{ {
s->sendDatagram(destination, buffer, size); s->sendDatagram(destination, buffer, size);
} }
@@ -92,19 +93,18 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
// Check if SRTP suite is found already or not // Check if SRTP suite is found already or not
if (mSrtpSuite == SRTP_NONE) if (mSrtpSuite == SRTP_NONE)
{ {
// RFC 4568 requires a unique tag per crypto attribute; use the suite id.
for (int suite = SRTP_AES_128_AUTH_80; suite <= SRTP_LAST; suite++) for (int suite = SRTP_AES_128_AUTH_80; suite <= SRTP_LAST; suite++)
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite, suite))); sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite)));
} }
else else
// Answer/re-offer: echo the tag of the negotiated attribute. sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite, mSrtpTag)));
} }
#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())
{ {
for (int i = 0; i < mCodecPriority.count(mTerminal.codeclist()); i++) for (int i=0; i<mCodecPriority.count(mTerminal.codeclist()); i++)
mCodecPriority.codecAt(mTerminal.codeclist(), i).updateSdp(sdp.codecs(), direction); mCodecPriority.codecAt(mTerminal.codeclist(), i).updateSdp(sdp.codecs(), direction);
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent); sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
} }
@@ -114,42 +114,27 @@ 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;
switch (mActive) switch (mActive)
{ {
case mfActive: case mfActive:
switch (mRemoteState) switch(mRemoteState)
{ {
case msSendonly: case msSendonly: attr = "recvonly"; break;
attr = "recvonly"; case msInactive: attr = "recvonly"; break;
break;
case msInactive:
attr = "recvonly";
break;
case msRecvonly:
case msSendRecv:
break; // Do nothing here
} }
break; break;
case mfPaused: case mfPaused:
switch (mRemoteState) switch (mRemoteState)
{ {
case msRecvonly: case msRecvonly: attr = "sendonly"; break;
attr = "sendonly"; case msSendonly: attr = "inactive"; break;
break; case msInactive: attr = "inactive"; break;
case msSendonly: case msSendRecv: attr = "sendonly"; break;
attr = "inactive";
break;
case msInactive:
attr = "inactive";
break;
case msSendRecv:
attr = "sendonly";
break;
} }
break; break;
} }
@@ -239,11 +224,13 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
// Use CodecListPriority mCodecPriority to work with codec priorities // Use CodecListPriority mCodecPriority to work with codec priorities
int pt; int pt;
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())
@@ -260,13 +247,11 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
{ {
const resip::Data& attr = *attrIter; const resip::Data& attr = *attrIter;
ByteBuffer tempkey; ByteBuffer tempkey;
int tag = 1; SrtpSuite suite = processCryptoAttribute(attr, tempkey);
SrtpSuite suite = processCryptoAttribute(attr, tempkey, &tag); if (suite > ss)
if (srtpSuiteStrength(suite) > srtpSuiteStrength(ss))
{ {
ss = suite; ss = suite;
mSrtpSuite = suite; mSrtpSuite = suite;
mSrtpTag = tag;
key = tempkey; key = tempkey;
} }
} }
@@ -311,30 +296,40 @@ MT::PStream AudioProvider::activeStream()
return mActiveStream; return mActiveStream;
} }
std::string AudioProvider::createCryptoAttribute(SrtpSuite suite, int tag) 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;
if (!keyBuffer)
return "";
resip::Data d(keyBuffer->data(), keyBuffer->size()); resip::Data d(keyBuffer->data(), keyBuffer->size());
resip::Data keyText = d.base64encode(); resip::Data keyText = d.base64encode();
return std::format("{} {} inline:{}", tag, toString(suite), keyText.c_str()); // Create "crypto" attribute value
char buffer[512];
const char* suiteName = NULL;
switch (suite)
{
case SRTP_AES_128_AUTH_80: suiteName = SRTP_SUITE_NAME_1; break;
case SRTP_AES_256_AUTH_80: suiteName = SRTP_SUITE_NAME_2; break;
default: assert(0);
}
sprintf(buffer, "%d %s inline:%s", srtpTag, suiteName, keyText.c_str());
return buffer;
} }
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag) SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
{ {
int srtpTag = 0; int srtpTag = 0;
char suite[64], keyChunk[256]; char suite[64], keyChunk[256];
int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk); int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk);
if (components != 3) if (components != 3)
return SRTP_NONE; return SRTP_NONE;
if (tag)
*tag = srtpTag;
const char* delimiter = strchr(keyChunk, '|'); const char* delimiter = strchr(keyChunk, '|');
resip::Data keyText; resip::Data keyText;
@@ -346,7 +341,15 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu
resip::Data rawkey = keyText.base64decode(); resip::Data rawkey = keyText.base64decode();
key = ByteBuffer(rawkey.c_str(), rawkey.size()); key = ByteBuffer(rawkey.c_str(), rawkey.size());
return toSrtpSuite(suite); // Open srtp
SrtpSuite result = SRTP_NONE;
if (strcmp(suite, SRTP_SUITE_NAME_1) == 0)
result = SRTP_AES_128_AUTH_80;
else
if (strcmp(suite, SRTP_SUITE_NAME_2) == 0)
result = SRTP_AES_256_AUTH_80;
return result;
} }
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs) void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
@@ -379,9 +382,9 @@ void AudioProvider::setupMirror(bool enable)
mActiveStream->setupMirror(enable); mActiveStream->setupMirror(enable);
} }
void AudioProvider::setStateImpl(unsigned int state) void AudioProvider::setStateImpl(unsigned int state) {
{
mState = state; mState = state;
if (mActiveStream) if (mActiveStream)
mActiveStream->setState(state); mActiveStream->setState(state);
} }
+13 -11
View File
@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * 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,15 +12,18 @@
#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>
class UserAgent; class UserAgent;
class AudioProvider : public DataProvider class AudioProvider: public DataProvider
{ {
public: public:
AudioProvider(UserAgent& agent, MT::Terminal& terminal); AudioProvider(UserAgent& agent, MT::Terminal& terminal);
virtual ~AudioProvider(); virtual ~AudioProvider();
@@ -34,11 +37,10 @@ public:
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override; void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
// Processes incoming data // Processes incoming data
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override; void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
unsigned int datasize) override;
// Updates SDP offer // Updates SDP offer
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override; void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
@@ -74,7 +76,6 @@ public:
void setupMirror(bool enable); void setupMirror(bool enable);
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag); void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag = nullptr);
protected: protected:
// SDP's stream name // SDP's stream name
@@ -93,12 +94,11 @@ protected:
unsigned mState; unsigned mState;
SrtpSuite mSrtpSuite; SrtpSuite mSrtpSuite;
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
struct RemoteCodec struct RemoteCodec
{ {
RemoteCodec(MT::Codec::Factory* factory, int payloadType) : mFactory(factory), mRemotePayloadType(payloadType) RemoteCodec(MT::Codec::Factory* factory, int payloadType)
{ :mFactory(factory), mRemotePayloadType(payloadType)
} { }
MT::Codec::Factory* mFactory; MT::Codec::Factory* mFactory;
int mRemotePayloadType; int mRemotePayloadType;
@@ -110,11 +110,13 @@ protected:
MT::Stream::MediaObserver* mMediaObserver = nullptr; MT::Stream::MediaObserver* mMediaObserver = nullptr;
void* mMediaObserverTag = nullptr; void* mMediaObserverTag = nullptr;
std::string createCryptoAttribute(SrtpSuite suite, int tag); 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)
void setStateImpl(unsigned state); void setStateImpl(unsigned state);
}; };
#endif #endif
+10 -8
View File
@@ -10,7 +10,7 @@ bool DataProvider::isSupported(const char* name)
{ {
return !strcmp(name, "audio"); return !strcmp(name, "audio");
// return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video")); //return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video"));
} }
void DataProvider::pause() void DataProvider::pause()
@@ -20,7 +20,7 @@ void DataProvider::pause()
// Stop receive RTP stream // Stop receive RTP stream
if (state() & (int)StreamState::Receiving) if (state() & (int)StreamState::Receiving)
setState(state() & ~(int)StreamState::Receiving); setState( state() & ~(int)StreamState::Receiving );
mActive = mfPaused; mActive = mfPaused;
} }
@@ -28,10 +28,10 @@ void DataProvider::pause()
void DataProvider::resume() void DataProvider::resume()
{ {
// Tell remote peer about resumed receiving in SDP // Tell remote peer about resumed receiving in SDP
// setState( state() | STATE_SIPRECV ); //setState( state() | STATE_SIPRECV );
// Start receive RTP stream // Start receive RTP stream
setState(state() | (int)StreamState::Receiving); setState( state() | (int)StreamState::Receiving );
mActive = mfActive; mActive = mfActive;
} }
@@ -44,15 +44,17 @@ bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& me
mRemoteState = msSendonly; mRemoteState = msSendonly;
setState(state() & ~(int)StreamState::Sending); setState(state() & ~(int)StreamState::Sending);
} }
else if (media.exists("recvonly")) else
if (media.exists("recvonly"))
{ {
mRemoteState = msRecvonly; mRemoteState = msRecvonly;
setState(state() & ~(int)StreamState::Receiving); setState(state() & ~(int)StreamState::Receiving);
} }
else if (media.exists("inactive")) else
if (media.exists("inactive"))
{ {
mRemoteState = msInactive; mRemoteState = msInactive;
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving)); setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving) );
} }
else else
{ {
@@ -64,7 +66,7 @@ bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& me
break; break;
case mfPaused: case mfPaused:
setState(state() | (int)StreamState::Sending); setState(state() | (int)StreamState::Sending );
break; break;
} }
} }
+3 -4
View File
@@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "resip/stack/SdpContents.hxx" #include "resip/stack/SdpContents.hxx"
#include "rutil/SharedPtr.hxx"
#include "../helper/HL_InternetAddress.h" #include "../helper/HL_InternetAddress.h"
#include "../helper/HL_NetworkSocket.h" #include "../helper/HL_NetworkSocket.h"
@@ -45,12 +46,10 @@ public:
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0; virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
// Processes incoming data // Processes incoming data
virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
InternetAddress& address) = 0;
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, virtual void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
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;
File diff suppressed because it is too large Load Diff
+34 -44
View File
@@ -47,7 +47,7 @@
#include "../ice/ICETime.h" #include "../ice/ICETime.h"
#include <sstream> #include <sstream>
#include <time.h> #include <time.h>
#include "../engine_config.h" #include "../config.h"
#include "EP_Session.h" #include "EP_Session.h"
#include "EP_Observer.h" #include "EP_Observer.h"
#include "EP_DataProvider.h" #include "EP_DataProvider.h"
@@ -87,8 +87,7 @@ enum
CONFIG_ICEUSERNAME, // Optional username for TURN server. String value. CONFIG_ICEUSERNAME, // Optional username for TURN server. String value.
CONFIG_ICEPASSWORD, // Optional password for TURN server. String value. CONFIG_ICEPASSWORD, // Optional password for TURN server. String value.
CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value. CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value.
CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_NAME.
// CONFIG_STUNSERVER_NAME.
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value. CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value. CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value. CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
@@ -99,8 +98,7 @@ enum
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
CONFIG_ROOTCERT, // Additional root cert in PEM format; string. CONFIG_ROOTCERT, // Additional root cert in PEM format; string.
CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to reduce number of steps of SIP transaction
// reduce number of steps of SIP transaction
CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true. CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
CONFIG_DEFERRELAYED, // Defer relayed media path CONFIG_DEFERRELAYED, // Defer relayed media path
@@ -110,7 +108,7 @@ enum
CONFIG_ACCOUNT, // VariantMap with account configuration CONFIG_ACCOUNT, // VariantMap with account configuration
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
CONFIG_OWN_DNS, // Use predefined DNS servers CONFIG_OWN_DNS, // Use predefined DNS servers
CONFIG_REGID // reg-id value from RFC5626, CONFIG_REGID // reg-id value from RFC5626
}; };
// Conntype parameter for OnSessionEstablished event // Conntype parameter for OnSessionEstablished event
@@ -141,11 +139,11 @@ enum
RemoteBye, RemoteBye,
LocalCancel, LocalCancel,
RemoteCancel, RemoteCancel,
Rejected, // Only as UAS, UAC has distinct onFailure callback Rejected, //Only as UAS, UAC has distinct onFailure callback
Referred Referred
}; };
class UserAgent : public resip::ClientRegistrationHandler, class UserAgent: public resip::ClientRegistrationHandler,
public resip::InviteSessionHandler, public resip::InviteSessionHandler,
public resip::DumShutdownHandler, public resip::DumShutdownHandler,
public resip::ExternalLogger, public resip::ExternalLogger,
@@ -154,21 +152,19 @@ 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;
friend class ResipSession; friend class ResipSession;
friend class NATDecorator; friend class NATDecorator;
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. /* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
* 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;
@@ -191,8 +187,7 @@ public:
This is asynchronous method. onStop() event will be called later */ This is asynchronous method. onStop() event will be called later */
void shutdown(); void shutdown();
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything. /* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything. onStop() is called in context of this method. */
* onStop() is called in context of this method. */
void stop(); void stop();
/* Checks if user agent is active (started). */ /* Checks if user agent is active (started). */
@@ -273,8 +268,7 @@ public:
virtual void onClientObserverStop(PClientObserver observer, int code); virtual void onClientObserverStop(PClientObserver observer, int code);
virtual void onServerObserverStop(PServerObserver observer, int code); virtual void onServerObserverStop(PServerObserver observer, int code);
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, const std::string& content);
const std::string& content);
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length); virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag); virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag); virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
@@ -286,17 +280,14 @@ public:
// InviteSessionHandler implementation // InviteSessionHandler implementation
#pragma region InviteSessionHandler implementation #pragma region InviteSessionHandler implementation
/// called when an initial INVITE or the intial response to an outoing invite /// called when an initial INVITE or the intial response to an outoing invite
virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
const resip::SipMessage& msg) override; virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat,
const resip::SipMessage& msg) override;
/// Received a failure response from UAS /// Received a failure response from UAS
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override; virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an in-dialog provisional response is received that contains an SDP body /// called when an in-dialog provisional response is received that contains an SDP body
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&, virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&, const resip::SdpContents&) override;
const resip::SdpContents&) override;
/// called when dialog enters the Early state - typically after getting 18x /// called when dialog enters the Early state - typically after getting 18x
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override; virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
@@ -307,8 +298,7 @@ public:
/// called when a dialog initiated as a UAS enters the connected state /// called when a dialog initiated as a UAS enters the connected state
virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override; virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason, virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related=0) override;
const resip::SipMessage* related = 0) override;
/// called when a fork that was created through a 1xx never receives a 2xx /// called when a fork that was created through a 1xx never receives a 2xx
/// because another fork answered and this fork was canceled by a proxy. /// because another fork answered and this fork was canceled by a proxy.
@@ -355,8 +345,7 @@ public:
/// rejected using the server subscription. If the offer is accepted, /// rejected using the server subscription. If the offer is accepted,
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an /// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
/// InviteSession that will send notify messages using the ServerSubscription /// InviteSession that will send notify messages using the ServerSubscription
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg) override;
const resip::SipMessage& msg) override;
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override; virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
@@ -364,11 +353,10 @@ public:
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override; virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message receives an accepted response /// called when an REFER message receives an accepted response
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg) override;
const resip::SipMessage& msg) override;
#pragma endregion #pragma endregion
// ClientRegistrationHandler implementation // ClientRegistrationHandler implementation
#pragma region ClientRegistrationHandler implementation #pragma region ClientRegistrationHandler implementation
/// Called when registraion succeeds or each time it is sucessfully /// Called when registraion succeeds or each time it is sucessfully
/// refreshed. /// refreshed.
@@ -389,9 +377,13 @@ public:
#pragma region ExternalLogger implementation #pragma region ExternalLogger implementation
/** return true to also do default logging, false to suppress default logging. */ /** return true to also do default logging, false to suppress default logging. */
virtual bool operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName, virtual bool operator()(resip::Log::Level level,
const char* file, int line, const resip::Data& message, const resip::Subsystem& subsystem,
const resip::Data& messageWithHeaders, const resip::Data& instanceName) override; const resip::Data& appName,
const char* file,
int line,
const resip::Data& message,
const resip::Data& messageWithHeaders) override;
#pragma endregion #pragma endregion
#pragma region DnsResultSink implementation #pragma region DnsResultSink implementation
@@ -405,7 +397,7 @@ public:
#pragma endregion #pragma endregion
#pragma region TransportLogger implementation #pragma region TransportLogger implementation
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen); void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
#pragma endregion #pragma endregion
#pragma region ClientPublicationHandler #pragma region ClientPublicationHandler
@@ -420,13 +412,13 @@ public:
void onUpdatePending(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override; void onUpdatePending(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
void onUpdateActive(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override; void onUpdateActive(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
// unknown Subscription-State value //unknown Subscription-State value
void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override; void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override; int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override;
// subscription can be ended through a notify or a failure response. //subscription can be ended through a notify or a failure response.
void onTerminated(resip::ClientSubscriptionHandle, const resip::SipMessage* msg) override; void onTerminated(resip::ClientSubscriptionHandle, const resip::SipMessage* msg) override;
// not sure if this has any value. //not sure if this has any value.
void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override; void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override;
/// called to allow app to adorn a message. /// called to allow app to adorn a message.
@@ -445,19 +437,17 @@ public:
#pragma region PagerHandler #pragma region PagerHandler
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override; void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, std::unique_ptr<resip::Contents> contents) override;
std::unique_ptr<resip::Contents> contents) override;
void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override; void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override;
#pragma endregion #pragma endregion
void onDumCanBeDeleted() override; void onDumCanBeDeleted() override;
protected: protected:
// Mutex to protect this instance // Mutex to protect this instance
Mutex mGuard; Mutex mGuard;
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here. // Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
std::shared_ptr<resip::MasterProfile> mProfile; resip::SharedPtr<resip::MasterProfile> mProfile;
// Resiprocate's SIP stack object pointer // Resiprocate's SIP stack object pointer
resip::SipStack* mStack; resip::SipStack* mStack;
+20 -14
View File
@@ -1,19 +1,23 @@
#include "EP_NetworkQueue.h" #include "EP_NetworkQueue.h"
#include "EP_Engine.h" #include "EP_Engine.h"
WatcherQueue::WatcherQueue(UserAgent& ua) : mActiveId(0), mAgent(ua) {} WatcherQueue::WatcherQueue(UserAgent& ua)
:mActiveId(0), mAgent(ua)
{}
WatcherQueue::~WatcherQueue() {} WatcherQueue::~WatcherQueue()
{}
int WatcherQueue::add(const std::string& peer, const std::string& package, void* tag) int WatcherQueue::add(std::string peer, std::string package, void* tag)
{ {
ice::Lock l(mGuard); ice::Lock l(mGuard);
// Check if queue has similar item // Check if queue has similar item
for (unsigned i = 0; i < mItemList.size(); i++) for (unsigned i=0; i<mItemList.size(); i++)
{ {
Item& item = mItemList[i]; Item& item = mItemList[i];
if (item.mTarget == peer && item.mPackage == package && item.mState != Item::State_Deleting) if (item.mTarget == peer && item.mPackage == package &&
item.mState != Item::State_Deleting)
return item.mId; return item.mId;
} }
@@ -40,9 +44,10 @@ void WatcherQueue::remove(int id)
ice::Lock l(mGuard); ice::Lock l(mGuard);
// Check if queue has similar item // Check if queue has similar item
for (auto& item : mItemList) for (unsigned i=0; i<mItemList.size(); i++)
{ {
if (item.mId == id && id) Item& item = mItemList[i];
if (item.mId == id && !id)
{ {
if (item.mState != Item::State_Deleting) if (item.mState != Item::State_Deleting)
item.mState = Item::State_ScheduledToDelete; item.mState = Item::State_ScheduledToDelete;
@@ -57,9 +62,10 @@ void WatcherQueue::refresh(int id)
ice::Lock l(mGuard); ice::Lock l(mGuard);
// Check if queue has similar item // Check if queue has similar item
for (auto& item : mItemList) for (unsigned i=0; i<mItemList.size(); i++)
{ {
if (item.mId == id && id) Item& item = mItemList[i];
if (item.mId == id && !id)
{ {
if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active) if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active)
item.mState = Item::State_ScheduledToRefresh; item.mState = Item::State_ScheduledToRefresh;
@@ -74,12 +80,12 @@ void WatcherQueue::process()
{ {
// Find next item to process // Find next item to process
ItemList::iterator i = mItemList.begin(); ItemList::iterator i = mItemList.begin();
for (; i != mItemList.end() && !i->scheduled(); i++) for (;i != mItemList.end() && !i->scheduled(); i++)
; ;
if (i == mItemList.end()) if (i == mItemList.end())
return; return;
std::shared_ptr<resip::SipMessage> msg; resip::SharedPtr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME; int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
switch (i->mState) switch (i->mState)
@@ -136,9 +142,9 @@ void WatcherQueue::onTerminated(int id, int code)
{ {
if (i->mSession) if (i->mSession)
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0); i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
mItemList.erase(i);
if (i->mId == mActiveId) if (i->mId == mActiveId)
mActiveId = 0; mActiveId = 0;
mItemList.erase(i);
} }
process(); process();
} }
@@ -158,7 +164,7 @@ void WatcherQueue::onEstablished(int id, int code)
WatcherQueue::ItemList::iterator WatcherQueue::findById(int id) WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
{ {
for (ItemList::iterator i = mItemList.begin(); i != mItemList.end(); i++) for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
if (i->mId == id) if (i->mId == id)
return i; return i;
return mItemList.end(); return mItemList.end();
@@ -167,7 +173,7 @@ WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
void WatcherQueue::clear() void WatcherQueue::clear()
{ {
ice::Lock l(mGuard); ice::Lock l(mGuard);
for (ItemList::iterator i = mItemList.begin(); i != mItemList.end(); i++) for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
{ {
if (i->mHandle.isValid()) if (i->mHandle.isValid())
i->mHandle->end(); i->mHandle->end();
+9 -8
View File
@@ -28,25 +28,26 @@ public:
}; };
resip::ClientSubscriptionHandle mHandle; // Subscription handle resip::ClientSubscriptionHandle mHandle; // Subscription handle
ResipSession* mSession = nullptr; ResipSession* mSession;
State mState = State::State_None; State mState;
std::string mTarget; // Target's address std::string mTarget; // Target's address
std::string mPackage; // Event package std::string mPackage; // Event package
void* mTag = nullptr; // User tag void* mTag; // User tag
int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only) int mId;
Item() {} Item()
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
{}
bool scheduled() bool scheduled()
{ {
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || mState == State_ScheduledToRefresh;
mState == State_ScheduledToRefresh;
} }
}; };
WatcherQueue(UserAgent& agent); WatcherQueue(UserAgent& agent);
~WatcherQueue(); ~WatcherQueue();
int add(const std::string& peer, const std::string& package, void* tag); int add(std::string peer, std::string package, void* tag);
void remove(int id); void remove(int id);
void refresh(int id); void refresh(int id);
void clear(); void clear();
+13 -4
View File
@@ -9,9 +9,13 @@
#include <resip/stack/Pidf.hxx> #include <resip/stack/Pidf.hxx>
#include <resip/dum/ClientSubscription.hxx> #include <resip/dum/ClientSubscription.hxx>
ClientObserver::ClientObserver() {} ClientObserver::ClientObserver()
{
}
ClientObserver::~ClientObserver() {} ClientObserver::~ClientObserver()
{
}
void ClientObserver::refresh() void ClientObserver::refresh()
{ {
@@ -23,7 +27,8 @@ void ClientObserver::stop()
{ {
if (mHandle.isValid()) if (mHandle.isValid())
mHandle->end(); mHandle->end();
else if (mSession) else
if (mSession)
{ {
mSession->runTerminatedEvent(ResipSession::Type_Subscription); mSession->runTerminatedEvent(ResipSession::Type_Subscription);
if (mSession) if (mSession)
@@ -37,7 +42,11 @@ std::string ClientObserver::peer()
return mPeer; return mPeer;
} }
ServerObserver::ServerObserver() : mState(State_Incoming) {} ServerObserver::ServerObserver()
:mState(State_Incoming)
{
}
ServerObserver::~ServerObserver() ServerObserver::~ServerObserver()
{ {
-2
View File
@@ -23,7 +23,6 @@ class ClientObserver
{ {
friend class Account; friend class Account;
friend class UserAgent; friend class UserAgent;
public: public:
ClientObserver(); ClientObserver();
~ClientObserver(); ~ClientObserver();
@@ -44,7 +43,6 @@ typedef std::shared_ptr<ClientObserver> PClientObserver;
class ServerObserver class ServerObserver
{ {
friend class UserAgent; friend class UserAgent;
public: public:
ServerObserver(); ServerObserver();
~ServerObserver(); ~ServerObserver();
+12 -13
View File
@@ -16,8 +16,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> # include <winsock2.h>
#include <windows.h> # include <windows.h>
#endif #endif
#include <algorithm> #include <algorithm>
@@ -44,6 +44,7 @@ ReliableTunnel::ReliableTunnel(const char* streamname)
mBandwidth = 0; mBandwidth = 0;
mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
} }
ReliableTunnel::~ReliableTunnel() ReliableTunnel::~ReliableTunnel()
@@ -140,7 +141,7 @@ DatagramSocket& ReliableTunnel::socket(int family)
bool ReliableTunnel::processSdpOffer(const resip::SdpContents::Session::Medium& media) bool ReliableTunnel::processSdpOffer(const resip::SdpContents::Session::Medium& media)
{ {
// check for default port number //check for default port number
mDestination.setPort(media.port()); mDestination.setPort(media.port());
return true; return true;
@@ -151,13 +152,13 @@ void ReliableTunnel::thread()
// Construct event array // Construct event array
while (true) while (true)
{ {
HANDLE eventarray[2] = {mDataSignal, mExitSignal}; HANDLE eventarray[2] = { mDataSignal, mExitSignal };
DWORD rescode = ::WaitForMultipleObjects(2, eventarray, FALSE, INFINITE); DWORD rescode = ::WaitForMultipleObjects(2, eventarray, FALSE, INFINITE);
if (rescode == WAIT_OBJECT_0) if (rescode == WAIT_OBJECT_0)
{ {
resip::Lock l(mNewQueuedGuard); resip::Lock l(mNewQueuedGuard);
for (unsigned i = 0; i < mNewQueued.size(); i++) for (unsigned i = 0; i<mNewQueued.size(); i++)
mStack.queueOutgoing(mNewQueued[i].c_str(), mNewQueued[i].size()); mStack.queueOutgoing(mNewQueued[i].c_str(), mNewQueued[i].size());
mNewQueued.clear(); mNewQueued.clear();
@@ -236,13 +237,12 @@ void ReliableTunnel::encrypt(void* dataPtr, int dataSize)
return; return;
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
for (unsigned i = 0; i < dataSize / blockSize(); i++) for (unsigned i=0; i<dataSize / blockSize(); i++)
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_ENCRYPT);
BF_ENCRYPT);
#endif #endif
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
for (unsigned i = 0; i < dataSize / blockSize(); i++) for (unsigned i=0; i<dataSize / blockSize(); i++)
mEncryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize()); mEncryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
#endif #endif
} }
@@ -253,13 +253,12 @@ void ReliableTunnel::decrypt(void* dataPtr, int dataSize)
if (mEncryptionKey.empty()) if (mEncryptionKey.empty())
return; return;
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
for (unsigned i = 0; i < dataSize / blockSize(); i++) for (unsigned i=0; i<dataSize / blockSize(); i++)
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_DECRYPT);
BF_DECRYPT);
#endif #endif
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
for (unsigned i = 0; i < dataSize / blockSize(); i++) for (unsigned i=0; i<dataSize / blockSize(); i++)
mDecryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize()); mDecryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
#endif #endif
} }
+5 -4
View File
@@ -26,13 +26,13 @@
#include "../ICE/ICEReliableTransport.h" #include "../ICE/ICEReliableTransport.h"
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
#include "../Libs/CryptoPP/blowfish.h" # include "../Libs/CryptoPP/blowfish.h"
#endif #endif
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
#include "../Libs/openssl/include/openssl/blowfish.h" # include "../Libs/openssl/include/openssl/blowfish.h"
#endif #endif
class ReliableTunnel : public DataProvider, public resip::ThreadIf, public ICEImpl::ReliableTransport::Encryption class ReliableTunnel: public DataProvider, public resip::ThreadIf, public ICEImpl::ReliableTransport::Encryption
{ {
public: public:
ReliableTunnel(const char* streamname); ReliableTunnel(const char* streamname);
@@ -111,7 +111,8 @@ protected:
// Mutex to protect queuing/sending outgoing data // Mutex to protect queuing/sending outgoing data
resip::Mutex mOutgoingMtx; resip::Mutex mOutgoingMtx;
std::vector<std::string> mNewQueued; std::vector<std::string>
mNewQueued;
resip::Mutex mNewQueuedGuard; resip::Mutex mNewQueuedGuard;
resip::Mutex mStackGuard; resip::Mutex mStackGuard;
+127 -128
View File
@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * 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,36 +7,43 @@
#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"
#define LOG_SUBSYSTEM "engine" #define LOG_SUBSYSTEM "[Engine]"
typedef resip::SdpContents::Session::Medium Medium; typedef resip::SdpContents::Session::Medium Medium;
typedef resip::SdpContents::Session::MediumContainer MediumContainer; typedef resip::SdpContents::Session::MediumContainer MediumContainer;
#define IS_MULTIPLEX() \ #define DOMULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
//------------ ResipSessionAppDialog ------------ //------------ ResipSessionAppDialog ------------
#pragma region ResipSessionAppDialog #pragma region ResipSessionAppDialog
ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham) {} ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham)
{
}
ResipSessionAppDialog::~ResipSessionAppDialog() {} ResipSessionAppDialog::~ResipSessionAppDialog()
{
}
#pragma endregion #pragma endregion
#pragma region ResipSession #pragma region ResipSession
std::atomic_int ResipSession::InstanceCounter; resip::AtomicCounter ResipSession::InstanceCounter;
ResipSession::ResipSession(resip::DialogUsageManager& dum) ResipSession::ResipSession(resip::DialogUsageManager& dum)
: resip::AppDialogSet(dum), mUserAgent(nullptr), mType(Type_None), mSessionId(0), mSession(0) : resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
{ {
ResipSession::InstanceCounter++; ResipSession::InstanceCounter.increment();
mTag = nullptr; mTag = NULL;
mTerminated = false; mTerminated = false;
mOnWatchingStartSent = false; mOnWatchingStartSent = false;
mSessionId = Session::generateId(); mSessionId = Session::generateId();
@@ -51,11 +58,11 @@ ResipSession::~ResipSession()
mSession->mResipSession = nullptr; mSession->mResipSession = nullptr;
runTerminatedEvent(Type_Auto, 0, 0); runTerminatedEvent(Type_Auto, 0, 0);
} }
catch (...) catch(...)
{ {
} }
ResipSession::InstanceCounter--; ResipSession::InstanceCounter.decrement();
} }
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg) resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
@@ -91,8 +98,7 @@ void ResipSession::runTerminatedEvent(Type type, int code, int reason)
case Type_Subscription: case Type_Subscription:
if (mSession) if (mSession)
{ {
UserAgent::ClientObserverMap::iterator observerIter = UserAgent::ClientObserverMap::iterator observerIter = mUserAgent->mClientObserverMap.find(mSession->sessionId());
mUserAgent->mClientObserverMap.find(mSession->sessionId());
if (observerIter != mUserAgent->mClientObserverMap.end()) if (observerIter != mUserAgent->mClientObserverMap.end())
mUserAgent->onClientObserverStop(observerIter->second, code); mUserAgent->onClientObserverStop(observerIter->second, code);
} }
@@ -161,12 +167,12 @@ int ResipSession::sessionId()
return mSessionId; return mSessionId;
} }
void ResipSession::setUASProfile(const std::shared_ptr<resip::UserProfile>& profile) void ResipSession::setUASProfile(std::shared_ptr<resip::UserProfile> profile)
{ {
mUASProfile = profile; mUASProfile = profile;
} }
std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg) resip::SharedPtr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
{ {
assert(mUserAgent != nullptr); assert(mUserAgent != nullptr);
@@ -178,15 +184,20 @@ std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const res
else else
return mUserAgent->mProfile; return mUserAgent->mProfile;
} }
return std::shared_ptr<resip::UserProfile>(); return resip::SharedPtr<resip::UserProfile>();
} }
#pragma endregion #pragma endregion
#pragma region Session::Stream #pragma region Session::Stream
Session::Stream::Stream() : mRtcpAttr(false), mRtcpMuxAttr(false) {} Session::Stream::Stream()
:mRtcpAttr(false), mRtcpMuxAttr(false)
{
}
Session::Stream::~Stream() {} Session::Stream::~Stream()
{
}
void Session::Stream::setProvider(PDataProvider provider) void Session::Stream::setProvider(PDataProvider provider)
{ {
@@ -251,11 +262,11 @@ void Session::Stream::setRtcpMuxAttr(bool value)
#pragma endregion #pragma endregion
#pragma region Session #pragma region Session
std::atomic_int Session::InstanceCounter; resip::AtomicCounter Session::InstanceCounter;
Session::Session(PAccount account) Session::Session(PAccount account)
{ {
InstanceCounter++; InstanceCounter.increment();
mAccount = account; mAccount = account;
mSessionId = Session::generateId(); mSessionId = Session::generateId();
mTag = NULL; mTag = NULL;
@@ -267,7 +278,7 @@ Session::Session(PAccount account)
mRole = Acceptor; mRole = Acceptor;
mGatheredCandidates = false; mGatheredCandidates = false;
mTerminated = false; mTerminated = false;
mRemoteOriginVersion = (uint64_t)-1; mRemoteOriginVersion = (UInt64)-1;
mResipSession = NULL; mResipSession = NULL;
mRefCount = 1; mRefCount = 1;
mOfferAnswerCounter = 0; mOfferAnswerCounter = 0;
@@ -283,15 +294,14 @@ Session::~Session()
mResipSession->setSession(NULL); mResipSession->setSession(NULL);
clearProvidersAndSockets(); clearProvidersAndSockets();
} }
catch (...) catch(...)
{ {}
} InstanceCounter.decrement();
InstanceCounter--;
} }
void Session::start(const std::string& peer) void Session::start(const std::string& peer)
{ {
ICELogInfo(<< "Attempt to start session to " << peer); ICELogInfo( << "Attempt to start session to " << peer);
Lock l(mGuard); Lock l(mGuard);
if (mResipSession) if (mResipSession)
@@ -328,7 +338,7 @@ void Session::stop()
{ {
ICELogInfo(<< "Stopping session " << mSessionId); ICELogInfo(<< "Stopping session " << mSessionId);
Lock l(mGuard); Lock l(mGuard);
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
{ {
Session::Stream& dataStream = mStreamList[i]; Session::Stream& dataStream = mStreamList[i];
@@ -338,12 +348,8 @@ void Session::stop()
dataStream.provider()->sessionTerminated(); dataStream.provider()->sessionTerminated();
// Free socket // Free socket
SocketHeap::instance().freeSocketPair(dataStream.socket4()); SocketHeap::instance().freeSocketPair( dataStream.socket4() );
SocketHeap::instance().freeSocketPair(dataStream.socket6()); SocketHeap::instance().freeSocketPair( dataStream.socket6() );
// Drop the references so the destructor's cleanup does not free them again
dataStream.setSocket4(RtpPair<PDatagramSocket>());
dataStream.setSocket6(RtpPair<PDatagramSocket>());
} }
} }
@@ -360,8 +366,7 @@ void Session::accept()
Lock locksession(mGuard); Lock locksession(mGuard);
// If ICE candidate gathering is not finished - just mark session as accepted. It will be accepted in ICE handling // If ICE candidate gathering is not finished - just mark session as accepted. It will be accepted in ICE handling code.
// code.
mAcceptedByUser = true; mAcceptedByUser = true;
if (mGatheredCandidates || mIceStack->state() == ice::IceNone) if (mGatheredCandidates || mIceStack->state() == ice::IceNone)
@@ -400,7 +405,7 @@ void Session::accept()
void Session::reject(int code) void Session::reject(int code)
{ {
ICELogInfo(<< "Attempt to reject session " << mSessionId); ICELogInfo( << "Attempt to reject session " << mSessionId);
Lock l(mGuard); Lock l(mGuard);
@@ -444,52 +449,56 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
MT::Statistics stat; MT::Statistics stat;
// Iterate all session providers // Iterate all session providers
Stream* media = nullptr; stat.reset();
for (Stream& stream : mStreamList) Stream* media = NULL;
for (unsigned streamIndex = 0; streamIndex < mStreamList.size(); streamIndex++)
{
Stream& stream = mStreamList[streamIndex];
if (stream.provider())
{ {
if (!stream.provider())
continue;
media = &stream; media = &stream;
MT::Statistics s = stream.provider()->getStatistics(); MT::Statistics s = stream.provider()->getStatistics();
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos()); #if defined(USE_PVQA_LIBRARY) && !defined(TARGET_SERVER)
if (options != InfoOptions::Standard)
{
// This information is available AFTER audio stream is deleted
info[SessionInfo_PvqaMos] = s.mPvqaMos;
info[SessionInfo_PvqaReport] = s.mPvqaReport;
}
#endif
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
info[SessionInfo_AudioCodec] = s.mCodecName; info[SessionInfo_AudioCodec] = s.mCodecName;
stat += s; stat += s;
} }
}
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived); info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent); info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
info[SessionInfo_ReceivedRtp] = static_cast<int>(stat.mReceivedRtp); info[SessionInfo_ReceivedRtp] = static_cast<int>(stat.mReceivedRtp);
info[SessionInfo_ReceivedRtpTraffic] = static_cast<int>(stat.mReceivedRtpBytes);
info[SessionInfo_ReceivedRtcp] = static_cast<int>(stat.mReceivedRtcp); info[SessionInfo_ReceivedRtcp] = static_cast<int>(stat.mReceivedRtcp);
info[SessionInfo_LostRtp] = static_cast<int>(stat.mPacketLoss); info[SessionInfo_LostRtp] = static_cast<int>(stat.mPacketLoss);
info[SessionInfo_DroppedRtp] = static_cast<int>(stat.mPacketDropped); info[SessionInfo_DroppedRtp] = static_cast<int>(stat.mPacketDropped);
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp); info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp); info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
if (stat.mFirstRtpTime) if (stat.mFirstRtpTime)
info[SessionInfo_Duration] = static_cast<int>( info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - *(stat.mFirstRtpTime)).count());
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;
if (stat.mReceivedRtp) if (stat.mReceivedRtp)
info[SessionInfo_PacketLoss] = static_cast<int>((stat.mPacketLoss * 1000) / stat.mReceivedRtp); info[SessionInfo_PacketLoss] = static_cast<int>((stat.mPacketLoss * 1000) / stat.mReceivedRtp);
if (media && mIceStack) if (media)
info[SessionInfo_AudioPeer] = info[SessionInfo_AudioPeer] = mIceStack->remoteAddress(media->iceInfo().mStreamId, media->iceInfo().mComponentId.mRtp).toStdString();
mIceStack->remoteAddress(media->iceInfo().mStreamId, media->iceInfo().mComponentId.mRtp).toStdString();
info[SessionInfo_Jitter] = stat.mJitter; info[SessionInfo_Jitter] = stat.mJitter;
if (stat.mRttDelay.is_initialized()) if (stat.mRttDelay.is_initialized())
info[SessionInfo_Rtt] = static_cast<float>(stat.mRttDelay * 1000); info[SessionInfo_Rtt] = static_cast<float>(stat.mRttDelay * 1000);
#if defined(USE_AMR_CODEC) #if defined(USE_AMR_CODEC)
info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter; info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter;
info[SessionInfo_CngCounter] = stat.mCng;
#endif #endif
// Variant stores VTYPE_INT here; keep the 32 bits (consumers read it back with asInt()). info[SessionInfo_SSRC] = stat.mSsrc;
info[SessionInfo_SSRC] = static_cast<int>(stat.mSsrc);
info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString(); info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString();
} }
@@ -503,8 +512,7 @@ PAccount Session::account()
return mAccount; return mAccount;
} }
void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize)
unsigned receivedSize)
{ {
Lock l(mGuard); Lock l(mGuard);
@@ -512,7 +520,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
return; return;
// Check if it STUN packet and must be processed by ICE stack // Check if it STUN packet and must be processed by ICE stack
// ICELogDebug (<< "Received UDP packet from " << src.ip() << ":" << src.port()); //ICELogDebug (<< "Received UDP packet from " << src.ip() << ":" << src.port());
ice::ByteBuffer received(receivedPtr, receivedSize); ice::ByteBuffer received(receivedPtr, receivedSize);
received.setRemoteAddress(src); received.setRemoteAddress(src);
@@ -530,7 +538,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
if (received.size() >= 4) if (received.size() >= 4)
{ {
bool turnPrefix = false; bool turnPrefix = false;
for (unsigned i = 0; i < mTurnPrefixList.size() && !turnPrefix; i++) for (unsigned i=0; i<mTurnPrefixList.size() && !turnPrefix; i++)
turnPrefix |= ice::Stack::isChannelData(received, mTurnPrefixList[i]); turnPrefix |= ice::Stack::isChannelData(received, mTurnPrefixList[i]);
if (turnPrefix) if (turnPrefix)
received.erase(0, 4); received.erase(0, 4);
@@ -541,11 +549,12 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
{ {
// Try to process incoming data by ICE stack // Try to process incoming data by ICE stack
int component = -1, stream = -1; int component = -1, stream = -1;
bool processed;
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component)) if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
{ {
ice::ByteBuffer buffer(receivedPtr, receivedSize); ice::ByteBuffer buffer(receivedPtr, receivedSize);
buffer.setRemoteAddress(src); buffer.setRemoteAddress(src);
/*bool processed = */ mIceStack->processIncomingData(stream, component, buffer); processed = mIceStack->processIncomingData(stream, component, buffer);
} }
} }
else else
@@ -593,7 +602,8 @@ void Session::onGathered(ice::Stack* stack, void* tag)
if (mRole == Initiator) if (mRole == Initiator)
mUserAgent->sendOffer(this); mUserAgent->sendOffer(this);
else if (mRole == Acceptor) else
if (mRole == Acceptor)
{ {
// Mark session as gathered ICE candidates // Mark session as gathered ICE candidates
mGatheredCandidates = true; mGatheredCandidates = true;
@@ -614,7 +624,7 @@ void Session::onSuccess(ice::Stack* stack, void* tag)
RtpPair<InternetAddress> t; RtpPair<InternetAddress> t;
for (unsigned i = 0; i < this->mStreamList.size(); i++) for (unsigned i=0; i<this->mStreamList.size(); i++)
{ {
PDataProvider p = mStreamList[i].provider(); PDataProvider p = mStreamList[i].provider();
if (p) if (p)
@@ -636,8 +646,8 @@ void Session::onSuccess(ice::Stack* stack, void* tag)
mUserAgent->onSessionEstablished(mUserAgent->getUserSession(mSessionId), EV_ICE, t); mUserAgent->onSessionEstablished(mUserAgent->getUserSession(mSessionId), EV_ICE, t);
// time to resend updated media info over SIP //time to resend updated media info over SIP
// TODO: //TODO:
} }
void Session::onFailed(ice::Stack* stack, void* tag) void Session::onFailed(ice::Stack* stack, void* tag)
@@ -645,17 +655,17 @@ void Session::onFailed(ice::Stack* stack, void* tag)
ICELogError(<< "ICE connectivity check failed for session " << mSessionId); ICELogError(<< "ICE connectivity check failed for session " << mSessionId);
mUserAgent->onConnectivityFailed(mUserAgent->getUserSession(mSessionId)); mUserAgent->onConnectivityFailed(mUserAgent->getUserSession(mSessionId));
// if (mInviteHandle.isValid()) //if (mInviteHandle.isValid())
// mInviteHandle->end(); // mInviteHandle->end();
} }
void Session::onNetworkChange(ice::Stack* stack, void* tag) void Session::onNetworkChange(ice::Stack *stack, void *tag)
{ {
ICELogInfo(<< "Network change detected by ICE stack for session " << mSessionId); ICELogInfo(<< "Network change detected by ICE stack for session " << mSessionId);
mUserAgent->onNetworkChange(mUserAgent->getUserSession(mSessionId)); mUserAgent->onNetworkChange(mUserAgent->getUserSession(mSessionId));
} }
void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection) void Session::buildSdp(resip::SdpContents &sdp, SdpDirection sdpDirection)
{ {
sdp.session().name() = "ICE_UA"; sdp.session().name() = "ICE_UA";
sdp.session().origin().user() = "user"; sdp.session().origin().user() = "user";
@@ -673,12 +683,8 @@ void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection)
ice::NetworkAddress defaultAddr = mIceStack->defaultAddress(mStreamList.front().iceInfo().mStreamId, ICE_RTP_ID); ice::NetworkAddress defaultAddr = mIceStack->defaultAddress(mStreamList.front().iceInfo().mStreamId, ICE_RTP_ID);
// Set IP address for origin and connection // Set IP address for origin and connection
sdp.session().origin().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET sdp.session().origin().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET ? resip::SdpContents::IP4 : resip::SdpContents::IP6);
? resip::SdpContents::IP4 sdp.session().connection().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET ? resip::SdpContents::IP4 : resip::SdpContents::IP6);
: resip::SdpContents::IP6);
sdp.session().connection().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET
? resip::SdpContents::IP4
: resip::SdpContents::IP6);
// Add ICE credentials // Add ICE credentials
if (mIceStack->state() > ice::IceNone) if (mIceStack->state() > ice::IceNone)
@@ -688,7 +694,7 @@ void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection)
} }
// Iterate media streams // Iterate media streams
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
{ {
Stream& stream = mStreamList[i]; Stream& stream = mStreamList[i];
if (!stream.provider()) if (!stream.provider())
@@ -700,18 +706,18 @@ void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection)
rtcpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTCP_ID); rtcpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTCP_ID);
// Define media stream SDP's header // Define media stream SDP's header
resip::SdpContents::Session::Medium media(resip::Data(provider.streamName()), rtpPort.port(), 0, resip::SdpContents::Session::Medium media(resip::Data(provider.streamName()), rtpPort.port(), 0, resip::Data(provider.streamProfile()));
resip::Data(provider.streamProfile()));
// Add "rtcp" attribute // Add "rtcp" attribute
if (mUserAgent->mConfig[CONFIG_RTCP_ATTR].asBool()) if (mUserAgent->mConfig[CONFIG_RTCP_ATTR].asBool())
{ {
if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool()) if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
rtcpPort = rtpPort; rtcpPort = rtpPort;
else if (rtcpPort.isEmpty()) else
if (rtcpPort.isEmpty())
{ {
rtcpPort = rtpPort; rtcpPort = rtpPort;
rtcpPort.setPort(rtpPort.port() + 1); rtcpPort.setPort( rtpPort.port() + 1);
} }
media.addAttribute("rtcp", resip::Data(rtcpPort.port())); media.addAttribute("rtcp", resip::Data(rtcpPort.port()));
@@ -734,7 +740,7 @@ void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection)
if (mIceStack->hasComponent(ii.mStreamId, ii.mComponentId.mRtcp)) if (mIceStack->hasComponent(ii.mStreamId, ii.mComponentId.mRtcp))
mIceStack->fillCandidateList(ii.mStreamId, ii.mComponentId.mRtcp, candidates); mIceStack->fillCandidateList(ii.mStreamId, ii.mComponentId.mRtcp, candidates);
for (unsigned c = 0; c < candidates.size(); c++) for (unsigned c=0; c<candidates.size(); c++)
media.addAttribute("candidate", candidates[c].c_str()); media.addAttribute("candidate", candidates[c].c_str());
} }
@@ -749,12 +755,9 @@ PDataProvider Session::findProviderByPort(int family, unsigned short port)
{ {
Stream& s = mStreamList[i]; Stream& s = mStreamList[i];
// Sockets may not be allocated yet (stream created from SDP, sockets follow later) if ((s.socket4().mRtp->localport() == port || s.socket4().mRtcp->localport() == port) && family == AF_INET)
if (family == AF_INET && s.socket4().mRtp && s.socket4().mRtcp &&
(s.socket4().mRtp->localport() == port || s.socket4().mRtcp->localport() == port))
return s.provider(); return s.provider();
if (family == AF_INET6 && s.socket6().mRtp && s.socket6().mRtcp && if ((s.socket6().mRtp->localport() == port || s.socket6().mRtcp->localport() == port) && family == AF_INET6)
(s.socket6().mRtp->localport() == port || s.socket6().mRtcp->localport() == port))
return s.provider(); return s.provider();
} }
@@ -768,7 +771,7 @@ void Session::addProvider(PDataProvider provider)
return; return;
// Avoid duplicating providers // Avoid duplicating providers
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
if (mStreamList[i].provider() == provider) if (mStreamList[i].provider() == provider)
return; return;
@@ -779,18 +782,18 @@ void Session::addProvider(PDataProvider provider)
{ {
if (!streamIter->provider() && (streamIter->socket4().mRtp->isValid() || streamIter->socket6().mRtp->isValid())) if (!streamIter->provider() && (streamIter->socket4().mRtp->isValid() || streamIter->socket6().mRtp->isValid()))
{ {
streamIter->setProvider(provider); streamIter->setProvider( provider );
provider->setSocket(streamIter->socket4(), streamIter->socket6()); provider->setSocket(streamIter->socket4(), streamIter->socket6());
return; return;
} }
} }
Stream s; Stream s;
s.setProvider(provider); s.setProvider( provider );
// Allocate socket for provider // Allocate socket for provider
s.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX())); s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()) );
s.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX())); s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()) );
s.provider()->setSocket(s.socket4(), s.socket6()); s.provider()->setSocket(s.socket4(), s.socket6());
// Create ICE stream/component // Create ICE stream/component
@@ -799,11 +802,10 @@ void Session::addProvider(PDataProvider provider)
ii.mPort4 = s.socket4().mRtp->localport(); ii.mPort4 = s.socket4().mRtp->localport();
ii.mPort6 = s.socket6().mRtp->localport(); ii.mPort6 = s.socket6().mRtp->localport();
ii.mComponentId.mRtp = ii.mComponentId.mRtp = mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(),
mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(), s.socket6().mRtp->localport()); s.socket6().mRtp->localport());
if (!mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool()) if (!mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
ii.mComponentId.mRtcp = ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtcp->localport(), s.socket6().mRtcp->localport());
mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtcp->localport(), s.socket6().mRtcp->localport());
s.setIceInfo(ii); s.setIceInfo(ii);
@@ -827,10 +829,10 @@ int Session::sessionId()
return mSessionId; return mSessionId;
} }
std::atomic_int Session::IdGenerator; resip::AtomicCounter Session::IdGenerator;
int Session::generateId() int Session::generateId()
{ {
return ++IdGenerator; return (int)IdGenerator.increment();
} }
std::string Session::remoteAddress() const std::string Session::remoteAddress() const
@@ -860,7 +862,7 @@ void Session::setTag(void* tag)
void Session::pause() void Session::pause()
{ {
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
{ {
Stream& s = mStreamList[i]; Stream& s = mStreamList[i];
if (s.provider()) if (s.provider())
@@ -871,7 +873,7 @@ void Session::pause()
void Session::resume() void Session::resume()
{ {
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
{ {
Stream& s = mStreamList[i]; Stream& s = mStreamList[i];
if (s.provider()) if (s.provider())
@@ -883,9 +885,9 @@ void Session::resume()
void Session::refreshMediaPath() void Session::refreshMediaPath()
{ {
// Recreate media sockets // Recreate media sockets
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
{ {
Stream& s = mStreamList[i]; Stream& s= mStreamList[i];
PDataProvider p = s.provider(); PDataProvider p = s.provider();
if (!p) if (!p)
continue; continue;
@@ -894,8 +896,8 @@ void Session::refreshMediaPath()
SocketHeap::instance().freeSocketPair(p->socket(AF_INET)); SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
// Bring new socket to provider and stream // Bring new socket to provider and stream
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()), RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX() ),
s6 = SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()); s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX());
p->setSocket(s4, s6); p->setSocket(s4, s6);
s.setSocket4(s4); s.setSocket4(s4);
@@ -912,7 +914,7 @@ void Session::refreshMediaPath()
} }
// Received offer with new SDP // Received offer with new SDP
int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd, std::string iceUfrag, int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media) std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
{ {
bool iceRestart = false; bool iceRestart = false;
@@ -932,7 +934,7 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
// Ask about provider if needed // Ask about provider if needed
if (!stream.provider()) if (!stream.provider())
stream.setProvider(mUserAgent->onProviderNeeded(remoteStream.name().c_str())); stream.setProvider( mUserAgent->onProviderNeeded(remoteStream.name().c_str()) );
// Check the stream validity // Check the stream validity
if (!stream.provider()) if (!stream.provider())
@@ -941,8 +943,8 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
continue; continue;
// See for rtcp & rtcp-mux attribute // See for rtcp & rtcp-mux attribute
stream.setRtcpAttr(remoteStream.exists("rtcp")); stream.setRtcpAttr( remoteStream.exists("rtcp") );
stream.setRtcpMuxAttr(remoteStream.exists("rtcp-mux")); stream.setRtcpMuxAttr( remoteStream.exists("rtcp-mux") );
// Set destination address // Set destination address
if (!remoteStream.getConnections().empty()) if (!remoteStream.getConnections().empty())
@@ -954,12 +956,12 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
targetAddr.mRtcp.setIp(remoteIp); targetAddr.mRtcp.setIp(remoteIp);
if (stream.rtcpMuxAttr()) if (stream.rtcpMuxAttr())
targetAddr.mRtcp.setPort(remoteStream.port()); targetAddr.mRtcp.setPort( remoteStream.port() );
else if (stream.rtcpAttr())
targetAddr.mRtcp.setPort(
strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1));
else else
targetAddr.mRtcp.setPort(remoteStream.port() + 1); if (stream.rtcpAttr())
targetAddr.mRtcp.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
else
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
stream.provider()->setDestinationAddress(targetAddr); stream.provider()->setDestinationAddress(targetAddr);
@@ -974,12 +976,12 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
{ {
try try
{ {
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX())); stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()));
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX())); stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()));
} }
catch (...) catch(...)
{ {
ICELogError(<< "Cannot create media socket."); ICELogError( << "Cannot create media socket.");
return 503; return 503;
} }
@@ -1008,15 +1010,13 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
// See what remote peer offers - offer only single ice component if it relies on multiplexing // See what remote peer offers - offer only single ice component if it relies on multiplexing
if (!targetAddr.multiplexed() && !mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool()) if (!targetAddr.multiplexed() && !mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, stream.socket4().mRtcp->localport(), ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, stream.socket4().mRtcp->localport(), stream.socket6().mRtcp->localport());
stream.socket6().mRtcp->localport());
stream.setIceInfo(ii); stream.setIceInfo(ii);
} }
if (iceAvailable) if (iceAvailable)
{ {
if (mIceStack->remotePassword(stream.iceInfo().mStreamId) != icePwd || if (mIceStack->remotePassword(stream.iceInfo().mStreamId) != icePwd || mIceStack->remoteUfrag(stream.iceInfo().mStreamId) != iceUfrag)
mIceStack->remoteUfrag(stream.iceInfo().mStreamId) != iceUfrag)
{ {
iceRestart = true; iceRestart = true;
mIceStack->setRemotePassword(icePwd, stream.iceInfo().mStreamId); mIceStack->setRemotePassword(icePwd, stream.iceInfo().mStreamId);
@@ -1037,11 +1037,9 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
if (candidateVector.empty()) if (candidateVector.empty())
iceAvailable = false; iceAvailable = false;
// Ask ICE stack to process this information. This call will remove also second component if it is not defined // Ask ICE stack to process this information. This call will remove also second component if it is not defined in remote sdp.
// in remote sdp.
if (iceAvailable) if (iceAvailable)
iceAvailable = mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteIp, remotePort, iceAvailable = mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteIp, remotePort, mUserAgent->mConfig[CONFIG_DEFERRELAYED].asBool());
mUserAgent->mConfig[CONFIG_DEFERRELAYED].asBool());
} }
// See if there are compatible media streams // See if there are compatible media streams
@@ -1088,22 +1086,22 @@ int Session::release()
void Session::clearProvidersAndSockets() void Session::clearProvidersAndSockets()
{ {
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
{ {
Session::Stream& ds = mStreamList[i]; Session::Stream& ds = mStreamList[i];
if (ds.provider()) if (ds.provider())
{ {
ds.provider()->sessionDeleted(); ds.provider()->sessionDeleted();
SocketHeap::instance().freeSocketPair(ds.socket4()); SocketHeap::instance().freeSocketPair( ds.socket4() );
SocketHeap::instance().freeSocketPair(ds.socket6()); SocketHeap::instance().freeSocketPair( ds.socket6() );
} }
} }
} }
void Session::clearProviders() void Session::clearProviders()
{ {
for (unsigned i = 0; i < mStreamList.size(); i++) for (unsigned i=0; i<mStreamList.size(); i++)
{ {
Session::Stream& ds = mStreamList[i]; Session::Stream& ds = mStreamList[i];
@@ -1139,13 +1137,14 @@ void Session::processQueuedOffer()
//-------------- ResipSessionFactory --------- //-------------- ResipSessionFactory ---------
#pragma region ResipSessionFactory #pragma region ResipSessionFactory
ResipSessionFactory::ResipSessionFactory(UserAgent* agent) : mAgent(agent) {} ResipSessionFactory::ResipSessionFactory(UserAgent* agent)
:mAgent(agent)
{}
resip::AppDialogSet* ResipSessionFactory::createAppDialogSet(resip::DialogUsageManager& dum, resip::AppDialogSet* ResipSessionFactory::createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg)
const resip::SipMessage& msg)
{ {
ResipSession* s = new ResipSession(dum); ResipSession* s = new ResipSession(dum);
s->setUa(mAgent); s->setUa( mAgent );
return s; return s;
} }
+19 -19
View File
@@ -28,14 +28,15 @@
#include "rutil/Logger.hxx" #include "rutil/Logger.hxx"
#include "rutil/Random.hxx" #include "rutil/Random.hxx"
#include "rutil/WinLeakCheck.hxx" #include "rutil/WinLeakCheck.hxx"
#include "rutil/AtomicCounter.hxx"
#include "../ice/ICEBox.h" #include "../ice/ICEBox.h"
#include <sstream> #include <sstream>
#include <atomic>
#include <time.h> #include <time.h>
#include "../engine_config.h" #include "../config.h"
#include "EP_Session.h"
#include "EP_Account.h" #include "EP_Account.h"
#include "EP_DataProvider.h" #include "EP_DataProvider.h"
#include "EP_AudioProvider.h" #include "EP_AudioProvider.h"
@@ -72,12 +73,12 @@ enum SessionInfo
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
SessionInfo_RemotePeer, SessionInfo_RemotePeer,
SessionInfo_SSRC, SessionInfo_SSRC,
SessionInfo_CngCounter, // For AMR codecs only
SessionInfo_ReceivedRtpTraffic // amount of received RTP traffic in bytes, RTCP excluded
}; };
class Session : public SocketSink, public ice::StageHandler class Session :
public SocketSink,
public ice::StageHandler
{ {
public: public:
class Command class Command
@@ -89,7 +90,8 @@ public:
// Describes ice stream/component // Describes ice stream/component
struct IceInfo struct IceInfo
{ {
IceInfo() : mStreamId(-1) IceInfo()
:mStreamId(-1)
{ {
mPort4 = mPort6 = 0; mPort4 = mPort6 = 0;
mComponentId.mRtp = mComponentId.mRtcp = -1; mComponentId.mRtp = mComponentId.mRtcp = -1;
@@ -212,8 +214,7 @@ public:
void refreshMediaPath(); void refreshMediaPath();
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error). // Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
// There are passing string objects by value; this is correct; this values will modified on the stack. int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media); std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
// Session ID // Session ID
@@ -223,7 +224,7 @@ public:
std::vector<Stream> mStreamList; std::vector<Stream> mStreamList;
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method // Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
std::shared_ptr<ice::Stack> mIceStack; resip::SharedPtr<ice::Stack> mIceStack;
// Pointer to owner user agent instance // Pointer to owner user agent instance
UserAgent* mUserAgent; UserAgent* mUserAgent;
@@ -236,7 +237,7 @@ public:
// SDP's origin version for sending // SDP's origin version for sending
int mOriginVersion; int mOriginVersion;
uint64_t mRemoteOriginVersion; UInt64 mRemoteOriginVersion;
// SDP's session version // SDP's session version
int mSessionVersion; int mSessionVersion;
@@ -317,8 +318,8 @@ public:
void enqueueOffer(); void enqueueOffer();
void processQueuedOffer(); void processQueuedOffer();
static int generateId(); static int generateId();
static std::atomic_int IdGenerator; static resip::AtomicCounter IdGenerator;
static std::atomic_int InstanceCounter; static resip::AtomicCounter InstanceCounter;
}; };
typedef std::shared_ptr<Session> PSession; typedef std::shared_ptr<Session> PSession;
@@ -339,10 +340,10 @@ public:
virtual ~ResipSessionAppDialog(); virtual ~ResipSessionAppDialog();
}; };
class ResipSession : public resip::AppDialogSet class ResipSession: public resip::AppDialogSet
{ {
friend class UserAgent; friend class UserAgent;
friend class Account; friend class Account;
public: public:
enum Type enum Type
@@ -353,13 +354,13 @@ public:
Type_Call, Type_Call,
Type_Auto Type_Auto
}; };
static std::atomic_int InstanceCounter; static resip::AtomicCounter InstanceCounter;
ResipSession(resip::DialogUsageManager& dum); ResipSession(resip::DialogUsageManager& dum);
virtual ~ResipSession(); virtual ~ResipSession();
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg); virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg); virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
void setType(Type type); void setType(Type type);
Type type(); Type type();
@@ -383,7 +384,7 @@ public:
void runTerminatedEvent(Type type, int code = 0, int reason = 0); void runTerminatedEvent(Type type, int code = 0, int reason = 0);
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile); void setUASProfile(std::shared_ptr<resip::UserProfile> profile);
protected: protected:
bool mTerminated; bool mTerminated;
@@ -403,7 +404,6 @@ class ResipSessionFactory : public resip::AppDialogSetFactory
public: public:
ResipSessionFactory(UserAgent* agent); ResipSessionFactory(UserAgent* agent);
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg); virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
protected: protected:
UserAgent* mAgent; UserAgent* mAgent;
}; };
-119
View File
@@ -1,119 +0,0 @@
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __TOOLKIT_CONFIG_H
#define __TOOLKIT_CONFIG_H
#define USE_SPEEX_AEC
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
// #define USE_WEBRTC_AEC
#define USER
#define AUDIO_SAMPLE_WIDTH 16
#define AUDIO_CHANNELS 1
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
#define AUDIO_SAMPLERATE 48000
#define AUDIO_MIC_BUFFER_COUNT 16
#define AUDIO_MIC_BUFFER_LENGTH 10
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_SPK_BUFFER_COUNT 16
#define AUDIO_SPK_BUFFER_LENGTH 10
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_MIX_CHANNEL_COUNT 16
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
// Resampler quality on the Speex 0..10 scale. Quality 1 upsampling (e.g. G.711 8k -> 48k)
// adds audible aliasing and hurts PVQA/AQuA scores; 7 is a good fidelity/CPU balance for a
// single mono stream on modern ARM64. Raise toward 10 for max fidelity if CPU allows.
#define AUDIO_RESAMPLER_QUALITY 7
#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
// Milliseconds before
#define RTP_BUFFER_HIGH (2000)
#define RTP_BUFFER_LOW (0)
#define RTP_BUFFER_PREBUFFER (100)
#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 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
// Number of samples
#define MT_MAX_DECODEBUFFER 32768
#endif
+4 -7
View File
@@ -1,20 +1,17 @@
cmake_minimum_required (VERSION 3.15)
project (helper_lib) project (helper_lib)
# Rely on C++ 11 # Rely on C++ 11
set (CMAKE_CXX_STANDARD 20) set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_STANDARD_REQUIRED ON)
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 PUBLIC ../../libs/ ../../engine ../ .) target_include_directories(helper_lib PRIVATE ../../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()
+8 -2
View File
@@ -5,6 +5,12 @@
#include "HL_AsyncCommand.h" #include "HL_AsyncCommand.h"
AsyncCommand::AsyncCommand() {} AsyncCommand::AsyncCommand()
{
AsyncCommand::~AsyncCommand() {} }
AsyncCommand::~AsyncCommand()
{
}
+71 -119
View File
@@ -7,11 +7,9 @@ const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; "0123456789+/";
class Base64 class Base64 {
{ public:
public: static bool Encode(const std::string &in, std::string *out) {
static bool Encode(const std::string& in, std::string* out)
{
int i = 0, j = 0; int i = 0, j = 0;
size_t enc_len = 0; size_t enc_len = 0;
unsigned char a3[3]; unsigned char a3[3];
@@ -22,15 +20,12 @@ public:
int input_len = in.size(); int input_len = in.size();
std::string::const_iterator input = in.begin(); std::string::const_iterator input = in.begin();
while (input_len--) while (input_len--) {
{
a3[i++] = *(input++); a3[i++] = *(input++);
if (i == 3) if (i == 3) {
{
a3_to_a4(a4, a3); a3_to_a4(a4, a3);
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++) {
{
(*out)[enc_len++] = kBase64Alphabet[a4[i]]; (*out)[enc_len++] = kBase64Alphabet[a4[i]];
} }
@@ -38,22 +33,18 @@ public:
} }
} }
if (i) if (i) {
{ for (j = i; j < 3; j++) {
for (j = i; j < 3; j++)
{
a3[j] = '\0'; a3[j] = '\0';
} }
a3_to_a4(a4, a3); a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++) for (j = 0; j < i + 1; j++) {
{
(*out)[enc_len++] = kBase64Alphabet[a4[j]]; (*out)[enc_len++] = kBase64Alphabet[a4[j]];
} }
while ((i++ < 3)) while ((i++ < 3)) {
{
(*out)[enc_len++] = '='; (*out)[enc_len++] = '=';
} }
} }
@@ -61,27 +52,22 @@ public:
return (enc_len == out->size()); return (enc_len == out->size());
} }
static bool Encode(const char* input, size_t input_length, char* out, size_t out_length) static bool Encode(const char *input, size_t input_length, char *out, size_t out_length) {
{
int i = 0, j = 0; int i = 0, j = 0;
char* out_begin = out; char *out_begin = out;
unsigned char a3[3]; unsigned char a3[3];
unsigned char a4[4]; unsigned char a4[4];
size_t encoded_length = EncodedLength(input_length); size_t encoded_length = EncodedLength(input_length);
if (out_length < encoded_length) if (out_length < encoded_length) return false;
return false;
while (input_length--) while (input_length--) {
{
a3[i++] = *input++; a3[i++] = *input++;
if (i == 3) if (i == 3) {
{
a3_to_a4(a4, a3); a3_to_a4(a4, a3);
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++) {
{
*out++ = kBase64Alphabet[a4[i]]; *out++ = kBase64Alphabet[a4[i]];
} }
@@ -89,22 +75,18 @@ public:
} }
} }
if (i) if (i) {
{ for (j = i; j < 3; j++) {
for (j = i; j < 3; j++)
{
a3[j] = '\0'; a3[j] = '\0';
} }
a3_to_a4(a4, a3); a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++) for (j = 0; j < i + 1; j++) {
{
*out++ = kBase64Alphabet[a4[j]]; *out++ = kBase64Alphabet[a4[j]];
} }
while ((i++ < 3)) while ((i++ < 3)) {
{
*out++ = '='; *out++ = '=';
} }
} }
@@ -112,8 +94,7 @@ public:
return (out == (out_begin + encoded_length)); return (out == (out_begin + encoded_length));
} }
static bool Decode(const std::string& in, std::string* out) static bool Decode(const std::string &in, std::string *out) {
{
int i = 0, j = 0; int i = 0, j = 0;
size_t dec_len = 0; size_t dec_len = 0;
unsigned char a3[3]; unsigned char a3[3];
@@ -124,25 +105,20 @@ public:
out->resize(DecodedLength(in)); out->resize(DecodedLength(in));
while (input_len--) while (input_len--) {
{ if (*input == '=') {
if (*input == '=')
{
break; break;
} }
a4[i++] = *(input++); a4[i++] = *(input++);
if (i == 4) if (i == 4) {
{ for (i = 0; i <4; i++) {
for (i = 0; i < 4; i++)
{
a4[i] = b64_lookup(a4[i]); a4[i] = b64_lookup(a4[i]);
} }
a4_to_a3(a3, a4); a4_to_a3(a3,a4);
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++) {
{
(*out)[dec_len++] = a3[i]; (*out)[dec_len++] = a3[i];
} }
@@ -150,22 +126,18 @@ public:
} }
} }
if (i) if (i) {
{ for (j = i; j < 4; j++) {
for (j = i; j < 4; j++)
{
a4[j] = '\0'; a4[j] = '\0';
} }
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++) {
{
a4[j] = b64_lookup(a4[j]); a4[j] = b64_lookup(a4[j]);
} }
a4_to_a3(a3, a4); a4_to_a3(a3,a4);
for (j = 0; j < i - 1; j++) for (j = 0; j < i - 1; j++) {
{
(*out)[dec_len++] = a3[j]; (*out)[dec_len++] = a3[j];
} }
} }
@@ -173,37 +145,30 @@ public:
return (dec_len == out->size()); return (dec_len == out->size());
} }
static bool Decode(const char* input, size_t input_length, char* out, size_t out_length) static bool Decode(const char *input, size_t input_length, char *out, size_t out_length) {
{
int i = 0, j = 0; int i = 0, j = 0;
char* out_begin = out; char *out_begin = out;
unsigned char a3[3]; unsigned char a3[3];
unsigned char a4[4]; unsigned char a4[4];
size_t decoded_length = DecodedLength(input, input_length); size_t decoded_length = DecodedLength(input, input_length);
if (out_length < decoded_length) if (out_length < decoded_length) return false;
return false;
while (input_length--) while (input_length--) {
{ if (*input == '=') {
if (*input == '=')
{
break; break;
} }
a4[i++] = *(input++); a4[i++] = *(input++);
if (i == 4) if (i == 4) {
{ for (i = 0; i <4; i++) {
for (i = 0; i < 4; i++)
{
a4[i] = b64_lookup(a4[i]); a4[i] = b64_lookup(a4[i]);
} }
a4_to_a3(a3, a4); a4_to_a3(a3,a4);
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++) {
{
*out++ = a3[i]; *out++ = a3[i];
} }
@@ -211,22 +176,18 @@ public:
} }
} }
if (i) if (i) {
{ for (j = i; j < 4; j++) {
for (j = i; j < 4; j++)
{
a4[j] = '\0'; a4[j] = '\0';
} }
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++) {
{
a4[j] = b64_lookup(a4[j]); a4[j] = b64_lookup(a4[j]);
} }
a4_to_a3(a3, a4); a4_to_a3(a3,a4);
for (j = 0; j < i - 1; j++) for (j = 0; j < i - 1; j++) {
{
*out++ = a3[j]; *out++ = a3[j];
} }
} }
@@ -234,71 +195,62 @@ public:
return (out == (out_begin + decoded_length)); return (out == (out_begin + decoded_length));
} }
static int DecodedLength(const char* in, size_t in_length) static int DecodedLength(const char *in, size_t in_length) {
{
int numEq = 0; int numEq = 0;
const char* in_end = in + in_length; const char *in_end = in + in_length;
while (*--in_end == '=') while (*--in_end == '=') ++numEq;
++numEq;
return ((6 * in_length) / 8) - numEq; return ((6 * in_length) / 8) - numEq;
} }
static int DecodedLength(const std::string& in) static int DecodedLength(const std::string &in) {
{
int numEq = 0; int numEq = 0;
int n = in.size(); int n = in.size();
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) {
{
++numEq; ++numEq;
} }
return ((6 * n) / 8) - numEq; return ((6 * n) / 8) - numEq;
} }
inline static int EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; } inline static int EncodedLength(size_t length) {
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
inline static int EncodedLength(const std::string& in) { return EncodedLength(in.length()); }
inline static void StripPadding(std::string* in)
{
while (!in->empty() && *(in->rbegin()) == '=')
in->resize(in->size() - 1);
} }
private: inline static int EncodedLength(const std::string &in) {
static inline void a3_to_a4(unsigned char* a4, unsigned char* a3) return EncodedLength(in.length());
{ }
inline static void StripPadding(std::string *in) {
while (!in->empty() && *(in->rbegin()) == '=') in->resize(in->size() - 1);
}
private:
static inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
a4[0] = (a3[0] & 0xfc) >> 2; a4[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f); a4[3] = (a3[2] & 0x3f);
} }
static inline void a4_to_a3(unsigned char* a3, unsigned char* a4) static inline void a4_to_a3(unsigned char * a3, unsigned char * a4) {
{
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
} }
static inline unsigned char b64_lookup(unsigned char c) static inline unsigned char b64_lookup(unsigned char c) {
{ if(c >='A' && c <='Z') return c - 'A';
if (c >= 'A' && c <= 'Z') if(c >='a' && c <='z') return c - 71;
return c - 'A'; if(c >='0' && c <='9') return c + 4;
if (c >= 'a' && c <= 'z') if(c == '+') return 62;
return c - 71; if(c == '/') return 63;
if (c >= '0' && c <= '9')
return c + 4;
if (c == '+')
return 62;
if (c == '/')
return 63;
return 255; return 255;
} }
}; };
#endif // HL_BASE64_H #endif // HL_BASE64_H
+157 -170
View File
@@ -11,11 +11,11 @@
namespace Calc namespace Calc
{ {
class Parser; class Parser;
namespace Ast namespace Ast
{ {
enum class Type enum class Type
{ {
None, None,
And, And,
Or, Or,
@@ -32,22 +32,33 @@ enum class Type
Number, Number,
String, String,
Var Var
}; };
class Item; class Item;
typedef Item* PItem; typedef Item* PItem;
class Item class Item
{ {
friend class Calc::Parser; friend class Calc::Parser;
public:
bool isVariable() const
{
return mType == Type::Var;
}
bool isFixed() const
{
return mType == Type::Number || mType == Type::String;
}
public: bool isOperation() const
bool isVariable() const { return mType == Type::Var; } {
bool isFixed() const { return mType == Type::Number || mType == Type::String; } return mType >= Type::And && mType <= Type::Div;
}
bool isOperation() const { return mType >= Type::And && mType <= Type::Div; } bool hasBrackets() const
{
bool hasBrackets() const { return mHasBrackets; } return mHasBrackets;
}
int getOperatorLevel() const int getOperatorLevel() const
{ {
@@ -83,13 +94,25 @@ public:
assert(0); assert(0);
} }
Type getType() const { return mType; } Type getType() const
{
return mType;
}
std::string getName() const { return mName; } std::string getName() const
{
return mName;
}
Variant& value() { return mValue; } Variant& value()
{
return mValue;
}
std::vector<PItem>& children() { return mChildren; } std::vector<PItem>& children()
{
return mChildren;
}
typedef std::map<std::string, std::string> NameMap; typedef std::map<std::string, std::string> NameMap;
@@ -103,54 +126,21 @@ public:
oss << " "; oss << " ";
switch (mType) switch (mType)
{ {
case Type::Number: case Type::Number: oss << mValue.asStdString(); break;
oss << mValue.asStdString(); case Type::String: oss << '"' << mValue.asStdString() << '"'; break;
break; case Type::Var: { NameMap::const_iterator iter = nm.find(mName); oss << ((iter != nm.end()) ? iter->second : mName);} break;
case Type::String: case Type::Add: oss << "+"; break;
oss << '"' << mValue.asStdString() << '"'; case Type::Mul: oss << "*"; break;
break; case Type::Div: oss << "/"; break;
case Type::Var: case Type::Sub: oss << "-"; break;
{ case Type::Equal: oss << "=="; break;
NameMap::const_iterator iter = nm.find(mName); case Type::NotEqual: oss << "!="; break;
oss << ((iter != nm.end()) ? iter->second : mName); case Type::Less: oss << "<"; break;
} case Type::LessOrEqual: oss << "<="; break;
break; case Type::Greater: oss << ">"; break;
case Type::Add: case Type::GreatorOrEqual: oss << ">="; break;
oss << "+"; case Type::Or: oss << "or"; break;
break; case Type::And: oss << "and"; break;
case Type::Mul:
oss << "*";
break;
case Type::Div:
oss << "/";
break;
case Type::Sub:
oss << "-";
break;
case Type::Equal:
oss << "==";
break;
case Type::NotEqual:
oss << "!=";
break;
case Type::Less:
oss << "<";
break;
case Type::LessOrEqual:
oss << "<=";
break;
case Type::Greater:
oss << ">";
break;
case Type::GreatorOrEqual:
oss << ">=";
break;
case Type::Or:
oss << "or";
break;
case Type::And:
oss << "and";
break;
default: default:
throw std::runtime_error("operator expected"); throw std::runtime_error("operator expected");
} }
@@ -177,56 +167,23 @@ public:
switch (mType) switch (mType)
{ {
case Type::Number: case Type::Number:
case Type::String: case Type::String: result = mValue; break;
result = mValue;
case Type::Var: { auto iter = vm.find(mName); if (iter != vm.end()) return iter->second; else throw std::runtime_error("Variable " + mName + " did not find."); }
break; break;
case Type::Var: case Type::Add: result = left + right; break;
{ case Type::Mul: result = left * right; break;
auto iter = vm.find(mName); case Type::Div: result = left / right; break;
if (iter != vm.end()) case Type::Sub: result = left - right; break;
return iter->second; case Type::Equal: result = left == right; break;
else case Type::NotEqual: result = left != right; break;
throw std::runtime_error("Variable " + mName + " did not find."); case Type::Less: result = left < right; break;
} case Type::LessOrEqual: result = left <= right; break;
break; case Type::Greater: result = left > right; break;
case Type::GreatorOrEqual: result = left >= right; break;
case Type::Add: case Type::Or: result = left.asBool() || right.asBool(); break;
result = left + right; case Type::And: result = left.asBool() && right.asBool(); break;
break;
case Type::Mul:
result = left * right;
break;
case Type::Div:
result = left / right;
break;
case Type::Sub:
result = left - right;
break;
case Type::Equal:
result = left == right;
break;
case Type::NotEqual:
result = left != right;
break;
case Type::Less:
result = left < right;
break;
case Type::LessOrEqual:
result = left <= right;
break;
case Type::Greater:
result = left > right;
break;
case Type::GreatorOrEqual:
result = left >= right;
break;
case Type::Or:
result = left.asBool() || right.asBool();
break;
case Type::And:
result = left.asBool() && right.asBool();
break;
default: default:
assert(0); assert(0);
} }
@@ -235,30 +192,30 @@ public:
~Item() ~Item()
{ {
for (auto node : mChildren) for (auto node: mChildren)
delete node; delete node;
mChildren.clear(); mChildren.clear();
} }
private: private:
Type mType = Type::None; Type mType = Type::None;
std::string mName; std::string mName;
Variant mValue; Variant mValue;
std::vector<PItem> mChildren; std::vector<PItem> mChildren;
bool mHasBrackets = false; bool mHasBrackets = false;
}; };
} // namespace Ast }
static bool ishex(int c) static bool ishex(int c)
{ {
if (isdigit(c)) if (isdigit(c))
return true; return true;
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
} }
class Parser class Parser
{ {
private: private:
enum class LexemType enum class LexemType
{ {
None, None,
@@ -277,9 +234,15 @@ private:
LexemType mType = LexemType::None; LexemType mType = LexemType::None;
std::string mValue; std::string mValue;
operator bool() const { return mType != LexemType::None; } operator bool () const
{
return mType != LexemType::None;
}
std::string toString() const { return std::to_string((int)mType) + " : " + mValue; } std::string toString() const
{
return std::to_string((int)mType) + " : " + mValue;
}
}; };
Lexem mCurrentLexem; Lexem mCurrentLexem;
@@ -289,16 +252,20 @@ private:
if (c == '(') if (c == '(')
mCurrentLexem.mType = LexemType::OpenBracket; mCurrentLexem.mType = LexemType::OpenBracket;
else if (c == ')') else
if (c == ')')
mCurrentLexem.mType = LexemType::CloseBracket; mCurrentLexem.mType = LexemType::CloseBracket;
else if (isdigit(c)) else
if (isdigit(c))
mCurrentLexem.mType = LexemType::Dec; mCurrentLexem.mType = LexemType::Dec;
else if (isalpha(c)) else
if (isalpha(c))
mCurrentLexem.mType = LexemType::Var; mCurrentLexem.mType = LexemType::Var;
else if (c == '+' || c == '-' || c == '/' || c == '*' || c == '=' || c == '<' || c == '>' || c == '&' || else
c == '|') if (c == '+' || c == '-' || c == '/' || c == '*' || c == '=' || c == '<' || c == '>' || c == '&' || c == '|')
mCurrentLexem.mType = LexemType::Oper; mCurrentLexem.mType = LexemType::Oper;
else if (c == '"') else
if (c == '"')
mCurrentLexem.mType = LexemType::Str; mCurrentLexem.mType = LexemType::Str;
else else
return Lexem(); return Lexem();
@@ -316,9 +283,15 @@ private:
if (mCurrentLexem.mType == LexemType::Oper) if (mCurrentLexem.mType == LexemType::Oper)
{ {
if (mCurrentLexem.mValue == "+" || mCurrentLexem.mValue == "-" || mCurrentLexem.mValue == "*" || if (mCurrentLexem.mValue == "+" ||
mCurrentLexem.mValue == "/" || mCurrentLexem.mValue == ">=" || mCurrentLexem.mValue == "<=" || mCurrentLexem.mValue == "-" ||
mCurrentLexem.mValue == "==" || mCurrentLexem.mValue == "||" || mCurrentLexem.mValue == "&&") mCurrentLexem.mValue == "*" ||
mCurrentLexem.mValue == "/" ||
mCurrentLexem.mValue == ">=" ||
mCurrentLexem.mValue == "<=" ||
mCurrentLexem.mValue == "==" ||
mCurrentLexem.mValue == "||" ||
mCurrentLexem.mValue == "&&")
{ {
// Lexem finished // Lexem finished
result = mCurrentLexem; result = mCurrentLexem;
@@ -339,7 +312,7 @@ private:
{ {
// Dot is here - is it float // Dot is here - is it float
bool isFloat = false; bool isFloat = false;
strx::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat); StringHelper::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
if (isFloat) if (isFloat)
mCurrentLexem.mType = LexemType::Float; mCurrentLexem.mType = LexemType::Float;
else else
@@ -384,7 +357,8 @@ private:
case LexemType::Dec: case LexemType::Dec:
if (c == 'x' && mCurrentLexem.mValue == "0") if (c == 'x' && mCurrentLexem.mValue == "0")
mCurrentLexem.mType = LexemType::Hex; mCurrentLexem.mType = LexemType::Hex;
else if (isdigit(c) || c == '.') else
if (isdigit(c) || c == '.')
{ {
mCurrentLexem.mValue.push_back(c); mCurrentLexem.mValue.push_back(c);
} }
@@ -440,14 +414,14 @@ private:
if (putback) if (putback)
input.putback(c); input.putback(c);
else if (!result) else
if (!result)
c = input.get(); c = input.get();
} }
checkNumericLexem(); checkNumericLexem();
// Recover partially processed lexem - maybe we finish processing at all but there is dec / float / string / // Recover partially processed lexem - maybe we finish processing at all but there is dec / float / string / variable
// variable
if (mCurrentLexem.mType != LexemType::None && result.mType == LexemType::None) if (mCurrentLexem.mType != LexemType::None && result.mType == LexemType::None)
result = mCurrentLexem; result = mCurrentLexem;
@@ -467,27 +441,38 @@ private:
case LexemType::Oper: case LexemType::Oper:
if (l.mValue == "-") if (l.mValue == "-")
result->mType = Ast::Type::Sub; result->mType = Ast::Type::Sub;
else if (l.mValue == "+") else
if (l.mValue == "+")
result->mType = Ast::Type::Add; result->mType = Ast::Type::Add;
else if (l.mValue == "*") else
if (l.mValue == "*")
result->mType = Ast::Type::Mul; result->mType = Ast::Type::Mul;
else if (l.mValue == "/") else
if (l.mValue == "/")
result->mType = Ast::Type::Div; result->mType = Ast::Type::Div;
else if (l.mValue == "<") else
if (l.mValue == "<")
result->mType = Ast::Type::Less; result->mType = Ast::Type::Less;
else if (l.mValue == "<=") else
if (l.mValue == "<=")
result->mType = Ast::Type::LessOrEqual; result->mType = Ast::Type::LessOrEqual;
else if (l.mValue == ">") else
if (l.mValue == ">")
result->mType = Ast::Type::Greater; result->mType = Ast::Type::Greater;
else if (l.mValue == ">=") else
if (l.mValue == ">=")
result->mType = Ast::Type::GreatorOrEqual; result->mType = Ast::Type::GreatorOrEqual;
else if (l.mValue == "==") else
if (l.mValue == "==")
result->mType = Ast::Type::Equal; result->mType = Ast::Type::Equal;
else if (l.mValue == "!=") else
if (l.mValue == "!=")
result->mType = Ast::Type::NotEqual; result->mType = Ast::Type::NotEqual;
else if (l.mValue == "&&") else
if (l.mValue == "&&")
result->mType = Ast::Type::And; result->mType = Ast::Type::And;
else if (l.mValue == "||") else
if (l.mValue == "||")
result->mType = Ast::Type::Or; result->mType = Ast::Type::Or;
break; break;
@@ -503,7 +488,7 @@ private:
case LexemType::Hex: case LexemType::Hex:
result->mType = Ast::Type::Number; result->mType = Ast::Type::Number;
result->mValue = strx::fromHex2Int(l.mValue); result->mValue = StringHelper::fromHex2Int(l.mValue);
break; break;
case LexemType::Float: case LexemType::Float:
@@ -524,11 +509,11 @@ private:
} }
Lexem mLexem; Lexem mLexem;
public:
public:
Ast::PItem parseExpression(std::istream& input) Ast::PItem parseExpression(std::istream& input)
{ {
Ast::PItem operationNode(nullptr), leftNode(nullptr), rightNode(nullptr), currentOperation(nullptr); Ast::PItem operationNode(nullptr), leftNode(nullptr), rightNode(nullptr),
currentOperation(nullptr);
// While we have lexem // While we have lexem
while (mLexem = getLexem(input)) while (mLexem = getLexem(input))
@@ -560,7 +545,8 @@ public:
throw std::runtime_error("Open bracket or constant / number / string / variable expected."); throw std::runtime_error("Open bracket or constant / number / string / variable expected.");
} }
} }
else if (!operationNode) else
if (!operationNode)
{ {
// Well, there is left node already // Well, there is left node already
// See operation here // See operation here
@@ -587,12 +573,10 @@ public:
// Parse rest of expression // Parse rest of expression
rightNode = parseExpression(input); rightNode = parseExpression(input);
// If right part of expression is operation - make left side child of right part - to allow calculation // If right part of expression is operation - make left side child of right part - to allow calculation in right order
// in right order
if (operationNode) if (operationNode)
{ {
if (rightNode->isOperation() && if (rightNode->isOperation() && rightNode->getOperatorLevel() <= operationNode->getOperatorLevel() && !rightNode->hasBrackets())
rightNode->getOperatorLevel() <= operationNode->getOperatorLevel() && !rightNode->hasBrackets())
{ {
// Get left child of right expression - make it our right child // Get left child of right expression - make it our right child
operationNode->children().push_back(leftNode); operationNode->children().push_back(leftNode);
@@ -614,8 +598,11 @@ public:
return currentOperation ? currentOperation : leftNode; return currentOperation ? currentOperation : leftNode;
} }
public: public:
Ast::PItem parse(std::istream& input) { return nullptr; } Ast::PItem parse(std::istream& input)
{
return nullptr;
}
void testLexemParser(const std::string& test) void testLexemParser(const std::string& test)
{ {
@@ -626,14 +613,14 @@ public:
std::cout << "Lexem type: " << (int)l.mType << ", value: " << l.mValue << std::endl; std::cout << "Lexem type: " << (int)l.mType << ", value: " << l.mValue << std::endl;
} }
} }
}; };
class Worker class Worker
{ {
public: public:
Variant eval(Ast::PItem ast); Variant eval(Ast::PItem ast);
}; };
} // namespace Calc }
#endif #endif
+28 -23
View File
@@ -17,19 +17,19 @@ BOOL WINAPI CrashReporter::Callback(LPVOID arg)
return TRUE; return TRUE;
} }
typedef int(__stdcall* CrInstallProc)(__in PCR_INSTALL_INFOW pInfo); typedef int(__stdcall *CrInstallProc)(__in PCR_INSTALL_INFOW pInfo);
static CrInstallProc CrInstall = nullptr; static CrInstallProc CrInstall = nullptr;
typedef int(__stdcall* CrUninstallProc)(); typedef int(__stdcall *CrUninstallProc)();
static CrUninstallProc CrUninstall = nullptr; static CrUninstallProc CrUninstall = nullptr;
typedef int(__stdcall* CrInstallIntoCurrentThreadProc)(DWORD dwFlags); typedef int(__stdcall *CrInstallIntoCurrentThreadProc)(DWORD dwFlags);
static CrInstallIntoCurrentThreadProc CrInstallIntoCurrentThread = nullptr; static CrInstallIntoCurrentThreadProc CrInstallIntoCurrentThread = nullptr;
typedef int(__stdcall* CrUninstallFromCurrentThreadProc)(); typedef int(__stdcall *CrUninstallFromCurrentThreadProc)();
static CrUninstallFromCurrentThreadProc CrUninstallFromCurrentThread = nullptr; static CrUninstallFromCurrentThreadProc CrUninstallFromCurrentThread = nullptr;
typedef int(__stdcall* CrGetLastErrorMsgProc)(LPWSTR buffer, UINT size); typedef int(__stdcall *CrGetLastErrorMsgProc)(LPWSTR buffer, UINT size);
static CrGetLastErrorMsgProc CrGetLastErrorMsg = nullptr; static CrGetLastErrorMsgProc CrGetLastErrorMsg = nullptr;
static HMODULE CrLibraryHandle = NULL; static HMODULE CrLibraryHandle = NULL;
@@ -49,10 +49,8 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW"); CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW");
CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall"); CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall");
CrInstallIntoCurrentThread = CrInstallIntoCurrentThread = (CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2");
(CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2"); CrUninstallFromCurrentThread = (CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread");
CrUninstallFromCurrentThread =
(CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread");
CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW"); CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW");
#else #else
CrInstall = &crInstallW; CrInstall = &crInstallW;
@@ -72,7 +70,8 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
{ {
std::wstring appname, version, url; std::wstring appname, version, url;
} unicode; } unicode;
unicode.appname = StringHelper::makeTstring(appname), unicode.version = StringHelper::makeTstring(version), unicode.appname = StringHelper::makeTstring(appname),
unicode.version = StringHelper::makeTstring(version),
unicode.url = StringHelper::makeTstring(url); unicode.url = StringHelper::makeTstring(url);
if (unicode.appname.empty()) if (unicode.appname.empty())
@@ -87,9 +86,9 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
info.pszAppName = unicode.appname.c_str(); info.pszAppName = unicode.appname.c_str();
info.pszAppVersion = unicode.version.c_str(); info.pszAppVersion = unicode.version.c_str();
info.pszEmailSubject = TEXT("Crash report"); info.pszEmailSubject = TEXT("Crash report");
// info.pszEmailTo = L"amegyeri@minerva-soft.com"; //info.pszEmailTo = L"amegyeri@minerva-soft.com";
// info.pszUrl = L"http://ftp.minerva-soft.com/crashlog/crashrpt.php"; //info.pszUrl = L"http://ftp.minerva-soft.com/crashlog/crashrpt.php";
// info.pszUrl = L"http://sip.crypttalk.com/crashlog/crashrpt.php"; //info.pszUrl = L"http://sip.crypttalk.com/crashlog/crashrpt.php";
info.pszUrl = unicode.url.c_str(); info.pszUrl = unicode.url.c_str();
info.pfnCrashCallback = Callback; info.pfnCrashCallback = Callback;
info.uPriorities[CR_HTTP] = 1; info.uPriorities[CR_HTTP] = 1;
@@ -104,8 +103,7 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
wchar_t errorMsg[512] = L""; wchar_t errorMsg[512] = L"";
CrGetLastErrorMsg(errorMsg, 512); CrGetLastErrorMsg(errorMsg, 512);
OutputDebugStringW(errorMsg); OutputDebugStringW(errorMsg);
// LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " << //LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " << errorMsg);
// errorMsg);
} }
#endif #endif
} }
@@ -122,8 +120,7 @@ void CrashReporter::free()
CrUninstallFromCurrentThread = nullptr; CrUninstallFromCurrentThread = nullptr;
CrGetLastErrorMsg = nullptr; CrGetLastErrorMsg = nullptr;
#if defined(CRASHRPT_DYNAMIC) #if defined(CRASHRPT_DYNAMIC)
::FreeLibrary(CrLibraryHandle); ::FreeLibrary(CrLibraryHandle); CrLibraryHandle = NULL;
CrLibraryHandle = NULL;
#endif #endif
} }
#endif #endif
@@ -132,8 +129,8 @@ void CrashReporter::free()
bool CrashReporter::isLoaded() bool CrashReporter::isLoaded()
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg || !CrInstallIntoCurrentThread || return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg ||
!CrUninstallFromCurrentThread); !CrInstallIntoCurrentThread || !CrUninstallFromCurrentThread);
#else #else
return false; return false;
#endif #endif
@@ -177,12 +174,20 @@ CrashReporterGuard::~CrashReporterGuard()
#else #else
CrashReporterThreadPoint::CrashReporterThreadPoint() {} CrashReporterThreadPoint::CrashReporterThreadPoint()
{
}
CrashReporterThreadPoint::~CrashReporterThreadPoint() {} CrashReporterThreadPoint::~CrashReporterThreadPoint()
{
}
CrashReporterGuard::CrashReporterGuard() {} CrashReporterGuard::CrashReporterGuard()
{
}
CrashReporterGuard::~CrashReporterGuard() {} CrashReporterGuard::~CrashReporterGuard()
{
}
#endif #endif
+4 -5
View File
@@ -4,8 +4,8 @@
#include <string> #include <string>
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
#include <WinSock2.h> # include <WinSock2.h>
#include <Windows.h> # include <Windows.h>
#endif #endif
// Helper class to translate SEH exceptions to C++ - sometimes it is needed // Helper class to translate SEH exceptions to C++ - sometimes it is needed
@@ -15,7 +15,6 @@ class SE_Exception
{ {
private: private:
unsigned int nSE; unsigned int nSE;
public: public:
SE_Exception() {} SE_Exception() {}
SE_Exception(unsigned int n) : nSE(n) {} SE_Exception(unsigned int n) : nSE(n) {}
@@ -27,10 +26,10 @@ extern void SEHToCpp(unsigned int, EXCEPTION_POINTERS*);
// Although better way is to have _set_se_translator set - in our case we do not call it. // Although better way is to have _set_se_translator set - in our case we do not call it.
// The cause is usage of CrashRpt libraries - it gives better control on exception reporting. // The cause is usage of CrashRpt libraries - it gives better control on exception reporting.
#define SET_SEH_TO_CPP # define SET_SEH_TO_CPP
//_set_se_translator(&SEHToCpp) //_set_se_translator(&SEHToCpp)
#else #else
#define SET_SEH_TO_CPP # define SET_SEH_TO_CPP
#endif #endif
class CrashReporter class CrashReporter
+7 -4
View File
@@ -2,9 +2,12 @@
#include "HL_String.h" #include "HL_String.h"
// --------- CsvFile ---------------- // --------- CsvFile ----------------
CsvReader::CsvReader(std::istream& stream) : mInputStream(stream) {} CsvReader::CsvReader(std::istream& stream)
:mInputStream(stream)
{}
CsvReader::~CsvReader() {} CsvReader::~CsvReader()
{}
std::istream& CsvReader::stream() const std::istream& CsvReader::stream() const
{ {
@@ -17,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;
strx::trim(line); StringHelper::trim(line);
if (line.empty()) if (line.empty())
return false; return false;
strx::split(line, cells, ",;"); StringHelper::split(line, cells, ",;");
return true; return true;
} }
+24 -12
View File
@@ -10,7 +10,7 @@
#include <memory.h> #include <memory.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <cstdio>
enum enum
{ {
ERR_MEDIA_SOCKET_FAILED = 1, // Failed to create media socket ERR_MEDIA_SOCKET_FAILED = 1, // Failed to create media socket
@@ -38,36 +38,48 @@ enum
ERR_OPENSLES = 23 // OpenSL ES failed. Subcode has actual error code. ERR_OPENSLES = 23 // OpenSL ES failed. Subcode has actual error code.
}; };
class Exception : public std::exception class Exception: public std::exception
{ {
public: public:
Exception(int code, int subcode = 0) : mCode(code), mSubcode(subcode) Exception(int code, int subcode = 0)
:mCode(code), mSubcode(subcode)
{ {
std::snprintf(mMessage, sizeof(mMessage), "%d-%d", code, subcode); sprintf(mMessage, "%d-%d", code, subcode);
} }
Exception(int code, const char* message) Exception(int code, const char* message)
{ {
if (message) if (message)
strncpy(mMessage, message, (sizeof mMessage) - 1); strncpy(mMessage, message, (sizeof mMessage) - 1 );
} }
Exception(const Exception& src) : mCode(src.mCode), mSubcode(src.mSubcode) Exception(const Exception& src)
:mCode(src.mCode), mSubcode(src.mSubcode)
{ {
memcpy(mMessage, src.mMessage, sizeof mMessage); memcpy(mMessage, src.mMessage, sizeof mMessage);
} }
~Exception() {} ~Exception()
{ }
int code() const { return mCode; } int code() const
{
return mCode;
}
int subcode() const { return mSubcode; } int subcode() const
{
return mSubcode;
}
const char* what() const noexcept { return mMessage; } const char* what() const noexcept
{
return mMessage;
}
protected: protected:
int mCode = 0, mSubcode = 0; int mCode, mSubcode;
char mMessage[256] = {0}; char mMessage[256];
}; };
#endif #endif
+22 -54
View File
@@ -1,10 +1,10 @@
#include "HL_File.h" #include "HL_File.h"
#include <fstream> #include <fstream>
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX) || defined(TARGET_OSX)
#include <unistd.h> # include <unistd.h>
#include <sys/statvfs.h> # include <sys/statvfs.h>
#include <memory.h> # include <memory.h>
#endif #endif
bool FileHelper::exists(const std::string& s) bool FileHelper::exists(const std::string& s)
@@ -32,24 +32,23 @@ void FileHelper::remove(const char* s)
::remove(s); ::remove(s);
} }
// std::string FileHelper::gettempname() std::string FileHelper::gettempname()
// { {
// #if defined(TARGET_LINUX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX)
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp"; char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
// int code = mkstemp(template_filename); mkstemp(template_filename);
return template_filename;
#elif defined(TARGET_WIN)
char buffer[L_tmpnam];
tmpnam(buffer);
// return template_filename; return buffer;
// #elif defined(TARGET_WIN) #elif defined(TARGET_OSX)
// char buffer[L_tmpnam]; char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
// tmpnam(buffer); mktemp(template_filename);
return template_filename;
// return buffer; #endif
// #elif defined(TARGET_OSX) }
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
// mktemp(template_filename);
// return template_filename;
// #endif
// }
bool FileHelper::isAbsolute(const std::string& s) bool FileHelper::isAbsolute(const std::string& s)
{ {
@@ -65,7 +64,7 @@ std::string FileHelper::getCurrentDir()
return std::string(); return std::string();
#endif #endif
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX) || defined(TARGET_OSX)
char buf[512]; char buf[512];
if (getcwd(buf, sizeof buf) != nullptr) if (getcwd(buf, sizeof buf) != nullptr)
return buf; return buf;
@@ -102,8 +101,7 @@ size_t FileHelper::getFreespace(const std::string& path)
size_t r = static_cast<size_t>(-1); size_t r = static_cast<size_t>(-1);
#if defined(TARGET_LINUX) #if defined(TARGET_LINUX)
struct statvfs stats; struct statvfs stats; memset(&stats, 0, sizeof stats);
memset(&stats, 0, sizeof stats);
int retcode = statvfs(path.c_str(), &stats); int retcode = statvfs(path.c_str(), &stats);
if (retcode == 0) if (retcode == 0)
@@ -111,33 +109,3 @@ size_t FileHelper::getFreespace(const std::string& path)
#endif #endif
return r; return r;
} }
std::string FileHelper::expandUserHome(const std::string& path)
{
if (path.empty() || path[0] != '~')
return path; // No expansion needed
const char* home_dir = nullptr;
#ifdef TARGET_WIN
home_dir = std::getenv("USERPROFILE");
if (!home_dir)
{
home_dir = std::getenv("HOMEDRIVE");
const char* homepath = std::getenv("HOMEPATH");
if (home_dir && homepath)
{
std::string fullpath(home_dir);
fullpath += homepath;
return fullpath + path.substr(1);
}
}
#else
home_dir = std::getenv("HOME");
#endif
if (!home_dir)
throw std::runtime_error("Unable to determine the home directory");
return std::string(home_dir) + path.substr(1);
}
+1 -2
View File
@@ -12,7 +12,7 @@ public:
static void remove(const std::string& s); static void remove(const std::string& s);
static void remove(const char* s); static void remove(const char* s);
// static std::string gettempname(); static std::string gettempname();
static bool isAbsolute(const std::string& s); static bool isAbsolute(const std::string& s);
static std::string getCurrentDir(); static std::string getCurrentDir();
@@ -23,7 +23,6 @@ public:
// Returns free space on volume for path // Returns free space on volume for path
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too) // Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
static size_t getFreespace(const std::string& path); static size_t getFreespace(const std::string& path);
static std::string expandUserHome(const std::string& path);
}; };
#endif #endif
+17 -52
View File
@@ -102,7 +102,8 @@ bool Packet::parseV3(const ByteBuffer& packet)
if (!sourceAddr4.isEmpty()) if (!sourceAddr4.isEmpty())
mSourceAddress = sourceAddr4; mSourceAddress = sourceAddr4;
else if (!sourceAddr6.isEmpty()) else
if (!sourceAddr6.isEmpty())
mSourceAddress = sourceAddr6; mSourceAddress = sourceAddr6;
if (!mSourceAddress.isEmpty()) if (!mSourceAddress.isEmpty())
@@ -110,7 +111,8 @@ bool Packet::parseV3(const ByteBuffer& packet)
if (!destAddr4.isEmpty()) if (!destAddr4.isEmpty())
mDestinationAddress = destAddr4; mDestinationAddress = destAddr4;
else if (!destAddr6.isEmpty()) else
if (!destAddr6.isEmpty())
mDestinationAddress = destAddr6; mDestinationAddress = destAddr6;
if (!mDestinationAddress.isEmpty()) if (!mDestinationAddress.isEmpty())
@@ -119,7 +121,7 @@ bool Packet::parseV3(const ByteBuffer& packet)
return true; return true;
} }
bool Packet::parseV2(const ByteBuffer& packet) bool Packet::parseV2(const ByteBuffer &packet)
{ {
if (packet.size() < 31) if (packet.size() < 31)
return false; return false;
@@ -146,53 +148,16 @@ bool Packet::parseV2(const ByteBuffer& packet)
return true; return true;
} }
#define WRITE_CHUNK_UCHAR(T, V) \ #define WRITE_CHUNK_UCHAR(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(1); w.writeUChar((uint8_t)V);}
{ \ #define WRITE_CHUNK_USHORT(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(2); w.writeUShort((uint16_t)V);}
w.writeUShort((uint16_t)mVendorId); \ #define WRITE_CHUNK_UINT(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(4); w.writeUInt((uint32_t)V);}
w.writeUShort((uint16_t)T); \ #define WRITE_CHUNK_IP4(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(4); w.writeIp(V);}
w.writeUShort(1); \ #define WRITE_CHUNK_IP6(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(8); w.writeIp(V);}
w.writeUChar((uint8_t)V); \ #define WRITE_CHUNK_BUFFER(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(8); w.writeBuffer(V.data(), V.size());}
}
#define WRITE_CHUNK_USHORT(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(2); \
w.writeUShort((uint16_t)V); \
}
#define WRITE_CHUNK_UINT(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(4); \
w.writeUInt((uint32_t)V); \
}
#define WRITE_CHUNK_IP4(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(4); \
w.writeIp(V); \
}
#define WRITE_CHUNK_IP6(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(8); \
w.writeIp(V); \
}
#define WRITE_CHUNK_BUFFER(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(8); \
w.writeBuffer(V.data(), V.size()); \
}
ByteBuffer Packet::buildV3() ByteBuffer Packet::buildV3()
{ {
ByteBuffer r; ByteBuffer r; r.resize(mBody.size() + 512);
r.resize(mBody.size() + 512);
BufferWriter w(r); BufferWriter w(r);
// Signature // Signature
@@ -209,7 +174,8 @@ ByteBuffer Packet::buildV3()
{ {
if (mSourceAddress.isV4()) if (mSourceAddress.isV4())
WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress) WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress)
else if (mSourceAddress.isV6()) else
if (mSourceAddress.isV6())
WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress); WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port()); WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port());
@@ -220,7 +186,8 @@ ByteBuffer Packet::buildV3()
{ {
if (mDestinationAddress.isV4()) if (mDestinationAddress.isV4())
WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress) WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress)
else if (mDestinationAddress.isV6()) else
if (mDestinationAddress.isV6())
WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress); WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port()); WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port());
@@ -249,9 +216,7 @@ ByteBuffer Packet::buildV3()
r.resize(w.offset()); r.resize(w.offset());
w.rewind(); w.rewind(); w.skip(4); w.writeUShort((uint16_t)r.size());
w.skip(4);
w.writeUShort((uint16_t)r.size());
return r; return r;
} }
+19 -15
View File
@@ -6,8 +6,8 @@
namespace HEP namespace HEP
{ {
enum class ChunkType enum class ChunkType
{ {
None = 0, None = 0,
IPProtocolFamily, IPProtocolFamily,
IPProtocolID, IPProtocolID,
@@ -26,10 +26,10 @@ enum class ChunkType
PacketPayload, PacketPayload,
CompressedPayload, CompressedPayload,
InternalC InternalC
}; };
enum class VendorId enum class VendorId
{ {
None, None,
FreeSwitch, FreeSwitch,
Kamailio, Kamailio,
@@ -37,10 +37,10 @@ enum class VendorId
Asterisk, Asterisk,
Homer, Homer,
SipXecs SipXecs
}; };
enum class ProtocolId enum class ProtocolId
{ {
Reserved = 0, Reserved = 0,
SIP, SIP,
XMPP, XMPP,
@@ -54,17 +54,21 @@ enum class ProtocolId
IAX, IAX,
H322, H322,
H321 H321
}; };
struct Packet struct Packet
{ {
bool parseV3(const ByteBuffer& packet); bool parseV3(const ByteBuffer& packet);
bool parseV2(const ByteBuffer& packet); bool parseV2(const ByteBuffer& packet);
ByteBuffer buildV3(); ByteBuffer buildV3();
uint8_t mIpProtocolFamily, mIpProtocolId; uint8_t
mIpProtocolFamily,
mIpProtocolId;
InternetAddress mSourceAddress, mDestinationAddress; InternetAddress
mSourceAddress,
mDestinationAddress;
timeval mTimestamp; timeval mTimestamp;
ProtocolId mProtocolType; ProtocolId mProtocolType;
@@ -74,8 +78,8 @@ struct Packet
ByteBuffer mBody; ByteBuffer mBody;
VendorId mVendorId; VendorId mVendorId;
uint32_t mBodyOffset = 0; uint32_t mBodyOffset = 0;
}; };
} // namespace HEP }
#endif #endif
File diff suppressed because it is too large Load Diff
+1
View File
@@ -36,3 +36,4 @@ public:
#endif // HL_IUUP_H #endif // HL_IUUP_H
+1
View File
@@ -2,3 +2,4 @@
* 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/. */
+4 -4
View File
@@ -11,13 +11,13 @@
using ice::GLogger; using ice::GLogger;
using ice::LogLock; using ice::LogLock;
using ice::LL_CRITICAL;
using ice::LL_DEBUG;
using ice::LL_ERROR;
using ice::LL_INFO;
using ice::LL_MEDIA; using ice::LL_MEDIA;
using ice::LL_DEBUG;
using ice::LL_INFO;
using ice::LL_CRITICAL;
using ice::LL_NONE; using ice::LL_NONE;
using ice::LL_SPECIAL; using ice::LL_SPECIAL;
using ice::LL_ERROR;
using ice::LogLevelHelper; using ice::LogLevelHelper;
#endif #endif
+73 -86
View File
@@ -11,36 +11,15 @@
#define MPLS_STACK_MASK (0x00000100) #define MPLS_STACK_MASK (0x00000100)
#define MPLS_STACK_SHIFT (8) #define MPLS_STACK_SHIFT (8)
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForRaw(const Packet& data) NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{ {
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData); PacketData result(packet);
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(packet.mData);
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
result.mData += sizeof(EthernetHeader); packet.mData += sizeof(EthernetHeader);
result.mLength -= sizeof(EthernetHeader); packet.mLength -= sizeof(EthernetHeader);
// See if there is Vlan header // See if there is Vlan header
uint16_t proto = 0; uint16_t proto = 0;
@@ -49,11 +28,12 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
// Skip 1 or more VLAN headers // Skip 1 or more VLAN headers
do do
{ {
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(result.mData); const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData);
result.mData += sizeof(VlanHeader); packet.mData += sizeof(VlanHeader);
result.mLength -= sizeof(VlanHeader); packet.mLength -= sizeof(VlanHeader);
proto = ntohs(vlan->mData); proto = ntohs(vlan->mData);
} while (proto == 0x8100); }
while (proto == 0x8100);
} }
// Skip MPLS headers // Skip MPLS headers
@@ -62,11 +42,11 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
case ETHERTYPE_MPLS_UC: case ETHERTYPE_MPLS_UC:
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*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0) bottomOfStack = ((ntohl(*(uint32_t*)(packet.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
{ {
result.mData += 4; packet.mData += 4;
result.mLength -= 4; packet.mLength -=4;
} }
break; break;
@@ -79,86 +59,86 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
break; break;
} }
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(result.mData); const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return Payload(); return PacketData();
switch (ip4->version()) switch (ip4->version())
{ {
case 4: case 4:
return GetUdpPayloadForIp4(result); return GetUdpPayloadForIp4(packet, source, destination);
case 6: case 6:
return GetUdpPayloadForIp6(result); return GetUdpPayloadForIp6(packet, source, destination);
default: default:
return Payload(); return PacketData();
} }
} }
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForSLL(const Packet& data) NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForSLL(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{ {
Packet result(data); PacketData result(packet);
if (result.mLength < 16) if (packet.mLength < 16)
return Payload(); return PacketData();
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(result.mData); const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(packet.mData);
result.mData += sizeof(LinuxSllHeader); packet.mData += sizeof(LinuxSllHeader);
result.mLength -= sizeof(LinuxSllHeader); packet.mLength -= sizeof(LinuxSllHeader);
switch (ntohs(sll->mProtocolType)) switch (ntohs(sll->mProtocolType))
{ {
case 0x0800: case 0x0800:
return GetUdpPayloadForIp4(result); return GetUdpPayloadForIp4(packet, source, destination);
case 0x86DD: case 0x86DD:
return GetUdpPayloadForIp6(result); return GetUdpPayloadForIp6(packet, source, destination);
default: default:
return Payload(); return PacketData();
} }
} }
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForLoopback(const Packet& data) NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForLoopback(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{ {
Packet result(data); PacketData result(packet);
if (result.mLength < 16) if (packet.mLength < 16)
return Payload(); return PacketData();
struct LoopbackHeader struct LoopbackHeader
{ {
uint32_t mProtocolType; uint32_t mProtocolType;
}; };
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(result.mData); const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(packet.mData);
result.mData += sizeof(LoopbackHeader); packet.mData += sizeof(LoopbackHeader);
result.mLength -= sizeof(LoopbackHeader); packet.mLength -= sizeof(LoopbackHeader);
switch (lh->mProtocolType) switch (lh->mProtocolType)
{ {
case AF_INET: case AF_INET:
return GetUdpPayloadForIp4(result); return GetUdpPayloadForIp4(packet, source, destination);
case AF_INET6: case AF_INET6:
return GetUdpPayloadForIp6(result); return GetUdpPayloadForIp6(packet, source, destination);
default: default:
return Payload(); return PacketData();
} }
} }
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data) NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{ {
Packet result(data); PacketData result(packet);
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData); const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return Payload(); return PacketData(nullptr, 0);
result.mData += ip4->headerLength(); result.mData += ip4->headerLength();
result.mLength -= ip4->headerLength(); result.mLength -= ip4->headerLength();
@@ -172,30 +152,32 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
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);
InternetAddress addr_source; source.setIp(ip4->mSource);
addr_source.setIp(ip4->mSource); source.setPort(ntohs(udp->mSourcePort));
addr_source.setPort(ntohs(udp->mSourcePort));
InternetAddress addr_dest; destination.setIp(ip4->mDestination);
addr_dest.setIp(ip4->mDestination); destination.setPort(ntohs(udp->mDestinationPort));
addr_dest.setPort(ntohs(udp->mDestinationPort));
return {.data = result, .source = addr_source, .dest = addr_dest}; return result;
} }
struct Ip6Header struct Ip6Header
{ {
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t traffic_class_hi : 4, version : 4; uint8_t traffic_class_hi:4,
uint8_t flow_label_hi : 4, traffic_class_lo : 4; version:4;
uint8_t flow_label_hi:4,
traffic_class_lo:4;
uint16_t flow_label_lo; uint16_t flow_label_lo;
#elif __BYTE_ORDER == __BIG_ENDIAN #elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t version : 4, traffic_class_hi : 4; uint8_t version:4,
uint8_t traffic_class_lo : 4, flow_label_hi : 4; traffic_class_hi:4;
uint8_t traffic_class_lo:4,
flow_label_hi:4;
uint16_t flow_label_lo; uint16_t flow_label_lo;
#else #else
#error "Please fix endianness defines" # error "Please fix endianness defines"
#endif #endif
uint16_t payload_len; uint16_t payload_len;
@@ -206,28 +188,33 @@ struct Ip6Header
struct in6_addr dst_ip; struct in6_addr dst_ip;
}; };
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data) NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{ {
Packet result(data); PacketData result(packet);
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData); const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(packet.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);
*/ */
result.mData += sizeof(Ip6Header); result.mData += sizeof(Ip6Header);
result.mLength -= sizeof(Ip6Header); result.mLength -= sizeof(Ip6Header);
// std::cout << sizeof(Ip6Header) << std::endl; //std::cout << sizeof(Ip6Header) << std::endl;
const UdpHeader* udp = reinterpret_cast<const UdpHeader*>(result.mData); const UdpHeader* udp = reinterpret_cast<const UdpHeader*>(result.mData);
result.mData += sizeof(UdpHeader); result.mData += sizeof(UdpHeader);
result.mLength -= sizeof(UdpHeader); result.mLength -= sizeof(UdpHeader);
InternetAddress addr_source; /*
addr_source.setIp(ip6->src_ip); if (result.mLength != ntohs(udp->mDatagramLength))
addr_source.setPort(ntohs(udp->mSourcePort)); return PacketData(nullptr, 0);
*/
InternetAddress addr_dest; source.setIp(ip6->src_ip);
addr_dest.setIp(ip6->dst_ip); source.setPort(ntohs(udp->mSourcePort));
addr_dest.setPort(ntohs(udp->mDestinationPort)); //std::cout << source.toStdString() << " - ";
return {.data = result, .source = addr_source, .dest = addr_dest}; destination.setIp(ip6->dst_ip);
destination.setPort(ntohs(udp->mDestinationPort));
//std::cout << destination.toStdString() << std::endl;
return result;
} }
+25 -23
View File
@@ -7,31 +7,25 @@
class NetworkFrame class NetworkFrame
{ {
public: public:
struct Packet struct PacketData
{ {
const uint8_t* mData; const uint8_t* mData;
size_t mLength; size_t mLength;
Packet(const uint8_t* data, size_t length) : mData(data), mLength(length) {} PacketData(const uint8_t* data, size_t length)
:mData(data), mLength(length)
{}
Packet() : mData(nullptr), mLength(0) {} PacketData()
:mData(nullptr), mLength(0)
bool is_empty() const { return mData == nullptr || mLength == 0; } {}
}; };
struct Payload static PacketData GetUdpPayloadForEthernet(PacketData& packet, InternetAddress& source, InternetAddress& destination);
{ static PacketData GetUdpPayloadForIp4(PacketData& packet, InternetAddress& source, InternetAddress& destination);
Packet data; static PacketData GetUdpPayloadForIp6(PacketData& packet, InternetAddress& source, InternetAddress& destination);
InternetAddress source; static PacketData GetUdpPayloadForSLL(PacketData& packet, InternetAddress& source, InternetAddress& destination);
InternetAddress dest; static PacketData GetUdpPayloadForLoopback(PacketData& packet, InternetAddress& source, InternetAddress& destination);
};
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
{ {
@@ -44,7 +38,7 @@ public:
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
struct /*__attribute__((packed))*/ LinuxSllHeader struct /*__attribute__((packed))*/ LinuxSllHeader
#else #else
struct __attribute__((packed)) LinuxSllHeader struct __attribute__((packed)) LinuxSllHeader
#endif #endif
{ {
@@ -75,11 +69,18 @@ public:
uint8_t mTtl; /* time to live */ uint8_t mTtl; /* time to live */
uint8_t mProtocol; /* protocol */ uint8_t mProtocol; /* protocol */
uint16_t mChecksum; /* checksum */ uint16_t mChecksum; /* checksum */
in_addr mSource, mDestination; /* source and dest address */ in_addr mSource,
mDestination; /* source and dest address */
int headerLength() const { return (mVhl & 0x0f) * 4; } int headerLength() const
{
return (mVhl & 0x0f) * 4;
}
int version() const { return mVhl >> 4; } int version() const
{
return mVhl >> 4;
}
const in_addr& source4() const { return mSource; } const in_addr& source4() const { return mSource; }
const in_addr& dest4() const { return mDestination; } const in_addr& dest4() const { return mDestination; }
@@ -113,10 +114,11 @@ public:
#define TH_URG 0x20 #define TH_URG 0x20
#define TH_ECE 0x40 #define TH_ECE 0x40
#define TH_CWR 0x80 #define TH_CWR 0x80
#define TH_FLAGS (TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR) #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
uint16_t mWindow; /* window */ uint16_t mWindow; /* window */
uint16_t mChecksum; /* checksum */ uint16_t mChecksum; /* checksum */
uint16_t mUrgentPointer; /* urgent pointer */ uint16_t mUrgentPointer; /* urgent pointer */
}; };
}; };
#endif #endif
+34 -58
View File
@@ -1,32 +1,33 @@
/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
#include <asm/ioctls.h> # include <asm/ioctls.h>
#endif #endif
#include "../engine_config.h" #include "../config.h"
#include "HL_NetworkSocket.h" #include "HL_NetworkSocket.h"
#include "HL_Log.h"
#if defined(TARGET_OSX) || defined(TARGET_LINUX) #if defined(TARGET_OSX) || defined(TARGET_LINUX)
#include <fcntl.h> # include <fcntl.h>
#endif #endif
#if !defined(TARGET_WIN) #if !defined(TARGET_WIN)
#include <unistd.h> # include <unistd.h>
#endif #endif
#include <assert.h> #include <assert.h>
#define LOG_SUBSYSTEM "network" DatagramSocket::DatagramSocket()
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
{
DatagramSocket::DatagramSocket() : mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0) {} }
DatagramSocket::~DatagramSocket() DatagramSocket::~DatagramSocket()
{ {
internalClose(); closeSocket();
} }
void DatagramSocket::open(int family) void DatagramSocket::open(int family)
@@ -39,10 +40,8 @@ void DatagramSocket::open(int family)
mHandle = ::socket(mFamily, SOCK_DGRAM, IPPROTO_UDP); mHandle = ::socket(mFamily, SOCK_DGRAM, IPPROTO_UDP);
if (mHandle != INVALID_SOCKET) if (mHandle != INVALID_SOCKET)
{ {
sockaddr_in addr4; sockaddr_in addr4; sockaddr_in6 addr6;
sockaddr_in6 addr6; memset(&addr4, 0, sizeof(addr4)); memset(&addr6, 0, sizeof(addr6));
memset(&addr4, 0, sizeof(addr4));
memset(&addr6, 0, sizeof(addr6));
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6); socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l); int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
if (!retcode) if (!retcode)
@@ -57,50 +56,36 @@ int DatagramSocket::localport()
return mLocalPort; return mLocalPort;
} }
void DatagramSocket::sendDatagram(InternetAddress& dest, const void* packetData, unsigned int packetSize) void DatagramSocket::sendDatagram(InternetAddress &dest, const void *packetData, unsigned int packetSize)
{ {
if (mHandle == INVALID_SOCKET) if (mHandle == INVALID_SOCKET)
return; return;
/*int sent = */ ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), int sent = ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
dest.sockaddrLen());
} }
unsigned DatagramSocket::recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity) unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
{ {
if (mHandle == INVALID_SOCKET) if (mHandle == INVALID_SOCKET)
return 0; return 0;
sockaddr* addr = nullptr; sockaddr_in sourceaddr;
socklen_t addrLen = 0; #ifdef WIN32
sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}}; int addrlen = sizeof(sourceaddr);
sockaddr_in6 addr_6 = {AF_INET6, 0, 0, {0}, 0}; #else
switch (mFamily) socklen_t addrlen = sizeof(sourceaddr);
{ #endif
case AF_INET: int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, (sockaddr*)&sourceaddr, &addrlen);
addr = (sockaddr*)&addr_4;
addrLen = sizeof(addr_4);
break;
case AF_INET6:
addr = (sockaddr*)&addr_6;
addrLen = sizeof(addr_6);
break;
default:
assert(0);
}
if (!addr || !addrLen)
return 0;
int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, addr, &addrLen);
if (received > 0) if (received > 0)
{ {
src = InternetAddress((sockaddr&)*addr, addrLen); src = InternetAddress((sockaddr&)sourceaddr, addrlen);
return received; return received;
} }
else
return 0; return 0;
} }
void DatagramSocket::internalClose() void DatagramSocket::closeSocket()
{ {
if (mHandle != INVALID_SOCKET) if (mHandle != INVALID_SOCKET)
{ {
@@ -113,11 +98,6 @@ void DatagramSocket::internalClose()
} }
} }
void DatagramSocket::closeSocket()
{
internalClose();
}
bool DatagramSocket::isValid() const bool DatagramSocket::isValid() const
{ {
return mHandle != INVALID_SOCKET; return mHandle != INVALID_SOCKET;
@@ -138,7 +118,7 @@ bool DatagramSocket::setBlocking(bool blocking)
int flags = fcntl(mHandle, F_GETFL, 0); int flags = fcntl(mHandle, F_GETFL, 0);
if (flags < 0) if (flags < 0)
return false; return false;
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
return (fcntl(mHandle, F_SETFL, flags) == 0) ? true : false; return (fcntl(mHandle, F_SETFL, flags) == 0) ? true : false;
#endif #endif
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
@@ -159,19 +139,15 @@ DatagramAgreggator::DatagramAgreggator()
mMaxHandle = 0; mMaxHandle = 0;
} }
DatagramAgreggator::~DatagramAgreggator() {} DatagramAgreggator::~DatagramAgreggator()
{
}
void DatagramAgreggator::addSocket(PDatagramSocket socket) void DatagramAgreggator::addSocket(PDatagramSocket socket)
{ {
if (socket->mHandle == INVALID_SOCKET) if (socket->mHandle == INVALID_SOCKET)
return; return;
if (mSocketVector.size() >= 62)
{
ICELogError(<< "fd_set overflow; too much sockets");
return;
}
FD_SET(socket->mHandle, &mReadSet); FD_SET(socket->mHandle, &mReadSet);
if (socket->mHandle > mMaxHandle) if (socket->mHandle > mMaxHandle)
mMaxHandle = socket->mHandle; mMaxHandle = socket->mHandle;
@@ -187,7 +163,7 @@ unsigned DatagramAgreggator::count()
bool DatagramAgreggator::hasDataAtIndex(unsigned index) bool DatagramAgreggator::hasDataAtIndex(unsigned index)
{ {
PDatagramSocket socket = mSocketVector[index]; PDatagramSocket socket = mSocketVector[index];
return FD_ISSET(socket->mHandle, &mReadSet); return (FD_ISSET(socket->mHandle, &mReadSet) != 0);
} }
PDatagramSocket DatagramAgreggator::socketAt(unsigned index) PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
@@ -195,12 +171,12 @@ PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
return mSocketVector[index]; return mSocketVector[index];
} }
bool DatagramAgreggator::waitForData(std::chrono::milliseconds timeout) bool DatagramAgreggator::waitForData(unsigned milliseconds)
{ {
timeval tv; timeval tv;
tv.tv_sec = timeout.count() / 1000; tv.tv_sec = milliseconds / 1000;
tv.tv_usec = (timeout.count() % 1000) * 1000; tv.tv_usec = (milliseconds % 1000) * 1000;
int rescode = ::select(mMaxHandle + 1, &mReadSet, nullptr, nullptr, &tv); int rescode = ::select(mMaxHandle, &mReadSet, NULL, NULL, &tv);
return rescode > 0; return rescode > 0;
} }
+5 -8
View File
@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -9,19 +9,18 @@
#include "HL_InternetAddress.h" #include "HL_InternetAddress.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <chrono>
class NetworkSocket class NetworkSocket
{ {
public: public:
virtual int localport() = 0; virtual int localport() = 0;
}; };
class DatagramSocket class DatagramSocket
{ {
friend class SocketHeap; friend class SocketHeap;
friend class DatagramAgreggator; friend class DatagramAgreggator;
public: public:
DatagramSocket(); DatagramSocket();
virtual ~DatagramSocket(); virtual ~DatagramSocket();
@@ -37,12 +36,10 @@ public:
virtual SOCKET socket() const; virtual SOCKET socket() const;
virtual void open(int family); virtual void open(int family);
protected: protected:
int mFamily; int mFamily;
SOCKET mHandle; SOCKET mHandle;
int mLocalPort; int mLocalPort;
void internalClose();
}; };
typedef std::shared_ptr<DatagramSocket> PDatagramSocket; typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
@@ -57,7 +54,7 @@ public:
bool hasDataAtIndex(unsigned index); bool hasDataAtIndex(unsigned index);
PDatagramSocket socketAt(unsigned index); PDatagramSocket socketAt(unsigned index);
bool waitForData(std::chrono::milliseconds timeout); bool waitForData(unsigned milliseconds);
protected: protected:
typedef std::vector<PDatagramSocket> SocketList; typedef std::vector<PDatagramSocket> SocketList;
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -10,7 +10,7 @@
#include <windows.h> #include <windows.h>
#if defined(USE_MINIDUMP) #if defined(USE_MINIDUMP)
#include <DbgHelp.h> # include <DbgHelp.h>
#endif #endif
int winVersion() int winVersion()
@@ -39,17 +39,17 @@ int winVersion()
return Win_Seven; return Win_Seven;
else else
return Win_Vista; return Win_Vista;
} }
// ----------------- CrashMiniDump ----------------- // ----------------- CrashMiniDump -----------------
#if defined(USE_MINIDUMP) #if defined(USE_MINIDUMP)
static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{ {
// Open the file // Open the file
HANDLE hFile = HANDLE hFile = CreateFile( L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE,
CreateFile(L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )
{ {
// Create the minidump // Create the minidump
MINIDUMP_EXCEPTION_INFORMATION mdei; MINIDUMP_EXCEPTION_INFORMATION mdei;
@@ -60,11 +60,11 @@ static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
MINIDUMP_TYPE mdt = MiniDumpWithFullMemory; MINIDUMP_TYPE mdt = MiniDumpWithFullMemory;
BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(),
(ExceptionInfo != 0) ? &mdei : 0, 0, 0); hFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, 0 );
// Close the file // Close the file
CloseHandle(hFile); CloseHandle( hFile );
} }
else else
{ {
@@ -140,7 +140,7 @@ int _kbhit()
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf); fcntl(STDIN_FILENO, F_SETFL, oldf);
if (ch != EOF) if(ch != EOF)
{ {
ungetc(ch, stdin); ungetc(ch, stdin);
return 1; return 1;
+2 -2
View File
@@ -41,7 +41,7 @@ int iosVersion();
#include <sys/select.h> #include <sys/select.h>
#include <termios.h> #include <termios.h>
#if defined(TARGET_LINUX) #if defined(TARGET_LINUX)
// # include <stropts.h> //# include <stropts.h>
#endif #endif
extern int _kbhit(); extern int _kbhit();
@@ -49,6 +49,6 @@ extern int _kbhit();
#endif #endif
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
#include <conio.h> # include <conio.h>
#endif #endif
#endif #endif
+4 -2
View File
@@ -5,9 +5,11 @@
#include "HL_Pointer.h" #include "HL_Pointer.h"
UsageCounter::UsageCounter() {} UsageCounter::UsageCounter()
{}
UsageCounter::~UsageCounter() {} UsageCounter::~UsageCounter()
{}
int UsageCounter::obtain(int usageId) int UsageCounter::obtain(int usageId)
{ {
-399
View File
@@ -1,399 +0,0 @@
#pragma once
/// @file
/// @brief A thread-local fixed-block memory pool plus a std-conforming Allocator wrapper, used to
/// pool the small, short-lived, per-RTP-packet shared_ptr nodes produced by std::allocate_shared
/// (e.g. allocate_shared<jrtplib::RTPPacket> on the capture path and allocate_shared<RtpBuffer::
/// Packet> in the jitter buffer). Those objects are fixed-size and churn at the packet rate, so a
/// pool removes them from the general allocator's hot path.
///
/// Pooling is active by default. Define HL_RTP_POOL=0 at compile time to make hl::PoolAllocator a
/// transparent passthrough to the global allocator (i.e. allocate_shared behaves like make_shared)
/// for A/B benchmarking without touching the call sites.
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <new>
#include <vector>
#ifndef HL_RTP_POOL
#define HL_RTP_POOL 1
#endif
namespace hl
{
#if HL_RTP_POOL
/// @class FixedBlockPool
/// A process-wide, fixed-block pool with a lock-free thread-local fast path: uniform 256-byte
/// blocks carved from 64 KB chunks, an intrusive thread-local free list, and a per-block header
/// tag so deallocate() is O(1) and lock-free for any block (and can tell pooled blocks from the
/// global-allocator fallback used for oversized requests) regardless of the freeing thread.
///
/// CROSS-THREAD RECLAIM (2026-07-26) — fixes an unbounded capacity ratchet. A uniform block size
/// makes a block allocated on one thread safe to free on another, but "safe" is not "reclaimed".
/// With a purely thread-local free list the block joins the FREEING thread's list, so a
/// producer/consumer split strands it: vq-core allocates RTPPacket / jitter-buffer nodes on the
/// capture threads and frees them on the interval/reap side, so the capture threads' lists were
/// perpetually empty and refill() carved a fresh chunk every BlocksPerChunk allocations, forever.
/// Observed on vqmonitor (the only box with >1 capture thread): capacityBlocks() 256 -> 11.8M
/// = 2.7 GiB over 4 days at ~25 MiB/h, while activeBlocks() drained to 0 every idle interval.
///
/// The fix is a process-wide depot of free-block batches. A thread whose free list grows past
/// ThreadCacheHighWater hands a batch down to the depot; a thread that runs dry takes a batch
/// from the depot before carving a new chunk; a thread flushes its remainder to the depot on
/// exit. The depot is touched once per BlocksPerChunk operations and the hysteresis between
/// "empty" and the high-water keeps it off the per-packet path, so the fast path stays lock-free.
/// capacityBlocks() now plateaus at the true concurrent high-water instead of ratcheting.
class FixedBlockPool
{
public:
/// Usable bytes handed back to the caller from a pooled block. Comfortably covers the
/// shared_ptr nodes we pool (control block + RTPPacket / RtpBuffer::Packet, ~90-130 bytes).
static constexpr std::size_t PayloadSize = 240;
static constexpr std::size_t BlocksPerChunk = 256;
/// Free blocks a thread keeps to itself before handing a BlocksPerChunk batch to the depot.
/// Must exceed BlocksPerChunk so a thread that churns around the boundary does not bounce
/// on the depot mutex; 2x leaves a full batch in hand after every release.
static constexpr std::size_t ThreadCacheHighWater = 2 * BlocksPerChunk;
static void* allocate(std::size_t size)
{
if (size > PayloadSize)
{
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
tagOf(raw) = TagGlobal;
s_activeGlobal.fetch_add(1, std::memory_order_relaxed);
return raw + HeaderSize;
}
ThreadCache& tc = cache();
if (tc.head == nullptr)
{
// Reclaim from the depot before asking the OS for more memory. Only when the
// depot is dry as well has the pool genuinely outgrown its current capacity.
std::size_t taken = 0;
tc.head = depot().takeBatch(taken);
if (tc.head == nullptr)
{
tc.head = registry().refill();
taken = BlocksPerChunk;
}
tc.count = taken;
}
uint8_t* block = static_cast<uint8_t*>(tc.head);
tc.head = nextOf(block);
--tc.count;
s_activeBlocks.fetch_add(1, std::memory_order_relaxed);
return block + HeaderSize;
}
static void deallocate(void* ptr) noexcept
{
if (ptr == nullptr)
return;
uint8_t* block = static_cast<uint8_t*>(ptr) - HeaderSize;
if (tagOf(block) == TagPool)
{
ThreadCache& tc = cache();
nextOf(block) = tc.head;
tc.head = block;
++tc.count;
s_activeBlocks.fetch_sub(1, std::memory_order_relaxed);
// The one line that closes the ratchet: surplus goes back to the depot, where
// the thread that actually allocates can reach it.
if (tc.count >= ThreadCacheHighWater)
releaseBatch(tc, BlocksPerChunk);
}
else
{
::operator delete(static_cast<void*>(block));
s_activeGlobal.fetch_sub(1, std::memory_order_relaxed);
}
}
/// @name Diagnostics (relaxed gauges — not synchronization)
/// @{
/// Blocks currently handed out from the pool = pooled allocate() minus
/// pooled deallocate(). Unlike capacityBlocks() (the chunk high-water,
/// which only ever grows) this DROPS when objects are freed, so a rising
/// activeBlocks() is a genuine leak of referenced objects rather than a
/// traffic peak that merely carved extra chunks. Covers every pooled
/// shared_ptr node (allocate_shared<RTPPacket> + jitter-buffer packets).
static std::int64_t activeBlocks() noexcept { return s_activeBlocks.load(std::memory_order_relaxed); }
/// Live oversized allocations that overflowed to ::operator new.
static std::int64_t activeGlobalAllocations() noexcept { return s_activeGlobal.load(std::memory_order_relaxed); }
/// Total blocks ever carved (chunks x BlocksPerChunk) = high-water capacity.
/// With cross-thread reclaim this PLATEAUS once the pool has covered the
/// concurrent high-water; sustained linear growth means reclaim is not working.
static std::int64_t capacityBlocks() noexcept
{
return s_chunks.load(std::memory_order_relaxed) * static_cast<std::int64_t>(BlocksPerChunk);
}
/// Free blocks parked in the process-wide depot, i.e. reclaimed from a freeing
/// thread and available to any allocating thread. Healthy steady state is a
/// non-zero, bounded value: it is the surplus that used to be stranded.
static std::int64_t depotBlocks() noexcept { return s_depotBlocks.load(std::memory_order_relaxed); }
/// Approximate live pooled payload bytes (excludes per-block header).
static std::int64_t activeBytes() noexcept { return activeBlocks() * static_cast<std::int64_t>(PayloadSize); }
/// @}
private:
inline static std::atomic<std::int64_t> s_activeBlocks{0};
inline static std::atomic<std::int64_t> s_activeGlobal{0};
inline static std::atomic<std::int64_t> s_chunks{0};
inline static std::atomic<std::int64_t> s_depotBlocks{0};
static constexpr std::size_t HeaderSize =
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
static constexpr uint64_t TagPool = 0x504F4F4C52545008ULL; // "POOLRTP\b"
static constexpr uint64_t TagGlobal = 0x474C4F42524C0808ULL; // "GLOBRL\b\b"
/// A free block's payload is dead space, so the list links live there. Slot 0 chains
/// blocks within a list/batch; slots 1-2 are used only on a batch's head block, to chain
/// batches inside the depot and to carry the batch length (batches from a thread-exit
/// flush are shorter than BlocksPerChunk).
static_assert(PayloadSize >= 2 * sizeof(void*) + sizeof(std::size_t),
"pooled block payload must hold the free-list, depot and length slots");
static uint64_t& tagOf(void* block) noexcept { return *reinterpret_cast<uint64_t*>(block); }
static void*& nextOf(void* block) noexcept
{
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize);
}
static void*& batchNextOf(void* block) noexcept
{
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize + sizeof(void*));
}
static std::size_t& batchLenOf(void* block) noexcept
{
return *reinterpret_cast<std::size_t*>(static_cast<uint8_t*>(block) + HeaderSize + 2 * sizeof(void*));
}
/// Per-thread free list. The destructor hands the remainder to the depot so a thread that
/// exits does not strand its blocks — the same ratchet as the cross-thread case, just
/// triggered once per thread instead of continuously.
struct ThreadCache
{
void* head = nullptr;
std::size_t count = 0;
~ThreadCache()
{
while (count > 0 && head != nullptr)
releaseBatch(*this, count);
}
};
static ThreadCache& cache() noexcept
{
static thread_local ThreadCache tc;
return tc;
}
/// Detach the first `blocks` entries of the thread list and park them in the depot.
static void releaseBatch(ThreadCache& tc, std::size_t blocks) noexcept
{
if (blocks == 0 || tc.head == nullptr)
return;
void* batchHead = tc.head;
void* batchTail = batchHead;
std::size_t length = 1;
for (; length < blocks && nextOf(batchTail) != nullptr; ++length)
batchTail = nextOf(batchTail);
tc.head = nextOf(batchTail);
tc.count -= length;
nextOf(batchTail) = nullptr;
batchLenOf(batchHead) = length;
depot().giveBatch(batchHead, length);
}
/// Process-wide store of reclaimed batches. Intrusive (the links live in the free blocks
/// themselves) so neither path can allocate or throw — deallocate() is noexcept. The mutex
/// is taken once per BlocksPerChunk operations, not per packet.
class Depot
{
public:
void* takeBatch(std::size_t& lengthOut) noexcept
{
std::lock_guard<std::mutex> lock(m_Mutex);
void* batch = m_Head;
if (batch == nullptr)
{
lengthOut = 0;
return nullptr;
}
m_Head = batchNextOf(batch);
lengthOut = batchLenOf(batch);
batchNextOf(batch) = nullptr;
s_depotBlocks.fetch_sub(static_cast<std::int64_t>(lengthOut), std::memory_order_relaxed);
return batch;
}
void giveBatch(void* batch, std::size_t length) noexcept
{
std::lock_guard<std::mutex> lock(m_Mutex);
batchNextOf(batch) = m_Head;
m_Head = batch;
s_depotBlocks.fetch_add(static_cast<std::int64_t>(length), std::memory_order_relaxed);
}
private:
std::mutex m_Mutex;
void* m_Head = nullptr;
};
class ChunkRegistry
{
public:
void* refill()
{
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
s_chunks.fetch_add(1, std::memory_order_relaxed);
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Chunks.push_back(chunk);
}
void* list = nullptr;
for (std::size_t i = 0; i < BlocksPerChunk; ++i)
{
uint8_t* block = chunk + i * BlockSize;
tagOf(block) = TagPool;
nextOf(block) = list;
list = block;
}
return list;
}
private:
std::mutex m_Mutex;
std::vector<uint8_t*> m_Chunks;
};
/// Both singletons are deliberately immortal (leaked at exit, reclaimed by the OS).
/// A thread_local ThreadCache is destroyed at thread exit — for the main thread that is
/// during static destruction — and its destructor touches the depot, so a depot with a
/// destructor could be used after being destroyed. The same applies to the chunk memory:
/// any pooled object outliving the registry would free into deleted chunks. Never
/// destroying them removes both hazards; the process is exiting either way.
static Depot& depot() noexcept
{
static Depot* instance = new Depot();
return *instance;
}
static ChunkRegistry& registry()
{
static ChunkRegistry* instance = new ChunkRegistry();
return *instance;
}
};
#endif // HL_RTP_POOL
/// Mode-independent accessors for the pool diagnostics, so callers need not know
/// whether HL_RTP_POOL is compiled in. When pooling is disabled they return -1
/// ("not applicable") since allocate_shared then behaves like make_shared.
#if HL_RTP_POOL
inline std::int64_t poolActiveBlocks() noexcept
{
return FixedBlockPool::activeBlocks();
}
inline std::int64_t poolCapacityBlocks() noexcept
{
return FixedBlockPool::capacityBlocks();
}
inline std::int64_t poolDepotBlocks() noexcept
{
return FixedBlockPool::depotBlocks();
}
inline std::int64_t poolActiveGlobal() noexcept
{
return FixedBlockPool::activeGlobalAllocations();
}
inline std::int64_t poolActiveBytes() noexcept
{
return FixedBlockPool::activeBytes();
}
#else
inline std::int64_t poolActiveBlocks() noexcept
{
return -1;
}
inline std::int64_t poolCapacityBlocks() noexcept
{
return -1;
}
inline std::int64_t poolDepotBlocks() noexcept
{
return -1;
}
inline std::int64_t poolActiveGlobal() noexcept
{
return -1;
}
inline std::int64_t poolActiveBytes() noexcept
{
return -1;
}
#endif
/// @class PoolAllocator
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
/// that does not fit a pooled block) it delegates to the global allocator, matching make_shared.
template<class T>
struct PoolAllocator
{
using value_type = T;
PoolAllocator() noexcept = default;
template<class U>
PoolAllocator(const PoolAllocator<U>&) noexcept
{
}
T* allocate(std::size_t n)
{
#if HL_RTP_POOL
return static_cast<T*>(FixedBlockPool::allocate(n * sizeof(T)));
#else
return static_cast<T*>(::operator new(n * sizeof(T)));
#endif
}
void deallocate(T* p, std::size_t /*n*/) noexcept
{
#if HL_RTP_POOL
FixedBlockPool::deallocate(p);
#else
::operator delete(static_cast<void*>(p));
#endif
}
template<class U>
bool operator==(const PoolAllocator<U>&) const noexcept
{
return true;
}
template<class U>
bool operator!=(const PoolAllocator<U>&) const noexcept
{
return false;
}
};
} // namespace hl
+65 -70
View File
@@ -1,15 +1,15 @@
#include "HL_Process.h" #include "HL_Process.h"
#include <thread> #include <thread>
#include <memory> #include <memory>
#include <algorithm>
#ifdef TARGET_WIN #ifdef TARGET_WIN
#define popen _popen # define popen _popen
#define pclose _pclose # define pclose _pclose
#endif #endif
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
#include <Windows.h> #include <Windows.h>
#include <iostream>
#include "helper/HL_String.h" #include "helper/HL_String.h"
int OsProcess::execSystem(const std::string& cmd) int OsProcess::execSystem(const std::string& cmd)
@@ -22,38 +22,38 @@ std::string OsProcess::execCommand(const std::string& cmd)
std::string output; std::string output;
HANDLE hPipeRead, hPipeWrite; HANDLE hPipeRead, hPipeWrite;
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)}; SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process. saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor = NULL; saAttr.lpSecurityDescriptor = NULL;
// Create a pipe to get results from child's stdout. // Create a pipe to get results from child's stdout.
if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0)) if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) )
return output; return output;
STARTUPINFOA si = {sizeof(STARTUPINFOA)}; STARTUPINFOA si = { sizeof(STARTUPINFOA) };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite; si.hStdOutput = hPipeWrite;
si.hStdError = hPipeWrite; si.hStdError = hPipeWrite;
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags. si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi = {0}; PROCESS_INFORMATION pi = { 0 };
char* cmdline = (char*)_alloca(cmd.size() + 1); char* cmdline = (char*)_alloca(cmd.size()+1);
strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str()); strcpy(cmdline, StringHelper::replace(cmd, "/", "\\").c_str());
BOOL fSuccess = CreateProcessA(nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (!fSuccess) if (! fSuccess)
{ {
CloseHandle(hPipeWrite); CloseHandle( hPipeWrite );
CloseHandle(hPipeRead); CloseHandle( hPipeRead );
return output; return output;
} }
bool bProcessEnded = false; bool bProcessEnded = false;
for (; !bProcessEnded;) for (; !bProcessEnded ;)
{ {
// Give some timeslice (50ms), so we won't waste 100% cpu. // Give some timeslice (50ms), so we won't waste 100% cpu.
bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0; bProcessEnded = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0;
// Even if process exited - we continue reading, if there is some data available over pipe. // Even if process exited - we continue reading, if there is some data available over pipe.
for (;;) for (;;)
@@ -68,42 +68,42 @@ std::string OsProcess::execCommand(const std::string& cmd)
if (!dwAvail) // no data available, return if (!dwAvail) // no data available, return
break; break;
if (!::ReadFile(hPipeRead, buf, std::min(sizeof(buf) - 1, (size_t)dwAvail), &dwRead, NULL) || !dwRead) if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
// error, the child process might ended // error, the child process might ended
break; break;
buf[dwRead] = 0; buf[dwRead] = 0;
output += buf; output += buf;
} }
} // for } //for
CloseHandle(hPipeWrite); CloseHandle( hPipeWrite );
CloseHandle(hPipeRead); CloseHandle( hPipeRead );
CloseHandle(pi.hProcess); CloseHandle( pi.hProcess );
CloseHandle(pi.hThread); CloseHandle( pi.hThread );
return output; return output;
} }
std::shared_ptr<std::thread> std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline,
OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> callback, std::function<void(const std::string& line)> callback,
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag) std::function<void(const std::string& reason)> finished_callback,
bool& finish_flag)
{ {
// std::cout << cmdline << std::endl; // std::cout << cmdline << std::endl;
std::string output; std::string output;
HANDLE hPipeRead, hPipeWrite; HANDLE hPipeRead, hPipeWrite;
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE}; SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE };
saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process. saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor = nullptr; saAttr.lpSecurityDescriptor = nullptr;
// Create a pipe to get results from child's stdout. // Create a pipe to get results from child's stdout.
if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0)) if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) )
return std::shared_ptr<std::thread>(); return std::shared_ptr<std::thread>();
STARTUPINFOA si; STARTUPINFOA si; memset(&si, 0, sizeof si);
memset(&si, 0, sizeof si);
si.cb = sizeof(STARTUPINFOA); si.cb = sizeof(STARTUPINFOA);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite; si.hStdOutput = hPipeWrite;
@@ -113,28 +113,27 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof pi); memset(&pi, 0, sizeof pi);
char* cmdbuffer = (char*)_alloca(cmdline.size() + 1); char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str()); strcpy(cmdbuffer, StringHelper::replace(cmdline, "/", "\\").c_str());
BOOL fSuccess = BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
CreateProcessA(nullptr, cmdbuffer, nullptr, nullptr, TRUE, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi); CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi);
if (!fSuccess) if (! fSuccess)
{ {
CloseHandle(hPipeWrite); CloseHandle( hPipeWrite );
CloseHandle(hPipeRead); CloseHandle( hPipeRead );
return std::shared_ptr<std::thread>(); return std::shared_ptr<std::thread>();
} }
std::shared_ptr<std::thread> r = std::make_shared<std::thread>( std::shared_ptr<std::thread> r = std::make_shared<std::thread>(
[&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]() [&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]()
{ {
char buf[4096]; char buf[4096]; memset(buf, 0, sizeof buf);
memset(buf, 0, sizeof buf); for (; !finish_flag ;)
for (; !finish_flag;)
{ {
// Give some timeslice (50ms), so we won't waste 100% cpu. // Give some timeslice (50ms), so we won't waste 100% cpu.
bool timeouted = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0; bool timeouted = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0;
// Even if process exited - we continue reading, if there is some data available over pipe. // Even if process exited - we continue reading, if there is some data available over pipe.
for (;;) for (;;)
@@ -149,9 +148,7 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
break; break;
int filled = strlen(buf); int filled = strlen(buf);
if (!::ReadFile(hPipeRead, buf + filled, std::min(sizeof(buf) - 1 - filled, (size_t)dwAvail), if (!::ReadFile(hPipeRead, buf + filled, min(sizeof(buf) - 1 - filled, dwAvail), &dwRead, nullptr) || !dwRead)
&dwRead, nullptr) ||
!dwRead)
// error, the child process might ended // error, the child process might ended
break; break;
@@ -161,33 +158,33 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
const char* cr; const char* cr;
while ((cr = strchr(buf, '\n')) != nullptr) while ((cr = strchr(buf, '\n')) != nullptr)
{ {
std::string line(buf, cr - buf - 1); std::string line(buf, cr - buf -1);
if (callback) if (callback)
callback(strx::trim(line)); callback(StringHelper::trim(line));
memmove(buf, cr + 1, strlen(cr + 1) + 1); memmove(buf, cr + 1, strlen(cr+1) + 1);
} }
} }
} // for } //for
if (buf[0]) if (buf[0])
callback(strx::trim(std::string(buf))); callback(StringHelper::trim(std::string(buf)));
char ctrlc = 3; char ctrlc = 3;
// if (finish_flag) //if (finish_flag)
// ::WriteFile(hPipeWrite, &ctrlc, 1, nullptr, nullptr); // ::WriteFile(hPipeWrite, &ctrlc, 1, nullptr, nullptr);
// GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId); // GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId);
CloseHandle(hPipeWrite); CloseHandle( hPipeWrite );
CloseHandle(hPipeRead); CloseHandle( hPipeRead );
if (finish_flag) if (finish_flag)
{ {
// GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); //GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
// Close underlying process // Close underlying process
// TerminateProcess(pi.hProcess, 3); //TerminateProcess(pi.hProcess, 3);
} }
CloseHandle(pi.hProcess); CloseHandle( pi.hProcess );
CloseHandle(pi.hThread); CloseHandle( pi.hThread );
if (finished_callback) if (finished_callback)
finished_callback(std::string()); finished_callback(std::string());
}); });
@@ -232,12 +229,12 @@ int OsProcess::execSystem(const std::string& cmd)
#include "helper/HL_String.h" #include "helper/HL_String.h"
#include "helper/HL_Sync.h" #include "helper/HL_Sync.h"
std::shared_ptr<std::thread> std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline,
OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback, std::function<void(const std::string& line)> line_callback,
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag) std::function<void(const std::string& reason)> finished_callback,
bool& finish_flag)
{ {
std::shared_ptr<std::thread> t = std::make_shared<std::thread>( std::shared_ptr<std::thread> t = std::make_shared<std::thread>([cmdline, line_callback, finished_callback, &finish_flag]()
[cmdline, line_callback, finished_callback, &finish_flag]()
{ {
ThreadHelper::setName("OsProcess::asyncExecCommand"); ThreadHelper::setName("OsProcess::asyncExecCommand");
std::string cp = cmdline; std::string cp = cmdline;
@@ -260,7 +257,7 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
while (!feof(pipe) && !finish_flag) while (!feof(pipe) && !finish_flag)
{ {
// Wait for more data // Wait for more data
struct pollfd pfd{.fd = fno, .events = POLLIN}; struct pollfd pfd{ .fd = fno, .events = POLLIN };
while (poll(&pfd, 1, 0) == 0 && !finish_flag) while (poll(&pfd, 1, 0) == 0 && !finish_flag)
; ;
@@ -278,18 +275,19 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
buffer[r] = 0; buffer[r] = 0;
lines += std::string(buffer); lines += std::string(buffer);
} }
} 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(strx::trim(lines.substr(p, d - p))); line_callback(StringHelper::trim(lines.substr(p, d-p)));
p = d + 1; p = d + 1;
} }
} }
@@ -313,8 +311,6 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
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
@@ -324,7 +320,7 @@ pid_t OsProcess::findPid(const std::string& cmdline)
std::string output = execCommand(oss.str()); std::string output = execCommand(oss.str());
return std::atoi(output.c_str()); return std::atoi(output.c_str());
} }
catch (...) catch(...)
{ {
return 0; return 0;
} }
@@ -336,6 +332,5 @@ void OsProcess::killByPid(pid_t pid)
return; return;
execSystem("kill -9 " + std::to_string(pid) + " &"); execSystem("kill -9 " + std::to_string(pid) + " &");
} }
#endif
#endif #endif
+5 -3
View File
@@ -11,13 +11,15 @@ class OsProcess
public: public:
static std::string execCommand(const std::string& cmdline); static std::string execCommand(const std::string& cmdline);
static int execSystem(const std::string& cmdline); static int execSystem(const std::string& cmdline);
static std::shared_ptr<std::thread> static std::shared_ptr<std::thread> asyncExecCommand(const std::string& cmdline,
asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback, std::function<void(const std::string& line)> line_callback,
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag); std::function<void(const std::string& reason)> finished_callback,
bool& finish_flag);
#if defined(TARGET_OSX) || defined(TARGET_LINUX) #if defined(TARGET_OSX) || defined(TARGET_LINUX)
static pid_t findPid(const std::string& cmdline); static pid_t findPid(const std::string& cmdline);
static void killByPid(pid_t pid); static void killByPid(pid_t pid);
#endif #endif
}; };
#endif #endif
+81 -382
View File
@@ -1,44 +1,37 @@
/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
#include <WinSock2.h> # include <WinSock2.h>
#include <Windows.h> # include <Windows.h>
#endif
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX)
#include <arpa/inet.h>
#endif #endif
#include "HL_Rtp.h" #include "HL_Rtp.h"
#include "HL_Exception.h" #include "HL_Exception.h"
#include "HL_Log.h" #include "HL_String.h"
#include "jrtplib/src/rtprawpacket.h" #if defined(USE_RTP_DUMP)
#include "jrtplib/src/rtpipv4address.h" # include "jrtplib/src/rtprawpacket.h"
# include "jrtplib/src/rtpipv4address.h"
#endif
#include <stdexcept> #if !defined(TARGET_WIN)
#include <fstream> # include <alloca.h>
#include <cstring> #endif
#include <cstdio>
#include <chrono>
#define LOG_SUBSYSTEM "network" #include <sstream>
#include <tuple>
static constexpr size_t MAX_RTP_PACKET_SIZE = 65535;
static const char RTPDUMP_SHEBANG[] = "#!rtpplay1.0";
// RTP fixed header (little-endian bit-field layout)
struct RtpHeader struct RtpHeader
{ {
unsigned char cc : 4; /* CSRC count */ unsigned char cc:4; /* CSRC count */
unsigned char x : 1; /* header extension flag */ unsigned char x:1; /* header extension flag */
unsigned char p : 1; /* padding flag */ unsigned char p:1; /* padding flag */
unsigned char version : 2; /* protocol version */ unsigned char version:2; /* protocol version */
unsigned char pt : 7; /* payload type */ unsigned char pt:7; /* payload type */
unsigned char m : 1; /* marker bit */ unsigned char m:1; /* marker bit */
unsigned short seq; /* sequence number */ unsigned short seq; /* sequence number */
unsigned int ts; /* timestamp */ unsigned int ts; /* timestamp */
unsigned int ssrc; /* synchronization source */ unsigned int ssrc; /* synchronization source */
@@ -46,56 +39,32 @@ struct RtpHeader
struct RtcpHeader struct RtcpHeader
{ {
unsigned char rc : 5; /* reception report count */ unsigned char rc:5; /* reception report count */
unsigned char p : 1; /* padding flag */ unsigned char p:1; /* padding flag */
unsigned char version : 2; /* protocol version */ unsigned char version:2; /* protocol version */
unsigned char pt; /* payload type */ unsigned char pt:8; /* payload type */
uint16_t len; /* length */ uint16_t len; /* length */
uint32_t ssrc; /* synchronization source */ uint32_t ssrc; /* synchronization source */
}; };
// --- IPv4 address helpers ---
static std::string ipToString(uint32_t ip)
{
// ip in host byte order → dotted-decimal
return std::to_string((ip >> 24) & 0xFF) + "." + std::to_string((ip >> 16) & 0xFF) + "." +
std::to_string((ip >> 8) & 0xFF) + "." + std::to_string(ip & 0xFF);
}
static uint32_t stringToIp(const std::string& s)
{
unsigned a = 0, b = 0, c = 0, d = 0;
if (std::sscanf(s.c_str(), "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
return 0;
if (a > 255 || b > 255 || c > 255 || d > 255)
return 0;
return (a << 24) | (b << 16) | (c << 8) | d;
}
// --- RtpHelper implementation ---
bool RtpHelper::isRtp(const void* buffer, size_t length) bool RtpHelper::isRtp(const void* buffer, size_t length)
{ {
if (length < 12) if (length < 12)
return false; return false;
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer); unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
if (h->version != 2) bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35);
return false;
unsigned char pt = h->pt;
bool rtp = (pt >= 96 && pt <= 127) || (pt < 35);
return rtp; return rtp;
} }
bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length) bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
{ {
// A minimal RTCP packet (e.g. an empty receiver report) is 8 bytes if (length < 12)
if (length < 8)
return false; return false;
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer); unsigned char b = ((const unsigned char*)buffer)[0];
return h->version == 2;
return (b & 0xC0 ) == 128;
} }
bool RtpHelper::isRtcp(const void* buffer, size_t length) bool RtpHelper::isRtcp(const void* buffer, size_t length)
@@ -106,18 +75,9 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
unsigned RtpHelper::findSsrc(const void* buffer, size_t length) unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
{ {
if (isRtp(buffer, length)) if (isRtp(buffer, length))
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc); return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
else if (isRtpOrRtcp(buffer, length)) else
return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc); return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
return 0;
}
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
{
if (isRtp(buffer, length))
reinterpret_cast<RtpHeader*>(buffer)->ssrc = htonl(ssrc);
else if (isRtpOrRtcp(buffer, length))
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)
@@ -128,7 +88,7 @@ int RtpHelper::findPtype(const void* buffer, size_t length)
return -1; return -1;
} }
int RtpHelper::findPacketNo(const void* buffer, size_t length) int RtpHelper::findPacketNo(const void *buffer, size_t length)
{ {
if (isRtp(buffer, length)) if (isRtp(buffer, length))
return ntohs(reinterpret_cast<const RtpHeader*>(buffer)->seq); return ntohs(reinterpret_cast<const RtpHeader*>(buffer)->seq);
@@ -138,202 +98,47 @@ int RtpHelper::findPacketNo(const void* buffer, size_t length)
int RtpHelper::findPayloadLength(const void* buffer, size_t length) int RtpHelper::findPayloadLength(const void* buffer, size_t length)
{ {
if (!isRtp(buffer, length)) if (isRtp(buffer, length))
return -1;
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
const uint8_t* p = static_cast<const uint8_t*>(buffer);
// Fixed header (12 bytes) + CSRC list (4 * CC bytes)
size_t offset = 12 + 4u * h->cc;
if (offset > length)
return -1;
// Header extension
if (h->x)
{ {
if (offset + 4 > length) return length - 12;
return -1;
uint16_t extWords = (static_cast<uint16_t>(p[offset + 2]) << 8) | p[offset + 3];
offset += 4 + 4u * extWords;
if (offset > length)
return -1;
} }
else
size_t payloadLen = length - offset;
// Padding
if (h->p && payloadLen > 0)
{
uint8_t padBytes = p[length - 1];
if (padBytes > payloadLen)
return -1; return -1;
payloadLen -= padBytes;
}
return static_cast<int>(payloadLen);
} }
std::chrono::microseconds RtpHelper::toMicroseconds(const jrtplib::RTPTime& t) #if defined(USE_RTPDUMP)
RtpDump::RtpDump(const char *filename)
:mFilename(filename)
{}
RtpDump::~RtpDump()
{ {
return std::chrono::microseconds(uint64_t(t.GetDouble() * 1000000)); flush();
} for (PacketList::iterator packetIter=mPacketList.begin(); packetIter!=mPacketList.end(); ++packetIter)
// --- RtpDump implementation ---
std::shared_ptr<jrtplib::RTPPacket> RtpDump::parseRtpData(const uint8_t* data, size_t len)
{
if (!data || len < 12 || !RtpHelper::isRtp(data, len))
return nullptr;
try
{ {
// Both are heap-allocated; RTPRawPacket takes ownership and deletes them //free(packetIter->mData);
jrtplib::RTPIPAddress senderAddress = {jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0))}; delete packetIter->mPacket;
uint8_t* dataCopy = new uint8_t[len];
std::memcpy(dataCopy, data, len);
jrtplib::RTPRawPacket raw(dataCopy, len, senderAddress, jrtplib::RTPTime(0), true);
auto packet = std::make_shared<jrtplib::RTPPacket>(raw);
if (packet->GetCreationError() != 0)
return nullptr;
return packet;
} }
catch (const std::exception& e)
{
ICELogInfo(<< "Failed to parse RTP packet: " << e.what());
return nullptr;
}
}
RtpDump::RtpDump(const char* filename) : mFilename(filename ? filename : "") {}
RtpDump::~RtpDump() = default;
void RtpDump::setSource(uint32_t ip, uint16_t port)
{
mSourceIp = ip;
mSourcePort = port;
} }
void RtpDump::load() void RtpDump::load()
{ {
if (mFilename.empty()) FILE* f = fopen(mFilename.c_str(), "rb");
throw std::runtime_error("No filename specified"); if (!f)
throw Exception(ERR_WAVFILE_FAILED);
std::ifstream input(mFilename, std::ios::binary); while (!feof(f))
if (!input.is_open())
throw std::runtime_error("Failed to open RTP dump file: " + mFilename);
mPacketList.clear();
// --- 1. Text header: "#!rtpplay1.0 <ip>/<port>\n" ---
std::string textLine;
std::getline(input, textLine);
if (textLine.compare(0, sizeof(RTPDUMP_SHEBANG) - 1, RTPDUMP_SHEBANG) != 0)
throw std::runtime_error("Invalid rtpdump header: expected " + std::string(RTPDUMP_SHEBANG));
// Parse source address from the text line
size_t spacePos = textLine.find(' ');
if (spacePos != std::string::npos)
{ {
std::string addrPart = textLine.substr(spacePos + 1); RtpData data;
size_t slashPos = addrPart.find('/'); fread(&data.mLength, sizeof data.mLength, 1, f);
if (slashPos != std::string::npos) data.mData = new char[data.mLength];
{ fread(data.mData, 1, data.mLength, f);
mSourceIp = stringToIp(addrPart.substr(0, slashPos)); jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
try jrtplib::RTPTime t(0);
{ jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)data.mData, data.mLength, &addr, t, true);
mSourcePort = static_cast<uint16_t>(std::stoi(addrPart.substr(slashPos + 1))); data.mPacket = new jrtplib::RTPPacket(*raw);
mPacketList.push_back(data);
} }
catch (...)
{
mSourcePort = 0;
}
}
}
// --- 2. Binary file header (RD_hdr_t, 16 bytes) ---
uint32_t buf32;
uint16_t buf16;
input.read(reinterpret_cast<char*>(&buf32), 4);
mStartSec = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf32), 4);
mStartUsec = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf32), 4); // source IP (already NBO in file)
// The binary header stores IP in network byte order; convert to host
mSourceIp = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf16), 2);
mSourcePort = ntohs(buf16);
input.read(reinterpret_cast<char*>(&buf16), 2); // padding — discard
if (!input.good())
throw std::runtime_error("Failed to read rtpdump binary header");
// --- 3. Packet records ---
size_t packetCount = 0;
while (input.good() && input.peek() != EOF)
{
// Packet header: length(2) + plen(2) + offset(4) = 8 bytes
uint16_t recLength, plen;
uint32_t offsetMs;
input.read(reinterpret_cast<char*>(&recLength), 2);
if (input.gcount() != 2)
break;
recLength = ntohs(recLength);
input.read(reinterpret_cast<char*>(&plen), 2);
if (input.gcount() != 2)
break;
plen = ntohs(plen);
input.read(reinterpret_cast<char*>(&offsetMs), 4);
if (input.gcount() != 4)
break;
offsetMs = ntohl(offsetMs);
// All-zeros record signals end of file in some implementations
if (recLength == 0 && plen == 0 && offsetMs == 0)
break;
if (plen == 0 || plen > MAX_RTP_PACKET_SIZE)
throw std::runtime_error("Invalid packet payload length: " + std::to_string(plen));
if (recLength < plen + 8)
throw std::runtime_error("Record length (" + std::to_string(recLength) +
") smaller than payload + header (" + std::to_string(plen + 8) + ")");
// Read body
std::vector<uint8_t> body(plen);
input.read(reinterpret_cast<char*>(body.data()), plen);
if (static_cast<size_t>(input.gcount()) != plen)
throw std::runtime_error("Incomplete packet data in rtpdump file");
// Skip any padding between plen and recLength-8
size_t pad = static_cast<size_t>(recLength) - 8 - plen;
if (pad > 0)
input.seekg(static_cast<std::streamoff>(pad), std::ios::cur);
RtpData entry;
entry.mRawData = std::move(body);
entry.mOffsetMs = offsetMs;
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
mPacketList.push_back(std::move(entry));
packetCount++;
}
ICELogInfo(<< "Loaded " << packetCount << " packets from " << mFilename);
mLoaded = true;
} }
size_t RtpDump::count() const size_t RtpDump::count() const
@@ -343,145 +148,39 @@ size_t RtpDump::count() const
jrtplib::RTPPacket& RtpDump::packetAt(size_t index) jrtplib::RTPPacket& RtpDump::packetAt(size_t index)
{ {
if (index >= mPacketList.size())
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
if (!mPacketList[index].mPacket)
throw std::runtime_error("No parsed RTP data at index " + std::to_string(index));
return *mPacketList[index].mPacket; return *mPacketList[index].mPacket;
} }
const std::vector<uint8_t>& RtpDump::rawDataAt(size_t index) const
{
if (index >= mPacketList.size())
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
return mPacketList[index].mRawData;
}
uint32_t RtpDump::offsetAt(size_t index) const
{
if (index >= mPacketList.size())
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
return mPacketList[index].mOffsetMs;
}
void RtpDump::add(const void* buffer, size_t len) void RtpDump::add(const void* buffer, size_t len)
{ {
if (!buffer || len == 0) RtpData data;
return; data.mData = malloc(len);
memcpy(data.mData, buffer, len);
data.mLength = len;
uint32_t offsetMs = 0; jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
auto now = std::chrono::steady_clock::now(); jrtplib::RTPTime t(0);
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)const_cast<void*>(data.mData), data.mLength, &addr, t, true);
if (!mRecording) data.mPacket = new jrtplib::RTPPacket(*raw);
{ //delete raw;
mRecording = true; mPacketList.push_back(data);
mRecordStart = now;
// Capture wall-clock start time
auto wallNow = std::chrono::system_clock::now();
auto epoch = wallNow.time_since_epoch();
auto sec = std::chrono::duration_cast<std::chrono::seconds>(epoch);
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(epoch - sec);
mStartSec = static_cast<uint32_t>(sec.count());
mStartUsec = static_cast<uint32_t>(usec.count());
}
else
{
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - mRecordStart);
offsetMs = static_cast<uint32_t>(elapsed.count());
}
add(buffer, len, offsetMs);
}
void RtpDump::add(const void* buffer, size_t len, uint32_t offsetMs)
{
if (!buffer || len == 0)
return;
// The record length field is 16-bit and covers payload + 8 byte header
if (len > MAX_RTP_PACKET_SIZE - 8)
throw std::runtime_error("Packet too large: " + std::to_string(len));
RtpData entry;
entry.mRawData.assign(static_cast<const uint8_t*>(buffer), static_cast<const uint8_t*>(buffer) + len);
entry.mOffsetMs = offsetMs;
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
mPacketList.push_back(std::move(entry));
} }
void RtpDump::flush() void RtpDump::flush()
{ {
if (mFilename.empty()) FILE* f = fopen(mFilename.c_str(), "wb");
throw std::runtime_error("No filename specified"); if (!f)
throw Exception(ERR_WAVFILE_FAILED);
std::ofstream output(mFilename, std::ios::binary); PacketList::iterator packetIter = mPacketList.begin();
if (!output.is_open()) for (;packetIter != mPacketList.end(); ++packetIter)
throw std::runtime_error("Failed to open file for writing: " + mFilename);
// --- 1. Text header ---
std::string textLine =
std::string(RTPDUMP_SHEBANG) + " " + ipToString(mSourceIp) + "/" + std::to_string(mSourcePort) + "\n";
output.write(textLine.data(), static_cast<std::streamsize>(textLine.size()));
// --- 2. Binary file header (16 bytes) ---
uint32_t buf32;
uint16_t buf16;
buf32 = htonl(mStartSec);
output.write(reinterpret_cast<const char*>(&buf32), 4);
buf32 = htonl(mStartUsec);
output.write(reinterpret_cast<const char*>(&buf32), 4);
buf32 = htonl(mSourceIp);
output.write(reinterpret_cast<const char*>(&buf32), 4);
buf16 = htons(mSourcePort);
output.write(reinterpret_cast<const char*>(&buf16), 2);
buf16 = 0; // padding
output.write(reinterpret_cast<const char*>(&buf16), 2);
// --- 3. Packet records ---
size_t written = 0;
for (const auto& pkt : mPacketList)
{ {
if (pkt.mRawData.empty()) RtpData& data = *packetIter;
continue; // Disabled for debugging only
//fwrite(&data.mLength, sizeof data.mLength, 1, f);
uint16_t plen = static_cast<uint16_t>(pkt.mRawData.size()); fwrite(data.mData, data.mLength, 1, f);
uint16_t recLength = static_cast<uint16_t>(plen + 8);
buf16 = htons(recLength);
output.write(reinterpret_cast<const char*>(&buf16), 2);
buf16 = htons(plen);
output.write(reinterpret_cast<const char*>(&buf16), 2);
buf32 = htonl(pkt.mOffsetMs);
output.write(reinterpret_cast<const char*>(&buf32), 4);
output.write(reinterpret_cast<const char*>(pkt.mRawData.data()), plen);
written++;
} }
fclose(f);
if (!output.good())
throw std::runtime_error("Failed to write rtpdump file: " + mFilename);
ICELogInfo(<< "Wrote " << written << " packets to " << mFilename);
} }
#endif
void RtpDump::clear()
{
mPacketList.clear();
mLoaded = false;
mRecording = false;
}
+19 -91
View File
@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -6,14 +6,15 @@
#ifndef __HL_RTP_H #ifndef __HL_RTP_H
#define __HL_RTP_H #define __HL_RTP_H
#include "jrtplib/src/rtppacket.h" #if defined(USE_RTPDUMP)
# include "jrtplib/src/rtppacket.h"
#endif
#include "HL_Uuid.h"
#include "HL_InternetAddress.h"
#include <cstdint>
#include <cstdlib>
#include <vector> #include <vector>
#include <string> #include <string>
#include <memory>
#include <chrono>
// Class to carry rtp/rtcp socket pair // Class to carry rtp/rtcp socket pair
template<class T> template<class T>
@@ -22,11 +23,14 @@ struct RtpPair
T mRtp; T mRtp;
T mRtcp; T mRtcp;
RtpPair() {} RtpPair()
{}
RtpPair(const T& rtp, const T& rtcp) : mRtp(rtp), mRtcp(rtcp) {} RtpPair(const T& rtp, const T& rtcp)
:mRtp(rtp), mRtcp(rtcp)
{}
bool multiplexed() const { return mRtp == mRtcp; } bool multiplexed() { return mRtp == mRtcp; }
}; };
class RtpHelper class RtpHelper
@@ -38,110 +42,34 @@ 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);
static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t);
}; };
/** #if defined(USE_RTPDUMP)
* @brief Standard rtpdump file format (rtptools / Wireshark compatible)
*
* Conforms to the rtpdump format defined by rtptools:
* https://formats.kaitai.io/rtpdump/
*
* File layout:
* 1. Text header line:
* "#!rtpplay1.0 <source_ip>/<source_port>\n"
*
* 2. Binary file header (RD_hdr_t, 16 bytes, all big-endian):
* uint32_t start_sec - recording start time, seconds since epoch
* uint32_t start_usec - recording start time, microseconds
* uint32_t source_ip - source IP address (network byte order)
* uint16_t source_port - source port
* uint16_t padding - always 0
*
* 3. Packet records (repeated until EOF):
* Per-packet header (RD_packet_t, 8 bytes, all big-endian):
* uint16_t length - total record length (this 8-byte header + plen)
* uint16_t plen - RTP/RTCP payload length in bytes
* uint32_t offset - milliseconds since recording start
* Followed by plen bytes of RTP/RTCP packet data.
*
* Maximum single packet payload: 65535 bytes (enforced for safety).
*/
class RtpDump class RtpDump
{ {
protected: protected:
struct RtpData struct RtpData
{ {
std::shared_ptr<jrtplib::RTPPacket> mPacket; jrtplib::RTPPacket* mPacket;
std::vector<uint8_t> mRawData; void* mData;
uint32_t mOffsetMs = 0; size_t mLength;
}; };
typedef std::vector<RtpData> PacketList; typedef std::vector<RtpData> PacketList;
PacketList mPacketList; PacketList mPacketList;
std::string mFilename; std::string mFilename;
bool mLoaded = false;
// File header fields
uint32_t mSourceIp = 0;
uint16_t mSourcePort = 0;
uint32_t mStartSec = 0;
uint32_t mStartUsec = 0;
// Auto-compute packet offsets during recording
bool mRecording = false;
std::chrono::steady_clock::time_point mRecordStart;
std::shared_ptr<jrtplib::RTPPacket> parseRtpData(const uint8_t* data, size_t len);
public: public:
explicit RtpDump(const char* filename); RtpDump(const char* filename);
~RtpDump(); ~RtpDump();
/** Set source address for the file header (host byte order). */
void setSource(uint32_t ip, uint16_t port);
uint32_t sourceIp() const { return mSourceIp; }
uint16_t sourcePort() const { return mSourcePort; }
/**
* @brief Load packets from an rtpdump file
* @throws std::runtime_error on file/format error
*/
void load(); void load();
bool isLoaded() const { return mLoaded; }
size_t count() const; size_t count() const;
/**
* @brief Get parsed RTP packet at index
* @throws std::out_of_range if index is invalid
* @throws std::runtime_error if packet could not be parsed as RTP
*/
jrtplib::RTPPacket& packetAt(size_t index); jrtplib::RTPPacket& packetAt(size_t index);
/** @brief Get raw packet bytes at index */
const std::vector<uint8_t>& rawDataAt(size_t index) const;
/** @brief Get packet time offset in milliseconds */
uint32_t offsetAt(size_t index) const;
/** @brief Add a packet; time offset is auto-computed from first add() call */
void add(const void* data, size_t len); void add(const void* data, size_t len);
/** @brief Add a packet with an explicit millisecond offset */
void add(const void* data, size_t len, uint32_t offsetMs);
/**
* @brief Write all packets to file in rtpdump format
* @throws std::runtime_error on file error
*/
void flush(); void flush();
void clear();
const std::string& filename() const { return mFilename; }
}; };
#endif
#endif #endif
+3 -4
View File
@@ -4,13 +4,12 @@
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
template<class T> template <class T>
class SafeSingleton class SafeSingleton
{ {
protected: protected:
static std::atomic<T*> SharedInstance; static std::atomic<T*> SharedInstance;
static std::mutex mMutex; static std::mutex mMutex;
public: public:
static T& instance() static T& instance()
{ {
@@ -51,9 +50,9 @@ public:
} }
}; };
template<class T> template <class T>
std::atomic<T*> SafeSingleton<T>::SharedInstance; std::atomic<T*> SafeSingleton<T>::SharedInstance;
template<class T> template <class T>
std::mutex SafeSingleton<T>::mMutex; std::mutex SafeSingleton<T>::mMutex;
#endif #endif

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