Compare commits
4 Commits
master
..
e342abe2f5
| Author | SHA1 | Date | |
|---|---|---|---|
| e342abe2f5 | |||
| 9d79c01be0 | |||
| fa705e141b | |||
| 17e17d3d79 |
-89
@@ -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
|
||||
@@ -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 .
|
||||
|
||||
@@ -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/`
|
||||
@@ -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
@@ -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
@@ -1,141 +0,0 @@
|
||||
# RTPhone Platform
|
||||
|
||||
RTPhone is a comprehensive real-time communication (RTC) platform that provides a complete software stack for building VoIP/SIP-based communication applications. Developed by VoIP Objects (Sevana), RTPhone delivers production-ready voice communication capabilities with extensive codec support and cross-platform compatibility.
|
||||
|
||||
## Overview
|
||||
|
||||
RTPhone serves as a static library (`librtphone.a`) that can be integrated into larger telephony and communication systems. It provides a JSON-based command interface for easy integration and control, making it suitable for building softphones, PBX systems, WebRTC gateways, and carrier-grade voice solutions.
|
||||
|
||||
## Key Features
|
||||
|
||||
### Audio Codec Support
|
||||
RTPhone supports an extensive range of audio codecs:
|
||||
|
||||
**Standard Codecs:**
|
||||
- G.711 (A-law/¼-law)
|
||||
- G.722 (16kHz wideband)
|
||||
- G.729
|
||||
- GSM (Full Rate, Half Rate, Enhanced Full Rate)
|
||||
- iLBC (20ms/30ms)
|
||||
- ISAC (16kHz/32kHz)
|
||||
|
||||
**Advanced Codecs:**
|
||||
- AMR-NB/AMR-WB (Adaptive Multi-Rate Narrowband/Wideband) - please be aware - there is no patents for AMR codecs usage included ! You should acquire them on your own.
|
||||
- EVS (Enhanced Voice Services) - 3GPP's latest codec. Again - please be aware - there is no patents for EVS codec usage included ! You should acquire them on your own.
|
||||
- Opus - Modern low-latency codec
|
||||
- Speex (with acoustic echo cancellation)
|
||||
|
||||
**Codec Features:**
|
||||
- Bandwidth-efficient and octet-aligned modes
|
||||
- IuUP (Iu User Plane) protocol support for 3G networks
|
||||
- Dynamic codec switching
|
||||
- Packet loss concealment (PLC)
|
||||
- Comfort noise generation (CNG)
|
||||
|
||||
### Network & Protocol Support
|
||||
|
||||
**SIP Features:**
|
||||
- Full SIP 2.0 implementation via reSIProcate
|
||||
- Multiple transport protocols (UDP, TCP, TLS)
|
||||
- Registration, authentication, and session management
|
||||
- SIP MESSAGE, presence, and REFER support
|
||||
|
||||
**Media Transport:**
|
||||
- RTP/RTCP for media streaming
|
||||
- SRTP for secure media
|
||||
- ICE for NAT traversal with STUN/TURN support
|
||||
- WebRTC integration components
|
||||
- IPv4 and IPv6 support
|
||||
|
||||
### Cross-Platform Audio Support
|
||||
- DirectSound/WMME (Windows)
|
||||
- Core Audio (macOS/iOS)
|
||||
- ALSA/PulseAudio (Linux)
|
||||
- Oboe (Android) for low-latency audio
|
||||
- PortAudio fallback support
|
||||
|
||||
### Audio Quality Features
|
||||
- 48kHz sample rate support
|
||||
- Acoustic Echo Cancellation (AEC)
|
||||
- Audio resampling and format conversion
|
||||
- Multi-channel audio mixing
|
||||
- Perceptual Voice Quality Assessment (PVQA)
|
||||
|
||||
## Architecture
|
||||
|
||||
The platform is organized into several core modules:
|
||||
|
||||
- **Engine/Agent**: JSON-based command interface
|
||||
- **Engine/Endpoint**: SIP user agent implementation
|
||||
- **Engine/Media**: Audio codec management and processing
|
||||
- **Engine/Audio**: Cross-platform audio I/O handling
|
||||
- **Engine/Helper**: Utility functions (networking, logging, threading)
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Linux (x64, ARM/Raspberry Pi)
|
||||
- Windows (32/64-bit)
|
||||
- macOS
|
||||
- Android (with Oboe integration)
|
||||
- iOS
|
||||
|
||||
## Building
|
||||
|
||||
RTPhone uses a CMake-based build system with cross-compilation support:
|
||||
|
||||
### Linux
|
||||
```bash
|
||||
python3 build_linux.py
|
||||
```
|
||||
|
||||
### Android
|
||||
```bash
|
||||
python3 build_android.py
|
||||
# or
|
||||
./build_android.sh
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
- CMake 3.10+
|
||||
- OpenSSL 1.1+
|
||||
- Boost libraries
|
||||
- Platform-specific audio libraries
|
||||
|
||||
## Recent Updates
|
||||
|
||||
Recent development has focused on:
|
||||
- AMR codec parsing and decoding improvements
|
||||
- Octet-aligned mode fixes for AMR-WB
|
||||
- RTP SSRC handling enhancements
|
||||
- Build system optimizations
|
||||
- Code modernization to C++20
|
||||
|
||||
## Use Cases
|
||||
|
||||
RTPhone is good for building:
|
||||
- VoIP softphones and mobile applications
|
||||
- PBX and telephony server systems
|
||||
- RTP proxies
|
||||
- Carrier-grade voice communication platforms
|
||||
- 3GPP/IMS-compliant systems
|
||||
|
||||
## Security
|
||||
|
||||
RTPhone includes comprehensive security features:
|
||||
- OpenSSL 1.1 integration for encryption
|
||||
- TLS transport layer security
|
||||
- SRTP media encryption
|
||||
- Certificate management support
|
||||
|
||||
## Integration
|
||||
|
||||
The platform provides a developer-friendly interface with:
|
||||
- Event-driven architecture
|
||||
- Comprehensive logging system
|
||||
- Modern C++20 codebase
|
||||
|
||||
For detailed integration instructions and API documentation, please refer to the source code and header files in the `/src/engine/` directory.
|
||||
|
||||
## License
|
||||
|
||||
Our source code is licensed under the MPL license. Naturally, any third-party components we use are subject to their respective licenses.
|
||||
@@ -1,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()
|
||||
@@ -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}')
|
||||
@@ -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}')
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
+190
-342
@@ -1,381 +1,233 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(rtphone)
|
||||
|
||||
# Rely on C++ 20
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
macro(configure_msvc_runtime)
|
||||
if(MSVC)
|
||||
# Default to statically-linked runtime.
|
||||
if("${MSVC_RUNTIME}" STREQUAL "")
|
||||
set(MSVC_RUNTIME "static")
|
||||
endif()
|
||||
# Set compiler options.
|
||||
set(variables
|
||||
CMAKE_C_FLAGS_DEBUG
|
||||
CMAKE_C_FLAGS_MINSIZEREL
|
||||
CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS_DEBUG
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
)
|
||||
if(${MSVC_RUNTIME} STREQUAL "static")
|
||||
message(STATUS
|
||||
"rtphone: MSVC -> forcing use of statically-linked runtime."
|
||||
)
|
||||
foreach(variable ${variables})
|
||||
if(${variable} MATCHES "/MD")
|
||||
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS
|
||||
"rtphone: MSVC -> forcing use of dynamically-linked runtime."
|
||||
)
|
||||
foreach(variable ${variables})
|
||||
if(${variable} MATCHES "/MT")
|
||||
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(variable ${variables})
|
||||
string(REGEX REPLACE "/Z[iI7]" ""
|
||||
${variable}
|
||||
"${${variable}}")
|
||||
|
||||
set(${variable} "${${variable}} /Zi /Oy-")
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set (L libs)
|
||||
set (E engine)
|
||||
set (rtphone_libs libs)
|
||||
set (rtphone_engine engine)
|
||||
|
||||
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
|
||||
option (USE_EVS_CODEC "Use EVS codec." ON)
|
||||
option (USE_MUSL "Build with MUSL library" OFF)
|
||||
set (USE_AMR_CODEC OFF CACHE BOOL "Use AMR codec. Requires libraries.")
|
||||
set (USE_EVS_CODEC OFF CACHE BOOL "Use EVS codec." )
|
||||
set (USE_OPUS_CODEC OFF CACHE BOOL "Use Opus codec." )
|
||||
set (USE_PVQA_LIB OFF CACHE BOOL "Build with Sevana PVQA library" )
|
||||
set (USE_AQUA_LIB OFF CACHE BOOL "Build with Sevana AQuA library" )
|
||||
set (USE_MUSL OFF CACHE BOOL "Build with MUSL library" )
|
||||
|
||||
# PIC code by default
|
||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set (RUNTIME_CPU_CAPABILITY_DETECTION ON)
|
||||
set (CMAKE_WARN_DEPRECATED OFF)
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
set (OPENSSL_SSL OpenSSL::SSL)
|
||||
set (OPENSSL_CRYPTO OpenSSL::Crypto)
|
||||
if (NOT DEFINED LIB_PLATFORM)
|
||||
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries)
|
||||
endif()
|
||||
|
||||
message("Libraries: ${LIB_PLATFORM}")
|
||||
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.0/include)
|
||||
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
||||
message ("Using OpenSSL libs: ${OPENSSL_SSL} and ${OPENSSL_CRYPTO}")
|
||||
# include_directories(${OPENSSL_INCLUDE})
|
||||
|
||||
# Used defines for our project
|
||||
set (DEFINES -DUSE_OPENSSL)
|
||||
|
||||
# Libraries for our project
|
||||
set (LIBS_STATIC "")
|
||||
set (LIBS_DYNAMIC "")
|
||||
|
||||
# Try to prefer static libraries anyway
|
||||
set (CMAKE_FIND_LIBRARY_SUFFIXES .a .so .dylib)
|
||||
|
||||
# Windows-specific definitions
|
||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||
set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS -DNOMINMAX)
|
||||
set (TARGET_WIN ON)
|
||||
add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
# Linux-specific definitions
|
||||
if (CMAKE_SYSTEM MATCHES "Linux*")
|
||||
set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H)
|
||||
set (TARGET_LINUX ON)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
||||
add_definitions (-DTARGET_LINUX)
|
||||
endif()
|
||||
|
||||
# macOS-specific definitions
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
||||
set (DEFINES ${DEFINES} -DTARGET_OSX)
|
||||
set (TARGET_OSX ON)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
||||
add_definitions (-DTARGET_OSX)
|
||||
endif()
|
||||
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Android")
|
||||
# Disable opencore AMR codecs - there is no ready libraries in vcpkg right now
|
||||
set (USE_AMR_CODEC OFF)
|
||||
|
||||
message("Adding the Oboe library")
|
||||
set (OBOE_DIR libs/oboe)
|
||||
add_subdirectory (${OBOE_DIR} build_oboe)
|
||||
add_subdirectory (${OBOE_DIR} ./oboe)
|
||||
include_directories (${OBOE_DIR}/include)
|
||||
set (DEFINES ${DEFINES} -DTARGET_ANDROID -DHAVE_NETINET_IN_H)
|
||||
set (TARGET_ANDROID ON)
|
||||
set (LIBS_STATIC ${LIBS} oboe)
|
||||
endif()
|
||||
|
||||
if (USE_MUSL)
|
||||
set (DEFINES ${DEFINES} -DTARGET_MUSL)
|
||||
set (TARGET_MUSL ON)
|
||||
add_definitions(-DTARGET_MUSL)
|
||||
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()
|
||||
|
||||
set (RTPHONE_SOURCES
|
||||
${E}/engine_config.h
|
||||
${E}/media/MT_Statistics.cpp
|
||||
${E}/media/MT_WebRtc.cpp
|
||||
${E}/media/MT_Stream.cpp
|
||||
${E}/media/MT_SrtpHelper.cpp
|
||||
${E}/media/MT_SingleAudioStream.cpp
|
||||
${E}/media/MT_NativeRtpSender.cpp
|
||||
${E}/media/MT_Dtmf.cpp
|
||||
${E}/media/MT_CodecList.cpp
|
||||
${E}/media/MT_Codec.cpp
|
||||
${E}/media/MT_Box.cpp
|
||||
${E}/media/MT_AudioStream.cpp
|
||||
${E}/media/MT_AudioReceiver.cpp
|
||||
${E}/media/MT_AudioCodec.cpp
|
||||
${E}/media/MT_CngHelper.cpp
|
||||
${E}/agent/Agent_Impl.cpp
|
||||
${E}/agent/Agent_Impl.h
|
||||
${E}/agent/Agent_AudioManager.cpp
|
||||
${E}/agent/Agent_AudioManager.h
|
||||
${E}/endpoint/EP_Account.cpp
|
||||
${E}/endpoint/EP_Account.h
|
||||
${E}/endpoint/EP_AudioProvider.cpp
|
||||
${E}/endpoint/EP_AudioProvider.h
|
||||
${E}/endpoint/EP_DataProvider.cpp
|
||||
${E}/endpoint/EP_DataProvider.h
|
||||
${E}/endpoint/EP_Engine.cpp
|
||||
${E}/endpoint/EP_Engine.h
|
||||
${E}/endpoint/EP_NetworkQueue.cpp
|
||||
${E}/endpoint/EP_NetworkQueue.h
|
||||
${E}/endpoint/EP_Observer.cpp
|
||||
${E}/endpoint/EP_Observer.h
|
||||
${E}/endpoint/EP_Session.cpp
|
||||
${E}/endpoint/EP_Session.h
|
||||
${rtphone_engine}/media/MT_Statistics.cpp
|
||||
${rtphone_engine}/media/MT_WebRtc.cpp
|
||||
${rtphone_engine}/media/MT_Stream.cpp
|
||||
${rtphone_engine}/media/MT_SrtpHelper.cpp
|
||||
${rtphone_engine}/media/MT_SingleAudioStream.cpp
|
||||
${rtphone_engine}/media/MT_NativeRtpSender.cpp
|
||||
${rtphone_engine}/media/MT_Dtmf.cpp
|
||||
${rtphone_engine}/media/MT_CodecList.cpp
|
||||
${rtphone_engine}/media/MT_Codec.cpp
|
||||
${rtphone_engine}/media/MT_Box.cpp
|
||||
${rtphone_engine}/media/MT_AudioStream.cpp
|
||||
${rtphone_engine}/media/MT_AudioReceiver.cpp
|
||||
${rtphone_engine}/media/MT_AudioCodec.cpp
|
||||
${rtphone_engine}/media/MT_CngHelper.cpp
|
||||
${rtphone_engine}/agent/Agent_Impl.cpp
|
||||
${rtphone_engine}/agent/Agent_AudioManager.cpp
|
||||
${rtphone_engine}/endpoint/EP_Account.cpp
|
||||
${rtphone_engine}/endpoint/EP_AudioProvider.cpp
|
||||
${rtphone_engine}/endpoint/EP_DataProvider.cpp
|
||||
${rtphone_engine}/endpoint/EP_Engine.cpp
|
||||
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
|
||||
${rtphone_engine}/endpoint/EP_Observer.cpp
|
||||
${rtphone_engine}/endpoint/EP_Session.cpp
|
||||
|
||||
${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
|
||||
${rtphone_engine}/media/MT_Statistics.h
|
||||
${rtphone_engine}/media/MT_WebRtc.h
|
||||
${rtphone_engine}/media/MT_Stream.h
|
||||
${rtphone_engine}/media/MT_SrtpHelper.h
|
||||
${rtphone_engine}/media/MT_SingleAudioStream.h
|
||||
${rtphone_engine}/media/MT_NativeRtpSender.h
|
||||
${rtphone_engine}/media/MT_Dtmf.h
|
||||
${rtphone_engine}/media/MT_CodecList.h
|
||||
${rtphone_engine}/media/MT_Codec.h
|
||||
${rtphone_engine}/media/MT_Box.h
|
||||
${rtphone_engine}/media/MT_AudioStream.h
|
||||
${rtphone_engine}/media/MT_AudioReceiver.h
|
||||
${rtphone_engine}/media/MT_AudioCodec.h
|
||||
|
||||
${E}/media/MT_Statistics.cpp
|
||||
${E}/media/MT_WebRtc.cpp
|
||||
${E}/media/MT_Stream.cpp
|
||||
${E}/media/MT_SrtpHelper.cpp
|
||||
${E}/media/MT_SingleAudioStream.cpp
|
||||
${E}/media/MT_NativeRtpSender.cpp
|
||||
${E}/media/MT_Dtmf.cpp
|
||||
${E}/media/MT_CodecList.cpp
|
||||
${E}/media/MT_Codec.cpp
|
||||
${E}/media/MT_Box.cpp
|
||||
${E}/media/MT_AudioStream.cpp
|
||||
${E}/media/MT_AudioReceiver.cpp
|
||||
${E}/media/MT_AudioCodec.cpp
|
||||
${E}/media/MT_CngHelper.cpp
|
||||
${E}/media/MT_AmrCodec.cpp
|
||||
${E}/media/MT_EvsCodec.cpp
|
||||
${E}/media/MT_Statistics.h
|
||||
${E}/media/MT_WebRtc.h
|
||||
${E}/media/MT_Stream.h
|
||||
${E}/media/MT_SrtpHelper.h
|
||||
${E}/media/MT_SingleAudioStream.h
|
||||
${E}/media/MT_NativeRtpSender.h
|
||||
${E}/media/MT_Dtmf.h
|
||||
${E}/media/MT_CodecList.h
|
||||
${E}/media/MT_Codec.h
|
||||
${E}/media/MT_Box.h
|
||||
${E}/media/MT_AudioStream.h
|
||||
${E}/media/MT_AudioReceiver.h
|
||||
${E}/media/MT_AudioCodec.h
|
||||
${E}/media/MT_CngHelper.h
|
||||
${E}/media/MT_AmrCodec.h
|
||||
${E}/media/MT_EvsCodec.h
|
||||
|
||||
${E}/helper/HL_AsyncCommand.cpp
|
||||
${E}/helper/HL_AsyncCommand.h
|
||||
${E}/helper/HL_Base64.h
|
||||
${E}/helper/HL_ByteBuffer.h
|
||||
${E}/helper/HL_Calculator.cpp
|
||||
${E}/helper/HL_Calculator.h
|
||||
${E}/helper/HL_CrashRpt.cpp
|
||||
${E}/helper/HL_CrashRpt.h
|
||||
${E}/helper/HL_CsvReader.cpp
|
||||
${E}/helper/HL_CsvReader.h
|
||||
${E}/helper/HL_Epoll.cpp
|
||||
${E}/helper/HL_Epoll.h
|
||||
${E}/helper/HL_Exception.h
|
||||
${E}/helper/HL_File.cpp
|
||||
${E}/helper/HL_File.h
|
||||
${E}/helper/HL_HepSupport.cpp
|
||||
${E}/helper/HL_HepSupport.h
|
||||
${E}/helper/HL_InternetAddress.h
|
||||
${E}/helper/HL_IuUP.cpp
|
||||
${E}/helper/HL_IuUP.h
|
||||
${E}/helper/HL_Log.cpp
|
||||
${E}/helper/HL_Log.h
|
||||
${E}/helper/HL_NetworkFrame.cpp
|
||||
${E}/helper/HL_NetworkFrame.h
|
||||
${E}/helper/HL_NetworkSocket.cpp
|
||||
${E}/helper/HL_NetworkSocket.h
|
||||
${E}/helper/HL_Optional.hpp
|
||||
${E}/helper/HL_OsVersion.cpp
|
||||
${E}/helper/HL_OsVersion.h
|
||||
${E}/helper/HL_Pointer.cpp
|
||||
${E}/helper/HL_Pointer.h
|
||||
${E}/helper/HL_Process.cpp
|
||||
${E}/helper/HL_Process.h
|
||||
${E}/helper/HL_Rtp.cpp
|
||||
${E}/helper/HL_Rtp.h
|
||||
${E}/helper/HL_Singletone.cpp
|
||||
${E}/helper/HL_Singletone.h
|
||||
${E}/helper/HL_SocketHeap.cpp
|
||||
${E}/helper/HL_SocketHeap.h
|
||||
${E}/helper/HL_Statistics.cpp
|
||||
${E}/helper/HL_Statistics.h
|
||||
${E}/helper/HL_StreamState.h
|
||||
${E}/helper/HL_String.cpp
|
||||
${E}/helper/HL_String.h
|
||||
${E}/helper/HL_Sync.cpp
|
||||
${E}/helper/HL_Sync.h
|
||||
${E}/helper/HL_ThreadPool.cpp
|
||||
${E}/helper/HL_ThreadPool.h
|
||||
${E}/helper/HL_Time.cpp
|
||||
${E}/helper/HL_Time.h
|
||||
${E}/helper/HL_Types.h
|
||||
${E}/helper/HL_Types.cpp
|
||||
${E}/helper/HL_Usb.cpp
|
||||
${E}/helper/HL_Usb.h
|
||||
${E}/helper/HL_Uuid.cpp
|
||||
${E}/helper/HL_Uuid.h
|
||||
${E}/helper/HL_VariantMap.cpp
|
||||
${E}/helper/HL_VariantMap.h
|
||||
${E}/helper/HL_Xcap.cpp
|
||||
${E}/helper/HL_Xcap.h
|
||||
|
||||
${E}/audio/Audio_Resampler.cpp
|
||||
${E}/audio/Audio_Resampler.h
|
||||
${E}/audio/Audio_Quality.cpp
|
||||
${E}/audio/Audio_Quality.h
|
||||
${E}/audio/Audio_Mixer.cpp
|
||||
${E}/audio/Audio_Mixer.h
|
||||
${E}/audio/Audio_Interface.cpp
|
||||
${E}/audio/Audio_Interface.h
|
||||
${E}/audio/Audio_Helper.cpp
|
||||
${E}/audio/Audio_Helper.h
|
||||
${E}/audio/Audio_DataWindow.cpp
|
||||
${E}/audio/Audio_DataWindow.h
|
||||
${E}/audio/Audio_DevicePair.cpp
|
||||
${E}/audio/Audio_DevicePair.h
|
||||
${E}/audio/Audio_Player.cpp
|
||||
${E}/audio/Audio_Player.h
|
||||
${E}/audio/Audio_Null.cpp
|
||||
${E}/audio/Audio_Null.h
|
||||
${E}/audio/Audio_CoreAudio.cpp
|
||||
${E}/audio/Audio_CoreAudio.h
|
||||
${E}/audio/Audio_DirectSound.cpp
|
||||
${E}/audio/Audio_DirectSound.h
|
||||
${E}/audio/Audio_AndroidOboe.cpp
|
||||
${E}/audio/Audio_AndroidOboe.h
|
||||
${E}/audio/Audio_WavFile.cpp
|
||||
${E}/audio/Audio_WavFile.h
|
||||
|
||||
${L}/ice/hmac_sha1_impl.cpp
|
||||
${L}/ice/hmac_sha1_impl.h
|
||||
${L}/ice/ICEAction.h
|
||||
${L}/ice/ICEAddress.cpp
|
||||
${L}/ice/ICEAddress.h
|
||||
${L}/ice/ICEAuthTransaction.cpp
|
||||
${L}/ice/ICEAuthTransaction.h
|
||||
${L}/ice/ICEBinding.cpp
|
||||
${L}/ice/ICEBinding.h
|
||||
${L}/ice/ICEBox.cpp
|
||||
${L}/ice/ICEBox.h
|
||||
${L}/ice/ICEBoxImpl.cpp
|
||||
${L}/ice/ICEBoxImpl.h
|
||||
${L}/ice/ICEByteBuffer.cpp
|
||||
${L}/ice/ICEByteBuffer.h
|
||||
${L}/ice/ICECandidate.cpp
|
||||
${L}/ice/ICECandidate.h
|
||||
${L}/ice/ICECandidatePair.cpp
|
||||
${L}/ice/ICECandidatePair.h
|
||||
${L}/ice/ICECheckList.cpp
|
||||
${L}/ice/ICECheckList.h
|
||||
${L}/ice/ICECRC32.cpp
|
||||
${L}/ice/ICECRC32.h
|
||||
${L}/ice/ICEError.cpp
|
||||
${L}/ice/ICEError.h
|
||||
${L}/ice/ICEEvent.h
|
||||
${L}/ice/ICELog.cpp
|
||||
${L}/ice/ICELog.h
|
||||
${L}/ice/ICEMD5.cpp
|
||||
${L}/ice/ICEMD5.h
|
||||
${L}/ice/ICENetworkHelper.cpp
|
||||
${L}/ice/ICENetworkHelper.h
|
||||
${L}/ice/ICEPacketTimer.cpp
|
||||
${L}/ice/ICEPacketTimer.h
|
||||
${L}/ice/ICEPlatform.cpp
|
||||
${L}/ice/ICEPlatform.h
|
||||
${L}/ice/ICERelaying.cpp
|
||||
${L}/ice/ICERelaying.h
|
||||
${L}/ice/ICESession.cpp
|
||||
${L}/ice/ICESession.h
|
||||
${L}/ice/ICESHA1.cpp
|
||||
${L}/ice/ICESHA1.h
|
||||
${L}/ice/ICESocket.h
|
||||
${L}/ice/ICEStream.cpp
|
||||
${L}/ice/ICEStream.h
|
||||
${L}/ice/ICEStunAttributes.cpp
|
||||
${L}/ice/ICEStunAttributes.h
|
||||
${L}/ice/ICEStunConfig.cpp
|
||||
${L}/ice/ICEStunConfig.h
|
||||
${L}/ice/ICEStunMessage.cpp
|
||||
${L}/ice/ICEStunMessage.h
|
||||
${L}/ice/ICEStunTransaction.cpp
|
||||
${L}/ice/ICEStunTransaction.h
|
||||
${L}/ice/ICESync.cpp
|
||||
${L}/ice/ICESync.h
|
||||
${L}/ice/ICETime.cpp
|
||||
${L}/ice/ICETime.h
|
||||
${L}/ice/ICETransactionList.cpp
|
||||
${L}/ice/ICETransactionList.h
|
||||
${L}/ice/ICETypes.h
|
||||
${L}/ice/md5_impl.cpp
|
||||
${L}/ice/md5_impl.h
|
||||
${rtphone_engine}/media/MT_CngHelper.h
|
||||
${rtphone_engine}/agent/Agent_Impl.h
|
||||
${rtphone_engine}/agent/Agent_AudioManager.h
|
||||
${rtphone_engine}/endpoint/EP_Account.h
|
||||
${rtphone_engine}/endpoint/EP_AudioProvider.h
|
||||
${rtphone_engine}/endpoint/EP_DataProvider.h
|
||||
${rtphone_engine}/endpoint/EP_Engine.h
|
||||
${rtphone_engine}/endpoint/EP_NetworkQueue.h
|
||||
${rtphone_engine}/endpoint/EP_Observer.h
|
||||
${rtphone_engine}/endpoint/EP_Session.h
|
||||
)
|
||||
|
||||
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_subdirectory(${L}/resiprocate)
|
||||
add_subdirectory(${L}/jrtplib/src)
|
||||
add_subdirectory(${L}/libg729)
|
||||
add_subdirectory(${rtphone_libs}/resiprocate)
|
||||
add_subdirectory(${rtphone_libs}/ice)
|
||||
add_subdirectory(${rtphone_libs}/jrtplib/src)
|
||||
add_subdirectory(${rtphone_libs}/libg729)
|
||||
|
||||
if (USE_EVS_CODEC)
|
||||
add_subdirectory(${L}/libevs)
|
||||
add_subdirectory(${rtphone_libs}/libevs)
|
||||
endif()
|
||||
|
||||
add_subdirectory(${L}/libgsm)
|
||||
add_subdirectory(${L}/gsmhr)
|
||||
add_subdirectory(${L}/g722)
|
||||
add_subdirectory(${L}/speexdsp)
|
||||
add_subdirectory(${L}/libsrtp)
|
||||
add_subdirectory(${L}/webrtc)
|
||||
add_subdirectory(${L}/opus)
|
||||
add_subdirectory(${rtphone_libs}/libgsm)
|
||||
add_subdirectory(${rtphone_libs}/gsmhr)
|
||||
add_subdirectory(${rtphone_libs}/g722)
|
||||
add_subdirectory(${rtphone_libs}/speexdsp)
|
||||
add_subdirectory(${rtphone_libs}/srtp)
|
||||
add_subdirectory(${rtphone_libs}/webrtc)
|
||||
add_subdirectory(${rtphone_engine}/helper)
|
||||
add_subdirectory(${rtphone_engine}/audio)
|
||||
add_subdirectory(${rtphone_engine}/media)
|
||||
|
||||
# Suppose the subproject defines target "mylib"
|
||||
if(MSVC)
|
||||
# target_compile_options(opus PRIVATE /O2 /DNDEBUG)
|
||||
# Optional: enable whole program optimization on MSVC
|
||||
# target_compile_options(mylib PRIVATE /GL)
|
||||
# target_link_options(mylib PRIVATE /LTCG)
|
||||
set (LIBS ice_stack jrtplib g729_codec gsm_codec
|
||||
gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp
|
||||
uuid)
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Win*")
|
||||
set (LIBS ${LIBS} )
|
||||
else ()
|
||||
target_compile_options(opus PRIVATE -O3 -DNDEBUG)
|
||||
set (LIBS ${LIBS} dl uuid)
|
||||
endif ()
|
||||
|
||||
|
||||
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
|
||||
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
|
||||
if (CMAKE_SYSTEM MATCHES "Android")
|
||||
set (LIBS ${LIBS} oboe)
|
||||
endif()
|
||||
|
||||
if (USE_AMR_CODEC)
|
||||
message("Media: AMR NB and WB codecs will be included")
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/libs/opencore-amr build_opencore)
|
||||
set (OPENCORE_AMRWB opencore-amrwb)
|
||||
set (OPENCORE_AMRNB opencore-amrnb)
|
||||
set (LIBS ${LIBS})
|
||||
endif (USE_AMR_CODEC)
|
||||
|
||||
set (DEFINES ${DEFINES} -DUSE_AMR_CODEC)
|
||||
set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
||||
endif()
|
||||
target_link_libraries(rtphone
|
||||
ice_stack jrtplib g729_codec gsm_codec
|
||||
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
|
||||
PUBLIC
|
||||
@@ -383,19 +235,15 @@ target_include_directories(rtphone
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
||||
${LIB_PLATFORM}/opus/include
|
||||
${E}/helper
|
||||
${E}/audio
|
||||
${E}/media
|
||||
${L}
|
||||
${L}/ice
|
||||
PRIVATE
|
||||
${L}/libevs/lib_com
|
||||
${L}/libevs/lib_enc
|
||||
${L}/libevs/lib_dec
|
||||
${L}/speex/include
|
||||
${L}/libs/json
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
|
||||
)
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(rtphone PUBLIC OpenSSL::SSL)
|
||||
target_link_libraries(rtphone PUBLIC OpenSSL::Crypto)
|
||||
# For MSVC static builds
|
||||
configure_msvc_runtime()
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Agent_AudioManager.h"
|
||||
#include "../engine/audio/Audio_WavFile.h"
|
||||
#include "../engine/helper/HL_String.h"
|
||||
#include "../engine/audio/Audio_Null.h"
|
||||
#include "HL_String.h"
|
||||
|
||||
#if defined(TARGET_ANDROID)
|
||||
# include "../engine/audio/Audio_Android.h"
|
||||
#endif
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "AudioManager"
|
||||
|
||||
|
||||
// ---------------- AudioManager -------------
|
||||
//static AudioManager GAudioManager;
|
||||
|
||||
AudioManager::AudioManager()
|
||||
:mTerminal(nullptr), mAudioMonitoring(nullptr)
|
||||
:mTerminal(nullptr)
|
||||
{
|
||||
mPlayer.setDelegate(this);
|
||||
}
|
||||
@@ -26,6 +29,16 @@ AudioManager::~AudioManager()
|
||||
//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)
|
||||
{
|
||||
mTerminal = terminal;
|
||||
@@ -36,16 +49,6 @@ MT::Terminal* AudioManager::terminal()
|
||||
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)
|
||||
void AudioManager::start(int usageId)
|
||||
{
|
||||
@@ -57,7 +60,6 @@ void AudioManager::start(int usageId)
|
||||
if (mUsage.obtain(usageId) > 1)
|
||||
return;
|
||||
|
||||
// Maybe it is time to initialize global audio support
|
||||
if (Audio::OsEngine::instance())
|
||||
Audio::OsEngine::instance()->open();
|
||||
|
||||
@@ -66,29 +68,21 @@ void AudioManager::start(int usageId)
|
||||
// Disable AEC for now - because PVQA conflicts with speex AEC.
|
||||
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
|
||||
if (!mTerminal->audio())
|
||||
{
|
||||
auto audio = std::make_shared<Audio::DevicePair>();
|
||||
audio->setAgc(true);
|
||||
audio->setAec(false);
|
||||
audio->setMonitoring(mAudioMonitoring);
|
||||
|
||||
mTerminal->setAudio(audio);
|
||||
}
|
||||
mTerminal->setAudio(std::make_shared<Audio::DevicePair>(false, true));
|
||||
|
||||
if (!mAudioInput)
|
||||
{
|
||||
enumerator->open(Audio::myMicrophone);
|
||||
int inputIndex = enumerator->indexOfDefaultDevice();
|
||||
|
||||
// Construct default platform input device
|
||||
// Construct and set to terminal's audio pair input device
|
||||
if (usageId != atNull)
|
||||
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
|
||||
else
|
||||
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
|
||||
}
|
||||
// Bind input to the terminal's device pair regardless of whether it was
|
||||
// just constructed or externally injected via setAudioInput().
|
||||
|
||||
mTerminal->audio()->setInput(mAudioInput);
|
||||
}
|
||||
|
||||
if (!mAudioOutput)
|
||||
{
|
||||
@@ -96,7 +90,7 @@ void AudioManager::start(int usageId)
|
||||
enumerator->open(Audio::mySpeaker);
|
||||
int outputIndex = enumerator->indexOfDefaultDevice();
|
||||
|
||||
// Construct default platform output device
|
||||
// Construct and set terminal's audio pair output device
|
||||
if (usageId != atNull)
|
||||
{
|
||||
if (outputIndex >= enumerator->count())
|
||||
@@ -107,9 +101,10 @@ void AudioManager::start(int usageId)
|
||||
}
|
||||
else
|
||||
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
|
||||
}
|
||||
|
||||
mTerminal->audio()->setOutput(mAudioOutput);
|
||||
}
|
||||
}
|
||||
|
||||
// Open audio
|
||||
if (mAudioInput)
|
||||
@@ -158,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)
|
||||
{
|
||||
// Check if file exists
|
||||
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
||||
#ifdef TARGET_WIN
|
||||
r->open(strx::makeTstring(path));
|
||||
r->open(StringHelper::makeTstring(path));
|
||||
#else
|
||||
r->open(path);
|
||||
#endif
|
||||
@@ -205,6 +188,6 @@ void AudioManager::process()
|
||||
mPlayer.releasePlayed();
|
||||
std::vector<int> ids;
|
||||
mTerminal->audio()->player().retrieveUsageIds(ids);
|
||||
for (int id : ids)
|
||||
stop(id);
|
||||
for (unsigned i=0; i<ids.size(); i++)
|
||||
stop(ids[i]);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
|
||||
#include "../engine/audio/Audio_Interface.h"
|
||||
#include "../engine/audio/Audio_Player.h"
|
||||
#include "../engine/endpoint/EP_Engine.h"
|
||||
#include "../engine/media/MT_Box.h"
|
||||
#include "../engine/helper/HL_Log.h"
|
||||
#include "../engine/helper/HL_Sync.h"
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +40,7 @@ public:
|
||||
AudioManager();
|
||||
virtual ~AudioManager();
|
||||
|
||||
// static AudioManager& instance();
|
||||
static AudioManager& instance();
|
||||
|
||||
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
|
||||
void close();
|
||||
@@ -46,19 +49,10 @@ public:
|
||||
void setTerminal(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
|
||||
void start(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
|
||||
{
|
||||
atNull,
|
||||
@@ -75,7 +69,6 @@ public:
|
||||
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
|
||||
void stopPlayFile(int usageId);
|
||||
|
||||
|
||||
void onFilePlayed(Audio::Player::PlaylistItem& item);
|
||||
|
||||
// Must be called from main loop to release used audio devices
|
||||
@@ -86,7 +79,6 @@ protected:
|
||||
Audio::POutputDevice mAudioOutput;
|
||||
Audio::Player mPlayer;
|
||||
MT::Terminal* mTerminal;
|
||||
Audio::DataConnection* mAudioMonitoring;
|
||||
|
||||
std::map<int, int> UsageMap;
|
||||
UsageCounter mUsage;
|
||||
|
||||
+199
-100
@@ -3,12 +3,17 @@
|
||||
#include "helper/HL_String.h"
|
||||
#include "helper/HL_StreamState.h"
|
||||
#include "helper/HL_VariantMap.h"
|
||||
// #include "helper/HL_CsvReader.h"
|
||||
// #include "helper/HL_Base64.h"
|
||||
#include "media/MT_CodecList.h"
|
||||
#include "audio/Audio_Null.h"
|
||||
// #include <fstream>
|
||||
#include "helper/HL_CsvReader.h"
|
||||
#include "helper/HL_Base64.h"
|
||||
#include <fstream>
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
# include "pvqa++.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
# include "aqua++.h"
|
||||
#endif
|
||||
|
||||
const std::string Status_Ok = "ok";
|
||||
const std::string Status_SessionNotFound = "session not found";
|
||||
@@ -17,9 +22,8 @@ const std::string Status_FailedToOpenFile = "failed to open file";
|
||||
const std::string Status_NoActiveProvider = "no active provider";
|
||||
const std::string Status_NoMediaAction = "no valid media action";
|
||||
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()
|
||||
@@ -35,24 +39,6 @@ AgentImpl::~AgentImpl()
|
||||
stopAgentAndThread();
|
||||
}
|
||||
|
||||
// Get access to internal audio manager. Value can be nullptr.
|
||||
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
|
||||
{
|
||||
return mAudioManager;
|
||||
}
|
||||
|
||||
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
|
||||
{
|
||||
mAudioMonitoring = monitoring;
|
||||
if (mAudioManager)
|
||||
mAudioManager->setAudioMonitoring(monitoring);
|
||||
}
|
||||
|
||||
Audio::DataConnection* AgentImpl::monitoring() const
|
||||
{
|
||||
return mAudioMonitoring;
|
||||
}
|
||||
|
||||
void AgentImpl::run()
|
||||
{
|
||||
while (!mShutdown)
|
||||
@@ -78,7 +64,7 @@ std::string AgentImpl::command(const std::string& command)
|
||||
return "";
|
||||
|
||||
std::string cmd = d["command"].asString();
|
||||
if (cmd != "wait_for_event" && cmd != "agent_add_root_cert")
|
||||
if (cmd != "wait_for_event")
|
||||
{
|
||||
ICELogInfo(<< command);
|
||||
}
|
||||
@@ -100,12 +86,8 @@ std::string AgentImpl::command(const std::string& command)
|
||||
if (cmd == "account_setuserinfo")
|
||||
processSetUserInfoToAccount(d, answer);
|
||||
else
|
||||
if (cmd == "session_create") {
|
||||
// For Bugsnag test
|
||||
// int* v = nullptr;
|
||||
// *v = 0;
|
||||
if (cmd == "session_create")
|
||||
processCreateSession(d, answer);
|
||||
}
|
||||
else
|
||||
if (cmd == "session_start")
|
||||
processStartSession(d, answer);
|
||||
@@ -157,9 +139,7 @@ std::string AgentImpl::command(const std::string& command)
|
||||
{
|
||||
answer["status"] = e.what();
|
||||
}
|
||||
std::string result = answer.toStyledString();
|
||||
|
||||
return result;
|
||||
return answer.toStyledString();
|
||||
}
|
||||
|
||||
bool AgentImpl::waitForData(int /*milliseconds*/)
|
||||
@@ -176,11 +156,28 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||
|
||||
#if !defined(TARGET_ANDROID) && defined(USE_PVQA_LIBRARY)
|
||||
// It works for desktop OSes only
|
||||
// Because Android requires special initializing procedure (valid JNI environment context)
|
||||
std::string pvqaLicense = d["pvqa-license"].asString(), pvqaConfig = d["pvqa-config"].asString();
|
||||
if (!pvqaLicense.empty() && !pvqaConfig.empty())
|
||||
sevana::pvqa::initialize(pvqaLicense, pvqaConfig);
|
||||
#endif
|
||||
|
||||
#if !defined(TARGET_ANDROID) && defined(USE_AQUA_LIBRARY)
|
||||
std::string aquaLicense = d["aqua-license"].asString();
|
||||
if (!aquaLicense.empty())
|
||||
sevana::aqua::initialize(aquaLicense.c_str(), aquaLicense.size());
|
||||
#endif
|
||||
|
||||
std::string transport = d["transport"].asString();
|
||||
config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
|
||||
config()[CONFIG_IPV4] = d["ipv4"].asBool();
|
||||
config()[CONFIG_IPV6] = d["ipv6"].asBool();
|
||||
|
||||
if (transport == "tls")
|
||||
config()[CONFIG_SIPS] = true;
|
||||
|
||||
// Log file
|
||||
std::string logfile = d["logfile"].asString();
|
||||
ice::Logger& logger = ice::GLogger;
|
||||
@@ -192,8 +189,7 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
|
||||
|
||||
mUseNativeAudio = d["nativeaudio"].asBool();
|
||||
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
|
||||
config()[CONFIG_SIPS] = d["secure"].asBool() || transport == "tls";
|
||||
config()[CONFIG_STUNSERVER_IP] = d["stun_server"].asString();
|
||||
config()[CONFIG_SIPS] = d["secure"].asBool();
|
||||
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
@@ -214,8 +210,7 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
|
||||
SocketHeap::instance().start();
|
||||
|
||||
// Initialize terminal
|
||||
auto settings = MT::CodecList::Settings::getClientSettings();
|
||||
|
||||
MT::CodecList::Settings settings;
|
||||
mTerminal = std::make_shared<MT::Terminal>(settings);
|
||||
|
||||
// Enable/disable codecs
|
||||
@@ -224,15 +219,19 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
|
||||
for (int i=0; i<cl.count(); i++)
|
||||
priorityConfig->at(i) = i;
|
||||
|
||||
// Disable dynamic payload codec types - commented for now
|
||||
/*if (cl.codecAt(i).payloadType() < 96)
|
||||
priorityConfig->at(i) = i;
|
||||
else
|
||||
priorityConfig->at(i) = -1;*/
|
||||
|
||||
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
|
||||
|
||||
// Enable audio
|
||||
mAudioManager = std::make_shared<AudioManager>();
|
||||
mAudioManager->setTerminal(mTerminal.get());
|
||||
if (mAudioMonitoring)
|
||||
mAudioManager->setAudioMonitoring(mAudioMonitoring);
|
||||
|
||||
// Do not start audio manager here. Start right before call.
|
||||
// Do not start here. Start right before call.
|
||||
|
||||
// Initialize endpoint
|
||||
start();
|
||||
@@ -257,11 +256,9 @@ void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer)
|
||||
(*c)[CONFIG_USERNAME] = d["username"].asString();
|
||||
(*c)[CONFIG_PASSWORD] = d["password"].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();
|
||||
|
||||
auto nameAndPort = strx::parseHost(d["stun_server"].asString(), 3478);
|
||||
auto nameAndPort = StringHelper::parseHost(d["stun_server"].asString(), 3478);
|
||||
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
|
||||
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
|
||||
|
||||
@@ -329,15 +326,10 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||
{
|
||||
// Agent was not started
|
||||
ICELogError(<< "No audio manager installed.");
|
||||
answer["status"] = Status_NoAudioManager;
|
||||
answer["status"] = "Audio manager not started. Most probably agent is not started.";
|
||||
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);
|
||||
|
||||
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
||||
@@ -348,7 +340,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
||||
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
||||
|
||||
/*#if defined(USE_AQUA_LIBRARY)
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
std::string path_faults = request["path_faults"].asString();
|
||||
|
||||
sevana::aqua::config config = {
|
||||
@@ -372,8 +364,8 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||
};
|
||||
|
||||
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
|
||||
// if (temp_path.size())
|
||||
// config += " -fau " + temp_path;
|
||||
/*if (temp_path.size())
|
||||
config += " -fau " + temp_path; */
|
||||
|
||||
auto qc = std::make_shared<sevana::aqua>();
|
||||
if (!qc->is_open())
|
||||
@@ -386,7 +378,6 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||
mAquaMap[sessionIter->first] = qc;
|
||||
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
|
||||
#endif
|
||||
*/
|
||||
|
||||
// TODO: support SRTP via StreamState::Srtp option in audio provider state
|
||||
|
||||
@@ -428,19 +419,8 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
|
||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
||||
if (sessionIter != mSessionMap.end())
|
||||
{
|
||||
if (!mAudioManager)
|
||||
{
|
||||
ICELogError(<< "No audio manager installed.");
|
||||
answer["status"] = Status_NoAudioManager;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
|
||||
// Accept session on SIP level
|
||||
@@ -459,9 +439,9 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
|
||||
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
}
|
||||
else
|
||||
answer["status"] = Status_SessionNotFound;
|
||||
|
||||
}
|
||||
|
||||
void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer)
|
||||
@@ -472,26 +452,23 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
|
||||
auto sessionIter = mSessionMap.find(sessionId);
|
||||
if (sessionIter != mSessionMap.end())
|
||||
mSessionMap.erase(sessionIter);
|
||||
//#if defined(USE_AQUA_LIBRARY)
|
||||
// closeAqua(sessionId);
|
||||
//#endif
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
closeAqua(sessionId);
|
||||
#endif
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
|
||||
void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &answer)
|
||||
{
|
||||
// Deliberately does NOT take mAgentMutex: events are produced by the worker
|
||||
// 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.
|
||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||
|
||||
int timeout = 0;
|
||||
if (request.isMember("timeout"))
|
||||
timeout = request["timeout"].asInt();
|
||||
//int x = 0;
|
||||
//int y = 1/x;
|
||||
|
||||
int timeout = request["timeout"].asInt();
|
||||
std::unique_lock<std::mutex> eventLock(mEventListMutex);
|
||||
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout),
|
||||
[this]() { return !mEventList.empty(); });
|
||||
if (mEventList.empty())
|
||||
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout));
|
||||
|
||||
if (!mEventList.empty())
|
||||
{
|
||||
@@ -501,6 +478,41 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &ans
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
static JsonCpp::Value CsvReportToJson(const std::string& report)
|
||||
{
|
||||
JsonCpp::Value detectorValues;
|
||||
std::istringstream iss(report);
|
||||
CsvReader reader(iss);
|
||||
std::vector<std::string> cells;
|
||||
if (reader.readLine(cells))
|
||||
{
|
||||
JsonCpp::Value detectorNames;
|
||||
for (size_t nameIndex = 0; nameIndex < cells.size(); nameIndex++)
|
||||
detectorNames[static_cast<int>(nameIndex)] = 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)
|
||||
{
|
||||
@@ -511,14 +523,25 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
||||
{
|
||||
PSession session = sessionIter->second;
|
||||
VariantMap result;
|
||||
session->getSessionInfo(Session::InfoOptions::Detailed,
|
||||
bool includePvqa = request["include_pvqa"].asBool();
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
bool includeAqua = request["include_aqua"].asBool();
|
||||
std::string aquaReference = request["aqua_reference_audio"].asString();
|
||||
#endif
|
||||
session->getSessionInfo(includePvqa ? Session::InfoOptions::Detailed : Session::InfoOptions::Standard,
|
||||
result);
|
||||
|
||||
if (result.exists(SessionInfo_AudioCodec))
|
||||
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
|
||||
if (result.exists(SessionInfo_NetworkMos))
|
||||
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
|
||||
if (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();
|
||||
if (result.exists(SessionInfo_DroppedRtp))
|
||||
answer["rtp_dropped"] = result[SessionInfo_DroppedRtp].asInt();
|
||||
@@ -535,13 +558,79 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
||||
answer["rtt"] = result[SessionInfo_Rtt].asFloat();
|
||||
if (result.exists(SessionInfo_BitrateSwitchCounter))
|
||||
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))
|
||||
answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt();
|
||||
if (result.exists(SessionInfo_RemotePeer))
|
||||
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;
|
||||
}
|
||||
else
|
||||
@@ -562,16 +651,18 @@ void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answ
|
||||
std::string pem = request["cert"].asString();
|
||||
|
||||
std::string::size_type pb = 0, pe = 0;
|
||||
while (pb != std::string::npos && pe != std::string::npos) {
|
||||
pb = pem.find(BeginCertificate, pb);
|
||||
pe = pem.find(EndCertificate, pe);
|
||||
|
||||
if (pb != std::string::npos && pe != std::string::npos && pe > pb) {
|
||||
std::string cert = pem.substr(pb, pe - pb + EndCertificate.size());
|
||||
for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe);
|
||||
pb != std::string::npos && pe != std::string::npos;
|
||||
pb = pem.find(BeginCertificate, pb + BeginCertificate.size()), pe = pem.find(EndCertificate, pe + EndCertificate.size()))
|
||||
{
|
||||
// Get single certificate
|
||||
std::string cert = pem.substr(pb, pe + EndCertificate.size());
|
||||
//int size = cert.size();
|
||||
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
|
||||
|
||||
pb = ++pe;
|
||||
}
|
||||
// Delete processed part
|
||||
pem.erase(0, pe + EndCertificate.size());
|
||||
}
|
||||
|
||||
answer["status"] = Status_Ok;
|
||||
@@ -659,7 +750,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||
else
|
||||
{
|
||||
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
|
||||
if (!reader->open(strx::makeTstring(path)))
|
||||
if (!reader->open(StringHelper::makeTstring(path)))
|
||||
answer["status"] = Status_FailedToOpenFile;
|
||||
else
|
||||
{
|
||||
@@ -680,7 +771,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||
else
|
||||
{
|
||||
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;
|
||||
else
|
||||
{
|
||||
@@ -696,21 +787,32 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
else
|
||||
answer["status"] = Status_NoCommand;
|
||||
answer["status"] = Status_AccountNotFound;
|
||||
}
|
||||
else
|
||||
answer["status"] = Status_NoMediaAction;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
|
||||
{
|
||||
/*switch (direction)
|
||||
/* if (mIncomingAudioDump && direction == MT::Stream::MediaDirection::Incoming)
|
||||
mIncomingAudioDump->write(data, length);
|
||||
|
||||
if (mOutgoingAudioDump && direction == MT::Stream::MediaDirection::Outgoing)
|
||||
mOutgoingAudioDump->write(data, length);*/
|
||||
|
||||
// User tag points to accumulator object which includes
|
||||
// auto* sa = reinterpret_cast<sevana::aqua*>(userTag);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
|
||||
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Called on new incoming session; providers shoukld
|
||||
@@ -718,13 +820,11 @@ void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection
|
||||
|
||||
PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
|
||||
{
|
||||
assert(mTerminal);
|
||||
|
||||
EVENT_WITH_NAME("provider_needed");
|
||||
v["provider_name"] = name;
|
||||
addEvent(v);
|
||||
|
||||
return std::make_shared<AudioProvider>(*this, *mTerminal);
|
||||
return PDataProvider(new AudioProvider(*this, *mTerminal));
|
||||
}
|
||||
|
||||
// Called on new session offer
|
||||
@@ -746,7 +846,6 @@ void AgentImpl::onSessionTerminated(PSession s, int responsecode, int reason)
|
||||
if (mOutgoingAudioDump)
|
||||
mOutgoingAudioDump->close();
|
||||
*/
|
||||
if (mAudioManager)
|
||||
mAudioManager->stop(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
||||
// Gather statistics before
|
||||
EVENT_WITH_NAME("session_terminated");
|
||||
@@ -870,12 +969,12 @@ void AgentImpl::addEvent(const JsonCpp::Value& v)
|
||||
}
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
/*void AgentImpl::closeAqua(int sessionId)
|
||||
void AgentImpl::closeAqua(int sessionId)
|
||||
{
|
||||
auto aquaIter = mAquaMap.find(sessionId);
|
||||
if (aquaIter != mAquaMap.end()) {
|
||||
aquaIter->second->close();
|
||||
mAquaMap.erase(aquaIter);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,10 +13,18 @@
|
||||
#include "Agent_AudioManager.h"
|
||||
#include <mutex>
|
||||
#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:
|
||||
std::recursive_mutex mAgentMutex;
|
||||
@@ -31,12 +39,19 @@ protected:
|
||||
typedef std::map<int, PSession> SessionMap;
|
||||
SessionMap mSessionMap;
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
// Keys are the same as used in mSessionMap
|
||||
typedef std::map<int, std::shared_ptr<sevana::aqua>> AquaMap;
|
||||
AquaMap mAquaMap;
|
||||
ByteBuffer mAquaIncoming, mAquaOutgoing;
|
||||
void closeAqua(int sessionId);
|
||||
#endif
|
||||
|
||||
std::shared_ptr<std::thread> mThread;
|
||||
std::atomic<bool> mShutdown;
|
||||
volatile bool mShutdown;
|
||||
std::shared_ptr<MT::Terminal> mTerminal;
|
||||
std::shared_ptr<AudioManager> mAudioManager;
|
||||
Audio::DataConnection* mAudioMonitoring = nullptr;
|
||||
//Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump;
|
||||
|
||||
void run();
|
||||
void addEvent(const JsonCpp::Value& v);
|
||||
@@ -67,12 +82,6 @@ public:
|
||||
bool waitForData(int milliseconds);
|
||||
std::string read();
|
||||
|
||||
// Get access to internal audio manager. Value can be nullptr.
|
||||
const std::shared_ptr<AudioManager>& audioManager() const;
|
||||
|
||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||
Audio::DataConnection* monitoring() const;
|
||||
|
||||
// UserAgent overrides
|
||||
// Called on new incoming session; providers shoukld
|
||||
PDataProvider onProviderNeeded(const std::string& name) override;
|
||||
@@ -122,8 +131,10 @@ public:
|
||||
// Called when problem with SIP connection(s) detected
|
||||
void onSipConnectionFailed() override;
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
// Called on incoming & outgoing audio for voice sessions
|
||||
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -365,9 +365,8 @@ void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void *
|
||||
// ------------ AndroidOutputDevice -----------------
|
||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||
{
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
|
||||
}
|
||||
|
||||
AndroidOutputDevice::~AndroidOutputDevice()
|
||||
{
|
||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#ifdef TARGET_ANDROID
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "Audio"
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
@@ -136,7 +136,7 @@ int AndroidInputDevice::readBuffer(void* buffer)
|
||||
// ------------ AndroidOutputDevice -----------------
|
||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||
{
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
|
||||
}
|
||||
AndroidOutputDevice::~AndroidOutputDevice()
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "CoreAudio"
|
||||
|
||||
enum
|
||||
{
|
||||
|
||||
@@ -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
|
||||
* 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/. */
|
||||
@@ -9,40 +9,29 @@
|
||||
using namespace Audio;
|
||||
|
||||
DataWindow::DataWindow()
|
||||
{}
|
||||
{
|
||||
mFilled = 0;
|
||||
mData = NULL;
|
||||
mCapacity = 0;
|
||||
}
|
||||
|
||||
DataWindow::~DataWindow()
|
||||
{
|
||||
if (mData)
|
||||
{
|
||||
free(mData);
|
||||
mData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DataWindow::setCapacity(size_t capacity)
|
||||
void DataWindow::setCapacity(int capacity)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
|
||||
// The window only ever grows; a smaller request keeps the current buffer.
|
||||
if (capacity <= mCapacity)
|
||||
return;
|
||||
|
||||
size_t tail = capacity - mCapacity;
|
||||
char* buffer = mData;
|
||||
int tail = capacity - mCapacity;
|
||||
mData = (char*)realloc(mData, capacity);
|
||||
if (!mData)
|
||||
{
|
||||
// Realloc failed
|
||||
mData = buffer;
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
if (tail > 0)
|
||||
memset(mData + mCapacity, 0, tail);
|
||||
mCapacity = capacity;
|
||||
}
|
||||
|
||||
void DataWindow::addZero(size_t length)
|
||||
void DataWindow::addZero(int length)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
|
||||
@@ -61,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);
|
||||
|
||||
@@ -95,7 +84,7 @@ void DataWindow::add(short sample)
|
||||
add(&sample, sizeof sample);
|
||||
}
|
||||
|
||||
void DataWindow::erase(size_t length)
|
||||
void DataWindow::erase(int length)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
if (length > mFilled)
|
||||
@@ -121,21 +110,21 @@ void DataWindow::clear()
|
||||
mFilled = 0;
|
||||
}
|
||||
|
||||
short DataWindow::shortAt(size_t index) const
|
||||
short DataWindow::shortAt(int index) const
|
||||
{
|
||||
Lock l(mMutex);
|
||||
assert(index < mFilled / 2);
|
||||
return ((short*)mData)[index];
|
||||
}
|
||||
|
||||
void DataWindow::setShortAt(short value, size_t index)
|
||||
void DataWindow::setShortAt(short value, int index)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
assert(index < mFilled / 2);
|
||||
((short*)mData)[index] = value;
|
||||
}
|
||||
|
||||
size_t DataWindow::read(void* buffer, size_t length)
|
||||
int DataWindow::read(void* buffer, int length)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
if (length > mFilled)
|
||||
@@ -151,27 +140,25 @@ size_t DataWindow::read(void* buffer, size_t length)
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t DataWindow::filled() const
|
||||
int DataWindow::filled() const
|
||||
{
|
||||
Lock l(mMutex);
|
||||
return mFilled;
|
||||
}
|
||||
|
||||
void DataWindow::setFilled(size_t filled)
|
||||
void DataWindow::setFilled(int filled)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
if (filled > mCapacity)
|
||||
throw std::bad_alloc();
|
||||
mFilled = filled;
|
||||
}
|
||||
|
||||
size_t DataWindow::capacity() const
|
||||
int DataWindow::capacity() const
|
||||
{
|
||||
Lock l(mMutex);
|
||||
return mCapacity;
|
||||
}
|
||||
|
||||
void DataWindow::zero(size_t length)
|
||||
void DataWindow::zero(int length)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
assert(length <= mCapacity);
|
||||
@@ -179,25 +166,6 @@ void DataWindow::zero(size_t length)
|
||||
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)
|
||||
{
|
||||
Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "../helper/HL_ByteBuffer.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "Audio_Interface.h"
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
@@ -18,34 +17,31 @@ public:
|
||||
DataWindow();
|
||||
~DataWindow();
|
||||
|
||||
void setCapacity(size_t capacity);
|
||||
size_t capacity() const;
|
||||
void setCapacity(int capacity);
|
||||
int capacity() const;
|
||||
|
||||
void addZero(size_t length);
|
||||
void add(const void* data, size_t length);
|
||||
void addZero(int length);
|
||||
void add(const void* data, int length);
|
||||
void add(short sample);
|
||||
size_t read(void* buffer, size_t length);
|
||||
void erase(size_t length);
|
||||
int read(void* buffer, int length);
|
||||
void erase(int length = -1);
|
||||
const char* data() const;
|
||||
char* mutableData();
|
||||
size_t filled() const;
|
||||
void setFilled(size_t filled);
|
||||
int filled() const;
|
||||
void setFilled(int filled);
|
||||
void clear();
|
||||
|
||||
short shortAt(size_t index) const;
|
||||
void setShortAt(short value, size_t index);
|
||||
void zero(size_t length);
|
||||
size_t moveTo(DataWindow& dst, size_t size /* in bytes*/ );
|
||||
|
||||
std::chrono::milliseconds getTimeLength(const Format& fmt) const;
|
||||
short shortAt(int index) const;
|
||||
void setShortAt(short value, int index);
|
||||
void zero(int length);
|
||||
|
||||
static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
|
||||
|
||||
protected:
|
||||
mutable Mutex mMutex;
|
||||
char* mData = nullptr;
|
||||
size_t mFilled = 0;
|
||||
size_t mCapacity = 0;
|
||||
char* mData;
|
||||
int mFilled;
|
||||
int mCapacity;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,20 +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
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#define NOMINMAX
|
||||
#include "Audio_DevicePair.h"
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "Audio"
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
// --- DevicePair ---
|
||||
DevicePair::DevicePair()
|
||||
:mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS),
|
||||
mMonitoring(nullptr)
|
||||
DevicePair::DevicePair(bool aec, bool agc)
|
||||
:mConfig(NULL), mDelegate(NULL), mAec(aec), mAgc(agc), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS)
|
||||
{
|
||||
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
||||
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
||||
@@ -28,50 +28,26 @@ DevicePair::~DevicePair()
|
||||
if (mInput)
|
||||
{
|
||||
if (mInput->connection() == this)
|
||||
mInput->setConnection(nullptr);
|
||||
mInput->setConnection(NULL);
|
||||
mInput.reset();
|
||||
}
|
||||
|
||||
if (mOutput)
|
||||
{
|
||||
if (mOutput->connection() == this)
|
||||
mOutput->setConnection(nullptr);
|
||||
mOutput->setConnection(NULL);
|
||||
mOutput.reset();
|
||||
}
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setAec(bool aec)
|
||||
{
|
||||
mAec = aec;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DevicePair::aec()
|
||||
{
|
||||
return mAec;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setAgc(bool agc)
|
||||
{
|
||||
mAgc = agc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DevicePair::agc()
|
||||
{
|
||||
return mAgc;
|
||||
}
|
||||
|
||||
|
||||
VariantMap* DevicePair::config()
|
||||
{
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setConfig(VariantMap* config)
|
||||
void DevicePair::setConfig(VariantMap* config)
|
||||
{
|
||||
mConfig = config;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PInputDevice DevicePair::input()
|
||||
@@ -79,17 +55,15 @@ PInputDevice DevicePair::input()
|
||||
return mInput;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setInput(PInputDevice input)
|
||||
void DevicePair::setInput(PInputDevice input)
|
||||
{
|
||||
if (mInput == input)
|
||||
return *this;
|
||||
return;
|
||||
|
||||
mInput = input;
|
||||
mInput->setConnection(this);
|
||||
if (mDelegate)
|
||||
mDelegate->deviceChanged(this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
POutputDevice DevicePair::output()
|
||||
@@ -97,17 +71,14 @@ POutputDevice DevicePair::output()
|
||||
return mOutput;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setOutput(POutputDevice output)
|
||||
void DevicePair::setOutput(POutputDevice output)
|
||||
{
|
||||
if (output == mOutput)
|
||||
return *this;
|
||||
|
||||
return;
|
||||
mOutput = output;
|
||||
mOutput->setConnection(this);
|
||||
if (mDelegate)
|
||||
mDelegate->deviceChanged(this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DevicePair::start()
|
||||
@@ -117,7 +88,6 @@ bool DevicePair::start()
|
||||
result = mInput->open();
|
||||
if (mOutput && result)
|
||||
result &= mOutput->open();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -129,10 +99,9 @@ void DevicePair::stop()
|
||||
mOutput->close();
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setDelegate(Delegate* dc)
|
||||
void DevicePair::setDelegate(Delegate* dc)
|
||||
{
|
||||
mDelegate = dc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DevicePair::Delegate* DevicePair::delegate()
|
||||
@@ -140,17 +109,6 @@ DevicePair::Delegate* DevicePair::delegate()
|
||||
return mDelegate;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
|
||||
{
|
||||
mMonitoring = monitoring;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataConnection* DevicePair::monitoring()
|
||||
{
|
||||
return mMonitoring;
|
||||
}
|
||||
|
||||
Player& DevicePair::player()
|
||||
{
|
||||
return mPlayer;
|
||||
@@ -227,7 +185,6 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||
{
|
||||
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
|
||||
|
||||
// Ask audio data on main AUDIO_SAMPLERATE frequency
|
||||
if (mDelegate)
|
||||
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
|
||||
|
||||
@@ -240,12 +197,8 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||
|
||||
// Resample these 10 milliseconds it to native format
|
||||
size_t wasProcessed = 0;
|
||||
size_t wasProduced = mSpkResampler.resample(Format().mRate,
|
||||
mOutput10msBuffer.data(),
|
||||
mOutput10msBuffer.capacity(),
|
||||
wasProcessed, f.mRate,
|
||||
mOutputNativeData.mutableData() + mOutputNativeData.filled(),
|
||||
mOutputNativeData.capacity() - mOutputNativeData.filled());
|
||||
size_t wasProduced = mSpkResampler.resample(nativeFormat.mRate, mOutput10msBuffer.data(), mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
|
||||
mOutputNativeData.mutableData() + mOutputNativeData.filled(), mOutputNativeData.capacity() - mOutputNativeData.filled());
|
||||
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
|
||||
#ifdef CONSOLE_LOGGING
|
||||
printf("Resampled %d to %d\n", wasProcessed, wasProduced);
|
||||
@@ -261,10 +214,6 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||
|
||||
mOutputNativeData.read(buffer, length);
|
||||
|
||||
// Send data to monitoring if needed
|
||||
if (mMonitoring)
|
||||
mMonitoring->onSpkData(f, buffer, length);
|
||||
|
||||
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
|
||||
|
||||
// AEC filter wants frames.
|
||||
@@ -275,6 +224,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
|
||||
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
|
||||
}
|
||||
//ICELogMedia(<< "Audio::DevicePair::onSpkData() end")
|
||||
}
|
||||
|
||||
void DevicePair::processMicData(const Format& f, void* buffer, int length)
|
||||
|
||||
@@ -26,32 +26,29 @@ namespace Audio
|
||||
virtual void deviceChanged(DevicePair* dpair) = 0;
|
||||
};
|
||||
|
||||
DevicePair();
|
||||
DevicePair(bool aec = true, bool agc = true);
|
||||
virtual ~DevicePair();
|
||||
|
||||
DevicePair& setAec(bool aec);
|
||||
void setAec(bool aec);
|
||||
bool aec();
|
||||
DevicePair& setAgc(bool agc);
|
||||
void setAgc(bool agc);
|
||||
bool agc();
|
||||
|
||||
VariantMap* config();
|
||||
DevicePair& setConfig(VariantMap* config);
|
||||
void setConfig(VariantMap* config);
|
||||
|
||||
PInputDevice input();
|
||||
DevicePair& setInput(PInputDevice input);
|
||||
void setInput(PInputDevice input);
|
||||
|
||||
POutputDevice output();
|
||||
DevicePair& setOutput(POutputDevice output);
|
||||
void setOutput(POutputDevice output);
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
DevicePair& setDelegate(Delegate* dc);
|
||||
void setDelegate(Delegate* dc);
|
||||
Delegate* delegate();
|
||||
|
||||
DevicePair& setMonitoring(DataConnection* monitoring);
|
||||
DataConnection* monitoring();
|
||||
|
||||
Player& player();
|
||||
|
||||
protected:
|
||||
@@ -66,7 +63,6 @@ namespace Audio
|
||||
Player mPlayer;
|
||||
UniversalResampler mMicResampler, mSpkResampler;
|
||||
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
||||
DataConnection* mMonitoring;
|
||||
|
||||
#ifdef DUMP_NATIVEOUTPUT
|
||||
std::shared_ptr<WavFileWriter> mNativeOutputDump;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
|
||||
#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "DirectSound"
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifndef __AUDIO_DSOUND_H
|
||||
#define __AUDIO_DSOUND_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
@@ -29,6 +29,10 @@ TimeSource::TimeSource(int quantTime, int nrOfQuants)
|
||||
mTailTime = 0;
|
||||
}
|
||||
|
||||
TimeSource::~TimeSource()
|
||||
{
|
||||
}
|
||||
|
||||
void TimeSource::start()
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
@@ -98,12 +102,11 @@ unsigned TimeSource::time()
|
||||
#if defined(TARGET_ANDROID)
|
||||
assert(0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- StubTimer ---
|
||||
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
||||
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false), mCurrentTime(0)
|
||||
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false)
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Audio
|
||||
|
||||
public:
|
||||
TimeSource(int quantTime, int nrOfQuants);
|
||||
~TimeSource() = default;
|
||||
~TimeSource();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define __AUDIO_INTERFACE_H
|
||||
|
||||
#include <string>
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "../helper/HL_Types.h"
|
||||
#include "../helper/HL_VariantMap.h"
|
||||
#include "../helper/HL_Pointer.h"
|
||||
@@ -51,38 +51,12 @@ struct Format
|
||||
return float((milliseconds * mRate) / 500.0 * mChannels);
|
||||
}
|
||||
|
||||
size_t sizeFromTime(std::chrono::milliseconds ms) const
|
||||
{
|
||||
return sizeFromTime(ms.count());
|
||||
}
|
||||
|
||||
std::string toString()
|
||||
{
|
||||
char buffer[64];
|
||||
std::snprintf(buffer, sizeof(buffer), "%dHz %dch", mRate, mChannels);
|
||||
sprintf(buffer, "%dHz %dch", mRate, mChannels);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
bool operator == (const Format& rhs) const
|
||||
{
|
||||
return mRate == rhs.mRate && mChannels == rhs.mChannels;
|
||||
}
|
||||
|
||||
bool operator != (const Format& rhs) const
|
||||
{
|
||||
return mRate != rhs.mRate || mChannels != rhs.mChannels;
|
||||
}
|
||||
|
||||
int rate() const
|
||||
{
|
||||
return mRate;
|
||||
}
|
||||
|
||||
int channels() const
|
||||
{
|
||||
return mChannels;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class DataConnection
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include "Audio_Mixer.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "Mixer"
|
||||
using namespace Audio;
|
||||
|
||||
Mixer::Stream::Stream()
|
||||
@@ -94,7 +94,6 @@ Mixer::~Mixer()
|
||||
|
||||
void Mixer::unregisterChannel(void* channel)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
@@ -109,7 +108,6 @@ void Mixer::unregisterChannel(void* channel)
|
||||
|
||||
void Mixer::clear(void* context, unsigned ssrc)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
@@ -149,7 +147,6 @@ void Mixer::addPcm(void* context, unsigned ssrc,
|
||||
{
|
||||
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
|
||||
|
||||
Lock l(mMutex);
|
||||
int i;
|
||||
|
||||
// Locate a channel
|
||||
@@ -175,7 +172,6 @@ void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate,
|
||||
{
|
||||
assert(rate == 8000 || rate == 16000 || rate == 32000 || rate == 48000);
|
||||
|
||||
Lock l(mMutex);
|
||||
int i;
|
||||
|
||||
// Locate a channel
|
||||
@@ -200,8 +196,6 @@ void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate,
|
||||
|
||||
void Mixer::mix()
|
||||
{
|
||||
Lock l(mMutex);
|
||||
|
||||
// Current sample
|
||||
int sample = 0;
|
||||
|
||||
@@ -316,8 +310,6 @@ void Mixer::mix()
|
||||
|
||||
int Mixer::getPcm(void* outputData, int outputLength)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
|
||||
if (mOutput.filled() < outputLength)
|
||||
mix();
|
||||
|
||||
@@ -328,26 +320,14 @@ int Mixer::getPcm(void* outputData, int outputLength)
|
||||
|
||||
int Mixer::mixAndGetPcm(Audio::DataWindow& output)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
|
||||
// Mix
|
||||
mix();
|
||||
|
||||
size_t avail = mOutput.filled();
|
||||
if (!avail)
|
||||
{
|
||||
output.setFilled(0);
|
||||
return 0;
|
||||
}
|
||||
// Set output space
|
||||
output.setCapacity(mOutput.filled());
|
||||
|
||||
// Make sure output has enough space (setCapacity only ever grows the window)
|
||||
if (output.capacity() < avail)
|
||||
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);
|
||||
// Read mixed data to output
|
||||
return mOutput.read(output.mutableData(), output.capacity());
|
||||
}
|
||||
|
||||
int Mixer::available()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifndef _RX_MIXER_H
|
||||
#define _RX_MIXER_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "../helper/HL_ByteBuffer.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "Audio_Resampler.h"
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "Audio_Null.h"
|
||||
#include "helper/HL_Log.h"
|
||||
#include <assert.h>
|
||||
#include <chrono>
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "NULL 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)
|
||||
{
|
||||
start();
|
||||
@@ -33,23 +31,23 @@ void NullTimer::stop()
|
||||
|
||||
void NullTimer::run()
|
||||
{
|
||||
mTail = 0us;
|
||||
mTail = 0;
|
||||
while (!mShutdown)
|
||||
{
|
||||
// Get current timestamp
|
||||
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
|
||||
|
||||
while (mTail >= mInterval)
|
||||
while (mTail >= mInterval * 1000)
|
||||
{
|
||||
if (mDelegate)
|
||||
mDelegate->onTimerSignal(*this);
|
||||
mTail -= mInterval;
|
||||
mTail -= mInterval * 1000;
|
||||
}
|
||||
|
||||
// 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 + std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp);
|
||||
mTail += (int)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp).count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,24 +59,21 @@ NullInputDevice::NullInputDevice()
|
||||
|
||||
NullInputDevice::~NullInputDevice()
|
||||
{
|
||||
internalClose();
|
||||
close();
|
||||
}
|
||||
|
||||
bool NullInputDevice::open()
|
||||
{
|
||||
ICELogInfo(<< "Starting NullInputDevice for " << AUDIO_MIC_BUFFER_LENGTH << "ms buffers");
|
||||
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
|
||||
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
|
||||
mTimeCounter = 0; mDataCounter = 0;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void NullInputDevice::internalClose()
|
||||
void NullInputDevice::close()
|
||||
{
|
||||
ICELogInfo(<< "Stopping NullInputDevice");
|
||||
mTimer.reset();
|
||||
if (mBuffer)
|
||||
{
|
||||
@@ -88,16 +83,10 @@ void NullInputDevice::internalClose()
|
||||
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||
}
|
||||
|
||||
void NullInputDevice::close()
|
||||
{
|
||||
internalClose();
|
||||
}
|
||||
|
||||
Format NullInputDevice::getFormat()
|
||||
{
|
||||
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
||||
|
||||
return {}; // Return library-define default format
|
||||
return Format();
|
||||
}
|
||||
|
||||
void NullInputDevice::onTimerSignal(NullTimer& timer)
|
||||
@@ -116,7 +105,7 @@ NullOutputDevice::NullOutputDevice()
|
||||
|
||||
NullOutputDevice::~NullOutputDevice()
|
||||
{
|
||||
internalClose();
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
@@ -125,20 +114,15 @@ bool NullOutputDevice::open()
|
||||
mTimeCounter = 0; mDataCounter = 0;
|
||||
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
|
||||
// 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;
|
||||
}
|
||||
|
||||
void NullOutputDevice::internalClose()
|
||||
{
|
||||
mTimer.reset();
|
||||
free(mBuffer); mBuffer = nullptr;
|
||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||
}
|
||||
|
||||
void NullOutputDevice::close()
|
||||
{
|
||||
internalClose();
|
||||
mTimer.reset();
|
||||
free(mBuffer); mBuffer = nullptr;
|
||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||
}
|
||||
|
||||
Format NullOutputDevice::getFormat()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define __AUDIO_NULL_H
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include "Audio_Interface.h"
|
||||
|
||||
namespace Audio
|
||||
@@ -18,10 +17,10 @@ public:
|
||||
|
||||
protected:
|
||||
std::thread mWorkerThread;
|
||||
std::atomic_bool mShutdown = {false};
|
||||
Delegate* mDelegate = nullptr;
|
||||
std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds
|
||||
std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink
|
||||
volatile bool mShutdown;
|
||||
Delegate* mDelegate;
|
||||
int mInterval, // Interval - wanted number of milliseconds
|
||||
mTail; // Number of milliseconds that can be sent immediately to sink
|
||||
std::string mThreadName;
|
||||
|
||||
void start();
|
||||
@@ -29,7 +28,7 @@ protected:
|
||||
void run();
|
||||
public:
|
||||
/* 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();
|
||||
};
|
||||
|
||||
@@ -39,8 +38,6 @@ protected:
|
||||
void* mBuffer = nullptr;
|
||||
std::shared_ptr<NullTimer> mTimer;
|
||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||
void internalClose();
|
||||
|
||||
public:
|
||||
NullInputDevice();
|
||||
virtual ~NullInputDevice();
|
||||
@@ -58,8 +55,6 @@ protected:
|
||||
std::shared_ptr<NullTimer> mTimer;
|
||||
void* mBuffer = nullptr;
|
||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||
|
||||
void internalClose();
|
||||
public:
|
||||
NullOutputDevice();
|
||||
virtual ~NullOutputDevice();
|
||||
@@ -84,8 +79,8 @@ public:
|
||||
std::tstring nameAt(int index) override;
|
||||
int idAt(int index) override;
|
||||
int indexOfDefaultDevice() override;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "../helper/HL_Log.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "Player"
|
||||
|
||||
using namespace Audio;
|
||||
// -------------- Player -----------
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "Audio_Quality.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
#include "../helper/HL_Types.h"
|
||||
@@ -17,10 +17,7 @@
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
#ifndef SHRT_MAX
|
||||
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||
#endif
|
||||
|
||||
AgcFilter::AgcFilter(int channels)
|
||||
{
|
||||
static const float DefaultLevel = 0.8f;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef __AUDIO_QUALITY_H
|
||||
#define __AUDIO_QUALITY_H
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "Audio_Resampler.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
@@ -18,7 +18,9 @@ namespace Audio
|
||||
|
||||
|
||||
SpeexResampler::SpeexResampler()
|
||||
{}
|
||||
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0)
|
||||
{
|
||||
}
|
||||
|
||||
void SpeexResampler::start(int channels, int sourceRate, int destRate)
|
||||
{
|
||||
@@ -49,11 +51,6 @@ void SpeexResampler::stop()
|
||||
}
|
||||
}
|
||||
|
||||
bool SpeexResampler::isOpened() const
|
||||
{
|
||||
return mContext != nullptr;
|
||||
}
|
||||
|
||||
SpeexResampler::~SpeexResampler()
|
||||
{
|
||||
stop();
|
||||
@@ -116,25 +113,24 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
|
||||
return outLen * sizeof(short) * mChannels;
|
||||
}
|
||||
|
||||
int SpeexResampler::sourceRate() const
|
||||
int SpeexResampler::sourceRate()
|
||||
{
|
||||
return mSourceRate;
|
||||
}
|
||||
|
||||
int SpeexResampler::destRate() const
|
||||
int SpeexResampler::destRate()
|
||||
{
|
||||
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);
|
||||
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f) / 2 * 2;
|
||||
}
|
||||
|
||||
// Returns instance + speex resampler size in bytes
|
||||
@@ -277,7 +273,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
|
||||
PResampler r;
|
||||
if (resamplerIter == mResamplerMap.end())
|
||||
{
|
||||
r = std::make_shared<Resampler>();
|
||||
r = PResampler(new Resampler());
|
||||
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
||||
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
||||
}
|
||||
|
||||
@@ -24,25 +24,23 @@ namespace Audio
|
||||
|
||||
void start(int channels, int sourceRate, int destRate);
|
||||
void stop();
|
||||
bool isOpened() const;
|
||||
|
||||
size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed,
|
||||
void* dest, size_t destCapacity);
|
||||
int sourceRate() const;
|
||||
int destRate() const;
|
||||
size_t getDestLength(size_t sourceLen) const;
|
||||
size_t getSourceLength(size_t destLen) const;
|
||||
int sourceRate();
|
||||
int destRate();
|
||||
size_t getDestLength(size_t sourceLen);
|
||||
size_t getSourceLength(size_t destLen);
|
||||
|
||||
// Returns instance + speex encoder size in bytes
|
||||
size_t getSize() const;
|
||||
|
||||
protected:
|
||||
void* mContext = nullptr;
|
||||
int mErrorCode = 0;
|
||||
int mSourceRate = 0,
|
||||
mDestRate = 0,
|
||||
mChannels = 0;
|
||||
short mLastSample = 0;
|
||||
void* mContext;
|
||||
int mErrorCode;
|
||||
int mSourceRate,
|
||||
mDestRate,
|
||||
mChannels;
|
||||
short mLastSample;
|
||||
};
|
||||
|
||||
typedef SpeexResampler Resampler;
|
||||
|
||||
+146
-182
@@ -7,10 +7,9 @@
|
||||
#include "helper/HL_Exception.h"
|
||||
#include "helper/HL_String.h"
|
||||
#include "helper/HL_Log.h"
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef WORD
|
||||
# define WORD unsigned short
|
||||
@@ -32,7 +31,7 @@ WaveFormatEx;
|
||||
|
||||
#define WAVE_FORMAT_PCM 1
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
#define LOG_SUBSYSTEM "WavFileReader"
|
||||
|
||||
#define LOCK std::unique_lock<std::recursive_mutex> lock(mFileMtx);
|
||||
|
||||
@@ -40,7 +39,7 @@ using namespace Audio;
|
||||
|
||||
// ---------------------- WavFileReader -------------------------
|
||||
WavFileReader::WavFileReader()
|
||||
:mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
|
||||
:mHandle(nullptr), mRate(0), mLastError(0)
|
||||
{
|
||||
mDataOffset = 0;
|
||||
}
|
||||
@@ -53,45 +52,38 @@ WavFileReader::~WavFileReader()
|
||||
|
||||
std::string WavFileReader::readChunk()
|
||||
{
|
||||
char name[5] = {0};
|
||||
readBuffer(name, 4);
|
||||
char name[5];
|
||||
if (fread(name, 1, 4, mHandle) != 4)
|
||||
THROW_READERROR;
|
||||
|
||||
name[4] = 0;
|
||||
std::string result = name;
|
||||
uint32_t size = 0;
|
||||
readBuffer(&size, 4);
|
||||
unsigned size;
|
||||
if (fread(&size, 4, 1, mHandle) != 1)
|
||||
THROW_READERROR;
|
||||
|
||||
if (result == "data")
|
||||
mDataLength = size;
|
||||
if (result == "fact")
|
||||
fread(&mDataLength, 4, 1, mHandle);
|
||||
else
|
||||
// Skip the chunk body; RIFF chunks are word-aligned, so odd sizes carry a pad byte
|
||||
mInput->seekg(std::streamoff(size + (size & 1)), std::ios_base::cur);
|
||||
if (result != "data")
|
||||
fseek(mHandle, size, SEEK_CUR);
|
||||
else
|
||||
mDataLength = size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void WavFileReader::readBuffer(void* buffer, size_t sz)
|
||||
{
|
||||
auto p = mInput->tellg();
|
||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
||||
if (mInput->tellg() - p != sz)
|
||||
throw Exception(ERR_WAVFILE_FAILED);
|
||||
}
|
||||
|
||||
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
|
||||
{
|
||||
auto p = mInput->tellg();
|
||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
||||
return mInput->tellg() - p;
|
||||
}
|
||||
|
||||
bool WavFileReader::open(const std::filesystem::path& p)
|
||||
bool WavFileReader::open(const std::tstring& filename)
|
||||
{
|
||||
LOCK;
|
||||
try
|
||||
{
|
||||
mPath = p;
|
||||
mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in);
|
||||
if (!mInput->is_open())
|
||||
#ifdef WIN32
|
||||
mHandle = _wfopen(filename.c_str(), L"rb");
|
||||
#else
|
||||
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "rb");
|
||||
#endif
|
||||
if (NULL == mHandle)
|
||||
{
|
||||
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
mLastError = errno;
|
||||
@@ -105,64 +97,76 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
||||
|
||||
// Read the .WAV header
|
||||
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'))
|
||||
THROW_READERROR;
|
||||
|
||||
// Read the file size
|
||||
uint32_t filesize = 0;
|
||||
readBuffer(&filesize, sizeof(filesize));
|
||||
unsigned int filesize = 0;
|
||||
if (fread(&filesize, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
char wavefmt[9] = {0};
|
||||
readBuffer(wavefmt, 8);
|
||||
char wavefmt[9];
|
||||
if (fread(wavefmt, 8, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
wavefmt[8] = 0;
|
||||
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
||||
THROW_READERROR;
|
||||
|
||||
uint32_t fmtSize = 0;
|
||||
readBuffer(&fmtSize, sizeof(fmtSize));
|
||||
unsigned fmtSize = 0;
|
||||
if (fread(&fmtSize, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
auto fmtStart = mInput->tellg();
|
||||
unsigned fmtStart = ftell(mHandle);
|
||||
|
||||
uint16_t formattag = 0;
|
||||
readBuffer(&formattag, sizeof(formattag));
|
||||
unsigned short formattag = 0;
|
||||
if (fread(&formattag, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
||||
THROW_READERROR;
|
||||
|
||||
mChannels = 0;
|
||||
readBuffer(&mChannels, sizeof(mChannels));
|
||||
|
||||
mSamplerate = 0;
|
||||
readBuffer(&mSamplerate, sizeof(mSamplerate));
|
||||
|
||||
uint32_t avgbytespersec = 0;
|
||||
readBuffer(&avgbytespersec, sizeof(avgbytespersec));
|
||||
|
||||
uint16_t blockalign = 0;
|
||||
readBuffer(&blockalign, sizeof(blockalign));
|
||||
|
||||
mBits = 0;
|
||||
readBuffer(&mBits, sizeof(mBits));
|
||||
|
||||
// Only 16-bit PCM is supported: the read path feeds the data
|
||||
// directly into a 16-bit resampler.
|
||||
if (mBits != 16)
|
||||
if (fread(&mChannels, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
// Look for the chunk 'data'
|
||||
mInput->seekg(fmtStart + std::streampos(fmtSize));
|
||||
mRate = 0;
|
||||
if (fread(&mRate, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
unsigned int avgbytespersec = 0;
|
||||
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
unsigned short blockalign = 0;
|
||||
if (fread(&blockalign, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
mBits = 0;
|
||||
if (fread(&mBits, 2, 1, mHandle) < 1)
|
||||
THROW_READERROR;
|
||||
|
||||
if (mBits !=8 && mBits != 16)
|
||||
THROW_READERROR;
|
||||
|
||||
// Read the "chunk"
|
||||
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
|
||||
//unsigned pos = ftell(mHandle);
|
||||
mDataLength = 0;
|
||||
while (readChunk() != "data")
|
||||
;
|
||||
|
||||
mDataOffset = mInput->tellg();
|
||||
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
|
||||
mFileName = filename;
|
||||
mDataOffset = ftell(mHandle);
|
||||
|
||||
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
mInput.reset();
|
||||
fclose(mHandle); mHandle = nullptr;
|
||||
mLastError = static_cast<unsigned>(-1);
|
||||
}
|
||||
return isOpened();
|
||||
@@ -171,119 +175,78 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
||||
void WavFileReader::close()
|
||||
{
|
||||
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
|
||||
{
|
||||
return mChannels;
|
||||
}
|
||||
|
||||
size_t WavFileReader::read(void* buffer, size_t bytes)
|
||||
unsigned WavFileReader::read(void* buffer, unsigned bytes)
|
||||
{
|
||||
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
|
||||
}
|
||||
|
||||
size_t WavFileReader::readRaw(void* buffer, size_t bytes)
|
||||
{
|
||||
return readRaw((short*)buffer, bytes / channels() / sizeof(short)) * channels() * sizeof(short);
|
||||
}
|
||||
|
||||
size_t WavFileReader::read(short* buffer, size_t samples)
|
||||
unsigned WavFileReader::read(short* buffer, unsigned samples)
|
||||
{
|
||||
LOCK;
|
||||
|
||||
if (!mInput)
|
||||
if (!mHandle)
|
||||
return 0;
|
||||
|
||||
// Get number of samples that must be read from source file
|
||||
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
||||
bool useHeap = requiredBytes > sizeof mTempBuffer;
|
||||
void* temp;
|
||||
if (useHeap)
|
||||
temp = malloc(requiredBytes);
|
||||
else
|
||||
temp = mTempBuffer;
|
||||
|
||||
int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
||||
void* temp = alloca(requiredBytes);
|
||||
memset(temp, 0, requiredBytes);
|
||||
|
||||
// Find required size of input buffer
|
||||
if (mDataLength)
|
||||
{
|
||||
auto filePosition = mInput->tellg();
|
||||
unsigned filePosition = ftell(mHandle);
|
||||
|
||||
// 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;
|
||||
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
|
||||
}
|
||||
|
||||
size_t readBytes = tryReadBuffer(temp, requiredBytes);
|
||||
|
||||
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
|
||||
size_t processedBytes = 0;
|
||||
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes,
|
||||
size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
|
||||
buffer, samples * 2 * AUDIO_CHANNELS);
|
||||
|
||||
if (useHeap)
|
||||
free(temp);
|
||||
return result / 2 / AUDIO_CHANNELS;
|
||||
}
|
||||
|
||||
|
||||
size_t WavFileReader::readRaw(short* buffer, size_t samples)
|
||||
{
|
||||
LOCK;
|
||||
|
||||
if (!mInput)
|
||||
return 0;
|
||||
|
||||
// Get number of samples that must be read from source file
|
||||
size_t requiredBytes = samples * channels() * sizeof(short);
|
||||
|
||||
// Find required size of input buffer
|
||||
if (mDataLength)
|
||||
{
|
||||
auto filePosition = mInput->tellg();
|
||||
|
||||
// Check how much data we can read
|
||||
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()
|
||||
{
|
||||
LOCK;
|
||||
if (!mInput)
|
||||
return false;
|
||||
return mInput->is_open();
|
||||
|
||||
return (mHandle != 0);
|
||||
}
|
||||
|
||||
void WavFileReader::rewind()
|
||||
{
|
||||
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;
|
||||
return mPath;
|
||||
|
||||
return mFileName;
|
||||
}
|
||||
|
||||
size_t WavFileReader::size() const
|
||||
unsigned WavFileReader::size() const
|
||||
{
|
||||
LOCK;
|
||||
|
||||
return mDataLength;
|
||||
}
|
||||
|
||||
@@ -297,8 +260,9 @@ unsigned WavFileReader::lastError() const
|
||||
#define BITS_PER_CHANNEL 16
|
||||
|
||||
WavFileWriter::WavFileWriter()
|
||||
:mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||
{}
|
||||
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||
{
|
||||
}
|
||||
|
||||
WavFileWriter::~WavFileWriter()
|
||||
{
|
||||
@@ -311,75 +275,68 @@ void WavFileWriter::checkWriteResult(int result)
|
||||
throw Exception(ERR_WAVFILE_FAILED, errno);
|
||||
}
|
||||
|
||||
void WavFileWriter::writeBuffer(const void* buffer, size_t sz)
|
||||
{
|
||||
if (!mOutput)
|
||||
return;
|
||||
|
||||
auto p = mOutput->tellp();
|
||||
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
|
||||
if (mOutput->tellp() - p != sz)
|
||||
throw Exception(ERR_WAVFILE_FAILED);
|
||||
}
|
||||
|
||||
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
|
||||
bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
|
||||
{
|
||||
LOCK;
|
||||
|
||||
close();
|
||||
mSamplerate = samplerate;
|
||||
|
||||
mRate = rate;
|
||||
mChannels = channels;
|
||||
|
||||
mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc);
|
||||
if (!mOutput->is_open())
|
||||
#ifdef WIN32
|
||||
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 = " << p << " , error = " << errorcode);
|
||||
mOutput.reset();
|
||||
ICELogError(<< "Failed to create .wav file: filename = " << StringHelper::makeUtf8(filename) << " , error = " << errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the .WAV header
|
||||
const char* riff = "RIFF";
|
||||
writeBuffer(riff, 4);
|
||||
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
|
||||
|
||||
// Write the file size
|
||||
uint32_t filesize = 0;
|
||||
writeBuffer(&filesize, sizeof filesize);
|
||||
unsigned int filesize = 0;
|
||||
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
|
||||
|
||||
const char* wavefmt = "WAVEfmt ";
|
||||
writeBuffer(wavefmt, 8);
|
||||
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
|
||||
|
||||
// Set the format description
|
||||
uint32_t dwFmtSize = 16; /*= 16L*/;
|
||||
writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
|
||||
DWORD dwFmtSize = 16; /*= 16L*/;
|
||||
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
|
||||
|
||||
WaveFormatEx format;
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag));
|
||||
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
|
||||
|
||||
format.nChannels = mChannels;
|
||||
writeBuffer(&format.nChannels, sizeof(format.nChannels));
|
||||
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
|
||||
|
||||
format.nSamplesPerSec = mSamplerate;
|
||||
writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec));
|
||||
format.nSamplesPerSec = mRate;
|
||||
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
|
||||
|
||||
format.nAvgBytesPerSec = mSamplerate * 2 * mChannels;
|
||||
writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec));
|
||||
format.nAvgBytesPerSec = mRate * 2 * mChannels;
|
||||
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
|
||||
|
||||
format.nBlockAlign = 2 * mChannels;
|
||||
writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign));
|
||||
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
|
||||
|
||||
format.wBitsPerSample = BITS_PER_CHANNEL;
|
||||
writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample));
|
||||
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
|
||||
|
||||
const char* data = "data";
|
||||
writeBuffer(data, 4);
|
||||
checkWriteResult( fwrite(data, 4, 1, mHandle));
|
||||
|
||||
mPath = p;
|
||||
mFileName = filename;
|
||||
mWritten = 0;
|
||||
|
||||
mLengthOffset = mOutput->tellp();
|
||||
writeBuffer(&mWritten, sizeof mWritten);
|
||||
mLengthOffset = ftell(mHandle);
|
||||
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||
|
||||
return isOpened();
|
||||
}
|
||||
@@ -387,44 +344,51 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
|
||||
void WavFileWriter::close()
|
||||
{
|
||||
LOCK;
|
||||
mOutput.reset();
|
||||
|
||||
if (mHandle)
|
||||
{
|
||||
fclose(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
||||
{
|
||||
LOCK;
|
||||
|
||||
if (!mOutput)
|
||||
if (!mHandle)
|
||||
return 0;
|
||||
|
||||
// Seek the end of file - here new data will be written
|
||||
mOutput->seekp(0, std::ios_base::end);
|
||||
// Seek the end of file
|
||||
fseek(mHandle, 0, SEEK_END);
|
||||
mWritten += bytes;
|
||||
|
||||
// Write the data
|
||||
writeBuffer(buffer, bytes);
|
||||
fwrite(buffer, bytes, 1, mHandle);
|
||||
|
||||
// Write file length
|
||||
mOutput->seekp(4, std::ios_base::beg);
|
||||
uint32_t fl = mWritten + 36;
|
||||
writeBuffer(&fl, sizeof(fl));
|
||||
fseek(mHandle, 4, SEEK_SET);
|
||||
int32_t fl = mWritten + 36;
|
||||
fwrite(&fl, sizeof(fl), 1, mHandle);
|
||||
|
||||
// Write data length
|
||||
mOutput->seekp(mLengthOffset, std::ios_base::beg);
|
||||
writeBuffer(&mWritten, sizeof(mWritten));
|
||||
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
|
||||
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool WavFileWriter::isOpened() const
|
||||
bool WavFileWriter::isOpened()
|
||||
{
|
||||
LOCK;
|
||||
return mOutput && mOutput->is_open();
|
||||
|
||||
return (mHandle != nullptr);
|
||||
}
|
||||
|
||||
std::filesystem::path WavFileWriter::path() const
|
||||
std::tstring WavFileWriter::filename()
|
||||
{
|
||||
LOCK;
|
||||
return mPath;
|
||||
|
||||
return mFileName;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace Audio
|
||||
@@ -22,43 +20,35 @@ namespace Audio
|
||||
class WavFileReader
|
||||
{
|
||||
protected:
|
||||
uint16_t mChannels = 0;
|
||||
uint16_t mBits = 0;
|
||||
int mSamplerate = 0;
|
||||
std::filesystem::path mPath;
|
||||
FILE* mHandle;
|
||||
short mChannels;
|
||||
short mBits;
|
||||
int mRate;
|
||||
std::tstring mFileName;
|
||||
mutable std::recursive_mutex mFileMtx;
|
||||
size_t mDataOffset = 0;
|
||||
size_t mDataLength = 0;
|
||||
unsigned mDataOffset;
|
||||
unsigned mDataLength;
|
||||
Resampler mResampler;
|
||||
unsigned mLastError = 0;
|
||||
std::unique_ptr<std::ifstream> mInput;
|
||||
uint8_t mTempBuffer[16384];
|
||||
unsigned mLastError;
|
||||
|
||||
std::string readChunk();
|
||||
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
|
||||
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
|
||||
|
||||
public:
|
||||
WavFileReader();
|
||||
~WavFileReader();
|
||||
|
||||
bool open(const std::filesystem::path& p);
|
||||
bool open(const std::tstring& filename);
|
||||
void close();
|
||||
bool isOpened();
|
||||
void rewind();
|
||||
int samplerate() const;
|
||||
int channels() const;
|
||||
int rate() const;
|
||||
|
||||
// This method returns number of read bytes
|
||||
size_t read(void* buffer, size_t bytes);
|
||||
size_t readRaw(void* buffer, size_t bytes);
|
||||
unsigned read(void* buffer, unsigned bytes);
|
||||
|
||||
// This method returns number of read samples
|
||||
size_t read(short* buffer, size_t samples);
|
||||
size_t readRaw(short* buffer, size_t samples);
|
||||
|
||||
std::filesystem::path path() const;
|
||||
size_t size() const;
|
||||
unsigned read(short* buffer, unsigned samples);
|
||||
std::tstring filename() const;
|
||||
unsigned size() const;
|
||||
|
||||
unsigned lastError() const;
|
||||
};
|
||||
@@ -68,25 +58,25 @@ typedef std::shared_ptr<WavFileReader> PWavFileReader;
|
||||
class WavFileWriter
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
|
||||
std::filesystem::path mPath; /// Path to requested audio file.
|
||||
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||
size_t mWritten = 0; /// Amount of written data (in bytes)
|
||||
size_t mLengthOffset = 0; /// Position of length field.
|
||||
int mSamplerate = 0,
|
||||
mChannels = 0;
|
||||
FILE* mHandle; /// Handle of audio file.
|
||||
std::tstring mFileName; /// Path to requested audio file.
|
||||
std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||
int mWritten; /// Amount of written data (in bytes)
|
||||
int mLengthOffset; /// Position of length field.
|
||||
int mRate,
|
||||
mChannels;
|
||||
|
||||
void checkWriteResult(int result);
|
||||
void writeBuffer(const void* buffer, size_t sz);
|
||||
|
||||
public:
|
||||
WavFileWriter();
|
||||
~WavFileWriter();
|
||||
|
||||
bool open(const std::filesystem::path& p, int samplerate, int channels);
|
||||
bool open(const std::tstring& filename, int rate, int channels);
|
||||
void close();
|
||||
bool isOpened() const;
|
||||
bool isOpened();
|
||||
size_t write(const void* buffer, size_t bytes);
|
||||
std::filesystem::path path() const;
|
||||
std::tstring filename();
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
@@ -1,42 +1,27 @@
|
||||
project (audio_lib)
|
||||
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set (AUDIOLIB_SOURCES
|
||||
Audio_Resampler.cpp
|
||||
Audio_Resampler.h
|
||||
Audio_Quality.cpp
|
||||
Audio_Quality.h
|
||||
Audio_Mixer.cpp
|
||||
Audio_Mixer.h
|
||||
Audio_Interface.cpp
|
||||
Audio_Interface.h
|
||||
Audio_Helper.cpp
|
||||
Audio_Helper.h
|
||||
Audio_DataWindow.cpp
|
||||
Audio_DataWindow.h
|
||||
Audio_DevicePair.cpp
|
||||
Audio_DevicePair.h
|
||||
Audio_Player.cpp
|
||||
Audio_Player.h
|
||||
Audio_Null.cpp
|
||||
Audio_Null.h
|
||||
Audio_CoreAudio.cpp
|
||||
Audio_CoreAudio.h
|
||||
Audio_DirectSound.cpp
|
||||
Audio_DirectSound.h
|
||||
Audio_AndroidOboe.cpp
|
||||
Audio_AndroidOboe.h
|
||||
Audio_WavFile.cpp
|
||||
Audio_WavFile.h
|
||||
)
|
||||
|
||||
add_library(audio_lib ${AUDIOLIB_SOURCES})
|
||||
set_property(TARGET audio_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
##
|
||||
target_include_directories(audio_lib
|
||||
|
||||
@@ -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
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <resip/stack/Pidf.hxx>
|
||||
#include <resip/stack/PlainContents.hxx>
|
||||
|
||||
#define LOG_SUBSYSTEM "engine"
|
||||
#define LOG_SUBSYSTEM "Account"
|
||||
|
||||
#define CONFIG(X) mConfig->at(X)
|
||||
#define CONFIG_EXISTS(X) mConfig->exists(X)
|
||||
@@ -156,7 +156,7 @@ Account::Account(PVariantMap config, UserAgent& agent)
|
||||
: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();
|
||||
setup(*config);
|
||||
}
|
||||
@@ -201,7 +201,7 @@ void Account::setup(VariantMap &config)
|
||||
}
|
||||
|
||||
// NAT decorator
|
||||
mProfile->setOutboundDecorator(std::make_shared<NATDecorator>(mAgent));
|
||||
mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent)));
|
||||
|
||||
// Rinstance
|
||||
if (config.exists(CONFIG_INSTANCE_ID))
|
||||
@@ -266,7 +266,7 @@ void Account::start()
|
||||
|
||||
// Create registration
|
||||
mRegistration = new ResipSession(*mAgent.mDum);
|
||||
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
||||
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
||||
|
||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||
@@ -380,7 +380,7 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
|
||||
observer->mSessionId = observer->mSession->sessionId();
|
||||
observer->mPeer = target;
|
||||
|
||||
std::shared_ptr<resip::SipMessage> msg;
|
||||
resip::SharedPtr<resip::SipMessage> msg;
|
||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
||||
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
||||
@@ -498,7 +498,7 @@ void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
|
||||
//config.mDetectNetworkChange = true;
|
||||
//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->setRole(icerole);
|
||||
}
|
||||
@@ -552,8 +552,9 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
||||
}
|
||||
|
||||
mUsedTransport = response.getReceivedTransportTuple().getType();
|
||||
//bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS;
|
||||
const resip::Transport* transport = response.getReceivedTransport();
|
||||
mUsedTransport = transport->transport();
|
||||
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
|
||||
|
||||
// Retry registration for stream based transport too
|
||||
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
|
||||
@@ -595,7 +596,7 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||
//mProfile->setDefaultFrom(from);
|
||||
}
|
||||
mProfile->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++)
|
||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||
|
||||
@@ -739,8 +740,8 @@ Account::UserInfo Account::getUserInfo() const
|
||||
return mUserInfo;
|
||||
}
|
||||
|
||||
std::atomic_int Account::IdGenerator;
|
||||
resip::AtomicCounter Account::IdGenerator;
|
||||
int Account::generateId()
|
||||
{
|
||||
return ++IdGenerator;
|
||||
return IdGenerator.increment();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
void setup(VariantMap& config);
|
||||
|
||||
/* 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;
|
||||
void setUserInfo(const UserInfo& info);
|
||||
@@ -83,7 +83,7 @@ protected:
|
||||
RegistrationState mRegistrationState;
|
||||
|
||||
ice::NetworkAddress mExternalAddress;
|
||||
std::shared_ptr<resip::UserProfile> mProfile;
|
||||
resip::SharedPtr<resip::UserProfile> mProfile;
|
||||
UserAgent& mAgent;
|
||||
bool mPresenceOnline;
|
||||
std::string mPresenceContent;
|
||||
@@ -135,7 +135,7 @@ protected:
|
||||
#pragma endregion
|
||||
|
||||
static int generateId();
|
||||
static std::atomic_int IdGenerator;
|
||||
static resip::AtomicCounter IdGenerator;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Account> PAccount;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "../helper/HL_Log.h"
|
||||
#include "../helper/HL_String.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "engine"
|
||||
#define LOG_SUBSYSTEM "AudioProvider"
|
||||
|
||||
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
|
||||
:mUserAgent(agent), mTerminal(terminal), mState(0),
|
||||
@@ -64,7 +64,7 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer,
|
||||
}
|
||||
|
||||
// Processes incoming data
|
||||
void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source)
|
||||
void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source)
|
||||
{
|
||||
if (!mActiveStream)
|
||||
return;
|
||||
@@ -77,7 +77,7 @@ void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffe
|
||||
}
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer, unsigned int size)
|
||||
void AudioProvider::sendData(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size)
|
||||
{
|
||||
s->sendDatagram(destination, buffer, size);
|
||||
}
|
||||
@@ -93,14 +93,13 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||
// Check if SRTP suite is found already or not
|
||||
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++)
|
||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite, suite)));
|
||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite)));
|
||||
}
|
||||
else
|
||||
// Answer/re-offer: echo the tag of the negotiated attribute.
|
||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite, mSrtpTag)));
|
||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
||||
}
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
|
||||
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
||||
if (mAvailableCodecs.empty())
|
||||
@@ -115,7 +114,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||
if (mRemoteTelephoneCodec)
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Publish stream state
|
||||
const char* attr = nullptr;
|
||||
@@ -126,8 +125,6 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||
{
|
||||
case msSendonly: attr = "recvonly"; break;
|
||||
case msInactive: attr = "recvonly"; break;
|
||||
case msRecvonly:
|
||||
case msSendRecv: break; // Do nothing here
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -230,8 +227,10 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
||||
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
||||
{
|
||||
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
||||
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!mAvailableCodecs.size())
|
||||
@@ -248,13 +247,11 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
||||
{
|
||||
const resip::Data& attr = *attrIter;
|
||||
ByteBuffer tempkey;
|
||||
int tag = 1;
|
||||
SrtpSuite suite = processCryptoAttribute(attr, tempkey, &tag);
|
||||
if (srtpSuiteStrength(suite) > srtpSuiteStrength(ss))
|
||||
SrtpSuite suite = processCryptoAttribute(attr, tempkey);
|
||||
if (suite > ss)
|
||||
{
|
||||
ss = suite;
|
||||
mSrtpSuite = suite;
|
||||
mSrtpTag = tag;
|
||||
key = tempkey;
|
||||
}
|
||||
}
|
||||
@@ -299,30 +296,40 @@ MT::PStream AudioProvider::activeStream()
|
||||
return mActiveStream;
|
||||
}
|
||||
|
||||
std::string AudioProvider::createCryptoAttribute(SrtpSuite suite, int tag)
|
||||
std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
|
||||
{
|
||||
if (!mActiveStream)
|
||||
return "";
|
||||
|
||||
// Use tag 1 - it is ok, as we use only single crypto attribute
|
||||
int srtpTag = 1;
|
||||
|
||||
// Print key to base64 string
|
||||
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
||||
if (!keyBuffer)
|
||||
return "";
|
||||
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
||||
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;
|
||||
char suite[64], keyChunk[256];
|
||||
int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk);
|
||||
if (components != 3)
|
||||
return SRTP_NONE;
|
||||
if (tag)
|
||||
*tag = srtpTag;
|
||||
|
||||
const char* delimiter = strchr(keyChunk, '|');
|
||||
resip::Data keyText;
|
||||
@@ -334,7 +341,15 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu
|
||||
resip::Data rawkey = keyText.base64decode();
|
||||
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
||||
|
||||
return toSrtpSuite(suite);
|
||||
// Open srtp
|
||||
SrtpSuite result = SRTP_NONE;
|
||||
if (strcmp(suite, SRTP_SUITE_NAME_1) == 0)
|
||||
result = SRTP_AES_128_AUTH_80;
|
||||
else
|
||||
if (strcmp(suite, SRTP_SUITE_NAME_2) == 0)
|
||||
result = SRTP_AES_256_AUTH_80;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -12,7 +12,9 @@
|
||||
#include "../media/MT_Box.h"
|
||||
#include "../media/MT_Stream.h"
|
||||
#include "../media/MT_Codec.h"
|
||||
#include "../audio/Audio_Interface.h"
|
||||
|
||||
#include "rutil/ThreadIf.hxx"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
@@ -35,10 +37,10 @@ public:
|
||||
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
||||
|
||||
// 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
|
||||
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
|
||||
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
|
||||
|
||||
// Updates SDP offer
|
||||
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
||||
@@ -74,7 +76,6 @@ public:
|
||||
void setupMirror(bool enable);
|
||||
|
||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag = nullptr);
|
||||
|
||||
protected:
|
||||
// SDP's stream name
|
||||
@@ -93,7 +94,6 @@ protected:
|
||||
|
||||
unsigned mState;
|
||||
SrtpSuite mSrtpSuite;
|
||||
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
|
||||
struct RemoteCodec
|
||||
{
|
||||
RemoteCodec(MT::Codec::Factory* factory, int payloadType)
|
||||
@@ -110,7 +110,8 @@ protected:
|
||||
MT::Stream::MediaObserver* mMediaObserver = 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);
|
||||
|
||||
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "resip/stack/SdpContents.hxx"
|
||||
#include "rutil/SharedPtr.hxx"
|
||||
|
||||
#include "../helper/HL_InternetAddress.h"
|
||||
#include "../helper/HL_NetworkSocket.h"
|
||||
@@ -45,10 +46,10 @@ public:
|
||||
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
||||
|
||||
// Processes incoming data
|
||||
virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
||||
virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
||||
virtual void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
||||
|
||||
// Updates SDP offer
|
||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
||||
|
||||
@@ -37,67 +37,13 @@
|
||||
# include "resip/stack/ssl/WinSecurity.hxx"
|
||||
#endif
|
||||
|
||||
#define LOG_SUBSYSTEM "engine"
|
||||
#define LOG_SUBSYSTEM "[Engine]"
|
||||
#define LOCK Lock l(mGuard)
|
||||
#define CAST2RESIPSESSION(x) (x.isValid() ? (x->getAppDialogSet().isValid() ? dynamic_cast<ResipSession*>(x->getAppDialogSet().get()) : NULL) : NULL)
|
||||
|
||||
typedef resip::SdpContents::Session::Medium Medium;
|
||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||
|
||||
class TransportLogger: public resip::Transport::SipMessageLoggingHandler
|
||||
{
|
||||
public:
|
||||
void outboundMessage(const resip::Tuple &source, const resip::Tuple &destination, const resip::SipMessage &msg) override
|
||||
{
|
||||
std::ostringstream dest_buffer; dest_buffer << destination;
|
||||
std::ostringstream msg_buffer; msg_buffer << msg;
|
||||
std::string msg_text = msg_buffer.str();
|
||||
#if defined(TARGET_ANDROID)
|
||||
if (msg_text.size() > 512)
|
||||
{
|
||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << " :");
|
||||
msg_text = strx::prefixLines(msg_text, "<---");
|
||||
|
||||
auto lines = strx::split(msg_text);
|
||||
for (const auto& l: lines)
|
||||
ICELogDebug(<< l);
|
||||
}
|
||||
else
|
||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
||||
#else
|
||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Note: retransmissions store already encoded messages, so callback doesn't send SipMessage it sends
|
||||
// the encoded version of the SipMessage instead. If you need a SipMessage you will need to
|
||||
// re-parse back into a SipMessage in the callback handler.
|
||||
void outboundRetransmit(const resip::Tuple &source, const resip::Tuple &destination, const resip::SendData &data) override
|
||||
{}
|
||||
|
||||
void inboundMessage(const resip::Tuple& source, const resip::Tuple& destination, const resip::SipMessage &msg) override
|
||||
{
|
||||
std::ostringstream source_buffer; source_buffer << source;
|
||||
std::ostringstream msg_buffer; msg_buffer << msg;
|
||||
std::string msg_text = msg_buffer.str();
|
||||
#if defined(TARGET_ANDROID)
|
||||
if (msg_text.size() > 512)
|
||||
{
|
||||
ICELogDebug(<< "Received from " << source_buffer.str() << " :");
|
||||
msg_text = strx::prefixLines(msg_text, "--->");
|
||||
auto lines = strx::split(msg_text);
|
||||
for (const auto& l: lines)
|
||||
ICELogDebug(<< l);
|
||||
}
|
||||
else
|
||||
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
||||
#else
|
||||
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_buffer.str(), "--->"));
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//-------------- UserAgent -----------------------
|
||||
UserAgent::UserAgent()
|
||||
{
|
||||
@@ -108,7 +54,7 @@ UserAgent::UserAgent()
|
||||
mConfig[CONFIG_RTCP_ATTR] = true;
|
||||
|
||||
// Create master profile
|
||||
mProfile = std::make_shared<resip::MasterProfile>();
|
||||
mProfile = resip::SharedPtr<resip::MasterProfile>(new resip::MasterProfile());
|
||||
mProfile->clearSupportedMethods();
|
||||
mProfile->addSupportedMethod(resip::INVITE);
|
||||
mProfile->addSupportedMethod(resip::BYE);
|
||||
@@ -147,7 +93,7 @@ void UserAgent::start()
|
||||
}
|
||||
|
||||
// Initialize resip loggег
|
||||
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Info, "Client", *this);
|
||||
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Debug, "Client", *this);
|
||||
|
||||
// Build list of nameservers if specified
|
||||
resip::DnsStub::NameserverList nslist;
|
||||
@@ -159,7 +105,7 @@ void UserAgent::start()
|
||||
|
||||
while (std::getline(ss, line))
|
||||
{
|
||||
line = strx::trim(line);
|
||||
line = StringHelper::trim(line);
|
||||
ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized
|
||||
switch (addr.family())
|
||||
{
|
||||
@@ -174,7 +120,7 @@ void UserAgent::start()
|
||||
#if defined(TARGET_WIN)
|
||||
s = new resip::WinSecurity();
|
||||
#elif defined(TARGET_OSX)
|
||||
s = new resip::Security();
|
||||
s = new resip::MacSecurity();
|
||||
#elif defined(TARGET_LINUX)
|
||||
s = new resip::Security("/etc/ssl/certs");
|
||||
#elif defined(TARGET_ANDROID)
|
||||
@@ -196,14 +142,12 @@ void UserAgent::start()
|
||||
}
|
||||
}
|
||||
|
||||
mStack->setTransportSipMessageLoggingHandler(std::make_shared<TransportLogger>());
|
||||
|
||||
// Add transports
|
||||
mTransportList.clear();
|
||||
resip::InternalTransport* t;
|
||||
|
||||
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
|
||||
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { /*t->setTransportLogger(this);*/ mTransportList.push_back(t);}
|
||||
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
||||
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
||||
|
||||
switch (mConfig[CONFIG_TRANSPORT].asInt())
|
||||
{
|
||||
@@ -318,16 +262,16 @@ void UserAgent::shutdown()
|
||||
|
||||
{
|
||||
LOCK;
|
||||
for (auto& observerIter: mClientObserverMap)
|
||||
for (auto observerIter: mClientObserverMap)
|
||||
observerIter.second->stop();
|
||||
|
||||
for (auto& observerIter: mServerObserverMap)
|
||||
for (auto observerIter: mServerObserverMap)
|
||||
observerIter.second->stop();
|
||||
|
||||
for (auto& sessionIter: mSessionMap)
|
||||
for (auto sessionIter: mSessionMap)
|
||||
sessionIter.second->stop();
|
||||
|
||||
for (auto& accountIter: mAccountSet)
|
||||
for (auto accountIter: mAccountSet)
|
||||
accountIter->stop();
|
||||
}
|
||||
}
|
||||
@@ -335,24 +279,24 @@ void UserAgent::shutdown()
|
||||
bool UserAgent::active()
|
||||
{
|
||||
LOCK;
|
||||
return mStack != nullptr;
|
||||
return mStack != NULL;
|
||||
}
|
||||
|
||||
void UserAgent::refresh()
|
||||
{
|
||||
LOCK;
|
||||
|
||||
for (auto& acc: mAccountSet)
|
||||
for (auto acc: mAccountSet)
|
||||
acc->refresh();
|
||||
|
||||
for (auto& observer: mClientObserverMap)
|
||||
for (auto observer: mClientObserverMap)
|
||||
observer.second->refresh();
|
||||
}
|
||||
|
||||
void UserAgent::onDumCanBeDeleted()
|
||||
{
|
||||
delete mDum; mDum = nullptr;
|
||||
delete mStack; mStack = nullptr;
|
||||
delete mDum; mDum = NULL;
|
||||
delete mStack; mStack = NULL;
|
||||
|
||||
mClientObserverMap.clear();
|
||||
mServerObserverMap.clear();
|
||||
@@ -371,10 +315,7 @@ void UserAgent::stop()
|
||||
mTransportList.clear();
|
||||
|
||||
// Dump statistics here
|
||||
ICELogInfo(<< "Remaining "
|
||||
<< Session::InstanceCounter.load() << " session(s), "
|
||||
<< ResipSession::InstanceCounter.load() << " resip DialogSet(s), "
|
||||
<< resip::ClientRegistration::InstanceCounter.load() << " ClientRegistration(s)");
|
||||
ICELogInfo(<< "Remaining " << Session::InstanceCounter.value() << " session(s), " << ResipSession::InstanceCounter.value() << " resip DialogSet(s), " << resip::ClientRegistration::InstanceCounter.value() << " ClientRegistration(s)");
|
||||
|
||||
mDum->shutdown(this);
|
||||
onDumCanBeDeleted();
|
||||
@@ -438,14 +379,15 @@ void UserAgent::process()
|
||||
|
||||
// Find all sessions
|
||||
std::set<int> idSet;
|
||||
for (auto sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
||||
SessionMap::iterator sessionIter;
|
||||
for (sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
||||
idSet.insert(sessionIter->first);
|
||||
|
||||
// Now process session one by one checking if current is available yet
|
||||
std::set<int>::iterator resipIter;
|
||||
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
|
||||
{
|
||||
auto sessionIter = mSessionMap.find(*resipIter);
|
||||
SessionMap::iterator sessionIter = mSessionMap.find(*resipIter);
|
||||
if (sessionIter == mSessionMap.end())
|
||||
continue;
|
||||
|
||||
@@ -470,9 +412,7 @@ void UserAgent::process()
|
||||
// Send generated packet via provider's method to allow custom scheme of encryption
|
||||
ICELogDebug(<<"Sending ICE packet to " << buffer->remoteAddress().toStdString() << " with " << buffer->comment());
|
||||
|
||||
RtpPair<PDatagramSocket>& pair = buffer->remoteAddress().family() == AF_INET6 ? stream.socket6() : stream.socket4();
|
||||
PDatagramSocket s = iceComponentId == ICE_RTP_ID ? pair.mRtp : pair.mRtcp;
|
||||
if (s)
|
||||
PDatagramSocket s = iceComponentId == ICE_RTP_ID ? stream.socket4().mRtp : stream.socket4().mRtcp;
|
||||
stream.provider()->sendData(s, buffer->remoteAddress(), buffer->data(), buffer->size());
|
||||
break;
|
||||
}
|
||||
@@ -488,16 +428,9 @@ void UserAgent::process()
|
||||
void UserAgent::addRootCert(const ByteBuffer& data)
|
||||
{
|
||||
LOCK;
|
||||
if (!mStack)
|
||||
return;
|
||||
resip::Data b(data.data(), data.size());
|
||||
try {
|
||||
mStack->getSecurity()->addRootCertPEM(b);
|
||||
}
|
||||
catch(...) {
|
||||
// Ignore silently
|
||||
}
|
||||
}
|
||||
|
||||
PAccount UserAgent::createAccount(PVariantMap config)
|
||||
{
|
||||
@@ -531,7 +464,7 @@ PSession UserAgent::createSession(PAccount account)
|
||||
return session;
|
||||
}
|
||||
|
||||
std::string UserAgent::formatSipAddress(const std::string& sip)
|
||||
std::string UserAgent::formatSipAddress(std::string sip)
|
||||
{
|
||||
std::string result;
|
||||
if (sip.size())
|
||||
@@ -547,18 +480,17 @@ std::string UserAgent::formatSipAddress(const std::string& sip)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool UserAgent::isSipAddressValid(const std::string& sip)
|
||||
bool UserAgent::isSipAddressValid(std::string sip)
|
||||
{
|
||||
bool result = false;
|
||||
try
|
||||
{
|
||||
std::string s = sip;
|
||||
if (s.find('<') == std::string::npos)
|
||||
s = "<" + s;
|
||||
if (s.find('>') == std::string::npos)
|
||||
s += ">";
|
||||
if (sip.find('<') == std::string::npos)
|
||||
sip = "<" + sip;
|
||||
if (sip.find('>') == std::string::npos)
|
||||
sip += ">";
|
||||
|
||||
resip::Data d(formatSipAddress(s));
|
||||
resip::Data d(formatSipAddress(sip));
|
||||
resip::Uri uri(d);
|
||||
result = uri.isWellFormed();
|
||||
if (result)
|
||||
@@ -593,7 +525,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
|
||||
if (nameaddr.uri().port())
|
||||
{
|
||||
char porttext[32];
|
||||
std::snprintf(porttext, sizeof(porttext), ":%u", (unsigned)nameaddr.uri().port());
|
||||
sprintf(porttext, ":%u", (unsigned)nameaddr.uri().port());
|
||||
result.mDomain += porttext;
|
||||
}
|
||||
|
||||
@@ -608,7 +540,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool UserAgent::compareSipAddresses(const std::string& sip1, const std::string& sip2)
|
||||
bool UserAgent::compareSipAddresses(std::string sip1, std::string sip2)
|
||||
{
|
||||
if (sip1.empty() || sip2.empty())
|
||||
return false;
|
||||
@@ -653,7 +585,7 @@ void UserAgent::sendOffer(Session* session)
|
||||
if (session->mOriginVersion == 1)
|
||||
{
|
||||
// Construct INVITE session
|
||||
auto msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
||||
resip::SharedPtr<resip::SipMessage> msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
||||
|
||||
// Include user headers
|
||||
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++)
|
||||
@@ -723,19 +655,13 @@ void UserAgent::onFailure(resip::ClientRegistrationHandle h, const resip::SipMes
|
||||
|
||||
#pragma endregion
|
||||
|
||||
bool UserAgent::operator()(resip::Log::Level level,
|
||||
const resip::Subsystem& subsystem,
|
||||
const resip::Data& appName,
|
||||
const char* file,
|
||||
int line,
|
||||
const resip::Data& message,
|
||||
const resip::Data& messageWithHeaders,
|
||||
const resip::Data& instanceName)
|
||||
bool UserAgent::operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName, const char* file,
|
||||
int line, const resip::Data& message, const resip::Data& messageWithHeaders)
|
||||
{
|
||||
std::string filename = file;
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "File " << strx::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
||||
ss << "File " << StringHelper::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
||||
if (level <= resip::Log::Crit)
|
||||
ICELogCritical(<< ss.str())
|
||||
else
|
||||
@@ -807,10 +733,7 @@ void UserAgent::onEarlyMedia(resip::ClientInviteSessionHandle h, const resip::Si
|
||||
/// called when dialog enters the Early state - typically after getting 18x
|
||||
void UserAgent::onProvisional(resip::ClientInviteSessionHandle h, const resip::SipMessage& msg)
|
||||
{
|
||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
||||
if (!rs)
|
||||
return;
|
||||
PSession s = getUserSession(rs->mSessionId);
|
||||
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -826,10 +749,7 @@ void UserAgent::onProvisional(resip::ClientInviteSessionHandle h, const resip::S
|
||||
/// called when a dialog initiated as a UAC enters the connected state
|
||||
void UserAgent::onConnected(resip::ClientInviteSessionHandle h, const resip::SipMessage& msg)
|
||||
{
|
||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
||||
if (!rs)
|
||||
return;
|
||||
PSession s = getUserSession(rs->mSessionId);
|
||||
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -882,10 +802,7 @@ void UserAgent::onConnected(resip::InviteSessionHandle h, const resip::SipMessag
|
||||
|
||||
void UserAgent::onTerminated(resip::InviteSessionHandle h, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related)
|
||||
{
|
||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
||||
if (!rs)
|
||||
return;
|
||||
PSession s = getUserSession(rs->mSessionId);
|
||||
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -931,8 +848,6 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
||||
if (!resipSession)
|
||||
return;
|
||||
Session* s = resipSession->session();
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
bool iceAvailable = true;
|
||||
|
||||
@@ -1033,7 +948,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
||||
|
||||
// See if remote stream has "rtcp" or "rtcp-mux" attributes
|
||||
if (remoteStream.exists("rtcp"))
|
||||
addr2.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
|
||||
addr2.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
|
||||
else
|
||||
if (remoteStream.exists("rtcp-mux"))
|
||||
addr2.setPort( remoteDefaultPort );
|
||||
@@ -1075,15 +990,14 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
||||
s->mIceStack->clear();
|
||||
}
|
||||
|
||||
uint64_t version = sdp.session().origin().getVersion();
|
||||
UInt64 version = sdp.session().origin().getVersion();
|
||||
s->mRemoteOriginVersion = version;
|
||||
}
|
||||
|
||||
/// Called when an SDP offer is received - must send an answer soon after this
|
||||
void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& msg, const resip::SdpContents& sdp)
|
||||
{
|
||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
||||
PSession s = rs ? getUserSession(rs->mSessionId) : PSession();
|
||||
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||
if (!s)
|
||||
{
|
||||
h->reject(488);
|
||||
@@ -1101,13 +1015,12 @@ void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& m
|
||||
if (sdp.session().exists("ice-ufrag"))
|
||||
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
|
||||
|
||||
//ice::Stack& ice = *s->mIceStack;
|
||||
ice::Stack& ice = *s->mIceStack;
|
||||
|
||||
uint64_t version = sdp.session().origin().getVersion();
|
||||
UInt64 version = sdp.session().origin().getVersion();
|
||||
std::string remoteIp = sdp.session().connection().getAddress().c_str();
|
||||
// Default to 200: a retransmitted offer (same origin version) keeps the session.
|
||||
int code = 200;
|
||||
if ((uint64_t)-1 == s->mRemoteOriginVersion)
|
||||
int code;
|
||||
if ((UInt64)-1 == s->mRemoteOriginVersion)
|
||||
{
|
||||
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
|
||||
}
|
||||
@@ -1314,8 +1227,6 @@ void UserAgent::onPresenceUpdate(PClientObserver observer, const std::string& pe
|
||||
void UserAgent::onNewSubscription(resip::ServerSubscriptionHandle h, const resip::SipMessage& sub)
|
||||
{
|
||||
ResipSession* s = CAST2RESIPSESSION(h);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
// Get the event package name
|
||||
const char* event = sub.header(resip::h_Event).value().c_str();
|
||||
@@ -1610,7 +1521,7 @@ VariantMap& UserAgent::config()
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
/*static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
||||
static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
||||
{
|
||||
resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
|
||||
while (endLine != resip::Data::npos)
|
||||
@@ -1622,9 +1533,9 @@ VariantMap& UserAgent::config()
|
||||
}
|
||||
if (0 == startLine)
|
||||
output.push_back(content);
|
||||
}*/
|
||||
}
|
||||
|
||||
/*static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
||||
static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
||||
{
|
||||
resip::Data::size_type p = input.find(":");
|
||||
if (p == resip::Data::npos)
|
||||
@@ -1645,7 +1556,7 @@ VariantMap& UserAgent::config()
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
}
|
||||
|
||||
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
|
||||
{
|
||||
@@ -1653,17 +1564,17 @@ void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, con
|
||||
ice::NetworkAddress address(*addr, addrlen);
|
||||
std::string addressText = address.toStdString();
|
||||
|
||||
/*switch (flow)
|
||||
switch (flow)
|
||||
{
|
||||
case resip::InternalTransport::TransportLogger::Flow_Received:
|
||||
ICELogDebug(<< "Received from " << addressText << ":" << "\n"
|
||||
<< strx::prefixLines(d, "--->"));
|
||||
<< StringHelper::prefixLines(d, "--->"));
|
||||
break;
|
||||
|
||||
case resip::InternalTransport::TransportLogger::Flow_Sent:
|
||||
ICELogDebug(<< "Sent to " << addressText << "\n" << strx::prefixLines(d, "<---"));
|
||||
ICELogDebug(<< "Sent to " << addressText << "\n" << StringHelper::prefixLines(d, "<---"));
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
PSession UserAgent::getUserSession(int sessionId)
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
#include "../ice/ICETime.h"
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "EP_Session.h"
|
||||
#include "EP_Observer.h"
|
||||
#include "EP_DataProvider.h"
|
||||
@@ -108,7 +108,7 @@ enum
|
||||
CONFIG_ACCOUNT, // VariantMap with account configuration
|
||||
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
||||
CONFIG_OWN_DNS, // Use predefined DNS servers
|
||||
CONFIG_REGID // reg-id value from RFC5626,
|
||||
CONFIG_REGID // reg-id value from RFC5626
|
||||
};
|
||||
|
||||
// Conntype parameter for OnSessionEstablished event
|
||||
@@ -152,8 +152,8 @@ class UserAgent: public resip::ClientRegistrationHandler,
|
||||
public resip::ServerSubscriptionHandler,
|
||||
public resip::ClientPagerMessageHandler,
|
||||
public resip::ServerPagerMessageHandler,
|
||||
public resip::ClientPublicationHandler
|
||||
//public resip::InternalTransport::TransportLogger
|
||||
public resip::ClientPublicationHandler,
|
||||
public resip::InternalTransport::TransportLogger
|
||||
{
|
||||
friend class Account;
|
||||
friend class Session;
|
||||
@@ -162,9 +162,9 @@ class UserAgent: public resip::ClientRegistrationHandler,
|
||||
friend class WatcherQueue;
|
||||
public:
|
||||
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
||||
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
|
||||
static std::string formatSipAddress(const std::string& sip);
|
||||
static bool isSipAddressValid(const std::string& sip);
|
||||
static bool compareSipAddresses(std::string sip1, std::string sip2);
|
||||
static std::string formatSipAddress(std::string sip);
|
||||
static bool isSipAddressValid(std::string sip);
|
||||
struct SipAddress
|
||||
{
|
||||
bool mValid;
|
||||
@@ -383,8 +383,7 @@ public:
|
||||
const char* file,
|
||||
int line,
|
||||
const resip::Data& message,
|
||||
const resip::Data& messageWithHeaders,
|
||||
const resip::Data& instanceName) override;
|
||||
const resip::Data& messageWithHeaders) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DnsResultSink implementation
|
||||
@@ -398,7 +397,7 @@ public:
|
||||
#pragma endregion
|
||||
|
||||
#pragma region TransportLogger implementation
|
||||
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
|
||||
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ClientPublicationHandler
|
||||
@@ -448,7 +447,7 @@ protected:
|
||||
Mutex mGuard;
|
||||
|
||||
// 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
|
||||
resip::SipStack* mStack;
|
||||
|
||||
@@ -8,7 +8,7 @@ WatcherQueue::WatcherQueue(UserAgent& ua)
|
||||
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);
|
||||
|
||||
@@ -16,7 +16,8 @@ int WatcherQueue::add(const std::string& peer, const std::string& package, void*
|
||||
for (unsigned i=0; i<mItemList.size(); 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;
|
||||
}
|
||||
|
||||
@@ -43,9 +44,10 @@ void WatcherQueue::remove(int id)
|
||||
ice::Lock l(mGuard);
|
||||
|
||||
// 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)
|
||||
item.mState = Item::State_ScheduledToDelete;
|
||||
@@ -60,9 +62,10 @@ void WatcherQueue::refresh(int id)
|
||||
ice::Lock l(mGuard);
|
||||
|
||||
// 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)
|
||||
item.mState = Item::State_ScheduledToRefresh;
|
||||
@@ -82,7 +85,7 @@ void WatcherQueue::process()
|
||||
if (i == mItemList.end())
|
||||
return;
|
||||
|
||||
std::shared_ptr<resip::SipMessage> msg;
|
||||
resip::SharedPtr<resip::SipMessage> msg;
|
||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||
|
||||
switch (i->mState)
|
||||
@@ -139,9 +142,9 @@ void WatcherQueue::onTerminated(int id, int code)
|
||||
{
|
||||
if (i->mSession)
|
||||
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
|
||||
mItemList.erase(i);
|
||||
if (i->mId == mActiveId)
|
||||
mActiveId = 0;
|
||||
mItemList.erase(i);
|
||||
}
|
||||
process();
|
||||
}
|
||||
|
||||
@@ -28,14 +28,15 @@ public:
|
||||
};
|
||||
|
||||
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
||||
ResipSession* mSession = nullptr;
|
||||
State mState = State::State_None;
|
||||
ResipSession* mSession;
|
||||
State mState;
|
||||
std::string mTarget; // Target's address
|
||||
std::string mPackage; // Event package
|
||||
void* mTag = nullptr; // User tag
|
||||
int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only)
|
||||
void* mTag; // User tag
|
||||
int mId;
|
||||
|
||||
Item()
|
||||
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
|
||||
{}
|
||||
|
||||
bool scheduled()
|
||||
@@ -46,7 +47,7 @@ public:
|
||||
WatcherQueue(UserAgent& agent);
|
||||
~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 refresh(int id);
|
||||
void clear();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -7,16 +7,20 @@
|
||||
#include "EP_Engine.h"
|
||||
#include "EP_AudioProvider.h"
|
||||
#include "../media/MT_Stream.h"
|
||||
#include "../media/MT_AudioStream.h"
|
||||
#include "../media/MT_Dtmf.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
#include "../helper/HL_Exception.h"
|
||||
#include "../helper/HL_StreamState.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "../helper/HL_String.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "engine"
|
||||
#define LOG_SUBSYSTEM "[Engine]"
|
||||
|
||||
typedef resip::SdpContents::Session::Medium Medium;
|
||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||
|
||||
#define IS_MULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
||||
#define DOMULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
||||
|
||||
|
||||
//------------ ResipSessionAppDialog ------------
|
||||
@@ -33,13 +37,13 @@ ResipSessionAppDialog::~ResipSessionAppDialog()
|
||||
|
||||
#pragma region ResipSession
|
||||
|
||||
std::atomic_int ResipSession::InstanceCounter;
|
||||
resip::AtomicCounter ResipSession::InstanceCounter;
|
||||
|
||||
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++;
|
||||
mTag = nullptr;
|
||||
ResipSession::InstanceCounter.increment();
|
||||
mTag = NULL;
|
||||
mTerminated = false;
|
||||
mOnWatchingStartSent = false;
|
||||
mSessionId = Session::generateId();
|
||||
@@ -58,7 +62,7 @@ ResipSession::~ResipSession()
|
||||
{
|
||||
}
|
||||
|
||||
ResipSession::InstanceCounter--;
|
||||
ResipSession::InstanceCounter.decrement();
|
||||
}
|
||||
|
||||
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
|
||||
@@ -163,12 +167,12 @@ int ResipSession::sessionId()
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
void ResipSession::setUASProfile(const std::shared_ptr<resip::UserProfile>& profile)
|
||||
void ResipSession::setUASProfile(std::shared_ptr<resip::UserProfile> profile)
|
||||
{
|
||||
mUASProfile = profile;
|
||||
}
|
||||
|
||||
std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
|
||||
resip::SharedPtr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
|
||||
{
|
||||
assert(mUserAgent != nullptr);
|
||||
|
||||
@@ -180,7 +184,7 @@ std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const res
|
||||
else
|
||||
return mUserAgent->mProfile;
|
||||
}
|
||||
return std::shared_ptr<resip::UserProfile>();
|
||||
return resip::SharedPtr<resip::UserProfile>();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -258,11 +262,11 @@ void Session::Stream::setRtcpMuxAttr(bool value)
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Session
|
||||
std::atomic_int Session::InstanceCounter;
|
||||
resip::AtomicCounter Session::InstanceCounter;
|
||||
|
||||
Session::Session(PAccount account)
|
||||
{
|
||||
InstanceCounter++;
|
||||
InstanceCounter.increment();
|
||||
mAccount = account;
|
||||
mSessionId = Session::generateId();
|
||||
mTag = NULL;
|
||||
@@ -274,7 +278,7 @@ Session::Session(PAccount account)
|
||||
mRole = Acceptor;
|
||||
mGatheredCandidates = false;
|
||||
mTerminated = false;
|
||||
mRemoteOriginVersion = (uint64_t)-1;
|
||||
mRemoteOriginVersion = (UInt64)-1;
|
||||
mResipSession = NULL;
|
||||
mRefCount = 1;
|
||||
mOfferAnswerCounter = 0;
|
||||
@@ -292,7 +296,7 @@ Session::~Session()
|
||||
}
|
||||
catch(...)
|
||||
{}
|
||||
InstanceCounter--;
|
||||
InstanceCounter.decrement();
|
||||
}
|
||||
|
||||
void Session::start(const std::string& peer)
|
||||
@@ -346,10 +350,6 @@ void Session::stop()
|
||||
// Free socket
|
||||
SocketHeap::instance().freeSocketPair( dataStream.socket4() );
|
||||
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>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,19 +449,29 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||
MT::Statistics stat;
|
||||
|
||||
// Iterate all session providers
|
||||
Stream* media = nullptr;
|
||||
for (Stream& stream: mStreamList)
|
||||
stat.reset();
|
||||
Stream* media = NULL;
|
||||
for (unsigned streamIndex = 0; streamIndex < mStreamList.size(); streamIndex++)
|
||||
{
|
||||
Stream& stream = mStreamList[streamIndex];
|
||||
if (stream.provider())
|
||||
{
|
||||
if (!stream.provider())
|
||||
continue;
|
||||
|
||||
media = &stream;
|
||||
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;
|
||||
|
||||
stat += s;
|
||||
}
|
||||
}
|
||||
|
||||
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
|
||||
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
|
||||
@@ -472,14 +482,14 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
||||
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
||||
if (stat.mFirstRtpTime)
|
||||
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - *(stat.mFirstRtpTime)).count());
|
||||
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - *(stat.mFirstRtpTime)).count());
|
||||
else
|
||||
info[SessionInfo_Duration] = 0;
|
||||
|
||||
if (stat.mReceivedRtp)
|
||||
info[SessionInfo_PacketLoss] = static_cast<int>((stat.mPacketLoss * 1000) / stat.mReceivedRtp);
|
||||
|
||||
if (media && mIceStack)
|
||||
if (media)
|
||||
info[SessionInfo_AudioPeer] = mIceStack->remoteAddress(media->iceInfo().mStreamId, media->iceInfo().mComponentId.mRtp).toStdString();
|
||||
|
||||
info[SessionInfo_Jitter] = stat.mJitter;
|
||||
@@ -487,10 +497,8 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||
info[SessionInfo_Rtt] = static_cast<float>(stat.mRttDelay * 1000);
|
||||
#if defined(USE_AMR_CODEC)
|
||||
info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter;
|
||||
info[SessionInfo_CngCounter] = stat.mCng;
|
||||
#endif
|
||||
// Variant stores VTYPE_INT here; keep the 32 bits (consumers read it back with asInt()).
|
||||
info[SessionInfo_SSRC] = static_cast<int>(stat.mSsrc);
|
||||
info[SessionInfo_SSRC] = stat.mSsrc;
|
||||
info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString();
|
||||
}
|
||||
|
||||
@@ -541,11 +549,12 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
|
||||
{
|
||||
// Try to process incoming data by ICE stack
|
||||
int component = -1, stream = -1;
|
||||
bool processed;
|
||||
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
|
||||
{
|
||||
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
||||
buffer.setRemoteAddress(src);
|
||||
/*bool processed = */mIceStack->processIncomingData(stream, component, buffer);
|
||||
processed = mIceStack->processIncomingData(stream, component, buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -746,12 +755,9 @@ PDataProvider Session::findProviderByPort(int family, unsigned short port)
|
||||
{
|
||||
Stream& s = mStreamList[i];
|
||||
|
||||
// Sockets may not be allocated yet (stream created from SDP, sockets follow later)
|
||||
if (family == AF_INET && s.socket4().mRtp && s.socket4().mRtcp &&
|
||||
(s.socket4().mRtp->localport() == port || s.socket4().mRtcp->localport() == port))
|
||||
if ((s.socket4().mRtp->localport() == port || s.socket4().mRtcp->localport() == port) && family == AF_INET)
|
||||
return s.provider();
|
||||
if (family == AF_INET6 && s.socket6().mRtp && s.socket6().mRtcp &&
|
||||
(s.socket6().mRtp->localport() == port || s.socket6().mRtcp->localport() == port))
|
||||
if ((s.socket6().mRtp->localport() == port || s.socket6().mRtcp->localport() == port) && family == AF_INET6)
|
||||
return s.provider();
|
||||
}
|
||||
|
||||
@@ -786,8 +792,8 @@ void Session::addProvider(PDataProvider provider)
|
||||
s.setProvider( provider );
|
||||
|
||||
// Allocate socket for provider
|
||||
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()) );
|
||||
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()) );
|
||||
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()) );
|
||||
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()) );
|
||||
s.provider()->setSocket(s.socket4(), s.socket6());
|
||||
|
||||
// Create ICE stream/component
|
||||
@@ -823,10 +829,10 @@ int Session::sessionId()
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
std::atomic_int Session::IdGenerator;
|
||||
resip::AtomicCounter Session::IdGenerator;
|
||||
int Session::generateId()
|
||||
{
|
||||
return ++IdGenerator;
|
||||
return (int)IdGenerator.increment();
|
||||
}
|
||||
|
||||
std::string Session::remoteAddress() const
|
||||
@@ -890,8 +896,8 @@ void Session::refreshMediaPath()
|
||||
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
||||
|
||||
// Bring new socket to provider and stream
|
||||
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ),
|
||||
s6 = SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX());
|
||||
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX() ),
|
||||
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX());
|
||||
|
||||
p->setSocket(s4, s6);
|
||||
s.setSocket4(s4);
|
||||
@@ -908,7 +914,7 @@ void Session::refreshMediaPath()
|
||||
}
|
||||
|
||||
// Received offer with new SDP
|
||||
int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||
int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
|
||||
{
|
||||
bool iceRestart = false;
|
||||
@@ -953,7 +959,7 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
||||
targetAddr.mRtcp.setPort( remoteStream.port() );
|
||||
else
|
||||
if (stream.rtcpAttr())
|
||||
targetAddr.mRtcp.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
|
||||
targetAddr.mRtcp.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
|
||||
else
|
||||
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
||||
|
||||
@@ -970,8 +976,8 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
|
||||
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
|
||||
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()));
|
||||
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
||||
@@ -28,14 +28,15 @@
|
||||
#include "rutil/Logger.hxx"
|
||||
#include "rutil/Random.hxx"
|
||||
#include "rutil/WinLeakCheck.hxx"
|
||||
#include "rutil/AtomicCounter.hxx"
|
||||
|
||||
#include "../ice/ICEBox.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
#include <time.h>
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "EP_Session.h"
|
||||
#include "EP_Account.h"
|
||||
#include "EP_DataProvider.h"
|
||||
#include "EP_AudioProvider.h"
|
||||
@@ -72,7 +73,6 @@ enum SessionInfo
|
||||
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
|
||||
SessionInfo_RemotePeer,
|
||||
SessionInfo_SSRC,
|
||||
SessionInfo_CngCounter // For AMR codecs only
|
||||
};
|
||||
|
||||
|
||||
@@ -214,8 +214,7 @@ public:
|
||||
void refreshMediaPath();
|
||||
|
||||
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
||||
// There are passing string objects by value; this is correct; this values will modified on the stack.
|
||||
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
|
||||
int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
||||
|
||||
// Session ID
|
||||
@@ -225,7 +224,7 @@ public:
|
||||
std::vector<Stream> mStreamList;
|
||||
|
||||
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
||||
std::shared_ptr<ice::Stack> mIceStack;
|
||||
resip::SharedPtr<ice::Stack> mIceStack;
|
||||
|
||||
// Pointer to owner user agent instance
|
||||
UserAgent* mUserAgent;
|
||||
@@ -238,7 +237,7 @@ public:
|
||||
|
||||
// SDP's origin version for sending
|
||||
int mOriginVersion;
|
||||
uint64_t mRemoteOriginVersion;
|
||||
UInt64 mRemoteOriginVersion;
|
||||
|
||||
// SDP's session version
|
||||
int mSessionVersion;
|
||||
@@ -319,8 +318,8 @@ public:
|
||||
void enqueueOffer();
|
||||
void processQueuedOffer();
|
||||
static int generateId();
|
||||
static std::atomic_int IdGenerator;
|
||||
static std::atomic_int InstanceCounter;
|
||||
static resip::AtomicCounter IdGenerator;
|
||||
static resip::AtomicCounter InstanceCounter;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Session> PSession;
|
||||
@@ -355,13 +354,13 @@ public:
|
||||
Type_Call,
|
||||
Type_Auto
|
||||
};
|
||||
static std::atomic_int InstanceCounter;
|
||||
static resip::AtomicCounter InstanceCounter;
|
||||
|
||||
|
||||
ResipSession(resip::DialogUsageManager& dum);
|
||||
virtual ~ResipSession();
|
||||
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
||||
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||
virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||
|
||||
void setType(Type type);
|
||||
Type type();
|
||||
@@ -385,7 +384,7 @@ public:
|
||||
|
||||
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
||||
|
||||
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
|
||||
void setUASProfile(std::shared_ptr<resip::UserProfile> profile);
|
||||
|
||||
protected:
|
||||
bool mTerminated;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __TOOLKIT_CONFIG_H
|
||||
#define __TOOLKIT_CONFIG_H
|
||||
|
||||
#define USE_SPEEX_AEC
|
||||
|
||||
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
|
||||
//#define USE_WEBRTC_AEC
|
||||
#define USER
|
||||
|
||||
|
||||
#define AUDIO_SAMPLE_WIDTH 16
|
||||
#define AUDIO_CHANNELS 1
|
||||
|
||||
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
|
||||
#define AUDIO_SAMPLERATE 48000
|
||||
#define AUDIO_MIC_BUFFER_COUNT 16
|
||||
#define AUDIO_MIC_BUFFER_LENGTH 10
|
||||
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_SPK_BUFFER_COUNT 16
|
||||
#define AUDIO_SPK_BUFFER_LENGTH 10
|
||||
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_MIX_CHANNEL_COUNT 16
|
||||
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
|
||||
|
||||
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
|
||||
#define AUDIO_RESAMPLER_QUALITY 1
|
||||
#define AEC_FRAME_TIME 10
|
||||
#define AEC_TAIL_TIME 160
|
||||
|
||||
|
||||
// Defined these two lines to get dumping of audio input/output
|
||||
//#define AUDIO_DUMPINPUT
|
||||
//#define AUDIO_DUMPOUTPUT
|
||||
|
||||
|
||||
#define UA_REGISTRATION_TIME 3600
|
||||
#define UA_MEDIA_PORT_START 20000
|
||||
#define UA_MEDIA_PORT_FINISH 30000
|
||||
#define UA_MAX_UDP_PACKET_SIZE 576
|
||||
#define UA_PUBLICATION_ID "314"
|
||||
|
||||
#define MT_SAMPLERATE AUDIO_SAMPLERATE
|
||||
|
||||
#define MT_MAXAUDIOFRAME 1440
|
||||
#define MT_MAXRTPPACKET 1500
|
||||
#define MT_DTMF_END_PACKETS 3
|
||||
|
||||
// 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
|
||||
@@ -1,20 +1,17 @@
|
||||
cmake_minimum_required (VERSION 3.15)
|
||||
project (helper_lib)
|
||||
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set (USE_NULL_UUID OFF CACHE BOOL "When enabled linking to libuuid is avoided")
|
||||
|
||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
||||
|
||||
add_library(helper_lib ${HELPER_LIB_SOURCES})
|
||||
set_property(TARGET helper_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
# Private include directories
|
||||
target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .)
|
||||
target_include_directories(helper_lib PRIVATE ../../libs/ ../../engine ../)
|
||||
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
||||
if (TARGET_LINUX)
|
||||
target_link_libraries (helper_lib PUBLIC uuid)
|
||||
endif()
|
||||
|
||||
@@ -312,7 +312,7 @@ namespace Calc
|
||||
{
|
||||
// Dot is here - is it float
|
||||
bool isFloat = false;
|
||||
strx::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
|
||||
StringHelper::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
|
||||
if (isFloat)
|
||||
mCurrentLexem.mType = LexemType::Float;
|
||||
else
|
||||
@@ -488,7 +488,7 @@ namespace Calc
|
||||
|
||||
case LexemType::Hex:
|
||||
result->mType = Ast::Type::Number;
|
||||
result->mValue = strx::fromHex2Int(l.mValue);
|
||||
result->mValue = StringHelper::fromHex2Int(l.mValue);
|
||||
break;
|
||||
|
||||
case LexemType::Float:
|
||||
|
||||
@@ -20,10 +20,10 @@ bool CsvReader::readLine(std::vector<std::string>& cells)
|
||||
std::string line;
|
||||
if (!std::getline(mInputStream, line))
|
||||
return false;
|
||||
strx::trim(line);
|
||||
StringHelper::trim(line);
|
||||
if (line.empty())
|
||||
return false;
|
||||
|
||||
strx::split(line, cells, ",;");
|
||||
StringHelper::split(line, cells, ",;");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
enum
|
||||
{
|
||||
ERR_MEDIA_SOCKET_FAILED = 1, // Failed to create media socket
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
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)
|
||||
@@ -78,8 +78,8 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
int mCode = 0, mSubcode = 0;
|
||||
char mMessage[256] = {0};
|
||||
int mCode, mSubcode;
|
||||
char mMessage[256];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "HL_File.h"
|
||||
#include <fstream>
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
# include <unistd.h>
|
||||
# include <sys/statvfs.h>
|
||||
# include <memory.h>
|
||||
@@ -32,24 +32,23 @@ void FileHelper::remove(const char* s)
|
||||
::remove(s);
|
||||
}
|
||||
|
||||
// std::string FileHelper::gettempname()
|
||||
// {
|
||||
// #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
// int code = mkstemp(template_filename);
|
||||
std::string FileHelper::gettempname()
|
||||
{
|
||||
#if defined(TARGET_LINUX)
|
||||
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
mkstemp(template_filename);
|
||||
return template_filename;
|
||||
#elif defined(TARGET_WIN)
|
||||
char buffer[L_tmpnam];
|
||||
tmpnam(buffer);
|
||||
|
||||
// return template_filename;
|
||||
// #elif defined(TARGET_WIN)
|
||||
// char buffer[L_tmpnam];
|
||||
// tmpnam(buffer);
|
||||
|
||||
// return buffer;
|
||||
// #elif defined(TARGET_OSX)
|
||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
// mktemp(template_filename);
|
||||
// return template_filename;
|
||||
// #endif
|
||||
// }
|
||||
return buffer;
|
||||
#elif defined(TARGET_OSX)
|
||||
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||
mktemp(template_filename);
|
||||
return template_filename;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FileHelper::isAbsolute(const std::string& s)
|
||||
{
|
||||
@@ -65,7 +64,7 @@ std::string FileHelper::getCurrentDir()
|
||||
return std::string();
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
char buf[512];
|
||||
if (getcwd(buf, sizeof buf) != nullptr)
|
||||
return buf;
|
||||
@@ -110,32 +109,3 @@ size_t FileHelper::getFreespace(const std::string& path)
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string FileHelper::expandUserHome(const std::string &path)
|
||||
{
|
||||
if (path.empty() || path[0] != '~')
|
||||
return path; // No expansion needed
|
||||
|
||||
const char* home_dir = nullptr;
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
home_dir = std::getenv("USERPROFILE");
|
||||
if (!home_dir)
|
||||
{
|
||||
home_dir = std::getenv("HOMEDRIVE");
|
||||
const char* homepath = std::getenv("HOMEPATH");
|
||||
if (home_dir && homepath) {
|
||||
std::string fullpath(home_dir);
|
||||
fullpath += homepath;
|
||||
return fullpath + path.substr(1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
home_dir = std::getenv("HOME");
|
||||
#endif
|
||||
|
||||
if (!home_dir)
|
||||
throw std::runtime_error("Unable to determine the home directory");
|
||||
|
||||
return std::string(home_dir) + path.substr(1);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public:
|
||||
static void remove(const std::string& s);
|
||||
static void remove(const char* s);
|
||||
|
||||
// static std::string gettempname();
|
||||
static std::string gettempname();
|
||||
static bool isAbsolute(const std::string& s);
|
||||
|
||||
static std::string getCurrentDir();
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
// Returns free space on volume for path
|
||||
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
||||
static size_t getFreespace(const std::string& path);
|
||||
static std::string expandUserHome(const std::string& path);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,10 +5,6 @@ bool IuUP::TwoBytePseudoheader = false;
|
||||
|
||||
bool IuUP::parse(const uint8_t *packet, int size, IuUP::Frame &result)
|
||||
{
|
||||
// Data-with-CRC frames carry a 4 byte header
|
||||
if (size < 4)
|
||||
return false;
|
||||
|
||||
// Wrap incoming packet in byte buffer
|
||||
BitReader reader(packet, size);
|
||||
|
||||
@@ -40,19 +36,12 @@ uint16_t update_crc10_by_bytes(uint16_t crc10, const uint8_t *data_blk_ptr, int
|
||||
|
||||
bool IuUP::parse2(const uint8_t* packet, int size, Frame& result)
|
||||
{
|
||||
if (size < 2)
|
||||
return false;
|
||||
|
||||
if (TwoBytePseudoheader)
|
||||
{
|
||||
packet += 2;
|
||||
size -= 2;
|
||||
}
|
||||
|
||||
// Frame header is 3 bytes (no CRC) or 4 bytes (with CRC)
|
||||
if (size < 3)
|
||||
return false;
|
||||
|
||||
BitReader reader(packet, size);
|
||||
result.mPduType = (PduType)reader.readBits(4);
|
||||
|
||||
@@ -60,9 +49,6 @@ bool IuUP::parse2(const uint8_t* packet, int size, Frame& result)
|
||||
if (result.mPduType != PduType::DataNoCrc && result.mPduType != PduType::DataWithCrc)
|
||||
return false;
|
||||
|
||||
if (result.mPduType == PduType::DataWithCrc && size < 4)
|
||||
return false;
|
||||
|
||||
result.mFrameNumber = reader.readBits(4);
|
||||
result.mFqc = reader.readBits(2);
|
||||
result.mRfci = reader.readBits(6);
|
||||
@@ -1222,7 +1208,7 @@ static const uint16_t byte_crc10_table[256] = {
|
||||
/* Update the data block's CRC-10 remainder one byte at a time */
|
||||
uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size)
|
||||
{
|
||||
/*register*/ int i;
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < data_blk_size; i++) {
|
||||
crc10_accum = ((crc10_accum << 8) & 0x3ff)
|
||||
|
||||
@@ -11,36 +11,15 @@
|
||||
#define MPLS_STACK_MASK (0x00000100)
|
||||
#define MPLS_STACK_SHIFT (8)
|
||||
|
||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForRaw(const Packet& data)
|
||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||
{
|
||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
|
||||
PacketData result(packet);
|
||||
|
||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||
return Payload();
|
||||
|
||||
|
||||
switch (ip4->version())
|
||||
{
|
||||
case 4:
|
||||
return GetUdpPayloadForIp4(data);
|
||||
|
||||
case 6:
|
||||
return GetUdpPayloadForIp6(data);
|
||||
|
||||
default:
|
||||
return Payload();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
||||
{
|
||||
Packet result(data);
|
||||
|
||||
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(data.mData);
|
||||
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(packet.mData);
|
||||
|
||||
// Skip ethernet header
|
||||
result.mData += sizeof(EthernetHeader);
|
||||
result.mLength -= sizeof(EthernetHeader);
|
||||
packet.mData += sizeof(EthernetHeader);
|
||||
packet.mLength -= sizeof(EthernetHeader);
|
||||
|
||||
// See if there is Vlan header
|
||||
uint16_t proto = 0;
|
||||
@@ -49,9 +28,9 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
||||
// Skip 1 or more VLAN headers
|
||||
do
|
||||
{
|
||||
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(result.mData);
|
||||
result.mData += sizeof(VlanHeader);
|
||||
result.mLength -= sizeof(VlanHeader);
|
||||
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData);
|
||||
packet.mData += sizeof(VlanHeader);
|
||||
packet.mLength -= sizeof(VlanHeader);
|
||||
proto = ntohs(vlan->mData);
|
||||
}
|
||||
while (proto == 0x8100);
|
||||
@@ -64,10 +43,10 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
||||
case ETHERTYPE_MPLS_MC:
|
||||
// Parse MPLS here until marker "bottom of mpls stack"
|
||||
for(bool bottomOfStack = false; !bottomOfStack;
|
||||
bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
||||
bottomOfStack = ((ntohl(*(uint32_t*)(packet.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
||||
{
|
||||
result.mData += 4;
|
||||
result.mLength -=4;
|
||||
packet.mData += 4;
|
||||
packet.mLength -=4;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -80,86 +59,86 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
||||
break;
|
||||
}
|
||||
|
||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(result.mData);
|
||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
|
||||
|
||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||
return Payload();
|
||||
return PacketData();
|
||||
|
||||
|
||||
switch (ip4->version())
|
||||
{
|
||||
case 4:
|
||||
return GetUdpPayloadForIp4(result);
|
||||
return GetUdpPayloadForIp4(packet, source, destination);
|
||||
|
||||
case 6:
|
||||
return GetUdpPayloadForIp6(result);
|
||||
return GetUdpPayloadForIp6(packet, source, destination);
|
||||
|
||||
default:
|
||||
return Payload();
|
||||
return PacketData();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForSLL(const Packet& data)
|
||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForSLL(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||
{
|
||||
Packet result(data);
|
||||
PacketData result(packet);
|
||||
|
||||
if (result.mLength < 16)
|
||||
return Payload();
|
||||
if (packet.mLength < 16)
|
||||
return PacketData();
|
||||
|
||||
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(result.mData);
|
||||
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(packet.mData);
|
||||
|
||||
result.mData += sizeof(LinuxSllHeader);
|
||||
result.mLength -= sizeof(LinuxSllHeader);
|
||||
packet.mData += sizeof(LinuxSllHeader);
|
||||
packet.mLength -= sizeof(LinuxSllHeader);
|
||||
|
||||
switch (ntohs(sll->mProtocolType))
|
||||
{
|
||||
case 0x0800:
|
||||
return GetUdpPayloadForIp4(result);
|
||||
return GetUdpPayloadForIp4(packet, source, destination);
|
||||
|
||||
case 0x86DD:
|
||||
return GetUdpPayloadForIp6(result);
|
||||
return GetUdpPayloadForIp6(packet, source, destination);
|
||||
|
||||
default:
|
||||
return Payload();
|
||||
return PacketData();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForLoopback(const Packet& data)
|
||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForLoopback(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||
{
|
||||
Packet result(data);
|
||||
PacketData result(packet);
|
||||
|
||||
if (result.mLength < 16)
|
||||
return Payload();
|
||||
if (packet.mLength < 16)
|
||||
return PacketData();
|
||||
|
||||
struct LoopbackHeader
|
||||
{
|
||||
uint32_t mProtocolType;
|
||||
};
|
||||
|
||||
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(result.mData);
|
||||
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(packet.mData);
|
||||
|
||||
result.mData += sizeof(LoopbackHeader);
|
||||
result.mLength -= sizeof(LoopbackHeader);
|
||||
packet.mData += sizeof(LoopbackHeader);
|
||||
packet.mLength -= sizeof(LoopbackHeader);
|
||||
|
||||
switch (lh->mProtocolType)
|
||||
{
|
||||
case AF_INET:
|
||||
return GetUdpPayloadForIp4(result);
|
||||
return GetUdpPayloadForIp4(packet, source, destination);
|
||||
|
||||
case AF_INET6:
|
||||
return GetUdpPayloadForIp6(result);
|
||||
return GetUdpPayloadForIp6(packet, source, destination);
|
||||
|
||||
default:
|
||||
return Payload();
|
||||
return PacketData();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
|
||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||
{
|
||||
Packet result(data);
|
||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
|
||||
PacketData result(packet);
|
||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
|
||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||
return Payload();
|
||||
return PacketData(nullptr, 0);
|
||||
|
||||
result.mData += ip4->headerLength();
|
||||
result.mLength -= ip4->headerLength();
|
||||
@@ -173,15 +152,13 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
|
||||
if (length - sizeof(UdpHeader) < (size_t)result.mLength)
|
||||
result.mLength = length - sizeof(UdpHeader);
|
||||
|
||||
InternetAddress addr_source;
|
||||
addr_source.setIp(ip4->mSource);
|
||||
addr_source.setPort(ntohs(udp->mSourcePort));
|
||||
source.setIp(ip4->mSource);
|
||||
source.setPort(ntohs(udp->mSourcePort));
|
||||
|
||||
InternetAddress addr_dest;
|
||||
addr_dest.setIp(ip4->mDestination);
|
||||
addr_dest.setPort(ntohs(udp->mDestinationPort));
|
||||
destination.setIp(ip4->mDestination);
|
||||
destination.setPort(ntohs(udp->mDestinationPort));
|
||||
|
||||
return {.data = result, .source = addr_source, .dest = addr_dest};
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Ip6Header
|
||||
@@ -211,10 +188,10 @@ struct Ip6Header
|
||||
struct in6_addr dst_ip;
|
||||
};
|
||||
|
||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
|
||||
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||
{
|
||||
Packet result(data);
|
||||
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData);
|
||||
PacketData result(packet);
|
||||
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(packet.mData);
|
||||
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||
return PacketData(nullptr, 0);
|
||||
*/
|
||||
@@ -226,13 +203,18 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
|
||||
result.mData += sizeof(UdpHeader);
|
||||
result.mLength -= sizeof(UdpHeader);
|
||||
|
||||
InternetAddress addr_source;
|
||||
addr_source.setIp(ip6->src_ip);
|
||||
addr_source.setPort(ntohs(udp->mSourcePort));
|
||||
/*
|
||||
if (result.mLength != ntohs(udp->mDatagramLength))
|
||||
return PacketData(nullptr, 0);
|
||||
*/
|
||||
|
||||
InternetAddress addr_dest;
|
||||
addr_dest.setIp(ip6->dst_ip);
|
||||
addr_dest.setPort(ntohs(udp->mDestinationPort));
|
||||
source.setIp(ip6->src_ip);
|
||||
source.setPort(ntohs(udp->mSourcePort));
|
||||
//std::cout << source.toStdString() << " - ";
|
||||
|
||||
return {.data = result, .source = addr_source, .dest = addr_dest};
|
||||
destination.setIp(ip6->dst_ip);
|
||||
destination.setPort(ntohs(udp->mDestinationPort));
|
||||
//std::cout << destination.toStdString() << std::endl;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -7,38 +7,25 @@
|
||||
class NetworkFrame
|
||||
{
|
||||
public:
|
||||
struct Packet
|
||||
struct PacketData
|
||||
{
|
||||
const uint8_t* mData;
|
||||
size_t mLength;
|
||||
|
||||
Packet(const uint8_t* data, size_t length)
|
||||
PacketData(const uint8_t* data, size_t length)
|
||||
:mData(data), mLength(length)
|
||||
{}
|
||||
|
||||
Packet()
|
||||
PacketData()
|
||||
:mData(nullptr), mLength(0)
|
||||
{}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return mData == nullptr || mLength == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Payload
|
||||
{
|
||||
Packet data;
|
||||
InternetAddress source;
|
||||
InternetAddress dest;
|
||||
};
|
||||
|
||||
static Payload GetUdpPayloadForEthernet(const Packet& data);
|
||||
static Payload GetUdpPayloadForIp4(const Packet& data);
|
||||
static Payload GetUdpPayloadForIp6(const Packet& data);
|
||||
static Payload GetUdpPayloadForSLL(const Packet& data);
|
||||
static Payload GetUdpPayloadForLoopback(const Packet& data);
|
||||
static Payload GetUdpPayloadForRaw(const Packet& data);
|
||||
static PacketData GetUdpPayloadForEthernet(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||
static PacketData GetUdpPayloadForIp4(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||
static PacketData GetUdpPayloadForIp6(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||
static PacketData GetUdpPayloadForSLL(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||
static PacketData GetUdpPayloadForLoopback(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||
|
||||
struct EthernetHeader
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* 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
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -7,9 +7,8 @@
|
||||
# include <asm/ioctls.h>
|
||||
#endif
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "HL_NetworkSocket.h"
|
||||
#include "HL_Log.h"
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
# include <fcntl.h>
|
||||
@@ -20,15 +19,15 @@
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#define LOG_SUBSYSTEM "network"
|
||||
|
||||
DatagramSocket::DatagramSocket()
|
||||
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
|
||||
{}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DatagramSocket::~DatagramSocket()
|
||||
{
|
||||
internalClose();
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
void DatagramSocket::open(int family)
|
||||
@@ -62,7 +61,7 @@ void DatagramSocket::sendDatagram(InternetAddress &dest, const void *packetData,
|
||||
if (mHandle == INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
/*int sent = */::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
||||
int sent = ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
||||
}
|
||||
|
||||
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
||||
@@ -70,30 +69,23 @@ unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer,
|
||||
if (mHandle == INVALID_SOCKET)
|
||||
return 0;
|
||||
|
||||
sockaddr* addr = nullptr;
|
||||
socklen_t addrLen = 0;
|
||||
sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}};
|
||||
sockaddr_in6 addr_6 = {AF_INET6, 0, 0, {0}, 0};
|
||||
switch (mFamily)
|
||||
{
|
||||
case AF_INET: 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);
|
||||
sockaddr_in sourceaddr;
|
||||
#ifdef WIN32
|
||||
int addrlen = sizeof(sourceaddr);
|
||||
#else
|
||||
socklen_t addrlen = sizeof(sourceaddr);
|
||||
#endif
|
||||
int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, (sockaddr*)&sourceaddr, &addrlen);
|
||||
if (received > 0)
|
||||
{
|
||||
src = InternetAddress((sockaddr&)*addr, addrLen);
|
||||
src = InternetAddress((sockaddr&)sourceaddr, addrlen);
|
||||
return received;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DatagramSocket::internalClose()
|
||||
void DatagramSocket::closeSocket()
|
||||
{
|
||||
if (mHandle != INVALID_SOCKET)
|
||||
{
|
||||
@@ -106,11 +98,6 @@ void DatagramSocket::internalClose()
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramSocket::closeSocket()
|
||||
{
|
||||
internalClose();
|
||||
}
|
||||
|
||||
bool DatagramSocket::isValid() const
|
||||
{
|
||||
return mHandle != INVALID_SOCKET;
|
||||
@@ -161,12 +148,6 @@ void DatagramAgreggator::addSocket(PDatagramSocket socket)
|
||||
if (socket->mHandle == INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
if (mSocketVector.size() >= 62)
|
||||
{
|
||||
ICELogError(<< "fd_set overflow; too much sockets");
|
||||
return;
|
||||
}
|
||||
|
||||
FD_SET(socket->mHandle, &mReadSet);
|
||||
if (socket->mHandle > mMaxHandle)
|
||||
mMaxHandle = socket->mHandle;
|
||||
@@ -182,7 +163,7 @@ unsigned DatagramAgreggator::count()
|
||||
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
||||
{
|
||||
PDatagramSocket socket = mSocketVector[index];
|
||||
return FD_ISSET(socket->mHandle, &mReadSet);
|
||||
return (FD_ISSET(socket->mHandle, &mReadSet) != 0);
|
||||
}
|
||||
|
||||
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
||||
@@ -190,12 +171,12 @@ PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
||||
return mSocketVector[index];
|
||||
}
|
||||
|
||||
bool DatagramAgreggator::waitForData(std::chrono::milliseconds timeout)
|
||||
bool DatagramAgreggator::waitForData(unsigned milliseconds)
|
||||
{
|
||||
timeval tv;
|
||||
tv.tv_sec = timeout.count() / 1000;
|
||||
tv.tv_usec = (timeout.count() % 1000) * 1000;
|
||||
tv.tv_sec = milliseconds / 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* 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/. */
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "HL_InternetAddress.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
class NetworkSocket
|
||||
{
|
||||
@@ -37,12 +36,10 @@ public:
|
||||
virtual SOCKET socket() const;
|
||||
|
||||
virtual void open(int family);
|
||||
|
||||
protected:
|
||||
int mFamily;
|
||||
SOCKET mHandle;
|
||||
int mLocalPort;
|
||||
void internalClose();
|
||||
};
|
||||
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
||||
|
||||
@@ -57,7 +54,7 @@ public:
|
||||
bool hasDataAtIndex(unsigned index);
|
||||
PDatagramSocket socketAt(unsigned index);
|
||||
|
||||
bool waitForData(std::chrono::milliseconds timeout);
|
||||
bool waitForData(unsigned milliseconds);
|
||||
|
||||
protected:
|
||||
typedef std::vector<PDatagramSocket> SocketList;
|
||||
|
||||
@@ -1,179 +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 <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. Identical in
|
||||
/// design to the pcpp Layer pool: 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. Uniform block size makes a block
|
||||
/// allocated on one thread safe to free on another (it joins the freeing thread's free list).
|
||||
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;
|
||||
|
||||
static void* allocate(std::size_t size)
|
||||
{
|
||||
if (size > PayloadSize)
|
||||
{
|
||||
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
||||
tagOf(raw) = TagGlobal;
|
||||
return raw + HeaderSize;
|
||||
}
|
||||
|
||||
void*& head = freeListHead();
|
||||
if (head == nullptr)
|
||||
head = registry().refill();
|
||||
|
||||
uint8_t* block = static_cast<uint8_t*>(head);
|
||||
head = nextOf(block);
|
||||
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)
|
||||
{
|
||||
void*& head = freeListHead();
|
||||
nextOf(block) = head;
|
||||
head = block;
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(static_cast<void*>(block));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
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"
|
||||
|
||||
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*& freeListHead() noexcept
|
||||
{
|
||||
static thread_local void* head = nullptr;
|
||||
return head;
|
||||
}
|
||||
|
||||
class ChunkRegistry
|
||||
{
|
||||
public:
|
||||
~ChunkRegistry()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
for (uint8_t* chunk : m_Chunks)
|
||||
::operator delete(chunk);
|
||||
m_Chunks.clear();
|
||||
}
|
||||
|
||||
void* refill()
|
||||
{
|
||||
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
||||
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
|
||||
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
static ChunkRegistry& registry()
|
||||
{
|
||||
static ChunkRegistry instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
#endif // HL_RTP_POOL
|
||||
|
||||
/// @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
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "HL_Process.h"
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# define popen _popen
|
||||
@@ -10,6 +9,7 @@
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
#include "helper/HL_String.h"
|
||||
|
||||
int OsProcess::execSystem(const std::string& cmd)
|
||||
@@ -39,7 +39,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
|
||||
char* cmdline = (char*)_alloca(cmd.size()+1);
|
||||
strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str());
|
||||
strcpy(cmdline, StringHelper::replace(cmd, "/", "\\").c_str());
|
||||
|
||||
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
||||
if (! fSuccess)
|
||||
@@ -68,7 +68,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
||||
if (!dwAvail) // no data available, return
|
||||
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
|
||||
break;
|
||||
|
||||
@@ -114,7 +114,7 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||
memset(&pi, 0, sizeof pi);
|
||||
|
||||
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
||||
strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str());
|
||||
strcpy(cmdbuffer, StringHelper::replace(cmdline, "/", "\\").c_str());
|
||||
|
||||
|
||||
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
||||
@@ -148,7 +148,7 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||
break;
|
||||
|
||||
int filled = strlen(buf);
|
||||
if (!::ReadFile(hPipeRead, buf + filled, std::min(sizeof(buf) - 1 - filled, (size_t)dwAvail), &dwRead, nullptr) || !dwRead)
|
||||
if (!::ReadFile(hPipeRead, buf + filled, min(sizeof(buf) - 1 - filled, dwAvail), &dwRead, nullptr) || !dwRead)
|
||||
// error, the child process might ended
|
||||
break;
|
||||
|
||||
@@ -160,14 +160,14 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||
{
|
||||
std::string line(buf, cr - buf -1);
|
||||
if (callback)
|
||||
callback(strx::trim(line));
|
||||
callback(StringHelper::trim(line));
|
||||
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
||||
}
|
||||
}
|
||||
} //for
|
||||
|
||||
if (buf[0])
|
||||
callback(strx::trim(std::string(buf)));
|
||||
callback(StringHelper::trim(std::string(buf)));
|
||||
|
||||
char ctrlc = 3;
|
||||
//if (finish_flag)
|
||||
@@ -278,16 +278,16 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||
}
|
||||
while (r == sizeof(buffer) - 1);
|
||||
|
||||
if (lines.find('\n') != std::string::npos && line_callback)
|
||||
if (lines.find("\n") != std::string::npos && line_callback)
|
||||
{
|
||||
std::string::size_type p = 0;
|
||||
while (p < lines.size())
|
||||
{
|
||||
std::string::size_type d = lines.find('\n', p);
|
||||
std::string::size_type d = lines.find("\n", p);
|
||||
if (d != std::string::npos)
|
||||
{
|
||||
if (line_callback)
|
||||
line_callback(strx::trim(lines.substr(p, d-p)));
|
||||
line_callback(StringHelper::trim(lines.substr(p, d-p)));
|
||||
p = d + 1;
|
||||
}
|
||||
}
|
||||
@@ -311,8 +311,6 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
|
||||
return t;
|
||||
}
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
|
||||
pid_t OsProcess::findPid(const std::string& cmdline)
|
||||
{
|
||||
try
|
||||
@@ -334,6 +332,5 @@ void OsProcess::killByPid(pid_t pid)
|
||||
return;
|
||||
execSystem("kill -9 " + std::to_string(pid) + " &");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+72
-362
@@ -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
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -8,29 +8,22 @@
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX)
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "HL_Rtp.h"
|
||||
#include "HL_Exception.h"
|
||||
#include "HL_Log.h"
|
||||
#include "HL_String.h"
|
||||
|
||||
#if defined(USE_RTP_DUMP)
|
||||
# include "jrtplib/src/rtprawpacket.h"
|
||||
# include "jrtplib/src/rtpipv4address.h"
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <chrono>
|
||||
#if !defined(TARGET_WIN)
|
||||
# include <alloca.h>
|
||||
#endif
|
||||
|
||||
#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
|
||||
{
|
||||
unsigned char cc:4; /* CSRC count */
|
||||
@@ -49,55 +42,29 @@ struct RtcpHeader
|
||||
unsigned char rc:5; /* reception report count */
|
||||
unsigned char p:1; /* padding flag */
|
||||
unsigned char version:2; /* protocol version */
|
||||
unsigned char pt; /* payload type */
|
||||
unsigned char pt:8; /* payload type */
|
||||
uint16_t len; /* length */
|
||||
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)
|
||||
{
|
||||
if (length < 12)
|
||||
return false;
|
||||
|
||||
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
|
||||
if (h->version != 2)
|
||||
return false;
|
||||
|
||||
unsigned char pt = h->pt;
|
||||
bool rtp = (pt >= 96 && pt <= 127) || (pt < 35);
|
||||
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
|
||||
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35);
|
||||
return rtp;
|
||||
}
|
||||
|
||||
|
||||
bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
|
||||
{
|
||||
// A minimal RTCP packet (e.g. an empty receiver report) is 8 bytes
|
||||
if (length < 8)
|
||||
if (length < 12)
|
||||
return false;
|
||||
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
|
||||
return h->version == 2;
|
||||
unsigned char b = ((const unsigned char*)buffer)[0];
|
||||
|
||||
return (b & 0xC0 ) == 128;
|
||||
}
|
||||
|
||||
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||
@@ -108,18 +75,9 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
||||
{
|
||||
if (isRtp(buffer, length))
|
||||
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
|
||||
else if (isRtpOrRtcp(buffer, length))
|
||||
return ntohl(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);
|
||||
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
|
||||
else
|
||||
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
|
||||
}
|
||||
|
||||
int RtpHelper::findPtype(const void* buffer, size_t length)
|
||||
@@ -140,192 +98,48 @@ int RtpHelper::findPacketNo(const void *buffer, size_t length)
|
||||
|
||||
int RtpHelper::findPayloadLength(const void* buffer, size_t 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 -1;
|
||||
uint16_t extWords = (static_cast<uint16_t>(p[offset + 2]) << 8) | p[offset + 3];
|
||||
offset += 4 + 4u * extWords;
|
||||
if (offset > length)
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t payloadLen = length - offset;
|
||||
|
||||
// Padding
|
||||
if (h->p && payloadLen > 0) {
|
||||
uint8_t padBytes = p[length - 1];
|
||||
if (padBytes > payloadLen)
|
||||
return -1;
|
||||
payloadLen -= padBytes;
|
||||
}
|
||||
|
||||
return static_cast<int>(payloadLen);
|
||||
}
|
||||
|
||||
std::chrono::microseconds RtpHelper::toMicroseconds(const jrtplib::RTPTime& t)
|
||||
if (isRtp(buffer, length))
|
||||
{
|
||||
return std::chrono::microseconds(uint64_t(t.GetDouble() * 1000000));
|
||||
}
|
||||
|
||||
// --- 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
|
||||
jrtplib::RTPIPAddress senderAddress = {jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0))};
|
||||
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;
|
||||
}
|
||||
return length - 12;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(USE_RTPDUMP)
|
||||
RtpDump::RtpDump(const char *filename)
|
||||
: mFilename(filename ? filename : "")
|
||||
:mFilename(filename)
|
||||
{}
|
||||
|
||||
RtpDump::~RtpDump()
|
||||
{
|
||||
flush();
|
||||
for (PacketList::iterator packetIter=mPacketList.begin(); packetIter!=mPacketList.end(); ++packetIter)
|
||||
{
|
||||
//free(packetIter->mData);
|
||||
delete packetIter->mPacket;
|
||||
}
|
||||
|
||||
RtpDump::~RtpDump() = default;
|
||||
|
||||
void RtpDump::setSource(uint32_t ip, uint16_t port)
|
||||
{
|
||||
mSourceIp = ip;
|
||||
mSourcePort = port;
|
||||
}
|
||||
|
||||
void RtpDump::load()
|
||||
{
|
||||
if (mFilename.empty())
|
||||
throw std::runtime_error("No filename specified");
|
||||
FILE* f = fopen(mFilename.c_str(), "rb");
|
||||
if (!f)
|
||||
throw Exception(ERR_WAVFILE_FAILED);
|
||||
|
||||
std::ifstream input(mFilename, std::ios::binary);
|
||||
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);
|
||||
size_t slashPos = addrPart.find('/');
|
||||
if (slashPos != std::string::npos) {
|
||||
mSourceIp = stringToIp(addrPart.substr(0, slashPos));
|
||||
try {
|
||||
mSourcePort = static_cast<uint16_t>(std::stoi(addrPart.substr(slashPos + 1)));
|
||||
} catch (...) {
|
||||
mSourcePort = 0;
|
||||
while (!feof(f))
|
||||
{
|
||||
RtpData data;
|
||||
fread(&data.mLength, sizeof data.mLength, 1, f);
|
||||
data.mData = new char[data.mLength];
|
||||
fread(data.mData, 1, data.mLength, f);
|
||||
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
|
||||
jrtplib::RTPTime t(0);
|
||||
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)data.mData, data.mLength, &addr, t, true);
|
||||
data.mPacket = new jrtplib::RTPPacket(*raw);
|
||||
mPacketList.push_back(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 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
|
||||
{
|
||||
@@ -334,143 +148,39 @@ size_t RtpDump::count() const
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!buffer || len == 0)
|
||||
return;
|
||||
RtpData data;
|
||||
data.mData = malloc(len);
|
||||
memcpy(data.mData, buffer, len);
|
||||
data.mLength = len;
|
||||
|
||||
uint32_t offsetMs = 0;
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (!mRecording) {
|
||||
mRecording = true;
|
||||
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));
|
||||
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
|
||||
jrtplib::RTPTime t(0);
|
||||
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)const_cast<void*>(data.mData), data.mLength, &addr, t, true);
|
||||
data.mPacket = new jrtplib::RTPPacket(*raw);
|
||||
//delete raw;
|
||||
mPacketList.push_back(data);
|
||||
}
|
||||
|
||||
void RtpDump::flush()
|
||||
{
|
||||
if (mFilename.empty())
|
||||
throw std::runtime_error("No filename specified");
|
||||
FILE* f = fopen(mFilename.c_str(), "wb");
|
||||
if (!f)
|
||||
throw Exception(ERR_WAVFILE_FAILED);
|
||||
|
||||
std::ofstream output(mFilename, std::ios::binary);
|
||||
if (!output.is_open())
|
||||
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())
|
||||
continue;
|
||||
|
||||
uint16_t plen = static_cast<uint16_t>(pkt.mRawData.size());
|
||||
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++;
|
||||
}
|
||||
|
||||
if (!output.good())
|
||||
throw std::runtime_error("Failed to write rtpdump file: " + mFilename);
|
||||
|
||||
ICELogInfo(<< "Wrote " << written << " packets to " << mFilename);
|
||||
}
|
||||
|
||||
void RtpDump::clear()
|
||||
PacketList::iterator packetIter = mPacketList.begin();
|
||||
for (;packetIter != mPacketList.end(); ++packetIter)
|
||||
{
|
||||
mPacketList.clear();
|
||||
mLoaded = false;
|
||||
mRecording = false;
|
||||
RtpData& data = *packetIter;
|
||||
// Disabled for debugging only
|
||||
//fwrite(&data.mLength, sizeof data.mLength, 1, f);
|
||||
fwrite(data.mData, data.mLength, 1, f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+13
-88
@@ -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
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -6,14 +6,15 @@
|
||||
#ifndef __HL_RTP_H
|
||||
#define __HL_RTP_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 <string>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
// Class to carry rtp/rtcp socket pair
|
||||
template<class T>
|
||||
@@ -29,7 +30,7 @@ struct RtpPair
|
||||
:mRtp(rtp), mRtcp(rtcp)
|
||||
{}
|
||||
|
||||
bool multiplexed() const { return mRtp == mRtcp; }
|
||||
bool multiplexed() { return mRtp == mRtcp; }
|
||||
};
|
||||
|
||||
class RtpHelper
|
||||
@@ -41,110 +42,34 @@ public:
|
||||
static bool isRtpOrRtcp(const void* buffer, size_t length);
|
||||
static bool isRtcp(const void* buffer, size_t length);
|
||||
static unsigned findSsrc(const void* buffer, size_t length);
|
||||
static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
|
||||
static int findPayloadLength(const void* buffer, size_t length);
|
||||
|
||||
static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t);
|
||||
};
|
||||
|
||||
/**
|
||||
* @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).
|
||||
*/
|
||||
#if defined(USE_RTPDUMP)
|
||||
class RtpDump
|
||||
{
|
||||
protected:
|
||||
struct RtpData
|
||||
{
|
||||
std::shared_ptr<jrtplib::RTPPacket> mPacket;
|
||||
std::vector<uint8_t> mRawData;
|
||||
uint32_t mOffsetMs = 0;
|
||||
jrtplib::RTPPacket* mPacket;
|
||||
void* mData;
|
||||
size_t mLength;
|
||||
};
|
||||
|
||||
typedef std::vector<RtpData> PacketList;
|
||||
PacketList mPacketList;
|
||||
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:
|
||||
explicit RtpDump(const char* filename);
|
||||
RtpDump(const char* filename);
|
||||
~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();
|
||||
bool isLoaded() const { return mLoaded; }
|
||||
|
||||
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);
|
||||
|
||||
/** @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);
|
||||
|
||||
/** @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 clear();
|
||||
const std::string& filename() const { return mFilename; }
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <set>
|
||||
#include <assert.h>
|
||||
#include <chrono>
|
||||
|
||||
#if !defined(TARGET_WIN)
|
||||
# include <unistd.h> // Responsible for close() call on Linux
|
||||
#endif
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "HL_Sync.h"
|
||||
#include "HL_Exception.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "network"
|
||||
#define LOG_SUBSYSTEM "[SocketHeap]"
|
||||
|
||||
#ifndef WIN32
|
||||
#define WSAGetLastError(X) errno
|
||||
@@ -28,7 +28,6 @@
|
||||
#define WSAEADDRINUSE EADDRINUSE
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// ----------------------------- SocketSink -------------------------
|
||||
SocketSink::~SocketSink()
|
||||
@@ -97,20 +96,7 @@ RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink *sin
|
||||
rtcp = allocSocket(family, sink, rtp->localport() + 1);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// Release a partially allocated pair before retrying - otherwise
|
||||
// the RTP socket from this attempt leaks into the socket map.
|
||||
if (rtp)
|
||||
{
|
||||
freeSocket(rtp);
|
||||
rtp.reset();
|
||||
}
|
||||
if (rtcp)
|
||||
{
|
||||
freeSocket(rtcp);
|
||||
rtcp.reset();
|
||||
}
|
||||
}
|
||||
{}
|
||||
}
|
||||
|
||||
if (!rtp || !rtcp)
|
||||
@@ -141,7 +127,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||
if (sock == INVALID_SOCKET)
|
||||
{
|
||||
// Return null socket
|
||||
auto result = std::make_shared<DatagramSocket>();
|
||||
PDatagramSocket result(new DatagramSocket());
|
||||
result->mLocalPort = port;
|
||||
result->mFamily = family;
|
||||
return result;
|
||||
@@ -152,9 +138,6 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||
sockaddr_in6 addr6;
|
||||
int result = 0;
|
||||
int testport;
|
||||
// A fixed port cannot be retried (it would loop forever if the port is
|
||||
// owned by another process); random ports get a bounded number of attempts.
|
||||
int attemptsLeft = port ? 1 : 100;
|
||||
do
|
||||
{
|
||||
testport = port ? port : rand() % ((mFinish - mStart) / 2) * 2 + mStart;
|
||||
@@ -180,14 +163,14 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||
break;
|
||||
}
|
||||
|
||||
} while (result == WSAEADDRINUSE && --attemptsLeft > 0);
|
||||
} while (result == WSAEADDRINUSE);
|
||||
|
||||
if (result)
|
||||
{
|
||||
closesocket(sock);
|
||||
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
||||
}
|
||||
auto resultObject = std::make_shared<DatagramSocket>();
|
||||
PDatagramSocket resultObject(new DatagramSocket());
|
||||
resultObject->mLocalPort = testport;
|
||||
resultObject->mHandle = sock;
|
||||
if (!resultObject->setBlocking(false))
|
||||
@@ -270,7 +253,7 @@ void SocketHeap::thread()
|
||||
// If set is not empty
|
||||
if (agreggator.count() > 0)
|
||||
{
|
||||
if (agreggator.waitForData(10ms))
|
||||
if (agreggator.waitForData(10))
|
||||
{
|
||||
ICELogMedia(<< "There is data on UDP sockets");
|
||||
Lock l(mGuard);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifndef __SOCKET_HEAP_H
|
||||
#define __SOCKET_HEAP_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
+144
-113
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <iomanip>
|
||||
#include <memory.h>
|
||||
#include <algorithm>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# include <WinSock2.h>
|
||||
@@ -16,7 +15,7 @@
|
||||
# include <cctype>
|
||||
#endif
|
||||
|
||||
std::string strx::extractFilename(const std::string& path)
|
||||
std::string StringHelper::extractFilename(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
return std::string();
|
||||
@@ -31,7 +30,7 @@ std::string strx::extractFilename(const std::string& path)
|
||||
return path.substr(p_bs + 1);
|
||||
}
|
||||
|
||||
std::string strx::appendPath(const std::string& s1, const std::string& s2)
|
||||
std::string StringHelper::appendPath(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
std::string result = s1;
|
||||
if (!endsWith(result, "/") && !endsWith(result, "\\"))
|
||||
@@ -45,42 +44,36 @@ std::string strx::appendPath(const std::string& s1, const std::string& s2)
|
||||
return result + s2;
|
||||
}
|
||||
|
||||
std::string strx::makeUtf8(const std::tstring &arg)
|
||||
std::string StringHelper::makeUtf8(const std::tstring &arg)
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
int required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
|
||||
if (required <= 0)
|
||||
return std::string();
|
||||
std::string result(static_cast<size_t>(required), '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, &result[0], required, NULL, NULL);
|
||||
result.resize(strlen(result.c_str())); // strip the trailing NUL written by the API
|
||||
size_t required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
|
||||
char *result = (char*)_alloca(required + 1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, result, required+1, NULL, NULL);
|
||||
return result;
|
||||
#else
|
||||
return arg;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string strx::toUtf8(const std::tstring &arg)
|
||||
std::string StringHelper::toUtf8(const std::tstring &arg)
|
||||
{
|
||||
return makeUtf8(arg);
|
||||
}
|
||||
|
||||
std::tstring strx::makeTstring(const std::string& arg)
|
||||
std::tstring StringHelper::makeTstring(const std::string& arg)
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
int count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0);
|
||||
if (count <= 0)
|
||||
return std::tstring();
|
||||
std::wstring result(static_cast<size_t>(count), L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, &result[0], count);
|
||||
result.resize(wcslen(result.c_str())); // strip the trailing NUL written by the API
|
||||
size_t count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0);
|
||||
wchar_t* result = (wchar_t*)_alloca(count * 2);
|
||||
MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, result, count);
|
||||
return result;
|
||||
#else
|
||||
return arg;
|
||||
#endif
|
||||
}
|
||||
|
||||
int strx::toInt(const char *s, int defaultValue, bool* isOk)
|
||||
int StringHelper::toInt(const char *s, int defaultValue, bool* isOk)
|
||||
{
|
||||
int result;
|
||||
if (sscanf(s, "%d", &result) != 1)
|
||||
@@ -96,10 +89,14 @@ int strx::toInt(const char *s, int defaultValue, bool* isOk)
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||
uint64_t StringHelper::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||
{
|
||||
uint64_t result = def;
|
||||
if (sscanf(s, "%" SCNu64, &result) != 1)
|
||||
#if defined(TARGET_WIN)
|
||||
if (sscanf(s, "%I64d", &result) != 1)
|
||||
#else
|
||||
if (sscanf(s, "%llu", &result) != 1)
|
||||
#endif
|
||||
{
|
||||
if (isOk)
|
||||
*isOk = false;
|
||||
@@ -112,14 +109,14 @@ uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string strx::toHex(unsigned int value)
|
||||
std::string StringHelper::toHex(unsigned int value)
|
||||
{
|
||||
char buffer[32];
|
||||
std::snprintf(buffer, sizeof(buffer), "%x", value);
|
||||
sprintf(buffer, "%x", value);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string strx::toHex(const void *ptr)
|
||||
std::string StringHelper::toHex(const void *ptr)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr;
|
||||
@@ -129,7 +126,7 @@ std::string strx::toHex(const void *ptr)
|
||||
//must be lowercase for MD5
|
||||
static const char hexmap[] = "0123456789abcdef";
|
||||
|
||||
std::string strx::toHex(const uint8_t* input, size_t inputLength)
|
||||
std::string StringHelper::toHex(const uint8_t* input, size_t inputLength)
|
||||
{
|
||||
std::string result; result.resize(inputLength * 2);
|
||||
|
||||
@@ -145,11 +142,12 @@ std::string strx::toHex(const uint8_t* input, size_t inputLength)
|
||||
*r++ = hexmap[hi];
|
||||
*r++ = hexmap[low];
|
||||
}
|
||||
*r = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string strx::prefixLines(const std::string &source, const std::string &prefix)
|
||||
std::string StringHelper::prefixLines(const std::string &source, const std::string &prefix)
|
||||
{
|
||||
// Read source line by line
|
||||
std::istringstream iss(source);
|
||||
@@ -162,7 +160,7 @@ std::string strx::prefixLines(const std::string &source, const std::string &pref
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string strx::doubleToString(double value, int precision)
|
||||
std::string StringHelper::doubleToString(double value, int precision)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(precision) << value;
|
||||
@@ -170,15 +168,17 @@ std::string strx::doubleToString(double value, int precision)
|
||||
}
|
||||
|
||||
|
||||
const char* strx::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
|
||||
const char* StringHelper::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
|
||||
{
|
||||
// The buffer is not necessarily NUL-terminated, so a bounded search is
|
||||
// required on every platform (a memmem replacement for MSVC is provided below).
|
||||
#if defined(TARGET_WIN)
|
||||
return (const char*)strstr(buffer, substring);
|
||||
#else
|
||||
return (const char*)memmem(buffer, bufferLength, substring, strlen(substring));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void strx::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
|
||||
void StringHelper::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
|
||||
{
|
||||
dst.clear();
|
||||
std::string::size_type p = 0;
|
||||
@@ -202,21 +202,21 @@ void strx::split(const std::string& src, std::vector<std::string>& dst, const st
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> strx::split(const std::string& src, const std::string& delims)
|
||||
std::vector<std::string> StringHelper::split(const std::string& src, const std::string& delims)
|
||||
{
|
||||
std::vector<std::string> r;
|
||||
split(src, r, delims);
|
||||
return r;
|
||||
}
|
||||
|
||||
std::pair<std::string, int> strx::parseHost(const std::string& host, int defaultPort)
|
||||
std::pair<std::string, int> StringHelper::parseHost(const std::string& host, int defaultPort)
|
||||
{
|
||||
std::pair<std::string, int> result;
|
||||
std::size_t p = host.find(':');
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
result.first = host.substr(0, p);
|
||||
result.second = strx::toInt(host.c_str() + p + 1, defaultPort);
|
||||
result.second = StringHelper::toInt(host.c_str() + p + 1, defaultPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -226,15 +226,15 @@ std::pair<std::string, int> strx::parseHost(const std::string& host, int default
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> strx::parseAssignment(const std::string& s, bool trimQuotes)
|
||||
std::pair<std::string, std::string> StringHelper::parseAssignment(const std::string& s, bool trimQuotes)
|
||||
{
|
||||
std::pair<std::string, std::string> result;
|
||||
|
||||
std::string::size_type p = s.find('=');
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
result.first = strx::trim(s.substr(0, p));
|
||||
result.second = strx::trim(s.substr(p+1));
|
||||
result.first = StringHelper::trim(s.substr(0, p));
|
||||
result.second = StringHelper::trim(s.substr(p+1));
|
||||
if (trimQuotes && result.second.size() >= 2)
|
||||
{
|
||||
if ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') ||
|
||||
@@ -243,19 +243,19 @@ std::pair<std::string, std::string> strx::parseAssignment(const std::string& s,
|
||||
}
|
||||
}
|
||||
else
|
||||
result.first = strx::trim(s);
|
||||
result.first = StringHelper::trim(s);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string strx::intToString(int value)
|
||||
std::string StringHelper::intToString(int value)
|
||||
{
|
||||
char buffer[32];
|
||||
std::snprintf(buffer, sizeof(buffer), "%d", value);
|
||||
sprintf(buffer, "%d", value);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
float strx::toFloat(const std::string &s, float v, bool* isOk)
|
||||
float StringHelper::toFloat(const std::string &s, float v, bool* isOk)
|
||||
{
|
||||
float result = 0.0;
|
||||
int code = sscanf(s.c_str(), "%f", &result);
|
||||
@@ -274,14 +274,14 @@ float strx::toFloat(const std::string &s, float v, bool* isOk)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string strx::trim(const std::string &s)
|
||||
std::string StringHelper::trim(const std::string &s)
|
||||
{
|
||||
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; });
|
||||
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }).base();
|
||||
return (wsback <= wsfront ? std::string() : std::string(wsfront,wsback));
|
||||
}
|
||||
|
||||
std::string strx::timeToString(time_t t)
|
||||
std::string StringHelper::timeToString(time_t t)
|
||||
{
|
||||
char buffer[128] = "";
|
||||
struct tm lt;
|
||||
@@ -295,18 +295,15 @@ std::string strx::timeToString(time_t t)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string strx::millisecondsToString(uint64_t t)
|
||||
std::string StringHelper::millisecondsToString(uint64_t t)
|
||||
{
|
||||
return timeToString(t/1000);
|
||||
}
|
||||
|
||||
int strx::fromHex2Int(const std::string &s)
|
||||
int StringHelper::fromHex2Int(const std::string &s)
|
||||
{
|
||||
int result = 0;
|
||||
int retcode = sscanf(s.c_str(), "%x", &result);
|
||||
if (retcode == 0)
|
||||
return 0;
|
||||
|
||||
sscanf(s.c_str(), "%x", &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -321,32 +318,32 @@ static int hex2code(char s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*static int hex2code(const char* s)
|
||||
static int hex2code(const char* s)
|
||||
{
|
||||
return (hex2code(s[0]) << 4) + hex2code(s[1]);
|
||||
}*/
|
||||
}
|
||||
|
||||
std::string strx::fromHex2String(const std::string& s)
|
||||
std::string StringHelper::fromHex2String(const std::string& s)
|
||||
{
|
||||
std::string result; result.resize(s.size() / 2);
|
||||
const char* t = s.c_str();
|
||||
for (size_t i = 0; i < result.size(); i++)
|
||||
result[i] = static_cast<char>((hex2code(t[i*2]) << 4) | hex2code(t[i*2+1]));
|
||||
result[i] = hex2code(t[i*2]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string strx::replace(const std::string& s, char f, char r)
|
||||
std::string StringHelper::replace(const std::string& s, char f, char r)
|
||||
{
|
||||
std::string result(s);
|
||||
for (std::string::size_type i = 0; i < result.size(); i++)
|
||||
if (result[i] == f)
|
||||
if (result[i] == 'f')
|
||||
result[i] = r;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string strx::replace(const std::string& s, const std::string& tmpl, const std::string& n)
|
||||
std::string StringHelper::replace(const std::string& s, const std::string& tmpl, const std::string& n)
|
||||
{
|
||||
std::string result(s);
|
||||
std::string::size_type p = 0;
|
||||
@@ -359,20 +356,19 @@ std::string strx::replace(const std::string& s, const std::string& tmpl, const s
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string strx::decodeUri(const std::string& s)
|
||||
std::string StringHelper::decodeUri(const std::string& s)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(s.size());
|
||||
|
||||
char ch;
|
||||
|
||||
int i, ii = 0;
|
||||
int i, ii;
|
||||
for (i=0; i<(int)s.length(); i++)
|
||||
{
|
||||
if (s[i] == '%' && i + 2 < (int)s.length())
|
||||
{
|
||||
if (sscanf(s.substr(i+1,2).c_str(), "%x", &ii) == 1)
|
||||
if (s[i] == 37)
|
||||
{
|
||||
sscanf(s.substr(i+1,2).c_str(), "%x", &ii);
|
||||
ch = static_cast<char>(ii);
|
||||
ret += ch;
|
||||
i += 2;
|
||||
@@ -380,27 +376,22 @@ std::string strx::decodeUri(const std::string& s)
|
||||
else
|
||||
ret += s[i];
|
||||
}
|
||||
else
|
||||
ret += s[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool strx::startsWith(const std::string& s, const std::string& prefix)
|
||||
bool StringHelper::startsWith(const std::string& s, const std::string& prefix)
|
||||
{
|
||||
if (prefix.size() > s.size())
|
||||
return false;
|
||||
return s.compare(0, prefix.size(), prefix) == 0;
|
||||
std::string::size_type p = s.find(prefix);
|
||||
return p == 0;
|
||||
}
|
||||
|
||||
bool strx::endsWith(const std::string& s, const std::string& suffix)
|
||||
bool StringHelper::endsWith(const std::string& s, const std::string& suffix)
|
||||
{
|
||||
if (suffix.size() > s.size())
|
||||
return false;
|
||||
return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
std::string::size_type p = s.rfind(suffix);
|
||||
return (p == s.size() - suffix.size());
|
||||
}
|
||||
|
||||
int strx::stringToDuration(const std::string& s)
|
||||
int StringHelper::stringToDuration(const std::string& s)
|
||||
{
|
||||
if (endsWith(s, "ms"))
|
||||
return std::stoi(s.substr(0, s.size()-2));
|
||||
@@ -414,52 +405,92 @@ int strx::stringToDuration(const std::string& s)
|
||||
return std::stoi(s) * 1000;
|
||||
}
|
||||
|
||||
std::string strx::uppercase(const std::string& s)
|
||||
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
|
||||
// --------------------- XcapHelper -----------------
|
||||
std::string XcapHelper::buildBuddyList(std::string listName, std::vector<std::string> buddies)
|
||||
{
|
||||
std::string r(s);
|
||||
std::transform(r.begin(), r.end(), r.begin(), ::toupper);
|
||||
return r;
|
||||
}
|
||||
std::ostringstream result;
|
||||
result << XML_HEADER <<
|
||||
"<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">" <<
|
||||
"<list name=\"" << listName.c_str() << "\">";
|
||||
|
||||
std::string strx::lowercase(const std::string& s)
|
||||
// to test CT only!
|
||||
//result << "<entry uri=\"" << "sip:dbogovych1@10.11.1.25" << "\"/>";
|
||||
//result << "<entry uri=\"" << "sip:dbogovych2@10.11.1.25" << "\"/>";
|
||||
|
||||
for (unsigned i = 0; i<buddies.size(); i++)
|
||||
{
|
||||
std::string r(s);
|
||||
std::transform(r.begin(), r.end(), r.begin(), ::tolower);
|
||||
return r;
|
||||
result << "<entry uri=\"" << normalizeSipUri(buddies[i]).c_str() << "\"/>";
|
||||
}
|
||||
result << "</list></resource-lists>";
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string strx::removeQuotes(const std::string& s)
|
||||
std::string XcapHelper::buildRules(std::vector<std::string> buddies)
|
||||
{
|
||||
std::string r(s);
|
||||
if (s.empty())
|
||||
return s;
|
||||
std::ostringstream result;
|
||||
result << XML_HEADER <<
|
||||
"<ruleset xmlns=\"urn:ietf:params:xml:ns:common-policy\">" <<
|
||||
"<rule id=\"presence_allow\">" <<
|
||||
"<conditions>";
|
||||
|
||||
if (r.front() == '"')
|
||||
r = r.substr(1);
|
||||
|
||||
if (r.back() == '"')
|
||||
r = r.substr(0, r.size()-1);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
|
||||
// MSVC++ lacks memmem support
|
||||
const void *memmem(const void *haystack, size_t haystack_len,
|
||||
const void * const needle, const size_t needle_len)
|
||||
for (unsigned i = 0; i<buddies.size(); i++)
|
||||
{
|
||||
if (!haystack || !haystack_len || !needle || !needle_len)
|
||||
return nullptr;
|
||||
result << "<identity><one id=\"" <<
|
||||
normalizeSipUri(buddies[i]).c_str() << "\"/></identity>";
|
||||
}
|
||||
result << "</conditions>" <<
|
||||
"<actions>" <<
|
||||
"<sub-handling xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"allow" <<
|
||||
"</sub-handling>" <<
|
||||
"</actions>" <<
|
||||
"<transformations>" <<
|
||||
"<provide-devices xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"<all-devices/>" <<
|
||||
"</provide-devices>" <<
|
||||
"<provide-persons xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"<all-persons/>" <<
|
||||
"</provide-persons>" <<
|
||||
"<provide-services xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"<all-services/>" <<
|
||||
"</provide-services>" <<
|
||||
"</transformations>" <<
|
||||
"</rule>" <<
|
||||
"</ruleset>";
|
||||
return result.str();
|
||||
}
|
||||
|
||||
for (const char *h = (const char*)haystack;
|
||||
haystack_len >= needle_len;
|
||||
++h, --haystack_len) {
|
||||
if (!memcmp(h, needle, needle_len)) {
|
||||
return h;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
std::string XcapHelper::buildServices(std::string serviceUri, std::string listRef)
|
||||
{
|
||||
std::ostringstream result;
|
||||
|
||||
result << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl <<
|
||||
"<rls-services xmlns=\"urn:ietf:params:xml:ns:rls-services\"" << std::endl <<
|
||||
"xmlns:rl=\"urn:ietf:params:xml:ns:resource-lists\"" << std::endl <<
|
||||
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << std::endl <<
|
||||
"<service uri=\"" << normalizeSipUri(serviceUri).c_str() << "\">" << std::endl <<
|
||||
"<resource-list>" << listRef.c_str() << "</resource-list>" << std::endl <<
|
||||
"<packages>" << std::endl <<
|
||||
"<package>presence</package>" << std::endl <<
|
||||
"</packages>" << std::endl <<
|
||||
"</service>" << std::endl <<
|
||||
"</rls-services>";
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string XcapHelper::normalizeSipUri(std::string uri)
|
||||
{
|
||||
if (uri.length())
|
||||
{
|
||||
if (uri[0] == '<')
|
||||
uri.erase(0,1);
|
||||
if (uri.length())
|
||||
{
|
||||
if (uri[uri.length()-1] == '>')
|
||||
uri.erase(uri.length()-1, 1);
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cstdint>
|
||||
#include "HL_Types.h"
|
||||
|
||||
#ifdef TARGET_OSX
|
||||
@@ -17,7 +16,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
class strx
|
||||
class StringHelper
|
||||
{
|
||||
public:
|
||||
static std::string extractFilename(const std::string& path);
|
||||
@@ -26,7 +25,6 @@ public:
|
||||
static std::string makeUtf8(const std::tstring& arg);
|
||||
static std::string toUtf8(const std::tstring& arg);
|
||||
static std::tstring makeTstring(const std::string& arg);
|
||||
|
||||
static int toInt(const char* s, int defaultValue, bool* isOk = nullptr);
|
||||
static uint64_t toUint64(const char* s, uint64_t def, bool *isOk = nullptr);
|
||||
static std::string toHex(unsigned int value);
|
||||
@@ -35,13 +33,8 @@ public:
|
||||
static std::string intToString(int value);
|
||||
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
||||
static std::string doubleToString(double value, int precision);
|
||||
static int fromHex2Int(const std::string& s);
|
||||
static std::string fromHex2String(const std::string& s);
|
||||
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
|
||||
|
||||
|
||||
static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength);
|
||||
|
||||
static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
|
||||
static std::vector<std::string> split(const std::string& src, const std::string& delims = "\n");
|
||||
|
||||
@@ -51,7 +44,7 @@ public:
|
||||
std::ostringstream s;
|
||||
for (const auto& i : v)
|
||||
{
|
||||
if (&i != &v.front())
|
||||
if (&i != &v[0])
|
||||
s << delimiter;
|
||||
s << i;
|
||||
}
|
||||
@@ -60,19 +53,18 @@ public:
|
||||
|
||||
static std::pair<std::string, int> parseHost(const std::string& host, int defaultPort);
|
||||
static std::pair<std::string, std::string> parseAssignment(const std::string& s, bool trimQuotes = true);
|
||||
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
|
||||
static std::string trim(const std::string& s);
|
||||
static std::string timeToString(time_t t);
|
||||
static std::string millisecondsToString(uint64_t t);
|
||||
static int fromHex2Int(const std::string& s);
|
||||
static std::string fromHex2String(const std::string& s);
|
||||
static std::string replace(const std::string& s, char f, char r);
|
||||
static std::string replace(const std::string& s, const std::string& tmpl, const std::string& n);
|
||||
static std::string decodeUri(const std::string& s);
|
||||
static bool startsWith(const std::string& s, const std::string& prefix);
|
||||
static bool endsWith(const std::string& s, const std::string& suffix);
|
||||
static int stringToDuration(const std::string& s);
|
||||
|
||||
static std::string uppercase(const std::string& s);
|
||||
static std::string lowercase(const std::string& s);
|
||||
static std::string removeQuotes(const std::string& s);
|
||||
};
|
||||
|
||||
class XcapHelper
|
||||
@@ -85,11 +77,4 @@ public:
|
||||
};
|
||||
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
|
||||
// MSVC++ lacks memmem support
|
||||
extern const void *memmem(const void *haystack, size_t haystack_len,
|
||||
const void * const needle, const size_t needle_len);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
void SyncHelper::delay(unsigned int microseconds)
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
// Round up so sub-millisecond delays do not become Sleep(0)
|
||||
::Sleep((microseconds + 999) / 1000);
|
||||
::Sleep(microseconds/1000);
|
||||
#endif
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
timespec requested, remaining;
|
||||
@@ -33,12 +32,24 @@ void SyncHelper::delay(unsigned int microseconds)
|
||||
#endif
|
||||
}
|
||||
|
||||
long SyncHelper::increment(long *value)
|
||||
{
|
||||
assert(value);
|
||||
#ifdef TARGET_WIN
|
||||
return ::InterlockedIncrement((LONG*)value);
|
||||
#elif TARGET_OSX
|
||||
return OSAtomicIncrement32((int32_t*)value);
|
||||
#elif TARGET_LINUX
|
||||
return -1;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------- ThreadHelper -------------------
|
||||
void ThreadHelper::setName(const std::string &name)
|
||||
{
|
||||
#if defined(TARGET_LINUX)
|
||||
// The name will be truncated to 8 or 16 characters
|
||||
int retcode = pthread_setname_np(pthread_self(), name.c_str());
|
||||
if (retcode != 0)
|
||||
{
|
||||
@@ -66,77 +77,46 @@ uint64_t ThreadHelper::getCurrentId()
|
||||
// ------------------- TimeHelper ---------------
|
||||
using namespace std::chrono;
|
||||
|
||||
// Milliseconds starting from the epoch
|
||||
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
||||
|
||||
// Seconds starting from the epoch
|
||||
static time_t TimestampBase = time(nullptr);
|
||||
|
||||
// Returns number of milliseconds starting from 01 Jan 1970 GMT
|
||||
std::chrono::milliseconds chronox::getTimestamp()
|
||||
{
|
||||
time_point<steady_clock> t = steady_clock::now();
|
||||
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
||||
return std::chrono::milliseconds(ms - TimestampStartPoint + TimestampBase * 1000);
|
||||
}
|
||||
|
||||
std::chrono::milliseconds chronox::getUptime()
|
||||
uint64_t TimeHelper::getTimestamp()
|
||||
{
|
||||
time_point<steady_clock> t = steady_clock::now();
|
||||
|
||||
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
||||
|
||||
return std::chrono::milliseconds(ms - TimestampStartPoint);
|
||||
return ms - TimestampStartPoint + TimestampBase * 1000;
|
||||
}
|
||||
|
||||
uint32_t chronox::getDelta(uint32_t later, uint32_t earlier)
|
||||
uint64_t TimeHelper::getUptime()
|
||||
{
|
||||
time_point<steady_clock> t = steady_clock::now();
|
||||
|
||||
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
|
||||
|
||||
return ms - TimestampStartPoint;
|
||||
}
|
||||
|
||||
uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier)
|
||||
{
|
||||
if (later > earlier)
|
||||
return later - earlier;
|
||||
|
||||
// Counter wrapped: unsigned subtraction yields the correct modulo-2^32 delta
|
||||
if (later < earlier && later < 0x7FFFFFFF && earlier >= 0x7FFFFFFF)
|
||||
return later - earlier;
|
||||
return 0xFFFFFFFF - earlier + later;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
timespec chronox::toTimespec(uint64_t milliseconds)
|
||||
TimeHelper::ExecutionTime::ExecutionTime()
|
||||
{
|
||||
timespec r;
|
||||
r.tv_sec = milliseconds / 1000;
|
||||
r.tv_nsec = milliseconds % 1000;
|
||||
r.tv_nsec *= 1000 * 1000;
|
||||
return r;
|
||||
mStart = TimeHelper::getTimestamp();
|
||||
}
|
||||
|
||||
uint64_t chronox::toTimestamp(const timeval& ts)
|
||||
uint64_t TimeHelper::ExecutionTime::getSpentTime() const
|
||||
{
|
||||
return (uint64_t)ts.tv_sec * 1000 + ts.tv_usec / 1000;
|
||||
}
|
||||
|
||||
int64_t chronox::getDelta(const timespec& a, const timespec& b)
|
||||
{
|
||||
int64_t ms_a = (int64_t)a.tv_sec * 1000 + a.tv_nsec / 1000000;
|
||||
int64_t ms_b = (int64_t)b.tv_sec * 1000 + b.tv_nsec / 1000000;
|
||||
return ms_a - ms_b;
|
||||
}
|
||||
|
||||
int64_t chronox::getDelta(const timeval& a, const timeval& b)
|
||||
{
|
||||
int64_t diff_seconds = a.tv_sec - b.tv_sec;
|
||||
int64_t diff_microseconds = a.tv_usec - b.tv_usec;
|
||||
return diff_seconds * 1000 + diff_microseconds / 1000;
|
||||
}
|
||||
|
||||
chronox::ExecutionTime::ExecutionTime()
|
||||
{
|
||||
mStart = chronox::getTimestamp();
|
||||
}
|
||||
|
||||
std::chrono::milliseconds chronox::ExecutionTime::getSpentTime() const
|
||||
{
|
||||
return chronox::getTimestamp() - mStart;
|
||||
return TimeHelper::getTimestamp() - mStart;
|
||||
}
|
||||
|
||||
// --------------- BufferQueue -----------------
|
||||
@@ -164,11 +144,13 @@ void BufferQueue::push(const void* data, int bytes)
|
||||
BufferQueue::PBlock BufferQueue::pull(int milliseconds)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
mSignal.wait_for(l, std::chrono::milliseconds(milliseconds),
|
||||
[this]() { return !mBlockList.empty(); });
|
||||
std::cv_status status = mBlockList.empty() ? std::cv_status::timeout : std::cv_status::no_timeout;
|
||||
|
||||
if (mBlockList.empty())
|
||||
status = mSignal.wait_for(l, std::chrono::milliseconds(milliseconds));
|
||||
|
||||
PBlock r;
|
||||
if (!mBlockList.empty())
|
||||
if (status == std::cv_status::no_timeout && !mBlockList.empty())
|
||||
{
|
||||
r = mBlockList.front();
|
||||
mBlockList.pop_front();
|
||||
@@ -196,10 +178,10 @@ void Semaphore::wait()
|
||||
m_count--;
|
||||
}
|
||||
|
||||
bool Semaphore::waitFor(std::chrono::milliseconds timeout) {
|
||||
bool Semaphore::waitFor(int milliseconds) {
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
|
||||
if (!m_cv.wait_for(lock, timeout, [this]() { return m_count > 0; }))
|
||||
if (!m_cv.wait_for(lock, std::chrono::milliseconds(milliseconds), [this]() { return m_count > 0; }))
|
||||
return false;
|
||||
|
||||
m_count--;
|
||||
@@ -280,8 +262,7 @@ size_t TimerQueue::cancel(uint64_t id) {
|
||||
//! Cancels all timers
|
||||
// \return
|
||||
// The number of timers cancelled
|
||||
size_t TimerQueue::cancelAll()
|
||||
{
|
||||
size_t TimerQueue::cancelAll() {
|
||||
// Setting all "end" to 0 (for immediate execution) is ok,
|
||||
// since it maintains the heap integrity
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
@@ -306,8 +287,11 @@ void TimerQueue::run()
|
||||
auto end = calcWaitTime();
|
||||
if (end.first)
|
||||
{
|
||||
// Timers found, so wait until it expires (or something else changes)
|
||||
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end.second - std::chrono::steady_clock::now());
|
||||
// Timers found, so wait until it expires (or something else
|
||||
// changes)
|
||||
int milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(end.second - std::chrono::steady_clock::now()).count();
|
||||
//std::cout << "Waiting m_checkWork for " << milliseconds * 1000 << "ms." << std::endl;
|
||||
m_checkWork.waitFor(milliseconds);
|
||||
} else {
|
||||
// No timers exist, so wait forever until something changes
|
||||
|
||||
@@ -14,11 +14,6 @@
|
||||
#include <functional>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
typedef std::recursive_mutex Mutex;
|
||||
typedef std::unique_lock<std::recursive_mutex> Lock;
|
||||
|
||||
@@ -26,6 +21,7 @@ class SyncHelper
|
||||
{
|
||||
public:
|
||||
static void delay(unsigned microseconds);
|
||||
static long increment(long* value);
|
||||
};
|
||||
|
||||
class Semaphore
|
||||
@@ -35,7 +31,7 @@ public:
|
||||
|
||||
void notify();
|
||||
void wait();
|
||||
bool waitFor(std::chrono::milliseconds timeout);
|
||||
bool waitFor(int milliseconds);
|
||||
|
||||
private:
|
||||
std::mutex m_mtx;
|
||||
@@ -50,34 +46,26 @@ public:
|
||||
static uint64_t getCurrentId();
|
||||
};
|
||||
|
||||
class chronox
|
||||
class TimeHelper
|
||||
{
|
||||
public:
|
||||
// Returns current timestamp in milliseconds
|
||||
static std::chrono::milliseconds getTimestamp();
|
||||
static uint64_t getTimestamp();
|
||||
|
||||
// Returns uptime (of calling process) in milliseconds
|
||||
static std::chrono::milliseconds getUptime();
|
||||
static uint64_t getUptime();
|
||||
|
||||
// Finds time delta between 'later' and 'earlier' time points.
|
||||
// Handles cases when clock is wrapped.
|
||||
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
||||
|
||||
// Converts number of milliseconds starting from Epoch begin to timespec.
|
||||
static timespec toTimespec(uint64_t milliseconds);
|
||||
static uint64_t toTimestamp(const timeval& ts);
|
||||
|
||||
// Returns difference between timestamps in milliseconds
|
||||
static int64_t getDelta(const timespec& a, const timespec& b);
|
||||
static int64_t getDelta(const timeval& a, const timeval& b);
|
||||
|
||||
class ExecutionTime
|
||||
{
|
||||
public:
|
||||
ExecutionTime();
|
||||
std::chrono::milliseconds getSpentTime() const;
|
||||
uint64_t getSpentTime() const;
|
||||
protected:
|
||||
std::chrono::milliseconds mStart;
|
||||
uint64_t mStart;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ thread_pool::thread_pool(size_t num_of_threads, const std::string& name)
|
||||
num_of_threads = std::thread::hardware_concurrency();
|
||||
|
||||
for(size_t idx = 0; idx < num_of_threads; idx++)
|
||||
this->workers.emplace_back(std::thread(&thread_pool::run_worker, this));
|
||||
this->workers.push_back(std::thread(&thread_pool::run_worker, this));
|
||||
}
|
||||
|
||||
// Add new work item to the pool
|
||||
@@ -20,22 +20,6 @@ void thread_pool::enqueue(const thread_pool::task& t)
|
||||
this->condition.notify_one();
|
||||
}
|
||||
|
||||
void thread_pool::wait(std::chrono::milliseconds interval)
|
||||
{
|
||||
while (size() != 0)
|
||||
std::this_thread::sleep_for(interval);
|
||||
}
|
||||
|
||||
size_t thread_pool::size()
|
||||
{
|
||||
std::unique_lock l(this->queue_mutex);
|
||||
return this->tasks.size();
|
||||
}
|
||||
|
||||
size_t thread_pool::threads()
|
||||
{
|
||||
return this->workers.size();
|
||||
}
|
||||
|
||||
// the destructor joins all threads
|
||||
thread_pool::~thread_pool()
|
||||
@@ -59,13 +43,9 @@ void thread_pool::run_worker()
|
||||
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
||||
|
||||
this->condition.wait(lock, [this]{return !this->tasks.empty() || this->stop;});
|
||||
if (!tasks.empty())
|
||||
{
|
||||
t = tasks.front();
|
||||
tasks.pop();
|
||||
t = this->tasks.front();
|
||||
this->tasks.pop();
|
||||
}
|
||||
}
|
||||
if (t)
|
||||
t(); // function<void()> type
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,10 @@ class thread_pool
|
||||
public:
|
||||
typedef std::function<void()> task;
|
||||
|
||||
thread_pool(size_t num_of_threads, const std::string& thread_name);
|
||||
thread_pool(size_t num_of_threads, const std::string&);
|
||||
~thread_pool();
|
||||
|
||||
void enqueue(const task& task);
|
||||
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
|
||||
size_t size();
|
||||
size_t threads();
|
||||
|
||||
private:
|
||||
// need to keep track of threads so we can join them
|
||||
@@ -36,7 +33,7 @@ private:
|
||||
// synchronization
|
||||
std::mutex queue_mutex;
|
||||
std::condition_variable condition;
|
||||
std::atomic_bool stop = false;
|
||||
bool stop = false;
|
||||
|
||||
// thread name prefix for worker threads
|
||||
std::string name;
|
||||
|
||||
@@ -1,53 +1,10 @@
|
||||
#include "HL_Time.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <chrono>
|
||||
|
||||
/* return current time in milliseconds */
|
||||
double now_ms(void)
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
auto tp = std::chrono::steady_clock::now();
|
||||
auto result = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count();
|
||||
return result;
|
||||
#else
|
||||
double now_ms(void) {
|
||||
struct timespec res;
|
||||
clock_gettime(CLOCK_MONOTONIC, &res);
|
||||
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
|
||||
#endif
|
||||
}
|
||||
|
||||
int compare_timespec(const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
if (lhs.tv_sec < rhs.tv_sec)
|
||||
return -1;
|
||||
if (lhs.tv_sec > rhs.tv_sec)
|
||||
return 1;
|
||||
|
||||
if (lhs.tv_nsec < rhs.tv_nsec)
|
||||
return -1;
|
||||
if (lhs.tv_nsec > rhs.tv_nsec)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator < (const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
bool operator == (const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) == 0;
|
||||
}
|
||||
|
||||
bool operator > (const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) > 0;
|
||||
}
|
||||
|
||||
bool is_zero(const timespec& ts)
|
||||
{
|
||||
return !ts.tv_sec && !ts.tv_nsec;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
#ifndef __HELPER_TIME_H
|
||||
#define __HELPER_TIME_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
// Return current monotonic number of milliseconds starting from some point
|
||||
extern double now_ms();
|
||||
|
||||
// Compare the timespec.
|
||||
// Returns -1 if lhs < rhs, 1 if lhs > rhs, 0 if equal
|
||||
extern int compare_timespec(const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator < (const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator == (const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator > (const timespec& lhs, const timespec& rhs);
|
||||
extern bool is_zero(const timespec& ts);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "HL_Types.h"
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -26,139 +26,4 @@ enum SdpDirection
|
||||
Sdp_Offer
|
||||
};
|
||||
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <map>
|
||||
|
||||
template<
|
||||
class K, class V,
|
||||
class HashK = std::hash<K>, class EqK = std::equal_to<K>,
|
||||
class HashV = std::hash<V>, class EqV = std::equal_to<V>
|
||||
>
|
||||
class BiMap {
|
||||
public:
|
||||
using key_type = K;
|
||||
using mapped_type = V;
|
||||
|
||||
BiMap(const std::map<K,V>& initializers) {
|
||||
for (const auto& item: initializers) {
|
||||
insert(item.first, item.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert a new (key, value) pair. Returns false if either key or value already exists.
|
||||
bool insert(const K& k, const V& v) {
|
||||
if (contains_key(k) || contains_value(v)) return false;
|
||||
auto ok = forward_.emplace(k, v);
|
||||
try {
|
||||
auto ov = reverse_.emplace(v, k);
|
||||
if (!ov.second) { // shouldn't happen given the guard above
|
||||
forward_.erase(k);
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
forward_.erase(k);
|
||||
throw;
|
||||
}
|
||||
return ok.second;
|
||||
}
|
||||
|
||||
bool insert(K&& k, V&& v) {
|
||||
if (contains_key(k) || contains_value(v)) return false;
|
||||
auto ok = forward_.emplace(std::move(k), std::move(v));
|
||||
try {
|
||||
auto ov = reverse_.emplace(ok.first->second, ok.first->first); // use stored refs
|
||||
if (!ov.second) {
|
||||
forward_.erase(ok.first);
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
forward_.erase(ok.first);
|
||||
throw;
|
||||
}
|
||||
return ok.second;
|
||||
}
|
||||
|
||||
// Replace value for existing key (and update reverse map). Returns false if value is already bound elsewhere.
|
||||
bool replace_by_key(const K& k, const V& new_v) {
|
||||
auto it = forward_.find(k);
|
||||
if (it == forward_.end()) return false;
|
||||
if (contains_value(new_v)) return false;
|
||||
// remove old reverse, insert new reverse, then update forward
|
||||
reverse_.erase(it->second);
|
||||
reverse_.emplace(new_v, k);
|
||||
it->second = new_v;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replace key for existing value (and update forward map). Returns false if key is already bound elsewhere.
|
||||
bool replace_by_value(const V& v, const K& new_k) {
|
||||
auto it = reverse_.find(v);
|
||||
if (it == reverse_.end()) return false;
|
||||
if (contains_key(new_k)) return false;
|
||||
forward_.erase(it->second);
|
||||
forward_.emplace(new_k, v);
|
||||
it->second = new_k;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Erase by key/value. Return number erased (0 or 1).
|
||||
size_t erase_key(const K& k) {
|
||||
auto it = forward_.find(k);
|
||||
if (it == forward_.end()) return 0;
|
||||
reverse_.erase(it->second);
|
||||
forward_.erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t erase_value(const V& v) {
|
||||
auto it = reverse_.find(v);
|
||||
if (it == reverse_.end()) return 0;
|
||||
forward_.erase(it->second);
|
||||
reverse_.erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lookup
|
||||
bool contains_key(const K& k) const { return forward_.find(k) != forward_.end(); }
|
||||
bool contains_value(const V& v) const { return reverse_.find(v) != reverse_.end(); }
|
||||
|
||||
const V* find_by_key(const K& k) const {
|
||||
auto it = forward_.find(k);
|
||||
return (it == forward_.end()) ? nullptr : &it->second;
|
||||
}
|
||||
const K* find_by_value(const V& v) const {
|
||||
auto it = reverse_.find(v);
|
||||
return (it == reverse_.end()) ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
// at() variants throw std::out_of_range on missing entries
|
||||
const V& at_key(const K& k) const { return forward_.at(k); }
|
||||
const K& at_value(const V& v) const { return reverse_.at(v); }
|
||||
|
||||
void clear() noexcept {
|
||||
forward_.clear();
|
||||
reverse_.clear();
|
||||
}
|
||||
|
||||
bool empty() const noexcept { return forward_.empty(); }
|
||||
size_t size() const noexcept { return forward_.size(); }
|
||||
|
||||
// Reserve buckets for performance (optional)
|
||||
void reserve(size_t n) {
|
||||
forward_.reserve(n);
|
||||
reverse_.reserve(n);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<K, V, HashK, EqK> forward_;
|
||||
std::unordered_map<V, K, HashV, EqV> reverse_;
|
||||
};
|
||||
|
||||
#include <chrono>
|
||||
typedef std::chrono::steady_clock::time_point timepoint_t;
|
||||
typedef std::chrono::steady_clock monoclock_t;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,57 +1,77 @@
|
||||
#include "HL_Uuid.h"
|
||||
#include <memory.h>
|
||||
#include <random>
|
||||
#include <span>
|
||||
|
||||
#if defined(TARGET_LINUX)
|
||||
#define UUID_SYSTEM_GENERATOR
|
||||
#endif
|
||||
|
||||
#include "uuid.h"
|
||||
|
||||
Uuid::Uuid()
|
||||
{
|
||||
#if defined(TARGET_WIN) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
memset(&mUuid, 0, sizeof mUuid);
|
||||
#endif
|
||||
}
|
||||
|
||||
Uuid Uuid::generateOne()
|
||||
{
|
||||
Uuid result;
|
||||
// #if defined(TARGET_LINUX)
|
||||
// auto id = uuids::uuid_system_generator{}();
|
||||
// #else
|
||||
std::random_device rd;
|
||||
auto seed_data = std::array<int, std::mt19937::state_size> {};
|
||||
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
|
||||
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
|
||||
std::mt19937 generator(seq);
|
||||
uuids::uuid_random_generator gen{generator};
|
||||
|
||||
auto id = gen();
|
||||
// #endif
|
||||
memcpy(result.mUuid, id.as_bytes().data(), id.as_bytes().size_bytes());
|
||||
#if !defined(USE_NULL_UUID)
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
uuid_generate(result.mUuid);
|
||||
#endif
|
||||
#if defined(TARGET_WIN)
|
||||
UuidCreate(&result.mUuid);
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
Uuid Uuid::parse(const std::string &s)
|
||||
{
|
||||
Uuid result;
|
||||
auto id = uuids::uuid::from_string(s);
|
||||
if (id)
|
||||
memcpy(result.mUuid, id->as_bytes().data(), id->as_bytes().size_bytes());
|
||||
#if !defined(USE_NULL_UUID)
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
uuid_parse(s.c_str(), result.mUuid);
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
UuidFromStringA((RPC_CSTR)s.c_str(), &result.mUuid);
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Uuid::toString() const
|
||||
{
|
||||
auto s = std::span<uuids::uuid::value_type, 16>{(uuids::uuid::value_type*)mUuid, 16};
|
||||
uuids::uuid id(s);
|
||||
return uuids::to_string(id);
|
||||
#if defined(USE_NULL_UUID)
|
||||
return "UUID_disabled";
|
||||
#else
|
||||
char buf[64];
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
uuid_unparse_lower(mUuid, buf);
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
RPC_CSTR s = nullptr;
|
||||
UuidToStringA(&mUuid, &s);
|
||||
if (s)
|
||||
{
|
||||
strcpy(buf, (const char*)s);
|
||||
RpcStringFreeA(&s);
|
||||
s = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Uuid::operator < (const Uuid& right) const
|
||||
{
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
return memcmp(&mUuid, &right.mUuid, sizeof(mUuid)) < 0;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,14 @@
|
||||
#define __HL_UUID_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#if (defined(TARGET_LINUX) || defined(TARGET_OSX)) && !defined(USE_NULL_UUID)
|
||||
// Please do not forget "sudo apt install uuid-dev" on Ubuntu
|
||||
# include <uuid/uuid.h>
|
||||
#endif
|
||||
#if defined(TARGET_WIN)
|
||||
# include <rpc.h>
|
||||
#endif
|
||||
|
||||
class Uuid
|
||||
{
|
||||
@@ -14,7 +21,20 @@ public:
|
||||
bool operator < (const Uuid& right) const;
|
||||
|
||||
protected:
|
||||
uint8_t mUuid[16];
|
||||
#if defined(USE_NULL_UUID)
|
||||
unsigned char mUuid[16];
|
||||
#else
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
uuid_t mUuid;
|
||||
#endif
|
||||
#if defined(TARGET_WIN)
|
||||
UUID mUuid;
|
||||
#endif
|
||||
#if defined(TARGET_ANDROID)
|
||||
// Stub only
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ int64_t Variant::asInt64() const
|
||||
if (mType != VTYPE_INT64)
|
||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||
|
||||
return mInt64;
|
||||
return mInt;
|
||||
}
|
||||
|
||||
bool Variant::asBool() const
|
||||
@@ -299,18 +299,18 @@ std::string Variant::asStdString() const
|
||||
return mString;
|
||||
|
||||
case VTYPE_INT:
|
||||
std::snprintf(buffer, sizeof(buffer), "%d", mInt);
|
||||
sprintf(buffer, "%d", mInt);
|
||||
return buffer;
|
||||
|
||||
case VTYPE_INT64:
|
||||
std::snprintf(buffer, sizeof(buffer), "%lli", static_cast<long long int>(mInt64));
|
||||
sprintf(buffer, "%lli", static_cast<long long int>(mInt64));
|
||||
return buffer;
|
||||
|
||||
case VTYPE_BOOL:
|
||||
return mBool ? "true" : "false";
|
||||
|
||||
case VTYPE_FLOAT:
|
||||
std::snprintf(buffer, sizeof(buffer), "%f", mFloat);
|
||||
sprintf(buffer, "%f", mFloat);
|
||||
return buffer;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/* Copyright(C) 2007-2023 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "HL_Xcap.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <memory.h>
|
||||
#include <algorithm>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
# include <cctype>
|
||||
#endif
|
||||
|
||||
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
|
||||
// --------------------- XcapHelper -----------------
|
||||
std::string XcapHelper::buildBuddyList(const std::string& listName, const std::vector<std::string>& buddies)
|
||||
{
|
||||
std::ostringstream result;
|
||||
result << XML_HEADER <<
|
||||
"<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">" <<
|
||||
"<list name=\"" << listName.c_str() << "\">";
|
||||
|
||||
// to test CT only!
|
||||
//result << "<entry uri=\"" << "sip:dbogovych1@10.11.1.25" << "\"/>";
|
||||
//result << "<entry uri=\"" << "sip:dbogovych2@10.11.1.25" << "\"/>";
|
||||
|
||||
for (unsigned i = 0; i<buddies.size(); i++)
|
||||
{
|
||||
result << "<entry uri=\"" << normalizeSipUri(buddies[i]).c_str() << "\"/>";
|
||||
}
|
||||
result << "</list></resource-lists>";
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string XcapHelper::buildRules(const std::vector<std::string>& buddies)
|
||||
{
|
||||
std::ostringstream result;
|
||||
result << XML_HEADER <<
|
||||
"<ruleset xmlns=\"urn:ietf:params:xml:ns:common-policy\">" <<
|
||||
"<rule id=\"presence_allow\">" <<
|
||||
"<conditions>";
|
||||
|
||||
for (unsigned i = 0; i<buddies.size(); i++)
|
||||
{
|
||||
result << "<identity><one id=\"" <<
|
||||
normalizeSipUri(buddies[i]).c_str() << "\"/></identity>";
|
||||
}
|
||||
result << "</conditions>" <<
|
||||
"<actions>" <<
|
||||
"<sub-handling xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"allow" <<
|
||||
"</sub-handling>" <<
|
||||
"</actions>" <<
|
||||
"<transformations>" <<
|
||||
"<provide-devices xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"<all-devices/>" <<
|
||||
"</provide-devices>" <<
|
||||
"<provide-persons xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"<all-persons/>" <<
|
||||
"</provide-persons>" <<
|
||||
"<provide-services xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
|
||||
"<all-services/>" <<
|
||||
"</provide-services>" <<
|
||||
"</transformations>" <<
|
||||
"</rule>" <<
|
||||
"</ruleset>";
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string XcapHelper::buildServices(const std::string& serviceUri, const std::string& listRef)
|
||||
{
|
||||
std::ostringstream result;
|
||||
|
||||
result << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl <<
|
||||
"<rls-services xmlns=\"urn:ietf:params:xml:ns:rls-services\"" << std::endl <<
|
||||
"xmlns:rl=\"urn:ietf:params:xml:ns:resource-lists\"" << std::endl <<
|
||||
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << std::endl <<
|
||||
"<service uri=\"" << normalizeSipUri(serviceUri).c_str() << "\">" << std::endl <<
|
||||
"<resource-list>" << listRef.c_str() << "</resource-list>" << std::endl <<
|
||||
"<packages>" << std::endl <<
|
||||
"<package>presence</package>" << std::endl <<
|
||||
"</packages>" << std::endl <<
|
||||
"</service>" << std::endl <<
|
||||
"</rls-services>";
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string XcapHelper::normalizeSipUri(const std::string& uri)
|
||||
{
|
||||
std::string t(uri);
|
||||
|
||||
if (t.length())
|
||||
{
|
||||
if (t[0] == '<')
|
||||
t.erase(0,1);
|
||||
if (t.length())
|
||||
{
|
||||
if (t[t.length()-1] == '>')
|
||||
t.erase(uri.length()-1, 1);
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/* Copyright(C) 2007-2024 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __HELPER_STRING_H
|
||||
#define __HELPER_STRING_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
class XcapHelper
|
||||
{
|
||||
public:
|
||||
static std::string buildBuddyList(const std::string& listName, const std::vector<std::string>& buddies);
|
||||
static std::string buildRules(const std::vector<std::string>& buddies);
|
||||
static std::string buildServices(const std::string& serviceUri, const std::string& listRef);
|
||||
static std::string normalizeSipUri(const std::string& uri);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,16 +1,19 @@
|
||||
project (media_lib)
|
||||
|
||||
if (NOT DEFINED LIB_PLATFORM)
|
||||
message(FATAL_ERROR media_lib project requires LIB_PLATFORM to be set - it uses libraries from that directory)
|
||||
endif()
|
||||
|
||||
# Rely on C++ 20
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
# Rely on C++ 11
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Produce PIC code always
|
||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# Set of variables to control references to codecs
|
||||
set (USE_AMR_CODEC OFF CACHE BOOL "Use AMR codec. Requires libraries.")
|
||||
set (USE_EVS_CODEC OFF CACHE BOOL "Use EVS codec." )
|
||||
set (USE_OPUS_CODEC OFF CACHE BOOL "Use Opus codec." )
|
||||
set (USE_PVQA_LIB OFF CACHE BOOL "Build with Sevana PVQA library" )
|
||||
set (USE_AQUA_LIB OFF CACHE BOOL "Build with Sevana AQuA library" )
|
||||
|
||||
set (SOURCES
|
||||
MT_Statistics.cpp
|
||||
MT_WebRtc.cpp
|
||||
@@ -26,8 +29,6 @@ set (SOURCES
|
||||
MT_AudioReceiver.cpp
|
||||
MT_AudioCodec.cpp
|
||||
MT_CngHelper.cpp
|
||||
MT_AmrCodec.cpp
|
||||
MT_EvsCodec.cpp
|
||||
|
||||
MT_Statistics.h
|
||||
MT_WebRtc.h
|
||||
@@ -43,52 +44,44 @@ set (SOURCES
|
||||
MT_AudioReceiver.h
|
||||
MT_AudioCodec.h
|
||||
MT_CngHelper.h
|
||||
MT_AmrCodec.h
|
||||
MT_EvsCodec.h
|
||||
)
|
||||
|
||||
add_library(media_lib ${SOURCES})
|
||||
set (LIBS_CODEC)
|
||||
|
||||
if (USE_AMR_CODEC)
|
||||
include (${LIB_PLATFORM}/platform_libs.cmake)
|
||||
message("Media: AMR NB and WB codecs will be included.")
|
||||
target_compile_definitions(media_lib PUBLIC USE_AMR_CODEC)
|
||||
list (APPEND LIBS_CODEC ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
||||
message("AMR NB and WB codecs will be included.")
|
||||
add_definitions(-DUSE_AMR_CODEC)
|
||||
set(SOURCES ${SOURCES} MT_AmrCodec.cpp MT_AmrCodec.h)
|
||||
endif()
|
||||
|
||||
if (USE_EVS_CODEC)
|
||||
message("Media: EVS codec will be included.")
|
||||
target_compile_definitions (media_lib PUBLIC USE_EVS_CODEC)
|
||||
list (APPEND LIBS_CODEC evs_codec)
|
||||
message("EVS codec will be included.")
|
||||
add_definitions (-DUSE_EVS_CODEC)
|
||||
set (SOURCES ${SOURCES} MT_EvsCodec.cpp MT_EvsCodec.h)
|
||||
endif()
|
||||
|
||||
message("Media: Opus codec will be included.")
|
||||
list (APPEND LIBS_CODEC opus)
|
||||
|
||||
if(CMAKE_SYSTEM MATCHES "Linux*" OR CMAKE_SYSTEM MATCHES "Darwin*")
|
||||
target_compile_definitions(media_lib PUBLIC HAVE_NETINET_IN_H)
|
||||
if (USE_OPUS_CODEC)
|
||||
message("Opus codec will be included.")
|
||||
add_definitions(-DUSE_OPUS_CODEC)
|
||||
endif()
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM MATCHES "Linux*")
|
||||
add_definitions(-DHAVE_NETINET_IN_H)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM MATCHES "Darwin*")
|
||||
# OS X Specific flags
|
||||
add_definitions(-DHAVE_NETINET_IN_H)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||
# Windows Specific flags - MSVC expected
|
||||
target_compile_definitions(media_lib PRIVATE
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
HAVE_WINSOCK2_H
|
||||
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
|
||||
UNICODE
|
||||
_UNICODE )
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS -DHAVE_WINSOCK2_H
|
||||
-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -DUNICODE -D_UNICODE )
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
# Dependency on ice_stack - Linux build requires it
|
||||
# Codec libraries as well
|
||||
target_link_libraries(media_lib ice_stack ${LIBS_CODEC})
|
||||
|
||||
set_property(TARGET media_lib PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
add_library(media_lib ${SOURCES})
|
||||
# Depending on ice stack library to provide bit level operations
|
||||
target_link_libraries(media_lib PUBLIC ice_stack)
|
||||
|
||||
target_include_directories(media_lib
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/
|
||||
@@ -96,9 +89,9 @@ target_include_directories(media_lib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/srtp/crypto/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/webrtc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/opus/include/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/resiprocate/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs
|
||||
${LIB_PLATFORM}/opus/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/
|
||||
)
|
||||
|
||||
target_include_directories(media_lib
|
||||
@@ -108,5 +101,8 @@ target_include_directories(media_lib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_op
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../libs/libevs/basic_math
|
||||
)
|
||||
|
||||
if (USE_RESIP_INTEGRATION)
|
||||
message("USE_RESIP_INTEGRATION is turned on!")
|
||||
target_compile_definitions(media_lib PUBLIC -DUSE_RESIP_INTEGRATION)
|
||||
endif()
|
||||
|
||||
|
||||
+381
-289
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,8 @@
|
||||
#ifndef MT_AMRCODEC_H
|
||||
#define MT_AMRCODEC_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include <map>
|
||||
#include <span>
|
||||
|
||||
#include "MT_Codec.h"
|
||||
#include "../helper/HL_Pointer.h"
|
||||
|
||||
@@ -18,28 +16,21 @@ namespace MT
|
||||
{
|
||||
struct AmrCodecConfig
|
||||
{
|
||||
bool mIuUP = false;
|
||||
bool mOctetAligned = false;
|
||||
int mPayloadType = -1;
|
||||
bool mIuUP;
|
||||
bool mOctetAligned;
|
||||
int mPayloadType;
|
||||
};
|
||||
|
||||
class AmrNbCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
void* mEncoderCtx = nullptr;
|
||||
void* mDecoderCtx = nullptr;
|
||||
void* mEncoderCtx;
|
||||
void* mDecoderCtx;
|
||||
AmrCodecConfig mConfig;
|
||||
unsigned mCurrentDecoderTimestamp = 0;
|
||||
int mPreviousPacketLength = 0;
|
||||
size_t mCngCounter = 0;
|
||||
size_t mSwitchCounter = 0;
|
||||
unsigned mCurrentDecoderTimestamp;
|
||||
int mSwitchCounter;
|
||||
int mPreviousPacketLength;
|
||||
|
||||
// opencore-amr encoder/decoder state is allocated lazily on first encode/decode.
|
||||
// Network-MOS-only streams resolve codec metadata (name/samplerate/frame timing)
|
||||
// but never decode, so they must not pay for a context they never use - at scale
|
||||
// this is ~a decoder state (several KB) saved per network-only stream.
|
||||
void ensureEncoder();
|
||||
void ensureDecoder();
|
||||
public:
|
||||
class CodecFactory: public Factory
|
||||
{
|
||||
@@ -50,10 +41,11 @@ public:
|
||||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
void create(CodecMap& codecs) override;
|
||||
|
||||
#endif
|
||||
PCodec create() override;
|
||||
|
||||
protected:
|
||||
@@ -61,44 +53,29 @@ public:
|
||||
};
|
||||
|
||||
AmrNbCodec(const AmrCodecConfig& config);
|
||||
~AmrNbCodec();
|
||||
|
||||
Info info() override;
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
|
||||
virtual ~AmrNbCodec();
|
||||
const char* name() override;
|
||||
int pcmLength() override;
|
||||
int rtpLength() override;
|
||||
int frameTime() override;
|
||||
int samplerate() override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
int getSwitchCounter() const;
|
||||
int getCngCounter() const;
|
||||
};
|
||||
|
||||
struct AmrWbStatistics
|
||||
{
|
||||
int mDiscarded = 0;
|
||||
int mNonParsed = 0;
|
||||
};
|
||||
extern AmrWbStatistics GAmrWbStatistics;
|
||||
|
||||
class AmrWbCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
void* mEncoderCtx = nullptr;
|
||||
void* mDecoderCtx = nullptr;
|
||||
void* mEncoderCtx;
|
||||
void* mDecoderCtx;
|
||||
AmrCodecConfig mConfig;
|
||||
uint64_t mCurrentDecoderTimestamp = 0;
|
||||
size_t mSwitchCounter = 0;
|
||||
size_t mCngCounter = 0;
|
||||
|
||||
uint64_t mCurrentDecoderTimestamp;
|
||||
int mSwitchCounter;
|
||||
int mPreviousPacketLength;
|
||||
|
||||
// Decoder state is allocated lazily on first decode/plc (see AmrNbCodec) so
|
||||
// network-MOS-only streams never instantiate the AMR-WB decoder.
|
||||
void ensureDecoder();
|
||||
|
||||
DecodeResult decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output);
|
||||
DecodeResult decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output);
|
||||
|
||||
public:
|
||||
class CodecFactory: public Factory
|
||||
{
|
||||
@@ -109,10 +86,11 @@ public:
|
||||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
void create(CodecMap& codecs) override;
|
||||
|
||||
#endif
|
||||
PCodec create() override;
|
||||
|
||||
protected:
|
||||
@@ -122,21 +100,23 @@ public:
|
||||
AmrWbCodec(const AmrCodecConfig& config);
|
||||
virtual ~AmrWbCodec();
|
||||
|
||||
Info info() override;
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
const char* name() override;
|
||||
int pcmLength() override;
|
||||
int rtpLength() override;
|
||||
int frameTime() override;
|
||||
int samplerate() override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
int getSwitchCounter() const;
|
||||
int getCngCounter() const;
|
||||
};
|
||||
|
||||
class GsmEfrCodec : public Codec
|
||||
{
|
||||
protected:
|
||||
void* mEncoderCtx = nullptr;
|
||||
void* mDecoderCtx = nullptr;
|
||||
bool mIuUP = false;
|
||||
void* mEncoderCtx;
|
||||
void* mDecoderCtx;
|
||||
bool mIuUP;
|
||||
|
||||
public:
|
||||
class GsmEfrFactory: public Factory
|
||||
@@ -148,24 +128,29 @@ public:
|
||||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#ifdef USE_RESIP_INTEGRATION
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
void create(CodecMap& codecs) override;
|
||||
|
||||
#endif
|
||||
PCodec create() override;
|
||||
|
||||
protected:
|
||||
bool mIuUP;
|
||||
int mPayloadType;
|
||||
};
|
||||
|
||||
GsmEfrCodec(bool iuup = false);
|
||||
~GsmEfrCodec();
|
||||
|
||||
Info info() override;
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
virtual ~GsmEfrCodec();
|
||||
const char* name() override;
|
||||
int pcmLength() override;
|
||||
int rtpLength() override;
|
||||
int frameTime() override;
|
||||
int samplerate() override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
};
|
||||
|
||||
} // End of MT namespace
|
||||
|
||||
+386
-362
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
#ifndef __AUDIO_CODEC_H
|
||||
#define __AUDIO_CODEC_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include <map>
|
||||
#include "MT_Codec.h"
|
||||
#include "../audio/Audio_Resampler.h"
|
||||
@@ -24,7 +24,9 @@ extern "C"
|
||||
#include "libg729/g729_typedef.h"
|
||||
#include "libg729/g729_ld8a.h"
|
||||
|
||||
#if defined(USE_OPUS_CODEC)
|
||||
# include "opus.h"
|
||||
#endif
|
||||
|
||||
namespace MT
|
||||
{
|
||||
@@ -44,44 +46,45 @@ public:
|
||||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
|
||||
#endif
|
||||
PCodec create() override;
|
||||
};
|
||||
G729Codec();
|
||||
~G729Codec() override;
|
||||
|
||||
Info info() override;
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
const char* name() override;
|
||||
int pcmLength() override;
|
||||
int rtpLength() override;
|
||||
int frameTime() override;
|
||||
int samplerate() override;
|
||||
int channels() override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
};
|
||||
|
||||
#if defined(USE_OPUS_CODEC)
|
||||
class OpusCodec: public Codec
|
||||
{
|
||||
protected:
|
||||
OpusEncoder *mEncoderCtx = nullptr;
|
||||
OpusDecoder *mDecoderCtx = nullptr;
|
||||
int mPTime = 0,
|
||||
mSamplerate = 0,
|
||||
mChannels = 0;
|
||||
int mDecoderChannels = 0;
|
||||
|
||||
OpusEncoder *mEncoderCtx;
|
||||
OpusDecoder *mDecoderCtx;
|
||||
int mPTime, mSamplerate, mChannels;
|
||||
Audio::SpeexResampler mDecodeResampler;
|
||||
public:
|
||||
struct Params
|
||||
{
|
||||
bool mUseDtx = false,
|
||||
mUseInbandFec = false,
|
||||
mStereo = false;
|
||||
int mPtime = 0,
|
||||
mTargetBitrate = 0,
|
||||
mExpectedPacketLoss = 0;
|
||||
bool mUseDtx, mUseInbandFec, mStereo;
|
||||
int mPtime, mTargetBitrate, mExpectedPacketLoss;
|
||||
|
||||
Params();
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
resip::Data toString() const;
|
||||
void parse(const resip::Data& params);
|
||||
#endif
|
||||
};
|
||||
|
||||
class OpusFactory: public Factory
|
||||
@@ -99,31 +102,35 @@ public:
|
||||
int channels() override;
|
||||
int samplerate() override;
|
||||
int payloadType() override;
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
|
||||
#endif
|
||||
PCodec create() override;
|
||||
};
|
||||
|
||||
OpusCodec(Audio::Format fmt, int ptime);
|
||||
OpusCodec(int samplerate, int channels, int ptime);
|
||||
~OpusCodec();
|
||||
void applyParams(const Params& params);
|
||||
|
||||
Info info() override;
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
|
||||
size_t getNumberOfSamples(std::span<const uint8_t> payload);
|
||||
const char* name();
|
||||
int pcmLength();
|
||||
int rtpLength();
|
||||
int frameTime();
|
||||
int samplerate();
|
||||
int channels();
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int plc(int lostFrames, void* output, int outputCapacity);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class IlbcCodec: public Codec
|
||||
{
|
||||
protected:
|
||||
int mPacketTime = 0; /// Single frame time (20 or 30 ms)
|
||||
iLBC_encinst_t* mEncoderCtx = nullptr;
|
||||
iLBC_decinst_t* mDecoderCtx = nullptr;
|
||||
int mPacketTime; /// Single frame time (20 or 30 ms)
|
||||
iLBC_encinst_t* mEncoderCtx;
|
||||
iLBC_decinst_t* mDecoderCtx;
|
||||
|
||||
public:
|
||||
class IlbcFactory: public Factory
|
||||
@@ -138,19 +145,24 @@ public:
|
||||
const char* name();
|
||||
int samplerate();
|
||||
int payloadType();
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
void create(CodecMap& codecs);
|
||||
#endif
|
||||
PCodec create();
|
||||
};
|
||||
|
||||
IlbcCodec(int packetTime);
|
||||
virtual ~IlbcCodec();
|
||||
Info info() override;
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
const char* name();
|
||||
int pcmLength();
|
||||
int rtpLength();
|
||||
int frameTime();
|
||||
int samplerate();
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int plc(int lostFrames, void* output, int outputCapacity);
|
||||
};
|
||||
|
||||
class G711Codec: public Codec
|
||||
@@ -183,11 +195,15 @@ public:
|
||||
G711Codec(int type);
|
||||
~G711Codec();
|
||||
|
||||
Info info() override;
|
||||
const char* name();
|
||||
int pcmLength();
|
||||
int frameTime();
|
||||
int rtpLength();
|
||||
int samplerate();
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostSamples, std::span<uint8_t> output) override ;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int plc(int lostSamples, void* output, int outputCapacity);
|
||||
|
||||
protected:
|
||||
int mType; /// Determines if it is u-law or a-law codec. Its value is ALaw or ULaw.
|
||||
@@ -196,9 +212,9 @@ protected:
|
||||
class IsacCodec: public Codec
|
||||
{
|
||||
protected:
|
||||
int mSamplerate = 0;
|
||||
ISACFIX_MainStruct* mEncoderCtx = nullptr;
|
||||
ISACFIX_MainStruct* mDecoderCtx = nullptr;
|
||||
int mSamplerate;
|
||||
ISACFIX_MainStruct* mEncoderCtx;
|
||||
ISACFIX_MainStruct* mDecoderCtx;
|
||||
public:
|
||||
class IsacFactory16K: public Factory
|
||||
{
|
||||
@@ -230,11 +246,15 @@ public:
|
||||
IsacCodec(int sampleRate);
|
||||
~IsacCodec();
|
||||
|
||||
Info info() override;
|
||||
const char* name();
|
||||
int pcmLength();
|
||||
int rtpLength();
|
||||
int frameTime();
|
||||
int samplerate();
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int plc(int lostFrames, void* output, int outputCapacity);
|
||||
};
|
||||
|
||||
|
||||
@@ -298,13 +318,17 @@ public:
|
||||
GsmCodec(Type codecType);
|
||||
|
||||
/*! Destructor. */
|
||||
~GsmCodec();
|
||||
virtual ~GsmCodec();
|
||||
|
||||
Info info() override;
|
||||
const char* name();
|
||||
int pcmLength();
|
||||
int rtpLength();
|
||||
int frameTime();
|
||||
int samplerate();
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int plc(int lostFrames, void* output, int outputCapacity);
|
||||
};
|
||||
|
||||
/// GSM MIME name
|
||||
@@ -343,19 +367,25 @@ public:
|
||||
PCodec create();
|
||||
};
|
||||
G722Codec();
|
||||
~G722Codec();
|
||||
virtual ~G722Codec();
|
||||
|
||||
Info info() override;
|
||||
const char* name();
|
||||
int pcmLength();
|
||||
int rtpLength();
|
||||
int frameTime();
|
||||
int samplerate();
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
|
||||
int plc(int lostFrames, void* output, int outputCapacity);
|
||||
|
||||
//unsigned GetSamplerate() { return 16000; }
|
||||
};
|
||||
|
||||
class GsmHrCodec: public Codec
|
||||
{
|
||||
protected:
|
||||
void* mDecoder = nullptr;
|
||||
void* mDecoder;
|
||||
|
||||
public:
|
||||
class GsmHrFactory: public Factory
|
||||
@@ -375,11 +405,15 @@ public:
|
||||
GsmHrCodec();
|
||||
~GsmHrCodec() override;
|
||||
|
||||
Info info() override;
|
||||
const char* name() override;
|
||||
int pcmLength() override;
|
||||
int rtpLength() override;
|
||||
int frameTime() override;
|
||||
int samplerate() override;
|
||||
|
||||
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
|
||||
size_t plc(int lostFrames, std::span<uint8_t> output) override;
|
||||
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
|
||||
int plc(int lostFrames, void* output, int outputCapacity) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -6,23 +6,27 @@
|
||||
#ifndef __MT_AUDIO_RECEIVER_H
|
||||
#define __MT_AUDIO_RECEIVER_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "MT_Stream.h"
|
||||
#include "MT_CodecList.h"
|
||||
#include "MT_AudioCodec.h"
|
||||
#include "MT_CngHelper.h"
|
||||
|
||||
#include "../helper/HL_Pointer.h"
|
||||
#include "../helper/HL_Sync.h"
|
||||
#include "../helper/HL_Optional.hpp"
|
||||
|
||||
#include "jrtplib/src/rtppacket.h"
|
||||
#include "jrtplib/src/rtcppacket.h"
|
||||
#include "jrtplib/src/rtpsourcedata.h"
|
||||
#include "../audio/Audio_DataWindow.h"
|
||||
#include "../audio/Audio_Resampler.h"
|
||||
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
using namespace std::chrono_literals;
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
# include "pvqa++.h"
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
// #define DUMP_DECODED
|
||||
|
||||
namespace MT
|
||||
{
|
||||
@@ -30,99 +34,66 @@ using jrtplib::RTPPacket;
|
||||
class RtpBuffer
|
||||
{
|
||||
public:
|
||||
// Owns rtp packet data
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
Packet(const std::shared_ptr<RTPPacket>& packet, std::chrono::milliseconds timelen, int samplerate);
|
||||
std::shared_ptr<RTPPacket> rtp() const;
|
||||
|
||||
std::chrono::milliseconds timelength() const;
|
||||
int samplerate() const;
|
||||
|
||||
const std::vector<short>& pcm() const;
|
||||
std::vector<short>& pcm();
|
||||
|
||||
const std::chrono::microseconds& timestamp() const;
|
||||
std::chrono::microseconds& timestamp();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<RTPPacket> mRtp;
|
||||
std::chrono::milliseconds mTimelength = 0ms;
|
||||
int mSamplerate = 0;
|
||||
std::vector<short> mPcm;
|
||||
std::chrono::microseconds mTimestamp = 0us;
|
||||
};
|
||||
|
||||
struct FetchResult
|
||||
{
|
||||
enum class Status
|
||||
enum class FetchResult
|
||||
{
|
||||
RegularPacket,
|
||||
Gap,
|
||||
NoPacket
|
||||
};
|
||||
|
||||
Status mStatus = Status::NoPacket;
|
||||
std::shared_ptr<Packet> mPacket;
|
||||
// Owns rtp packet data
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
Packet(const std::shared_ptr<RTPPacket>& packet, int timelen, int rate);
|
||||
std::shared_ptr<RTPPacket> rtp() const;
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
switch (mStatus)
|
||||
{
|
||||
case Status::RegularPacket: return "packet";
|
||||
case Status::Gap: return "gap";
|
||||
case Status::NoPacket: return "empty";
|
||||
}
|
||||
}
|
||||
int timelength() const;
|
||||
int rate() const;
|
||||
|
||||
const std::vector<short>& pcm() const;
|
||||
std::vector<short>& pcm();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<RTPPacket> mRtp;
|
||||
int mTimelength = 0, mRate = 0;
|
||||
std::vector<short> mPcm;
|
||||
};
|
||||
|
||||
RtpBuffer(Statistics& stat);
|
||||
~RtpBuffer();
|
||||
|
||||
unsigned ssrc() const;
|
||||
unsigned ssrc();
|
||||
void setSsrc(unsigned ssrc);
|
||||
|
||||
void setHigh(std::chrono::milliseconds t);
|
||||
std::chrono::milliseconds high() const;
|
||||
void setHigh(int milliseconds);
|
||||
int high();
|
||||
|
||||
void setLow(std::chrono::milliseconds t);
|
||||
std::chrono::milliseconds low() const;
|
||||
void setLow(int milliseconds);
|
||||
int low();
|
||||
|
||||
void setPrebuffer(std::chrono::milliseconds t);
|
||||
std::chrono::milliseconds prebuffer() const;
|
||||
void setPrebuffer(int milliseconds);
|
||||
int prebuffer();
|
||||
|
||||
int getNumberOfReturnedPackets() const;
|
||||
int getNumberOfAddPackets() const;
|
||||
|
||||
std::chrono::milliseconds findTimelength();
|
||||
int findTimelength();
|
||||
int getCount() const;
|
||||
|
||||
// Returns false if packet was not add - maybe too old or too new or duplicate
|
||||
std::shared_ptr<Packet> add(const std::shared_ptr<RTPPacket>& packet, std::chrono::milliseconds timelength, int rate);
|
||||
std::shared_ptr<Packet> add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
|
||||
|
||||
typedef std::vector<std::shared_ptr<Packet>> ResultList;
|
||||
typedef std::shared_ptr<ResultList> PResultList;
|
||||
|
||||
FetchResult fetch();
|
||||
|
||||
// Drop oldest packets so buffered audio stays within the high-water mark,
|
||||
// recording packet-loss events for any sequence gaps crossed (the same
|
||||
// accounting fetch() performs). Used to bound memory on streams that never
|
||||
// call fetch() - i.e. network-MOS-only streams with audio decode disabled,
|
||||
// which would otherwise retain every packet for the whole call.
|
||||
//
|
||||
// maxPackets, when non-zero, additionally caps the buffer to that many packets
|
||||
// regardless of buffered time. The decode path (fetch()) leaves it 0 so jitter
|
||||
// tolerance stays governed by the time-based high-water mark; the network-only
|
||||
// path passes a small cap since those packets are never decoded.
|
||||
void trimToHighWater(size_t maxPackets = 0);
|
||||
FetchResult fetch(ResultList& rl);
|
||||
|
||||
protected:
|
||||
unsigned mSsrc = 0;
|
||||
std::chrono::milliseconds mHigh = std::chrono::milliseconds(RTP_BUFFER_HIGH),
|
||||
mLow = std::chrono::milliseconds(RTP_BUFFER_LOW),
|
||||
mPrebuffer = std::chrono::milliseconds(RTP_BUFFER_PREBUFFER);
|
||||
int mHigh = RTP_BUFFER_HIGH,
|
||||
mLow = RTP_BUFFER_LOW,
|
||||
mPrebuffer = RTP_BUFFER_PREBUFFER;
|
||||
int mReturnedCounter = 0,
|
||||
mAddCounter = 0;
|
||||
|
||||
@@ -133,12 +104,9 @@ protected:
|
||||
bool mFirstPacketWillGo = true;
|
||||
jrtplib::RTPSourceStats mRtpStats;
|
||||
std::shared_ptr<Packet> mFetchedPacket;
|
||||
std::optional<uint32_t> mLastSeqno;
|
||||
std::optional<jrtplib::RTPTime> mLastReceiveTime;
|
||||
|
||||
|
||||
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
|
||||
float mLastAddTime = 0.0f;
|
||||
float mLastAddTime = 0.0;
|
||||
};
|
||||
|
||||
class Receiver
|
||||
@@ -151,184 +119,106 @@ protected:
|
||||
Statistics& mStat;
|
||||
};
|
||||
|
||||
class DtmfReceiver: public Receiver
|
||||
{
|
||||
private:
|
||||
char mEvent = 0;
|
||||
bool mEventEnded = false;
|
||||
std::chrono::milliseconds mEventStart = 0ms;
|
||||
std::function<void(char)> mCallback;
|
||||
|
||||
public:
|
||||
DtmfReceiver(Statistics& stat);
|
||||
~DtmfReceiver();
|
||||
|
||||
void add(const std::shared_ptr<RTPPacket>& p);
|
||||
void setCallback(std::function<void(char tone)> callback);
|
||||
};
|
||||
|
||||
|
||||
class AudioReceiver: public Receiver
|
||||
{
|
||||
public:
|
||||
AudioReceiver(const CodecList::Settings& codecSettings, Statistics& stat);
|
||||
~AudioReceiver();
|
||||
|
||||
// Update codec settings
|
||||
void setCodecSettings(const CodecList::Settings& codecSettings);
|
||||
CodecList::Settings& getCodecSettings();
|
||||
|
||||
// Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding.
|
||||
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
|
||||
Codec* add(const std::shared_ptr<jrtplib::RTPPacket>& p);
|
||||
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
|
||||
|
||||
struct DecodeOptions
|
||||
// Returns false when there is no rtp data from jitter
|
||||
enum DecodeOptions
|
||||
{
|
||||
bool mRealtimeProcessing = false; // Target PCAP parsing by default
|
||||
bool mResampleToMainRate = true; // Resample all decoded audio to AUDIO_SAMPLERATE
|
||||
bool mFillGapByCNG = false; // Use CNG information if available
|
||||
bool mSkipDecode = false; // Don't do decode, just dry run - fetch packets, remove them from the jitter buffer
|
||||
std::chrono::milliseconds mElapsed = 0ms; // How much milliseconds should be decoded; zero value means "decode just next packet from the buffer"
|
||||
DecodeOptions decreaseElapsedBy(std::chrono::milliseconds delta)
|
||||
{
|
||||
return
|
||||
{
|
||||
.mRealtimeProcessing = mRealtimeProcessing,
|
||||
.mResampleToMainRate = mResampleToMainRate,
|
||||
.mFillGapByCNG = mFillGapByCNG,
|
||||
.mSkipDecode = mSkipDecode,
|
||||
.mElapsed = std::max(mElapsed - delta, 0ms)
|
||||
};
|
||||
}
|
||||
DecodeOptions_ResampleToMainRate = 0,
|
||||
DecodeOptions_DontResample = 1,
|
||||
DecodeOptions_FillCngGap = 2,
|
||||
DecodeOptions_SkipDecode = 4
|
||||
};
|
||||
|
||||
struct DecodeResult
|
||||
{
|
||||
enum class Status
|
||||
{
|
||||
Ok, // Decoded ok
|
||||
Skip, // Just no data - emit silence instead
|
||||
BadPacket // Error happened during the decode
|
||||
};
|
||||
|
||||
Status mStatus = Status::Ok;
|
||||
int mSamplerate = 0;
|
||||
int mChannels = 0;
|
||||
};
|
||||
|
||||
DecodeResult getAudioTo(Audio::DataWindow& output, DecodeOptions options);
|
||||
bool getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr);
|
||||
|
||||
// Looks for codec by payload type
|
||||
Codec* findCodec(int payloadType);
|
||||
RtpBuffer& getRtpBuffer() { return mRtpBuffer; }
|
||||
RtpBuffer& getRtpBuffer() { return mBuffer; }
|
||||
|
||||
// Returns size of AudioReceiver's instance in bytes (including size of all data + codecs + etc.)
|
||||
int getSize() const;
|
||||
|
||||
struct MediaInfo
|
||||
{
|
||||
std::chrono::milliseconds mTimeLength = 0ms;
|
||||
int mSamplerate = 0;
|
||||
};
|
||||
MediaInfo infoFor(jrtplib::RTPPacket& p);
|
||||
// Returns timelength for given packet
|
||||
int timelengthFor(jrtplib::RTPPacket& p);
|
||||
|
||||
void processDtmf();
|
||||
|
||||
void updateDecodingTimeStatistics();
|
||||
// Return samplerate for given packet
|
||||
int samplerateFor(jrtplib::RTPPacket& p);
|
||||
|
||||
protected:
|
||||
// Resolve (and lazily create) the codec for a payload type. Returns null when no
|
||||
// factory handles it. Used by add()/findCodec()/infoFor() so mLazyCodecMap works.
|
||||
Codec* ensureCodec(int payloadType);
|
||||
|
||||
// Lazily create the comfort-noise decoder on first use.
|
||||
CngDecoder& cng();
|
||||
|
||||
RtpBuffer mRtpBuffer; // RTP jitter buffer itself; here are audio packets
|
||||
RtpBuffer mDtmfBuffer; // These two (mDtmfBuffer / mDtmfReceiver) are for our analyzer stack only; in normal softphone logic DTMF packets goes via SingleAudioStream::mDtmfReceiver
|
||||
DtmfReceiver mDtmfReceiver;
|
||||
|
||||
RtpBuffer mBuffer;
|
||||
CodecMap mCodecMap;
|
||||
PCodec mCodec;
|
||||
int mFrameCount = 0;
|
||||
CodecList::Settings mCodecSettings;
|
||||
CodecList mCodecList;
|
||||
JitterStatistics mJitterStats;
|
||||
std::shared_ptr<RtpBuffer::Packet> mCngPacket;
|
||||
// Lazily created on first CNG use (getAudioTo); its ctor calls WebRtcCng_CreateDec,
|
||||
// which is wasted for the many streams that never decode comfort noise. Access via cng().
|
||||
std::unique_ptr<CngDecoder> mCngDecoder;
|
||||
size_t mDTXSamplesToEmit = 0; // How much silence (or CNG) should be emited before next RTP packet gets into the action
|
||||
std::shared_ptr<jrtplib::RTPPacket> mCngPacket;
|
||||
CngDecoder mCngDecoder;
|
||||
|
||||
// Already decoded data that can be retrieved without actual decoding - it may happen because of getAudioTo() may be limited by time interval
|
||||
Audio::DataWindow mAvailable;
|
||||
// Decode RTP early, do not wait for speaker callback
|
||||
bool mEarlyDecode = false;
|
||||
|
||||
// Decode/convert/resample scratch buffers. These were inline arrays
|
||||
// (MT_MAX_DECODEBUFFER * {1,2,1} * int16_t = 256 KB total) carried by every
|
||||
// AudioReceiver, hence by every StreamDecoder - including network-MOS-only
|
||||
// streams that never decode. They are now allocated lazily on the first
|
||||
// getAudioTo() call via ensureDecodeBuffers(); non-decoding streams keep them
|
||||
// empty. Once allocated they are sized to full capacity and reused, so decode
|
||||
// behaviour is unchanged.
|
||||
std::vector<int16_t> mDecodedFrame; // sized to MT_MAX_DECODEBUFFER
|
||||
size_t mDecodedLength = 0;
|
||||
// Buffer to hold decoded data
|
||||
char mDecodedFrame[65536];
|
||||
int mDecodedLength = 0;
|
||||
|
||||
// Buffer to hold data converted to stereo/mono; there is multiplier 2 as it can be stereo audio
|
||||
std::vector<int16_t> mConvertedFrame; // sized to MT_MAX_DECODEBUFFER * 2
|
||||
size_t mConvertedLength = 0;
|
||||
// Buffer to hold data converted to stereo/mono
|
||||
char mConvertedFrame[32768];
|
||||
int mConvertedLength = 0;
|
||||
|
||||
// Buffer to hold data resampled to AUDIO_SAMPLERATE
|
||||
std::vector<int16_t> mResampledFrame; // sized to MT_MAX_DECODEBUFFER
|
||||
size_t mResampledLength = 0;
|
||||
char mResampledFrame[65536];
|
||||
int mResampledLength = 0;
|
||||
|
||||
// Last packet time length
|
||||
int mLastPacketTimeLength = 0;
|
||||
std::optional<uint32_t> mLastPacketTimestamp;
|
||||
|
||||
int mFailedCount = 0;
|
||||
Audio::Resampler mResampler8,
|
||||
mResampler16,
|
||||
mResampler32,
|
||||
mResampler48;
|
||||
Audio::Resampler mResampler8, mResampler16,
|
||||
mResampler32, mResampler48;
|
||||
|
||||
Audio::PWavFileWriter mDecodedDump;
|
||||
|
||||
std::optional<std::chrono::steady_clock::time_point> mDecodeTimestamp; // Time last call happened to codec->decode()
|
||||
float mLastDecodeTime = 0.0; // Time last call happened to codec->decode()
|
||||
|
||||
float mIntervalSum = 0.0f;
|
||||
float mIntervalSum = 0.0;
|
||||
int mIntervalCount = 0;
|
||||
|
||||
std::chrono::milliseconds mRequestedAudio = 0ms;
|
||||
std::chrono::milliseconds mProducedAudio = 0ms;
|
||||
|
||||
// Lazily allocate the decode/convert/resample scratch buffers (mDecodedFrame,
|
||||
// mConvertedFrame, mResampledFrame) to full capacity on the first decode. A
|
||||
// no-op once allocated. Called at the top of getAudioTo(); network-MOS-only
|
||||
// streams never reach it, so they never pay the 256 KB.
|
||||
void ensureDecodeBuffers();
|
||||
|
||||
// Zero rate will make audio mono but resampling will be skipped
|
||||
void makeMonoAndResample(int rate, int channels);
|
||||
|
||||
// Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame
|
||||
void processDecoded(Audio::DataWindow& output, DecodeOptions options);
|
||||
void produceSilence(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options);
|
||||
void produceCNG(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options);
|
||||
void processDecoded(Audio::DataWindow& output, int options);
|
||||
|
||||
// Calculate bitrate switch statistics for AMR codecs
|
||||
void updateAmrCodecStats(Codec* c);
|
||||
#if defined(USE_PVQA_LIBRARY) && defined(PVQA_IN_RECEIVER)
|
||||
std::shared_ptr<sevana::pvqa> mPVQA;
|
||||
void initPvqa();
|
||||
void updatePvqa(const void* data, int size);
|
||||
float calculatePvqaMos(int rate, std::string& report);
|
||||
|
||||
DecodeResult decodeGapTo(Audio::DataWindow& output, DecodeOptions options);
|
||||
DecodeResult decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const std::shared_ptr<RtpBuffer::Packet>& p);
|
||||
DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options);
|
||||
|
||||
std::optional<std::chrono::steady_clock::time_point> mLastDecodeTimestamp;
|
||||
std::chrono::microseconds mIntervalBetweenDecode = 0us;
|
||||
size_t mDecodeCount = 0;
|
||||
void updateDecodeIntervalStatistics();
|
||||
std::shared_ptr<Audio::DataWindow> mPvqaBuffer;
|
||||
#endif
|
||||
|
||||
void processStatisticsWithAmrCodec(Codec* c);
|
||||
};
|
||||
|
||||
class DtmfReceiver: public Receiver
|
||||
{
|
||||
public:
|
||||
DtmfReceiver(Statistics& stat);
|
||||
~DtmfReceiver();
|
||||
|
||||
void add(std::shared_ptr<RTPPacket> p);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "jrtplib/src/rtptransmitter.h"
|
||||
#include "jrtplib/src/rtpsessionparams.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "media"
|
||||
#define LOG_SUBSYSTEM "AudioStream"
|
||||
|
||||
//#define DUMP_SENDING_AUDIO
|
||||
|
||||
@@ -210,23 +210,24 @@ void AudioStream::addData(const void* buffer, int bytes)
|
||||
if (mSendingDump)
|
||||
mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength());
|
||||
|
||||
auto r = codec->encode({(const uint8_t*)mCapturedAudio.data() + codec->pcmLength()*i, (size_t)codec->pcmLength()},
|
||||
{(uint8_t*)mFrameBuffer, MT_MAXAUDIOFRAME});
|
||||
int produced;
|
||||
produced = codec->encode((const char*)mCapturedAudio.data() + codec->pcmLength()*i,
|
||||
codec->pcmLength(), mFrameBuffer, MT_MAXAUDIOFRAME);
|
||||
|
||||
// Counter of processed input bytes of raw pcm data from microphone
|
||||
processed += codec->pcmLength();
|
||||
encodedTime += codec->frameTime();
|
||||
mEncodedTime += codec->frameTime();
|
||||
|
||||
if (r.mEncoded)
|
||||
if (produced)
|
||||
{
|
||||
mEncodedAudio.appendBuffer(mFrameBuffer, r.mEncoded);
|
||||
mEncodedAudio.appendBuffer(mFrameBuffer, produced);
|
||||
if (packetTime <= encodedTime)
|
||||
{
|
||||
// Time to send packet
|
||||
ICELogMedia(<< "Sending RTP packet pt = " << mTransmittingPayloadType << ", plength = " << (int)mEncodedAudio.size() << " to ");
|
||||
ICELogMedia(<< "Sending RTP packet pt = " << mTransmittingPayloadType << ", plength = " << (int)mEncodedAudio.size());
|
||||
mRtpSession.SendPacketEx(mEncodedAudio.data(), mEncodedAudio.size(), mTransmittingPayloadType, false,
|
||||
packetTime * codec->samplerate()/1000, 0, nullptr, 0);
|
||||
packetTime * codec->samplerate()/1000, 0, NULL, 0);
|
||||
mEncodedAudio.clear();
|
||||
encodedTime = 0;
|
||||
}
|
||||
@@ -238,9 +239,6 @@ void AudioStream::addData(const void* buffer, int bytes)
|
||||
|
||||
void AudioStream::copyDataTo(Audio::Mixer& mixer, int needed)
|
||||
{
|
||||
// mStreamMap is also mutated from the network thread (dataArrived)
|
||||
Lock l(mMutex);
|
||||
|
||||
// Local audio mixer - used to send audio to media observer
|
||||
Audio::Mixer localMixer;
|
||||
Audio::DataWindow forObserver;
|
||||
@@ -285,18 +283,13 @@ void AudioStream::copyDataTo(Audio::Mixer& mixer, int needed)
|
||||
|
||||
if (mMediaObserver)
|
||||
{
|
||||
int mixedBytes = localMixer.mixAndGetPcm(forObserver);
|
||||
if (mixedBytes > 0)
|
||||
mMediaObserver->onMedia(forObserver.data(), mixedBytes, MT::Stream::MediaDirection::Incoming, this, mMediaObserverTag);
|
||||
localMixer.mixAndGetPcm(forObserver);
|
||||
mMediaObserver->onMedia(forObserver.data(), forObserver.capacity(), MT::Stream::MediaDirection::Incoming, this, mMediaObserverTag);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length, InternetAddress& source)
|
||||
{
|
||||
// Protects mStreamMap (also iterated by copyDataTo on the audio thread)
|
||||
// and the receive/decrypt buffers.
|
||||
Lock l(mMutex);
|
||||
|
||||
jrtplib::RTPIPv6Address addr6;
|
||||
jrtplib::RTPIPv4Address addr4;
|
||||
jrtplib::RTPExternalTransmissionInfo* info = dynamic_cast<jrtplib::RTPExternalTransmissionInfo*>(mRtpSession.GetTransmissionInfo());
|
||||
@@ -305,7 +298,7 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||
// Drop RTP packets if stream is not receiving now; let RTCP go
|
||||
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtp(buffer, length))
|
||||
{
|
||||
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the RTP packet");
|
||||
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the packet");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -313,24 +306,22 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||
int receiveLength = length;
|
||||
memcpy(mReceiveBuffer, buffer, length);
|
||||
|
||||
bool srtpResult;
|
||||
if (mSrtpSession.active())
|
||||
{
|
||||
bool srtpResult;
|
||||
size_t srcLength = length; size_t dstLength = sizeof mSrtpDecodeBuffer;
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength);
|
||||
else
|
||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, srcLength, mSrtpDecodeBuffer, &dstLength);
|
||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength);
|
||||
if (!srtpResult)
|
||||
{
|
||||
ICELogError(<<"Cannot decrypt SRTP packet.");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(mReceiveBuffer, mSrtpDecodeBuffer, dstLength);
|
||||
receiveLength = dstLength;
|
||||
}
|
||||
|
||||
//ICELogDebug(<< "Packet no: " << RtpHelper::findPacketNo(mReceiveBuffer, receiveLength));
|
||||
|
||||
switch (source.family())
|
||||
{
|
||||
case AF_INET:
|
||||
@@ -352,20 +343,14 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||
}
|
||||
|
||||
mStat.mReceived += length;
|
||||
auto& perDst = mStat.mPerDestination[source];
|
||||
perDst.mReceivedBytes += length;
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
{
|
||||
if (!mStat.mFirstRtpTime)
|
||||
mStat.mFirstRtpTime = std::chrono::steady_clock::now();
|
||||
if (!mStat.mFirstRtpTime.is_initialized())
|
||||
mStat.mFirstRtpTime = std::chrono::system_clock::now();
|
||||
mStat.mReceivedRtp++;
|
||||
perDst.mReceivedRtp++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mStat.mReceivedRtcp++;
|
||||
perDst.mReceivedRtcp++;
|
||||
}
|
||||
|
||||
mRtpSession.Poll(); // maybe it is extra with external transmitter
|
||||
bool hasData = mRtpSession.GotoFirstSourceWithData();
|
||||
@@ -375,39 +360,21 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
||||
if (packet)
|
||||
{
|
||||
ICELogMedia(<< "jrtplib returned packet");
|
||||
|
||||
// Find right handler for rtp stream
|
||||
SingleAudioStream* rtpStream = nullptr;
|
||||
auto streamIter = mStreamMap.find(packet->GetSSRC());
|
||||
if (streamIter == mStreamMap.end()) {
|
||||
rtpStream = new SingleAudioStream(mCodecSettings, mStat);
|
||||
mStreamMap.insert({packet->GetSSRC(), rtpStream});
|
||||
}
|
||||
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());
|
||||
if (streamIter == mStreamMap.end())
|
||||
mStreamMap[packet->GetSSRC()] = rtpStream = new SingleAudioStream(mCodecSettings, mStat);
|
||||
else
|
||||
rtpStream = streamIter->second;
|
||||
|
||||
// Process incoming data packet
|
||||
rtpStream->process(packet);
|
||||
|
||||
// RTT sanity filter: jrtplib's INF_GetRoundtripTime() does the
|
||||
// RFC 3550 §6.4.1 math but omits clock-skew / outlier guards;
|
||||
// without these, a skewed or buggy peer can poison mRttDelay
|
||||
// (and therefore the Id term in MOS).
|
||||
double rtt = mRtpSession.GetCurrentSourceInfo()->INF_GetRoundtripTime().GetDouble();
|
||||
if (rtt > 0 && rtt < 30.0) // reject "RTT not making any sense" (>30s)
|
||||
{
|
||||
// Once an average is established, cap a new sample at 3x mean
|
||||
// so a single outlier can't skew the running RTT.
|
||||
constexpr double kRttNormalizeFactor = 3.0;
|
||||
const double meanRtt = mStat.mRttDelay.average();
|
||||
if (mStat.mRttDelay.is_initialized() && meanRtt > 0.0 &&
|
||||
rtt > meanRtt * kRttNormalizeFactor)
|
||||
{
|
||||
rtt = meanRtt * kRttNormalizeFactor;
|
||||
}
|
||||
if (rtt > 0)
|
||||
mStat.mRttDelay.process(rtt);
|
||||
}
|
||||
}
|
||||
hasData = mRtpSession.GotoNextSourceWithData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifndef __MT_AUDIOSTREAM_H
|
||||
#define __MT_AUDIOSTREAM_H
|
||||
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
#include "MT_Stream.h"
|
||||
#include "MT_NativeRtpSender.h"
|
||||
#include "MT_SingleAudioStream.h"
|
||||
@@ -62,16 +62,16 @@ public:
|
||||
protected:
|
||||
Audio::DataWindow mCapturedAudio; // Data from microphone
|
||||
Audio::DataWindow mStereoCapturedAudio;
|
||||
char mIncomingPcmBuffer[AUDIO_MIC_BUFFER_SIZE] = {0}; // Temporary buffer to allow reading from file
|
||||
char mResampleBuffer[AUDIO_MIC_BUFFER_SIZE*8] = {0}; // Temporary buffer to hold data
|
||||
char mStereoBuffer[AUDIO_MIC_BUFFER_SIZE*16] = {0}; // Temporary buffer to hold data converted to stereo
|
||||
char mIncomingPcmBuffer[AUDIO_MIC_BUFFER_SIZE]; // Temporary buffer to allow reading from file
|
||||
char mResampleBuffer[AUDIO_MIC_BUFFER_SIZE*8]; // Temporary buffer to hold data
|
||||
char mStereoBuffer[AUDIO_MIC_BUFFER_SIZE*16]; // Temporary buffer to hold data converted to stereo
|
||||
PCodec mTransmittingCodec; // Current encoding codec
|
||||
int mTransmittingPayloadType = -1; // Payload type to mark outgoing packets
|
||||
int mPacketTime = 0; // Required packet time
|
||||
int mTransmittingPayloadType; // Payload type to mark outgoing packets
|
||||
int mPacketTime; // Required packet time
|
||||
char mFrameBuffer[MT_MAXAUDIOFRAME]; // Temporary buffer to hold results of encoder
|
||||
ByteBuffer mEncodedAudio; // Encoded frame(s)
|
||||
int mEncodedTime = 0; // Time length of encoded audio
|
||||
CodecList::Settings mCodecSettings; // Configuration for stream
|
||||
int mEncodedTime; // Time length of encoded audio
|
||||
const CodecList::Settings& mCodecSettings; // Configuration for stream
|
||||
Mutex mMutex; // Mutex
|
||||
int mRemoteTelephoneCodec; // Payload for remote telephone codec
|
||||
jrtplib::RTPSession mRtpSession; // Rtp session
|
||||
@@ -87,8 +87,7 @@ protected:
|
||||
mCaptureResampler32,
|
||||
mCaptureResampler48;
|
||||
DtmfContext mDtmfContext;
|
||||
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE] = {0},
|
||||
mSrtpDecodeBuffer[MAX_VALID_UDPPACKET_SIZE] = {0};
|
||||
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE];
|
||||
|
||||
struct
|
||||
{
|
||||
@@ -106,7 +105,7 @@ protected:
|
||||
|
||||
Statistics* mFinalStatistics = nullptr;
|
||||
|
||||
// bool decryptSrtp(void* data, int* len);
|
||||
bool decryptSrtp(void* data, int* len);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "../helper/HL_StreamState.h"
|
||||
#include "../helper/HL_Log.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "media"
|
||||
#define LOG_SUBSYSTEM "MT::Box"
|
||||
|
||||
using namespace MT;
|
||||
|
||||
@@ -40,7 +40,7 @@ PStream Terminal::createStream(int type, VariantMap& /*config*/)
|
||||
switch (type)
|
||||
{
|
||||
case Stream::Audio:
|
||||
result = std::make_shared<AudioStream>(MT::CodecList::Settings::getClientSettings());
|
||||
result = PStream(new AudioStream(MT::CodecList::Settings::DefaultSettings));
|
||||
mAudioList.add(result);
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../engine_config.h"
|
||||
#include "../config.h"
|
||||
|
||||
#include "MT_CngHelper.h"
|
||||
#include <stdlib.h>
|
||||
@@ -135,7 +135,7 @@ namespace MT
|
||||
|
||||
// Get noise level
|
||||
unsigned char noiseLevel = *dataIn;
|
||||
float linear = 1.0f / float(noiseLevel ? noiseLevel : 1);
|
||||
float linear = float(1.0 / noiseLevel ? noiseLevel : 1);
|
||||
|
||||
// Generate white noise for 16KHz sample rate
|
||||
LPFilter lpf; HPFilter hpf;
|
||||
|
||||
@@ -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
|
||||
* 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/. */
|
||||
@@ -11,6 +11,8 @@ int Codec::Factory::channels()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
void Codec::Factory::create(CodecMap& codecs)
|
||||
{
|
||||
codecs[payloadType()] = std::shared_ptr<Codec>(create());
|
||||
@@ -36,4 +38,4 @@ int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecC
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+28
-42
@@ -1,4 +1,4 @@
|
||||
/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@@ -6,12 +6,13 @@
|
||||
#ifndef __MT_CODEC_H
|
||||
#define __MT_CODEC_H
|
||||
|
||||
#include <map>
|
||||
#include <span>
|
||||
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
# include "resiprocate/resip/stack/SdpContents.hxx"
|
||||
#endif
|
||||
#include "../helper/HL_Types.h"
|
||||
#include "../audio/Audio_Interface.h"
|
||||
#include <map>
|
||||
#include "../helper/HL_Pointer.h"
|
||||
|
||||
|
||||
namespace MT
|
||||
{
|
||||
@@ -19,12 +20,12 @@ class Codec;
|
||||
typedef std::shared_ptr<Codec> PCodec;
|
||||
|
||||
class CodecMap: public std::map<int, PCodec>
|
||||
{};
|
||||
{
|
||||
};
|
||||
|
||||
class Codec
|
||||
{
|
||||
public:
|
||||
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
@@ -35,59 +36,44 @@ public:
|
||||
virtual PCodec create() = 0;
|
||||
|
||||
virtual int channels();
|
||||
#if defined(USE_RESIP_INTEGRATION)
|
||||
typedef std::map<int, PCodec > CodecMap;
|
||||
virtual void create(CodecMap& codecs);
|
||||
virtual void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
// Returns payload type from chosen codec if success. -1 is returned for negative result.
|
||||
virtual int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction);
|
||||
resip::Codec resipCodec();
|
||||
#endif
|
||||
};
|
||||
virtual ~Codec() {}
|
||||
virtual const char* name() = 0;
|
||||
virtual int samplerate() = 0;
|
||||
virtual float timestampUnit() { return float(1.0 / samplerate()); }
|
||||
|
||||
struct Info
|
||||
{
|
||||
std::string mName;
|
||||
int mSamplerate = 0; // Hz
|
||||
int mChannels = 0;
|
||||
int mPcmLength = 0; // In bytes
|
||||
int mFrameTime = 0; // In milliseconds
|
||||
int mRtpLength = 0; // In bytes
|
||||
float mTimestampUnit = 0.0f;
|
||||
};
|
||||
// Returns information about this codec instance
|
||||
virtual Info info() = 0;
|
||||
// Size of decoded audio frame in bytes
|
||||
virtual int pcmLength() = 0;
|
||||
|
||||
// Helper functions to return information - they are based on info() method
|
||||
int pcmLength() { return info().mPcmLength; }
|
||||
int rtpLength() { return info().mRtpLength; }
|
||||
int channels() { return info().mChannels; }
|
||||
int samplerate() { return info().mSamplerate; }
|
||||
int frameTime() { return info().mFrameTime; }
|
||||
std::string name() { return info().mName; }
|
||||
float timestampUnit() { return info().mTimestampUnit == 0.0f ? 1.0f / info().mSamplerate : info().mTimestampUnit; }
|
||||
// Time length of single audio frame
|
||||
virtual int frameTime() = 0;
|
||||
|
||||
// Size of RTP frame in bytes. Can be zero for variable sized codecs.
|
||||
virtual int rtpLength() = 0;
|
||||
|
||||
// Number of audio channels
|
||||
virtual int channels() { return 1; }
|
||||
|
||||
Audio::Format getAudioFormat() {
|
||||
return Audio::Format(this->info().mSamplerate, this->info().mChannels);
|
||||
}
|
||||
|
||||
// Returns size of encoded data (RTP) in bytes
|
||||
struct EncodeResult
|
||||
{
|
||||
size_t mEncoded = 0; // Number of encoded bytes
|
||||
};
|
||||
virtual EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) = 0;
|
||||
virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
|
||||
|
||||
// Returns size of decoded data (PCM signed short) in bytes
|
||||
struct DecodeResult
|
||||
{
|
||||
size_t mDecoded = 0; // Number of decoded bytes
|
||||
bool mIsCng = false; // Should this packet to be used as CNG ? (used for AMR codecs)
|
||||
};
|
||||
virtual DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) = 0;
|
||||
virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
|
||||
|
||||
// Returns size of produced data (PCM signed short) in bytes
|
||||
virtual size_t plc(int lostFrames, std::span<uint8_t> output) = 0;
|
||||
virtual int plc(int lostFrames, void* output, int outputCapacity) = 0;
|
||||
|
||||
// Returns size of codec in memory
|
||||
virtual int getSize() const { return 0; };
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user