Compare commits
4 Commits
master
..
e342abe2f5
| Author | SHA1 | Date | |
|---|---|---|---|
| e342abe2f5 | |||
| 9d79c01be0 | |||
| fa705e141b | |||
| 17e17d3d79 |
@@ -1,69 +0,0 @@
|
|||||||
# Formatting rules for rtphone's own C++ sources (src/engine, src/libs/ice, test).
|
|
||||||
# Third-party code under src/libs/ is excluded via .clang-format-ignore.
|
|
||||||
---
|
|
||||||
Language: Cpp
|
|
||||||
BasedOnStyle: LLVM
|
|
||||||
Standard: c++20
|
|
||||||
|
|
||||||
# Indentation: 4 columns, spaces only.
|
|
||||||
IndentWidth: 4
|
|
||||||
TabWidth: 4
|
|
||||||
UseTab: Never
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
NamespaceIndentation: None
|
|
||||||
IndentCaseLabels: false
|
|
||||||
|
|
||||||
ColumnLimit: 120
|
|
||||||
|
|
||||||
# Allman braces, as used throughout the codebase.
|
|
||||||
BreakBeforeBraces: Allman
|
|
||||||
|
|
||||||
PointerAlignment: Left
|
|
||||||
ReferenceAlignment: Left
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
|
|
||||||
AllowShortFunctionsOnASingleLine: Inline
|
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
|
||||||
AllowShortLoopsOnASingleLine: false
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortBlocksOnASingleLine: Empty
|
|
||||||
|
|
||||||
# Include order matters here (platform headers, windows.h, vendored libs),
|
|
||||||
# so never reorder them.
|
|
||||||
SortIncludes: Never
|
|
||||||
IndentPPDirectives: None
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
FixNamespaceComments: true
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments
|
|
||||||
|
|
||||||
# Objective-C++ (src/libs/ice/ICEIosSupport.mm) — same rules as C++.
|
|
||||||
---
|
|
||||||
Language: ObjC
|
|
||||||
BasedOnStyle: LLVM
|
|
||||||
IndentWidth: 4
|
|
||||||
TabWidth: 4
|
|
||||||
UseTab: Never
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
NamespaceIndentation: None
|
|
||||||
IndentCaseLabels: false
|
|
||||||
ColumnLimit: 120
|
|
||||||
BreakBeforeBraces: Allman
|
|
||||||
PointerAlignment: Left
|
|
||||||
ReferenceAlignment: Left
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
AllowShortFunctionsOnASingleLine: Inline
|
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
|
||||||
AllowShortLoopsOnASingleLine: false
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortBlocksOnASingleLine: Empty
|
|
||||||
SortIncludes: Never
|
|
||||||
IndentPPDirectives: None
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
FixNamespaceComments: true
|
|
||||||
AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments
|
|
||||||
ObjCSpaceAfterProperty: false
|
|
||||||
ObjCSpaceBeforeProtocolList: true
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
src/libs/**
|
|
||||||
!src/libs/ice/ICE*
|
|
||||||
-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.
|
|
||||||
@@ -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)
|
project(rtphone)
|
||||||
|
|
||||||
# Rely on C++ 20
|
cmake_minimum_required(VERSION 3.0)
|
||||||
set (CMAKE_CXX_STANDARD 20)
|
|
||||||
|
macro(configure_msvc_runtime)
|
||||||
|
if(MSVC)
|
||||||
|
# Default to statically-linked runtime.
|
||||||
|
if("${MSVC_RUNTIME}" STREQUAL "")
|
||||||
|
set(MSVC_RUNTIME "static")
|
||||||
|
endif()
|
||||||
|
# Set compiler options.
|
||||||
|
set(variables
|
||||||
|
CMAKE_C_FLAGS_DEBUG
|
||||||
|
CMAKE_C_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_C_FLAGS_RELEASE
|
||||||
|
CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_CXX_FLAGS_DEBUG
|
||||||
|
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_CXX_FLAGS_RELEASE
|
||||||
|
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||||
|
)
|
||||||
|
if(${MSVC_RUNTIME} STREQUAL "static")
|
||||||
|
message(STATUS
|
||||||
|
"rtphone: MSVC -> forcing use of statically-linked runtime."
|
||||||
|
)
|
||||||
|
foreach(variable ${variables})
|
||||||
|
if(${variable} MATCHES "/MD")
|
||||||
|
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
message(STATUS
|
||||||
|
"rtphone: MSVC -> forcing use of dynamically-linked runtime."
|
||||||
|
)
|
||||||
|
foreach(variable ${variables})
|
||||||
|
if(${variable} MATCHES "/MT")
|
||||||
|
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(variable ${variables})
|
||||||
|
string(REGEX REPLACE "/Z[iI7]" ""
|
||||||
|
${variable}
|
||||||
|
"${${variable}}")
|
||||||
|
|
||||||
|
set(${variable} "${${variable}} /Zi /Oy-")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
|
||||||
|
# Rely on C++ 11
|
||||||
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set (L libs)
|
set (rtphone_libs libs)
|
||||||
set (E engine)
|
set (rtphone_engine engine)
|
||||||
|
|
||||||
option (USE_AMR_CODEC "Use AMR codec. Requires libraries." ON)
|
set (USE_AMR_CODEC OFF CACHE BOOL "Use AMR codec. Requires libraries.")
|
||||||
option (USE_EVS_CODEC "Use EVS codec." ON)
|
set (USE_EVS_CODEC OFF CACHE BOOL "Use EVS codec." )
|
||||||
option (USE_MUSL "Build with MUSL library" OFF)
|
set (USE_OPUS_CODEC OFF CACHE BOOL "Use Opus codec." )
|
||||||
|
set (USE_PVQA_LIB OFF CACHE BOOL "Build with Sevana PVQA library" )
|
||||||
|
set (USE_AQUA_LIB OFF CACHE BOOL "Build with Sevana AQuA library" )
|
||||||
|
set (USE_MUSL OFF CACHE BOOL "Build with MUSL library" )
|
||||||
|
|
||||||
# PIC code by default
|
# PIC code by default
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
set (RUNTIME_CPU_CAPABILITY_DETECTION ON)
|
|
||||||
set (CMAKE_WARN_DEPRECATED OFF)
|
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
if (NOT DEFINED LIB_PLATFORM)
|
||||||
set (OPENSSL_SSL OpenSSL::SSL)
|
set (LIB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries)
|
||||||
set (OPENSSL_CRYPTO OpenSSL::Crypto)
|
endif()
|
||||||
|
|
||||||
|
message("Libraries: ${LIB_PLATFORM}")
|
||||||
|
set (OPENSSL_INCLUDE ${LIB_PLATFORM}/openssl/1.0/include)
|
||||||
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
message ("Using OpenSSL include files from ${OPENSSL_INCLUDE}")
|
||||||
message ("Using OpenSSL libs: ${OPENSSL_SSL} and ${OPENSSL_CRYPTO}")
|
|
||||||
# include_directories(${OPENSSL_INCLUDE})
|
|
||||||
|
|
||||||
# Used defines for our project
|
|
||||||
set (DEFINES -DUSE_OPENSSL)
|
|
||||||
|
|
||||||
# Libraries for our project
|
|
||||||
set (LIBS_STATIC "")
|
|
||||||
set (LIBS_DYNAMIC "")
|
|
||||||
|
|
||||||
# Try to prefer static libraries anyway
|
|
||||||
set (CMAKE_FIND_LIBRARY_SUFFIXES .a .so .dylib)
|
|
||||||
|
|
||||||
# Windows-specific definitions
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Windows*")
|
if (CMAKE_SYSTEM MATCHES "Windows*")
|
||||||
set (DEFINES ${DEFINES} -DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS -DNOMINMAX)
|
add_definitions (-DTARGET_WIN -D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
|
||||||
set (TARGET_WIN ON)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Linux-specific definitions
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Linux*")
|
if (CMAKE_SYSTEM MATCHES "Linux*")
|
||||||
set (DEFINES ${DEFINES} -DTARGET_LINUX -DHAVE_NETINET_IN_H)
|
add_definitions (-DTARGET_LINUX)
|
||||||
set (TARGET_LINUX ON)
|
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# macOS-specific definitions
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
if (CMAKE_SYSTEM MATCHES "Darwin*")
|
||||||
set (DEFINES ${DEFINES} -DTARGET_OSX)
|
add_definitions (-DTARGET_OSX)
|
||||||
set (TARGET_OSX ON)
|
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} dl)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if (CMAKE_SYSTEM MATCHES "Android")
|
if (CMAKE_SYSTEM MATCHES "Android")
|
||||||
# Disable opencore AMR codecs - there is no ready libraries in vcpkg right now
|
|
||||||
set (USE_AMR_CODEC OFF)
|
|
||||||
|
|
||||||
message("Adding the Oboe library")
|
message("Adding the Oboe library")
|
||||||
set (OBOE_DIR libs/oboe)
|
set (OBOE_DIR libs/oboe)
|
||||||
add_subdirectory (${OBOE_DIR} build_oboe)
|
add_subdirectory (${OBOE_DIR} ./oboe)
|
||||||
include_directories (${OBOE_DIR}/include)
|
include_directories (${OBOE_DIR}/include)
|
||||||
set (DEFINES ${DEFINES} -DTARGET_ANDROID -DHAVE_NETINET_IN_H)
|
|
||||||
set (TARGET_ANDROID ON)
|
|
||||||
set (LIBS_STATIC ${LIBS} oboe)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_MUSL)
|
if (USE_MUSL)
|
||||||
set (DEFINES ${DEFINES} -DTARGET_MUSL)
|
add_definitions(-DTARGET_MUSL)
|
||||||
set (TARGET_MUSL ON)
|
endif()
|
||||||
|
|
||||||
|
if (USE_AQUA_LIB)
|
||||||
|
message("Use AQuA library")
|
||||||
|
add_definitions( -DUSE_AQUA_LIBRARY )
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_PVQA_LIBRARY)
|
||||||
|
message("Use PVQA libraries")
|
||||||
|
add_definitions( -DUSE_PVQA_LIBRARY )
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/pvqa++/include)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set (RTPHONE_SOURCES
|
set (RTPHONE_SOURCES
|
||||||
${E}/engine_config.h
|
${rtphone_engine}/media/MT_Statistics.cpp
|
||||||
${E}/media/MT_Statistics.cpp
|
${rtphone_engine}/media/MT_WebRtc.cpp
|
||||||
${E}/media/MT_WebRtc.cpp
|
${rtphone_engine}/media/MT_Stream.cpp
|
||||||
${E}/media/MT_Stream.cpp
|
${rtphone_engine}/media/MT_SrtpHelper.cpp
|
||||||
${E}/media/MT_SrtpHelper.cpp
|
${rtphone_engine}/media/MT_SingleAudioStream.cpp
|
||||||
${E}/media/MT_SingleAudioStream.cpp
|
${rtphone_engine}/media/MT_NativeRtpSender.cpp
|
||||||
${E}/media/MT_NativeRtpSender.cpp
|
${rtphone_engine}/media/MT_Dtmf.cpp
|
||||||
${E}/media/MT_Dtmf.cpp
|
${rtphone_engine}/media/MT_CodecList.cpp
|
||||||
${E}/media/MT_CodecList.cpp
|
${rtphone_engine}/media/MT_Codec.cpp
|
||||||
${E}/media/MT_Codec.cpp
|
${rtphone_engine}/media/MT_Box.cpp
|
||||||
${E}/media/MT_Box.cpp
|
${rtphone_engine}/media/MT_AudioStream.cpp
|
||||||
${E}/media/MT_AudioStream.cpp
|
${rtphone_engine}/media/MT_AudioReceiver.cpp
|
||||||
${E}/media/MT_AudioReceiver.cpp
|
${rtphone_engine}/media/MT_AudioCodec.cpp
|
||||||
${E}/media/MT_AudioCodec.cpp
|
${rtphone_engine}/media/MT_CngHelper.cpp
|
||||||
${E}/media/MT_CngHelper.cpp
|
${rtphone_engine}/agent/Agent_Impl.cpp
|
||||||
${E}/agent/Agent_Impl.cpp
|
${rtphone_engine}/agent/Agent_AudioManager.cpp
|
||||||
${E}/agent/Agent_Impl.h
|
${rtphone_engine}/endpoint/EP_Account.cpp
|
||||||
${E}/agent/Agent_AudioManager.cpp
|
${rtphone_engine}/endpoint/EP_AudioProvider.cpp
|
||||||
${E}/agent/Agent_AudioManager.h
|
${rtphone_engine}/endpoint/EP_DataProvider.cpp
|
||||||
${E}/endpoint/EP_Account.cpp
|
${rtphone_engine}/endpoint/EP_Engine.cpp
|
||||||
${E}/endpoint/EP_Account.h
|
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
|
||||||
${E}/endpoint/EP_AudioProvider.cpp
|
${rtphone_engine}/endpoint/EP_Observer.cpp
|
||||||
${E}/endpoint/EP_AudioProvider.h
|
${rtphone_engine}/endpoint/EP_Session.cpp
|
||||||
${E}/endpoint/EP_DataProvider.cpp
|
|
||||||
${E}/endpoint/EP_DataProvider.h
|
|
||||||
${E}/endpoint/EP_Engine.cpp
|
|
||||||
${E}/endpoint/EP_Engine.h
|
|
||||||
${E}/endpoint/EP_NetworkQueue.cpp
|
|
||||||
${E}/endpoint/EP_NetworkQueue.h
|
|
||||||
${E}/endpoint/EP_Observer.cpp
|
|
||||||
${E}/endpoint/EP_Observer.h
|
|
||||||
${E}/endpoint/EP_Session.cpp
|
|
||||||
${E}/endpoint/EP_Session.h
|
|
||||||
|
|
||||||
${E}/media/MT_Statistics.h
|
${rtphone_engine}/media/MT_Statistics.h
|
||||||
${E}/media/MT_WebRtc.h
|
${rtphone_engine}/media/MT_WebRtc.h
|
||||||
${E}/media/MT_Stream.h
|
${rtphone_engine}/media/MT_Stream.h
|
||||||
${E}/media/MT_SrtpHelper.h
|
${rtphone_engine}/media/MT_SrtpHelper.h
|
||||||
${E}/media/MT_SingleAudioStream.h
|
${rtphone_engine}/media/MT_SingleAudioStream.h
|
||||||
${E}/media/MT_NativeRtpSender.h
|
${rtphone_engine}/media/MT_NativeRtpSender.h
|
||||||
${E}/media/MT_Dtmf.h
|
${rtphone_engine}/media/MT_Dtmf.h
|
||||||
${E}/media/MT_CodecList.h
|
${rtphone_engine}/media/MT_CodecList.h
|
||||||
${E}/media/MT_Codec.h
|
${rtphone_engine}/media/MT_Codec.h
|
||||||
${E}/media/MT_Box.h
|
${rtphone_engine}/media/MT_Box.h
|
||||||
${E}/media/MT_AudioStream.h
|
${rtphone_engine}/media/MT_AudioStream.h
|
||||||
${E}/media/MT_AudioReceiver.h
|
${rtphone_engine}/media/MT_AudioReceiver.h
|
||||||
${E}/media/MT_AudioCodec.h
|
${rtphone_engine}/media/MT_AudioCodec.h
|
||||||
${E}/media/MT_CngHelper.h
|
|
||||||
|
|
||||||
${E}/media/MT_Statistics.cpp
|
${rtphone_engine}/media/MT_CngHelper.h
|
||||||
${E}/media/MT_WebRtc.cpp
|
${rtphone_engine}/agent/Agent_Impl.h
|
||||||
${E}/media/MT_Stream.cpp
|
${rtphone_engine}/agent/Agent_AudioManager.h
|
||||||
${E}/media/MT_SrtpHelper.cpp
|
${rtphone_engine}/endpoint/EP_Account.h
|
||||||
${E}/media/MT_SingleAudioStream.cpp
|
${rtphone_engine}/endpoint/EP_AudioProvider.h
|
||||||
${E}/media/MT_NativeRtpSender.cpp
|
${rtphone_engine}/endpoint/EP_DataProvider.h
|
||||||
${E}/media/MT_Dtmf.cpp
|
${rtphone_engine}/endpoint/EP_Engine.h
|
||||||
${E}/media/MT_CodecList.cpp
|
${rtphone_engine}/endpoint/EP_NetworkQueue.h
|
||||||
${E}/media/MT_Codec.cpp
|
${rtphone_engine}/endpoint/EP_Observer.h
|
||||||
${E}/media/MT_Box.cpp
|
${rtphone_engine}/endpoint/EP_Session.h
|
||||||
${E}/media/MT_AudioStream.cpp
|
|
||||||
${E}/media/MT_AudioReceiver.cpp
|
|
||||||
${E}/media/MT_AudioCodec.cpp
|
|
||||||
${E}/media/MT_CngHelper.cpp
|
|
||||||
${E}/media/MT_AmrCodec.cpp
|
|
||||||
${E}/media/MT_EvsCodec.cpp
|
|
||||||
${E}/media/MT_Statistics.h
|
|
||||||
${E}/media/MT_WebRtc.h
|
|
||||||
${E}/media/MT_Stream.h
|
|
||||||
${E}/media/MT_SrtpHelper.h
|
|
||||||
${E}/media/MT_SingleAudioStream.h
|
|
||||||
${E}/media/MT_NativeRtpSender.h
|
|
||||||
${E}/media/MT_Dtmf.h
|
|
||||||
${E}/media/MT_CodecList.h
|
|
||||||
${E}/media/MT_Codec.h
|
|
||||||
${E}/media/MT_Box.h
|
|
||||||
${E}/media/MT_AudioStream.h
|
|
||||||
${E}/media/MT_AudioReceiver.h
|
|
||||||
${E}/media/MT_AudioCodec.h
|
|
||||||
${E}/media/MT_CngHelper.h
|
|
||||||
${E}/media/MT_AmrCodec.h
|
|
||||||
${E}/media/MT_EvsCodec.h
|
|
||||||
|
|
||||||
${E}/helper/HL_AsyncCommand.cpp
|
|
||||||
${E}/helper/HL_AsyncCommand.h
|
|
||||||
${E}/helper/HL_Base64.h
|
|
||||||
${E}/helper/HL_ByteBuffer.h
|
|
||||||
${E}/helper/HL_Calculator.cpp
|
|
||||||
${E}/helper/HL_Calculator.h
|
|
||||||
${E}/helper/HL_CrashRpt.cpp
|
|
||||||
${E}/helper/HL_CrashRpt.h
|
|
||||||
${E}/helper/HL_CsvReader.cpp
|
|
||||||
${E}/helper/HL_CsvReader.h
|
|
||||||
${E}/helper/HL_Epoll.cpp
|
|
||||||
${E}/helper/HL_Epoll.h
|
|
||||||
${E}/helper/HL_Exception.h
|
|
||||||
${E}/helper/HL_File.cpp
|
|
||||||
${E}/helper/HL_File.h
|
|
||||||
${E}/helper/HL_HepSupport.cpp
|
|
||||||
${E}/helper/HL_HepSupport.h
|
|
||||||
${E}/helper/HL_InternetAddress.h
|
|
||||||
${E}/helper/HL_IuUP.cpp
|
|
||||||
${E}/helper/HL_IuUP.h
|
|
||||||
${E}/helper/HL_Log.cpp
|
|
||||||
${E}/helper/HL_Log.h
|
|
||||||
${E}/helper/HL_NetworkFrame.cpp
|
|
||||||
${E}/helper/HL_NetworkFrame.h
|
|
||||||
${E}/helper/HL_NetworkSocket.cpp
|
|
||||||
${E}/helper/HL_NetworkSocket.h
|
|
||||||
${E}/helper/HL_Optional.hpp
|
|
||||||
${E}/helper/HL_OsVersion.cpp
|
|
||||||
${E}/helper/HL_OsVersion.h
|
|
||||||
${E}/helper/HL_Pointer.cpp
|
|
||||||
${E}/helper/HL_Pointer.h
|
|
||||||
${E}/helper/HL_Process.cpp
|
|
||||||
${E}/helper/HL_Process.h
|
|
||||||
${E}/helper/HL_Rtp.cpp
|
|
||||||
${E}/helper/HL_Rtp.h
|
|
||||||
${E}/helper/HL_Singletone.cpp
|
|
||||||
${E}/helper/HL_Singletone.h
|
|
||||||
${E}/helper/HL_SocketHeap.cpp
|
|
||||||
${E}/helper/HL_SocketHeap.h
|
|
||||||
${E}/helper/HL_Statistics.cpp
|
|
||||||
${E}/helper/HL_Statistics.h
|
|
||||||
${E}/helper/HL_StreamState.h
|
|
||||||
${E}/helper/HL_String.cpp
|
|
||||||
${E}/helper/HL_String.h
|
|
||||||
${E}/helper/HL_Sync.cpp
|
|
||||||
${E}/helper/HL_Sync.h
|
|
||||||
${E}/helper/HL_ThreadPool.cpp
|
|
||||||
${E}/helper/HL_ThreadPool.h
|
|
||||||
${E}/helper/HL_Time.cpp
|
|
||||||
${E}/helper/HL_Time.h
|
|
||||||
${E}/helper/HL_Types.h
|
|
||||||
${E}/helper/HL_Types.cpp
|
|
||||||
${E}/helper/HL_Usb.cpp
|
|
||||||
${E}/helper/HL_Usb.h
|
|
||||||
${E}/helper/HL_Uuid.cpp
|
|
||||||
${E}/helper/HL_Uuid.h
|
|
||||||
${E}/helper/HL_VariantMap.cpp
|
|
||||||
${E}/helper/HL_VariantMap.h
|
|
||||||
${E}/helper/HL_Xcap.cpp
|
|
||||||
${E}/helper/HL_Xcap.h
|
|
||||||
|
|
||||||
${E}/audio/Audio_Resampler.cpp
|
|
||||||
${E}/audio/Audio_Resampler.h
|
|
||||||
${E}/audio/Audio_Quality.cpp
|
|
||||||
${E}/audio/Audio_Quality.h
|
|
||||||
${E}/audio/Audio_Mixer.cpp
|
|
||||||
${E}/audio/Audio_Mixer.h
|
|
||||||
${E}/audio/Audio_Interface.cpp
|
|
||||||
${E}/audio/Audio_Interface.h
|
|
||||||
${E}/audio/Audio_Helper.cpp
|
|
||||||
${E}/audio/Audio_Helper.h
|
|
||||||
${E}/audio/Audio_DataWindow.cpp
|
|
||||||
${E}/audio/Audio_DataWindow.h
|
|
||||||
${E}/audio/Audio_DevicePair.cpp
|
|
||||||
${E}/audio/Audio_DevicePair.h
|
|
||||||
${E}/audio/Audio_Player.cpp
|
|
||||||
${E}/audio/Audio_Player.h
|
|
||||||
${E}/audio/Audio_Null.cpp
|
|
||||||
${E}/audio/Audio_Null.h
|
|
||||||
${E}/audio/Audio_CoreAudio.cpp
|
|
||||||
${E}/audio/Audio_CoreAudio.h
|
|
||||||
${E}/audio/Audio_DirectSound.cpp
|
|
||||||
${E}/audio/Audio_DirectSound.h
|
|
||||||
${E}/audio/Audio_AndroidOboe.cpp
|
|
||||||
${E}/audio/Audio_AndroidOboe.h
|
|
||||||
${E}/audio/Audio_WavFile.cpp
|
|
||||||
${E}/audio/Audio_WavFile.h
|
|
||||||
|
|
||||||
${L}/ice/hmac_sha1_impl.cpp
|
|
||||||
${L}/ice/hmac_sha1_impl.h
|
|
||||||
${L}/ice/ICEAction.h
|
|
||||||
${L}/ice/ICEAddress.cpp
|
|
||||||
${L}/ice/ICEAddress.h
|
|
||||||
${L}/ice/ICEAuthTransaction.cpp
|
|
||||||
${L}/ice/ICEAuthTransaction.h
|
|
||||||
${L}/ice/ICEBinding.cpp
|
|
||||||
${L}/ice/ICEBinding.h
|
|
||||||
${L}/ice/ICEBox.cpp
|
|
||||||
${L}/ice/ICEBox.h
|
|
||||||
${L}/ice/ICEBoxImpl.cpp
|
|
||||||
${L}/ice/ICEBoxImpl.h
|
|
||||||
${L}/ice/ICEByteBuffer.cpp
|
|
||||||
${L}/ice/ICEByteBuffer.h
|
|
||||||
${L}/ice/ICECandidate.cpp
|
|
||||||
${L}/ice/ICECandidate.h
|
|
||||||
${L}/ice/ICECandidatePair.cpp
|
|
||||||
${L}/ice/ICECandidatePair.h
|
|
||||||
${L}/ice/ICECheckList.cpp
|
|
||||||
${L}/ice/ICECheckList.h
|
|
||||||
${L}/ice/ICECRC32.cpp
|
|
||||||
${L}/ice/ICECRC32.h
|
|
||||||
${L}/ice/ICEError.cpp
|
|
||||||
${L}/ice/ICEError.h
|
|
||||||
${L}/ice/ICEEvent.h
|
|
||||||
${L}/ice/ICELog.cpp
|
|
||||||
${L}/ice/ICELog.h
|
|
||||||
${L}/ice/ICEMD5.cpp
|
|
||||||
${L}/ice/ICEMD5.h
|
|
||||||
${L}/ice/ICENetworkHelper.cpp
|
|
||||||
${L}/ice/ICENetworkHelper.h
|
|
||||||
${L}/ice/ICEPacketTimer.cpp
|
|
||||||
${L}/ice/ICEPacketTimer.h
|
|
||||||
${L}/ice/ICEPlatform.cpp
|
|
||||||
${L}/ice/ICEPlatform.h
|
|
||||||
${L}/ice/ICERelaying.cpp
|
|
||||||
${L}/ice/ICERelaying.h
|
|
||||||
${L}/ice/ICESession.cpp
|
|
||||||
${L}/ice/ICESession.h
|
|
||||||
${L}/ice/ICESHA1.cpp
|
|
||||||
${L}/ice/ICESHA1.h
|
|
||||||
${L}/ice/ICESocket.h
|
|
||||||
${L}/ice/ICEStream.cpp
|
|
||||||
${L}/ice/ICEStream.h
|
|
||||||
${L}/ice/ICEStunAttributes.cpp
|
|
||||||
${L}/ice/ICEStunAttributes.h
|
|
||||||
${L}/ice/ICEStunConfig.cpp
|
|
||||||
${L}/ice/ICEStunConfig.h
|
|
||||||
${L}/ice/ICEStunMessage.cpp
|
|
||||||
${L}/ice/ICEStunMessage.h
|
|
||||||
${L}/ice/ICEStunTransaction.cpp
|
|
||||||
${L}/ice/ICEStunTransaction.h
|
|
||||||
${L}/ice/ICESync.cpp
|
|
||||||
${L}/ice/ICESync.h
|
|
||||||
${L}/ice/ICETime.cpp
|
|
||||||
${L}/ice/ICETime.h
|
|
||||||
${L}/ice/ICETransactionList.cpp
|
|
||||||
${L}/ice/ICETransactionList.h
|
|
||||||
${L}/ice/ICETypes.h
|
|
||||||
${L}/ice/md5_impl.cpp
|
|
||||||
${L}/ice/md5_impl.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (USE_AMR_CODEC)
|
||||||
|
add_definitions(-DUSE_AMR_CODEC)
|
||||||
|
set(RTPHONE_SOURCES ${RTPHONE_SOURCES} ${rtphone_engine}/media/MT_AmrCodec.cpp ${rtphone_engine}/media/MT_AmrCodec.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_EVS_CODEC)
|
||||||
|
add_definitions(-DUSE_EVS_CODEC)
|
||||||
|
set(RTPHONE_SOURCES ${RTPHONE_SOURCES} ${rtphone_engine}/media/MT_EvsCodec.cpp ${rtphone_engine}/media/MT_EvsCodec.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_OPUS_CODEC)
|
||||||
|
add_definitions(-DUSE_OPUS_CODEC)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library (rtphone STATIC ${RTPHONE_SOURCES})
|
add_library (rtphone STATIC ${RTPHONE_SOURCES})
|
||||||
|
|
||||||
add_subdirectory(${L}/resiprocate)
|
add_subdirectory(${rtphone_libs}/resiprocate)
|
||||||
add_subdirectory(${L}/jrtplib/src)
|
add_subdirectory(${rtphone_libs}/ice)
|
||||||
add_subdirectory(${L}/libg729)
|
add_subdirectory(${rtphone_libs}/jrtplib/src)
|
||||||
|
add_subdirectory(${rtphone_libs}/libg729)
|
||||||
|
|
||||||
if (USE_EVS_CODEC)
|
if (USE_EVS_CODEC)
|
||||||
add_subdirectory(${L}/libevs)
|
add_subdirectory(${rtphone_libs}/libevs)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(${L}/libgsm)
|
add_subdirectory(${rtphone_libs}/libgsm)
|
||||||
add_subdirectory(${L}/gsmhr)
|
add_subdirectory(${rtphone_libs}/gsmhr)
|
||||||
add_subdirectory(${L}/g722)
|
add_subdirectory(${rtphone_libs}/g722)
|
||||||
add_subdirectory(${L}/speexdsp)
|
add_subdirectory(${rtphone_libs}/speexdsp)
|
||||||
add_subdirectory(${L}/libsrtp)
|
add_subdirectory(${rtphone_libs}/srtp)
|
||||||
add_subdirectory(${L}/webrtc)
|
add_subdirectory(${rtphone_libs}/webrtc)
|
||||||
add_subdirectory(${L}/opus)
|
add_subdirectory(${rtphone_engine}/helper)
|
||||||
|
add_subdirectory(${rtphone_engine}/audio)
|
||||||
|
add_subdirectory(${rtphone_engine}/media)
|
||||||
|
|
||||||
# Suppose the subproject defines target "mylib"
|
set (LIBS ice_stack jrtplib g729_codec gsm_codec
|
||||||
if(MSVC)
|
gsmhr_codec g722_codec srtp resiprocate helper_lib audio_lib webrtc speexdsp
|
||||||
# target_compile_options(opus PRIVATE /O2 /DNDEBUG)
|
uuid)
|
||||||
# Optional: enable whole program optimization on MSVC
|
|
||||||
# target_compile_options(mylib PRIVATE /GL)
|
if (CMAKE_SYSTEM MATCHES "Win*")
|
||||||
# target_link_options(mylib PRIVATE /LTCG)
|
set (LIBS ${LIBS} )
|
||||||
else ()
|
else ()
|
||||||
target_compile_options(opus PRIVATE -O3 -DNDEBUG)
|
set (LIBS ${LIBS} dl uuid)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM MATCHES "Android")
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
|
set (LIBS ${LIBS} oboe)
|
||||||
gsmhr_codec g722_codec srtp3 resiprocate webrtc speexdsp)
|
endif()
|
||||||
|
|
||||||
if (USE_AMR_CODEC)
|
if (USE_AMR_CODEC)
|
||||||
message("Media: AMR NB and WB codecs will be included")
|
set (LIBS ${LIBS})
|
||||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/libs/opencore-amr build_opencore)
|
endif (USE_AMR_CODEC)
|
||||||
set (OPENCORE_AMRWB opencore-amrwb)
|
|
||||||
set (OPENCORE_AMRNB opencore-amrnb)
|
|
||||||
|
|
||||||
set (DEFINES ${DEFINES} -DUSE_AMR_CODEC)
|
target_link_libraries(rtphone
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})
|
ice_stack jrtplib g729_codec gsm_codec
|
||||||
endif()
|
gsmhr_codec g722_codec srtp resiprocate
|
||||||
|
helper_lib
|
||||||
|
audio_lib
|
||||||
|
webrtc
|
||||||
|
speexdsp
|
||||||
|
uuid
|
||||||
|
${OPENSSL_SSL}
|
||||||
|
${OPENSSL_CRYPTO}
|
||||||
|
${LIBS} )
|
||||||
|
|
||||||
if (USE_EVS_CODEC)
|
|
||||||
message("Media: EVS codec will be included.")
|
|
||||||
set (DEFINES ${DEFINES} -DUSE_EVS_CODEC)
|
|
||||||
set (LIBS_STATIC ${LIBS_STATIC} evs_codec)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_definitions(rtphone PUBLIC ${DEFINES} )
|
|
||||||
|
|
||||||
if (TARGET_LINUX)
|
|
||||||
# PRIVATE, not PUBLIC: rtphone is a STATIC library, so these link options are
|
|
||||||
# never used to build rtphone itself and must not propagate to consumers.
|
|
||||||
# As PUBLIC they leaked into every consumer's LINK_FLAGS as an adjacent
|
|
||||||
# "-Wl,-Bstatic -Wl,-Bdynamic" pair (the wrapped libraries land in a separate
|
|
||||||
# LINK_LIBRARIES section, so nothing is actually wrapped). The trailing
|
|
||||||
# -Bdynamic forced the linker back into dynamic-search mode, which broke
|
|
||||||
# fully-static consumers (e.g. vq-core built with SERVER_STATIC_LINKING=ON:
|
|
||||||
# "attempted static link of dynamic object libz.so").
|
|
||||||
target_link_options(rtphone PRIVATE -Wl,-Bstatic)
|
|
||||||
target_compile_options(rtphone PUBLIC -Wno-deprecated -Wno-deprecated-declarations)
|
|
||||||
endif()
|
|
||||||
target_link_libraries(rtphone PUBLIC ${LIBS_STATIC})
|
|
||||||
|
|
||||||
if (TARGET_LINUX)
|
|
||||||
target_link_options(rtphone PRIVATE -Wl,-Bdynamic)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_include_directories(rtphone
|
target_include_directories(rtphone
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@@ -383,19 +235,15 @@ target_include_directories(rtphone
|
|||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/engine>
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
${CMAKE_CURRENT_SOURCE_DIR}/libs
|
||||||
${LIB_PLATFORM}/opus/include
|
${LIB_PLATFORM}/opus/include
|
||||||
${E}/helper
|
|
||||||
${E}/audio
|
|
||||||
${E}/media
|
|
||||||
${L}
|
|
||||||
${L}/ice
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${L}/libevs/lib_com
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/
|
||||||
${L}/libevs/lib_enc
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_com
|
||||||
${L}/libevs/lib_dec
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_enc
|
||||||
${L}/speex/include
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/libevs/lib_dec
|
||||||
${L}/libs/json
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/speex/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/opus/include/
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libs/json
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
# For MSVC static builds
|
||||||
target_link_libraries(rtphone PUBLIC OpenSSL::SSL)
|
configure_msvc_runtime()
|
||||||
target_link_libraries(rtphone PUBLIC OpenSSL::Crypto)
|
|
||||||
|
|||||||
@@ -1,21 +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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "Agent_AudioManager.h"
|
#include "Agent_AudioManager.h"
|
||||||
#include "../engine/audio/Audio_WavFile.h"
|
#include "../engine/audio/Audio_WavFile.h"
|
||||||
|
#include "../engine/helper/HL_String.h"
|
||||||
#include "../engine/audio/Audio_Null.h"
|
#include "../engine/audio/Audio_Null.h"
|
||||||
#include "HL_String.h"
|
|
||||||
|
|
||||||
#if defined(TARGET_ANDROID)
|
#if defined(TARGET_ANDROID)
|
||||||
# include "../engine/audio/Audio_Android.h"
|
# include "../engine/audio/Audio_Android.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "AudioManager"
|
||||||
|
|
||||||
|
|
||||||
AudioManager::AudioManager() : mTerminal(nullptr), mAudioMonitoring(nullptr)
|
// ---------------- AudioManager -------------
|
||||||
|
//static AudioManager GAudioManager;
|
||||||
|
|
||||||
|
AudioManager::AudioManager()
|
||||||
|
:mTerminal(nullptr)
|
||||||
{
|
{
|
||||||
mPlayer.setDelegate(this);
|
mPlayer.setDelegate(this);
|
||||||
}
|
}
|
||||||
@@ -25,6 +29,16 @@ AudioManager::~AudioManager()
|
|||||||
//stop();
|
//stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioManager& AudioManager::instance()
|
||||||
|
{
|
||||||
|
static std::shared_ptr<AudioManager> GAudioManager;
|
||||||
|
|
||||||
|
if (!GAudioManager)
|
||||||
|
GAudioManager = std::make_shared<AudioManager>();
|
||||||
|
|
||||||
|
return *GAudioManager;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioManager::setTerminal(MT::Terminal* terminal)
|
void AudioManager::setTerminal(MT::Terminal* terminal)
|
||||||
{
|
{
|
||||||
mTerminal = terminal;
|
mTerminal = terminal;
|
||||||
@@ -35,16 +49,6 @@ MT::Terminal* AudioManager::terminal()
|
|||||||
return mTerminal;
|
return mTerminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::setAudioMonitoring(Audio::DataConnection* monitoring)
|
|
||||||
{
|
|
||||||
mAudioMonitoring = monitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
Audio::DataConnection* AudioManager::audioMonitoring()
|
|
||||||
{
|
|
||||||
return mAudioMonitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
|
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
|
||||||
void AudioManager::start(int usageId)
|
void AudioManager::start(int usageId)
|
||||||
{
|
{
|
||||||
@@ -56,7 +60,6 @@ void AudioManager::start(int usageId)
|
|||||||
if (mUsage.obtain(usageId) > 1)
|
if (mUsage.obtain(usageId) > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Maybe it is time to initialize global audio support
|
|
||||||
if (Audio::OsEngine::instance())
|
if (Audio::OsEngine::instance())
|
||||||
Audio::OsEngine::instance()->open();
|
Audio::OsEngine::instance()->open();
|
||||||
|
|
||||||
@@ -65,29 +68,21 @@ void AudioManager::start(int usageId)
|
|||||||
// Disable AEC for now - because PVQA conflicts with speex AEC.
|
// Disable AEC for now - because PVQA conflicts with speex AEC.
|
||||||
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
|
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
|
||||||
if (!mTerminal->audio())
|
if (!mTerminal->audio())
|
||||||
{
|
mTerminal->setAudio(std::make_shared<Audio::DevicePair>(false, true));
|
||||||
auto audio = std::make_shared<Audio::DevicePair>();
|
|
||||||
audio->setAgc(true);
|
|
||||||
audio->setAec(false);
|
|
||||||
audio->setMonitoring(mAudioMonitoring);
|
|
||||||
|
|
||||||
mTerminal->setAudio(audio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mAudioInput)
|
if (!mAudioInput)
|
||||||
{
|
{
|
||||||
enumerator->open(Audio::myMicrophone);
|
enumerator->open(Audio::myMicrophone);
|
||||||
int inputIndex = enumerator->indexOfDefaultDevice();
|
int inputIndex = enumerator->indexOfDefaultDevice();
|
||||||
|
|
||||||
// Construct default platform input device
|
// Construct and set to terminal's audio pair input device
|
||||||
if (usageId != atNull)
|
if (usageId != atNull)
|
||||||
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
|
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
|
||||||
else
|
else
|
||||||
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
|
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
|
||||||
}
|
|
||||||
// Bind input to the terminal's device pair regardless of whether it was
|
|
||||||
// just constructed or externally injected via setAudioInput().
|
|
||||||
mTerminal->audio()->setInput(mAudioInput);
|
mTerminal->audio()->setInput(mAudioInput);
|
||||||
|
}
|
||||||
|
|
||||||
if (!mAudioOutput)
|
if (!mAudioOutput)
|
||||||
{
|
{
|
||||||
@@ -95,19 +90,21 @@ void AudioManager::start(int usageId)
|
|||||||
enumerator->open(Audio::mySpeaker);
|
enumerator->open(Audio::mySpeaker);
|
||||||
int outputIndex = enumerator->indexOfDefaultDevice();
|
int outputIndex = enumerator->indexOfDefaultDevice();
|
||||||
|
|
||||||
// Construct default platform output device
|
// Construct and set terminal's audio pair output device
|
||||||
if (usageId != atNull)
|
if (usageId != atNull)
|
||||||
{
|
{
|
||||||
if (outputIndex >= enumerator->count())
|
if (outputIndex >= enumerator->count())
|
||||||
outputIndex = 0;
|
outputIndex = 0;
|
||||||
|
|
||||||
mAudioOutput = Audio::POutputDevice(Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
|
mAudioOutput = Audio::POutputDevice(
|
||||||
|
Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
|
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
|
||||||
}
|
|
||||||
mTerminal->audio()->setOutput(mAudioOutput);
|
mTerminal->audio()->setOutput(mAudioOutput);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Open audio
|
// Open audio
|
||||||
if (mAudioInput)
|
if (mAudioInput)
|
||||||
@@ -156,24 +153,12 @@ void AudioManager::stop(int usageId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::setAudioInput(Audio::PInputDevice input)
|
|
||||||
{
|
|
||||||
LOCK_MANAGER;
|
|
||||||
mAudioInput = std::move(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioManager::setAudioOutput(Audio::POutputDevice output)
|
|
||||||
{
|
|
||||||
LOCK_MANAGER;
|
|
||||||
mAudioOutput = std::move(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit)
|
void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit)
|
||||||
{
|
{
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
r->open(strx::makeTstring(path));
|
r->open(StringHelper::makeTstring(path));
|
||||||
#else
|
#else
|
||||||
r->open(path);
|
r->open(path);
|
||||||
#endif
|
#endif
|
||||||
@@ -194,13 +179,15 @@ void AudioManager::stopPlayFile(int usageId)
|
|||||||
mPlayer.release(usageId);
|
mPlayer.release(usageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item) {}
|
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void AudioManager::process()
|
void AudioManager::process()
|
||||||
{
|
{
|
||||||
mPlayer.releasePlayed();
|
mPlayer.releasePlayed();
|
||||||
std::vector<int> ids;
|
std::vector<int> ids;
|
||||||
mTerminal->audio()->player().retrieveUsageIds(ids);
|
mTerminal->audio()->player().retrieveUsageIds(ids);
|
||||||
for (int id : ids)
|
for (unsigned i=0; i<ids.size(); i++)
|
||||||
stop(id);
|
stop(ids[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,11 @@
|
|||||||
|
|
||||||
#include "../engine/audio/Audio_Interface.h"
|
#include "../engine/audio/Audio_Interface.h"
|
||||||
#include "../engine/audio/Audio_Player.h"
|
#include "../engine/audio/Audio_Player.h"
|
||||||
|
#include "../engine/endpoint/EP_Engine.h"
|
||||||
#include "../engine/media/MT_Box.h"
|
#include "../engine/media/MT_Box.h"
|
||||||
|
#include "../engine/helper/HL_Log.h"
|
||||||
|
#include "../engine/helper/HL_Sync.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@@ -36,7 +40,7 @@ public:
|
|||||||
AudioManager();
|
AudioManager();
|
||||||
virtual ~AudioManager();
|
virtual ~AudioManager();
|
||||||
|
|
||||||
// static AudioManager& instance();
|
static AudioManager& instance();
|
||||||
|
|
||||||
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
|
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
|
||||||
void close();
|
void close();
|
||||||
@@ -45,19 +49,10 @@ public:
|
|||||||
void setTerminal(MT::Terminal* terminal);
|
void setTerminal(MT::Terminal* terminal);
|
||||||
MT::Terminal* terminal();
|
MT::Terminal* terminal();
|
||||||
|
|
||||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
|
||||||
Audio::DataConnection* audioMonitoring();
|
|
||||||
|
|
||||||
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
|
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
|
||||||
void start(int usageId);
|
void start(int usageId);
|
||||||
void stop(int usageId);
|
void stop(int usageId);
|
||||||
|
|
||||||
// Inject a custom input device. Must be called before start(): when set,
|
|
||||||
// start() skips construction of the default platform microphone. Pass an
|
|
||||||
// empty pointer to clear the override.
|
|
||||||
void setAudioInput(Audio::PInputDevice input);
|
|
||||||
void setAudioOutput(Audio::POutputDevice output);
|
|
||||||
|
|
||||||
enum AudioTarget
|
enum AudioTarget
|
||||||
{
|
{
|
||||||
atNull,
|
atNull,
|
||||||
@@ -74,7 +69,6 @@ public:
|
|||||||
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
|
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
|
||||||
void stopPlayFile(int usageId);
|
void stopPlayFile(int usageId);
|
||||||
|
|
||||||
|
|
||||||
void onFilePlayed(Audio::Player::PlaylistItem& item);
|
void onFilePlayed(Audio::Player::PlaylistItem& item);
|
||||||
|
|
||||||
// Must be called from main loop to release used audio devices
|
// Must be called from main loop to release used audio devices
|
||||||
@@ -85,7 +79,6 @@ protected:
|
|||||||
Audio::POutputDevice mAudioOutput;
|
Audio::POutputDevice mAudioOutput;
|
||||||
Audio::Player mPlayer;
|
Audio::Player mPlayer;
|
||||||
MT::Terminal* mTerminal;
|
MT::Terminal* mTerminal;
|
||||||
Audio::DataConnection* mAudioMonitoring;
|
|
||||||
|
|
||||||
std::map<int, int> UsageMap;
|
std::map<int, int> UsageMap;
|
||||||
UsageCounter mUsage;
|
UsageCounter mUsage;
|
||||||
|
|||||||
+250
-144
@@ -3,12 +3,17 @@
|
|||||||
#include "helper/HL_String.h"
|
#include "helper/HL_String.h"
|
||||||
#include "helper/HL_StreamState.h"
|
#include "helper/HL_StreamState.h"
|
||||||
#include "helper/HL_VariantMap.h"
|
#include "helper/HL_VariantMap.h"
|
||||||
// #include "helper/HL_CsvReader.h"
|
#include "helper/HL_CsvReader.h"
|
||||||
// #include "helper/HL_Base64.h"
|
#include "helper/HL_Base64.h"
|
||||||
#include "media/MT_CodecList.h"
|
#include <fstream>
|
||||||
#include "audio/Audio_Null.h"
|
|
||||||
// #include <fstream>
|
|
||||||
|
|
||||||
|
#if defined(USE_PVQA_LIBRARY)
|
||||||
|
# include "pvqa++.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
# include "aqua++.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
const std::string Status_Ok = "ok";
|
const std::string Status_Ok = "ok";
|
||||||
const std::string Status_SessionNotFound = "session not found";
|
const std::string Status_SessionNotFound = "session not found";
|
||||||
@@ -17,11 +22,11 @@ const std::string Status_FailedToOpenFile = "failed to open file";
|
|||||||
const std::string Status_NoActiveProvider = "no active provider";
|
const std::string Status_NoActiveProvider = "no active provider";
|
||||||
const std::string Status_NoMediaAction = "no valid media action";
|
const std::string Status_NoMediaAction = "no valid media action";
|
||||||
const std::string Status_NoCommand = "no valid command";
|
const std::string Status_NoCommand = "no valid command";
|
||||||
const std::string Status_NoAudioManager = "no audio manager";
|
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "agent"
|
#define LOG_SUBSYSTEM "Agent"
|
||||||
|
|
||||||
AgentImpl::AgentImpl() : mShutdown(false), mEventListChangeCondVar()
|
AgentImpl::AgentImpl()
|
||||||
|
:mShutdown(false), mEventListChangeCondVar()
|
||||||
{
|
{
|
||||||
#if defined(TARGET_ANDROID) || defined(TARGET_WIN)
|
#if defined(TARGET_ANDROID) || defined(TARGET_WIN)
|
||||||
ice::GLogger.useDebugWindow(true);
|
ice::GLogger.useDebugWindow(true);
|
||||||
@@ -34,24 +39,6 @@ AgentImpl::~AgentImpl()
|
|||||||
stopAgentAndThread();
|
stopAgentAndThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get access to internal audio manager. Value can be nullptr.
|
|
||||||
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
|
|
||||||
{
|
|
||||||
return mAudioManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
|
|
||||||
{
|
|
||||||
mAudioMonitoring = monitoring;
|
|
||||||
if (mAudioManager)
|
|
||||||
mAudioManager->setAudioMonitoring(monitoring);
|
|
||||||
}
|
|
||||||
|
|
||||||
Audio::DataConnection* AgentImpl::monitoring() const
|
|
||||||
{
|
|
||||||
return mAudioMonitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AgentImpl::run()
|
void AgentImpl::run()
|
||||||
{
|
{
|
||||||
while (!mShutdown)
|
while (!mShutdown)
|
||||||
@@ -77,58 +64,71 @@ std::string AgentImpl::command(const std::string& command)
|
|||||||
return "";
|
return "";
|
||||||
|
|
||||||
std::string cmd = d["command"].asString();
|
std::string cmd = d["command"].asString();
|
||||||
if (cmd != "wait_for_event" && cmd != "agent_add_root_cert")
|
if (cmd != "wait_for_event")
|
||||||
{
|
{
|
||||||
ICELogInfo(<< command);
|
ICELogInfo(<< command);
|
||||||
}
|
}
|
||||||
if (cmd == "config")
|
if (cmd == "config")
|
||||||
processConfig(d, answer);
|
processConfig(d, answer);
|
||||||
else if (cmd == "start")
|
else
|
||||||
|
if (cmd == "start")
|
||||||
processStart(d, answer);
|
processStart(d, answer);
|
||||||
else if (cmd == "stop")
|
else
|
||||||
|
if (cmd == "stop")
|
||||||
processStop(d, answer);
|
processStop(d, answer);
|
||||||
else if (cmd == "account_create")
|
else
|
||||||
|
if (cmd == "account_create")
|
||||||
processCreateAccount(d, answer);
|
processCreateAccount(d, answer);
|
||||||
else if (cmd == "account_start")
|
else
|
||||||
|
if (cmd == "account_start")
|
||||||
processStartAccount(d, answer);
|
processStartAccount(d, answer);
|
||||||
else if (cmd == "account_setuserinfo")
|
else
|
||||||
|
if (cmd == "account_setuserinfo")
|
||||||
processSetUserInfoToAccount(d, answer);
|
processSetUserInfoToAccount(d, answer);
|
||||||
else if (cmd == "session_create")
|
else
|
||||||
{
|
if (cmd == "session_create")
|
||||||
// For Bugsnag test
|
|
||||||
// int* v = nullptr;
|
|
||||||
// *v = 0;
|
|
||||||
processCreateSession(d, answer);
|
processCreateSession(d, answer);
|
||||||
}
|
else
|
||||||
else if (cmd == "session_start")
|
if (cmd == "session_start")
|
||||||
processStartSession(d, answer);
|
processStartSession(d, answer);
|
||||||
else if (cmd == "session_stop")
|
else
|
||||||
|
if (cmd == "session_stop")
|
||||||
processStopSession(d, answer);
|
processStopSession(d, answer);
|
||||||
else if (cmd == "session_accept")
|
else
|
||||||
|
if (cmd == "session_accept")
|
||||||
processAcceptSession(d, answer);
|
processAcceptSession(d, answer);
|
||||||
else if (cmd == "session_destroy")
|
else
|
||||||
|
if (cmd == "session_destroy")
|
||||||
processDestroySession(d, answer);
|
processDestroySession(d, answer);
|
||||||
else if (cmd == "session_use_stream")
|
else
|
||||||
|
if (cmd == "session_use_stream")
|
||||||
processUseStreamForSession(d, answer);
|
processUseStreamForSession(d, answer);
|
||||||
else if (cmd == "wait_for_event")
|
else
|
||||||
|
if (cmd == "wait_for_event")
|
||||||
processWaitForEvent(d, answer);
|
processWaitForEvent(d, answer);
|
||||||
else if (cmd == "session_get_media_stats")
|
else
|
||||||
|
if (cmd == "session_get_media_stats")
|
||||||
processGetMediaStats(d, answer);
|
processGetMediaStats(d, answer);
|
||||||
else if (cmd == "agent_network_changed")
|
else
|
||||||
|
if (cmd == "agent_network_changed")
|
||||||
processNetworkChanged(d, answer);
|
processNetworkChanged(d, answer);
|
||||||
else if (cmd == "agent_add_root_cert")
|
else
|
||||||
|
if (cmd == "agent_add_root_cert")
|
||||||
processAddRootCert(d, answer);
|
processAddRootCert(d, answer);
|
||||||
else if (cmd == "detach_log")
|
else
|
||||||
|
if (cmd == "detach_log")
|
||||||
{
|
{
|
||||||
GLogger.closeFile();
|
GLogger.closeFile();
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else if (cmd == "attach_log")
|
else
|
||||||
|
if (cmd == "attach_log")
|
||||||
{
|
{
|
||||||
GLogger.openFile();
|
GLogger.openFile();
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else if (cmd == "log_message")
|
else
|
||||||
|
if (cmd == "log_message")
|
||||||
processLogMessage(d, answer);
|
processLogMessage(d, answer);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -139,9 +139,7 @@ std::string AgentImpl::command(const std::string& command)
|
|||||||
{
|
{
|
||||||
answer["status"] = e.what();
|
answer["status"] = e.what();
|
||||||
}
|
}
|
||||||
std::string result = answer.toStyledString();
|
return answer.toStyledString();
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AgentImpl::waitForData(int /*milliseconds*/)
|
bool AgentImpl::waitForData(int /*milliseconds*/)
|
||||||
@@ -158,14 +156,28 @@ void AgentImpl::processConfig(JsonCpp::Value& d, JsonCpp::Value& answer)
|
|||||||
{
|
{
|
||||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||||
|
|
||||||
|
#if !defined(TARGET_ANDROID) && defined(USE_PVQA_LIBRARY)
|
||||||
|
// It works for desktop OSes only
|
||||||
|
// Because Android requires special initializing procedure (valid JNI environment context)
|
||||||
|
std::string pvqaLicense = d["pvqa-license"].asString(), pvqaConfig = d["pvqa-config"].asString();
|
||||||
|
if (!pvqaLicense.empty() && !pvqaConfig.empty())
|
||||||
|
sevana::pvqa::initialize(pvqaLicense, pvqaConfig);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(TARGET_ANDROID) && defined(USE_AQUA_LIBRARY)
|
||||||
|
std::string aquaLicense = d["aqua-license"].asString();
|
||||||
|
if (!aquaLicense.empty())
|
||||||
|
sevana::aqua::initialize(aquaLicense.c_str(), aquaLicense.size());
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string transport = d["transport"].asString();
|
std::string transport = d["transport"].asString();
|
||||||
config()[CONFIG_TRANSPORT] =
|
config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
|
||||||
(transport == "any")
|
|
||||||
? TransportType_Any
|
|
||||||
: (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
|
|
||||||
config()[CONFIG_IPV4] = d["ipv4"].asBool();
|
config()[CONFIG_IPV4] = d["ipv4"].asBool();
|
||||||
config()[CONFIG_IPV6] = d["ipv6"].asBool();
|
config()[CONFIG_IPV6] = d["ipv6"].asBool();
|
||||||
|
|
||||||
|
if (transport == "tls")
|
||||||
|
config()[CONFIG_SIPS] = true;
|
||||||
|
|
||||||
// Log file
|
// Log file
|
||||||
std::string logfile = d["logfile"].asString();
|
std::string logfile = d["logfile"].asString();
|
||||||
ice::Logger& logger = ice::GLogger;
|
ice::Logger& logger = ice::GLogger;
|
||||||
@@ -174,12 +186,10 @@ void AgentImpl::processConfig(JsonCpp::Value& d, JsonCpp::Value& answer)
|
|||||||
|
|
||||||
config()[CONFIG_MULTIPLEXING] = true;
|
config()[CONFIG_MULTIPLEXING] = true;
|
||||||
config()[CONFIG_DISPLAYNAME] = "Voip quality tester";
|
config()[CONFIG_DISPLAYNAME] = "Voip quality tester";
|
||||||
config()[CONFIG_USERAGENT] = d["useragent"].asString();
|
|
||||||
|
|
||||||
mUseNativeAudio = d["nativeaudio"].asBool();
|
mUseNativeAudio = d["nativeaudio"].asBool();
|
||||||
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
|
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
|
||||||
config()[CONFIG_SIPS] = d["secure"].asBool() || transport == "tls";
|
config()[CONFIG_SIPS] = d["secure"].asBool();
|
||||||
config()[CONFIG_STUNSERVER_IP] = d["stun_server"].asString();
|
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
@@ -200,8 +210,7 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value& answer)
|
|||||||
SocketHeap::instance().start();
|
SocketHeap::instance().start();
|
||||||
|
|
||||||
// Initialize terminal
|
// Initialize terminal
|
||||||
auto settings = MT::CodecList::Settings::getClientSettings();
|
MT::CodecList::Settings settings;
|
||||||
|
|
||||||
mTerminal = std::make_shared<MT::Terminal>(settings);
|
mTerminal = std::make_shared<MT::Terminal>(settings);
|
||||||
|
|
||||||
// Enable/disable codecs
|
// Enable/disable codecs
|
||||||
@@ -210,15 +219,19 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value& answer)
|
|||||||
for (int i=0; i<cl.count(); i++)
|
for (int i=0; i<cl.count(); i++)
|
||||||
priorityConfig->at(i) = i;
|
priorityConfig->at(i) = i;
|
||||||
|
|
||||||
|
// Disable dynamic payload codec types - commented for now
|
||||||
|
/*if (cl.codecAt(i).payloadType() < 96)
|
||||||
|
priorityConfig->at(i) = i;
|
||||||
|
else
|
||||||
|
priorityConfig->at(i) = -1;*/
|
||||||
|
|
||||||
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
|
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
|
||||||
|
|
||||||
// Enable audio
|
// Enable audio
|
||||||
mAudioManager = std::make_shared<AudioManager>();
|
mAudioManager = std::make_shared<AudioManager>();
|
||||||
mAudioManager->setTerminal(mTerminal.get());
|
mAudioManager->setTerminal(mTerminal.get());
|
||||||
if (mAudioMonitoring)
|
|
||||||
mAudioManager->setAudioMonitoring(mAudioMonitoring);
|
|
||||||
|
|
||||||
// Do not start audio manager here. Start right before call.
|
// Do not start here. Start right before call.
|
||||||
|
|
||||||
// Initialize endpoint
|
// Initialize endpoint
|
||||||
start();
|
start();
|
||||||
@@ -243,11 +256,9 @@ void AgentImpl::processCreateAccount(JsonCpp::Value& d, JsonCpp::Value& answer)
|
|||||||
(*c)[CONFIG_USERNAME] = d["username"].asString();
|
(*c)[CONFIG_USERNAME] = d["username"].asString();
|
||||||
(*c)[CONFIG_PASSWORD] = d["password"].asString();
|
(*c)[CONFIG_PASSWORD] = d["password"].asString();
|
||||||
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
|
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
|
||||||
if (d.isMember("domain_port"))
|
|
||||||
(*c)[CONFIG_DOMAINPORT] = d["domain_port"].asInt();
|
|
||||||
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
|
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
|
||||||
|
|
||||||
auto nameAndPort = strx::parseHost(d["stun_server"].asString(), 3478);
|
auto nameAndPort = StringHelper::parseHost(d["stun_server"].asString(), 3478);
|
||||||
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
|
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
|
||||||
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
|
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
|
||||||
|
|
||||||
@@ -315,15 +326,10 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
|||||||
{
|
{
|
||||||
// Agent was not started
|
// Agent was not started
|
||||||
ICELogError(<< "No audio manager installed.");
|
ICELogError(<< "No audio manager installed.");
|
||||||
answer["status"] = Status_NoAudioManager;
|
answer["status"] = "Audio manager not started. Most probably agent is not started.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request["use_null_mic"].asBool())
|
|
||||||
mAudioManager->setAudioInput(std::make_shared<Audio::NullInputDevice>());
|
|
||||||
if (request["use_null_spk"].asBool())
|
|
||||||
mAudioManager->setAudioOutput(std::make_shared<Audio::NullOutputDevice>());
|
|
||||||
|
|
||||||
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
||||||
|
|
||||||
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
||||||
@@ -332,10 +338,9 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
|||||||
// Ensure audio provider is here
|
// Ensure audio provider is here
|
||||||
PSession session = sessionIter->second;
|
PSession session = sessionIter->second;
|
||||||
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
||||||
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) |
|
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
||||||
static_cast<int>(StreamState::Playing));
|
|
||||||
|
|
||||||
/*#if defined(USE_AQUA_LIBRARY)
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
std::string path_faults = request["path_faults"].asString();
|
std::string path_faults = request["path_faults"].asString();
|
||||||
|
|
||||||
sevana::aqua::config config = {
|
sevana::aqua::config config = {
|
||||||
@@ -358,10 +363,9 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
|||||||
{ "specp", "32"}
|
{ "specp", "32"}
|
||||||
};
|
};
|
||||||
|
|
||||||
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on
|
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
|
||||||
-spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
|
/*if (temp_path.size())
|
||||||
// if (temp_path.size())
|
config += " -fau " + temp_path; */
|
||||||
// config += " -fau " + temp_path;
|
|
||||||
|
|
||||||
auto qc = std::make_shared<sevana::aqua>();
|
auto qc = std::make_shared<sevana::aqua>();
|
||||||
if (!qc->is_open())
|
if (!qc->is_open())
|
||||||
@@ -374,7 +378,6 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
|||||||
mAquaMap[sessionIter->first] = qc;
|
mAquaMap[sessionIter->first] = qc;
|
||||||
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
|
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
|
||||||
#endif
|
#endif
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: support SRTP via StreamState::Srtp option in audio provider state
|
// TODO: support SRTP via StreamState::Srtp option in audio provider state
|
||||||
|
|
||||||
@@ -416,19 +419,8 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
|
|||||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||||
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
||||||
if (sessionIter != mSessionMap.end())
|
if (sessionIter != mSessionMap.end())
|
||||||
{
|
|
||||||
if (!mAudioManager)
|
|
||||||
{
|
|
||||||
ICELogError(<< "No audio manager installed.");
|
|
||||||
answer["status"] = Status_NoAudioManager;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Ensure audio manager is here
|
// Ensure audio manager is here
|
||||||
if (request["use_null_mic"].asBool())
|
|
||||||
mAudioManager->setAudioInput(std::make_shared<Audio::NullInputDevice>());
|
|
||||||
if (request["use_null_spk"].asBool())
|
|
||||||
mAudioManager->setAudioOutput(std::make_shared<Audio::NullOutputDevice>());
|
|
||||||
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
||||||
|
|
||||||
// Accept session on SIP level
|
// Accept session on SIP level
|
||||||
@@ -447,9 +439,9 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
|
|||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
else
|
||||||
answer["status"] = Status_SessionNotFound;
|
answer["status"] = Status_SessionNotFound;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer)
|
void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer)
|
||||||
@@ -460,26 +452,23 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
|
|||||||
auto sessionIter = mSessionMap.find(sessionId);
|
auto sessionIter = mSessionMap.find(sessionId);
|
||||||
if (sessionIter != mSessionMap.end())
|
if (sessionIter != mSessionMap.end())
|
||||||
mSessionMap.erase(sessionIter);
|
mSessionMap.erase(sessionIter);
|
||||||
// #if defined(USE_AQUA_LIBRARY)
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
// closeAqua(sessionId);
|
closeAqua(sessionId);
|
||||||
// #endif
|
#endif
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &answer)
|
void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &answer)
|
||||||
{
|
{
|
||||||
// Deliberately does NOT take mAgentMutex: events are produced by the worker
|
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||||
// thread inside process(), which needs mAgentMutex. Holding it here would
|
|
||||||
// stall all SIP/media processing for the whole timeout and guarantee that
|
|
||||||
// the awaited event can never arrive during the wait.
|
|
||||||
|
|
||||||
int timeout = 0;
|
//int x = 0;
|
||||||
if (request.isMember("timeout"))
|
//int y = 1/x;
|
||||||
timeout = request["timeout"].asInt();
|
|
||||||
|
|
||||||
|
int timeout = request["timeout"].asInt();
|
||||||
std::unique_lock<std::mutex> eventLock(mEventListMutex);
|
std::unique_lock<std::mutex> eventLock(mEventListMutex);
|
||||||
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout),
|
if (mEventList.empty())
|
||||||
[this]() { return !mEventList.empty(); });
|
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout));
|
||||||
|
|
||||||
if (!mEventList.empty())
|
if (!mEventList.empty())
|
||||||
{
|
{
|
||||||
@@ -489,6 +478,41 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& ans
|
|||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_PVQA_LIBRARY)
|
||||||
|
static JsonCpp::Value CsvReportToJson(const std::string& report)
|
||||||
|
{
|
||||||
|
JsonCpp::Value detectorValues;
|
||||||
|
std::istringstream iss(report);
|
||||||
|
CsvReader reader(iss);
|
||||||
|
std::vector<std::string> cells;
|
||||||
|
if (reader.readLine(cells))
|
||||||
|
{
|
||||||
|
JsonCpp::Value detectorNames;
|
||||||
|
for (size_t nameIndex = 0; nameIndex < cells.size(); nameIndex++)
|
||||||
|
detectorNames[static_cast<int>(nameIndex)] = StringHelper::trim(cells[nameIndex]);
|
||||||
|
// Put first line name of columns
|
||||||
|
detectorValues[0] = detectorNames;
|
||||||
|
|
||||||
|
int rowIndex = 1;
|
||||||
|
while (reader.readLine(cells))
|
||||||
|
{
|
||||||
|
// Skip last column for now
|
||||||
|
JsonCpp::Value row;
|
||||||
|
for (size_t valueIndex = 0; valueIndex < cells.size(); valueIndex++)
|
||||||
|
{
|
||||||
|
bool isFloat = true;
|
||||||
|
float v = StringHelper::toFloat(cells[valueIndex], 0.0, &isFloat);
|
||||||
|
if (isFloat)
|
||||||
|
row[static_cast<int>(valueIndex)] = static_cast<double>(v);
|
||||||
|
else
|
||||||
|
row[static_cast<int>(valueIndex)] = cells[valueIndex];
|
||||||
|
}
|
||||||
|
detectorValues[rowIndex++] = row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return detectorValues;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
|
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
|
||||||
{
|
{
|
||||||
@@ -499,13 +523,25 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
|||||||
{
|
{
|
||||||
PSession session = sessionIter->second;
|
PSession session = sessionIter->second;
|
||||||
VariantMap result;
|
VariantMap result;
|
||||||
session->getSessionInfo(Session::InfoOptions::Detailed, result);
|
bool includePvqa = request["include_pvqa"].asBool();
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
bool includeAqua = request["include_aqua"].asBool();
|
||||||
|
std::string aquaReference = request["aqua_reference_audio"].asString();
|
||||||
|
#endif
|
||||||
|
session->getSessionInfo(includePvqa ? Session::InfoOptions::Detailed : Session::InfoOptions::Standard,
|
||||||
|
result);
|
||||||
|
|
||||||
if (result.exists(SessionInfo_AudioCodec))
|
if (result.exists(SessionInfo_AudioCodec))
|
||||||
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
|
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
|
||||||
if (result.exists(SessionInfo_NetworkMos))
|
if (result.exists(SessionInfo_NetworkMos))
|
||||||
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
|
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
|
||||||
if (result.exists(SessionInfo_LostRtp))
|
#if defined(USE_PVQA_LIBRARY)
|
||||||
|
if (result.exists(SessionInfo_PvqaMos))
|
||||||
|
answer["pvqa_mos"] = result[SessionInfo_PvqaMos].asFloat();
|
||||||
|
if (result.exists(SessionInfo_PvqaReport))
|
||||||
|
answer["pvqa_report"] = CsvReportToJson(result[SessionInfo_PvqaReport].asStdString());
|
||||||
|
#endif
|
||||||
|
if (result.exists(SessionInfo_PacketLoss))
|
||||||
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
|
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
|
||||||
if (result.exists(SessionInfo_DroppedRtp))
|
if (result.exists(SessionInfo_DroppedRtp))
|
||||||
answer["rtp_dropped"] = result[SessionInfo_DroppedRtp].asInt();
|
answer["rtp_dropped"] = result[SessionInfo_DroppedRtp].asInt();
|
||||||
@@ -514,9 +550,6 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
|||||||
answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt();
|
answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt();
|
||||||
if (result.exists(SessionInfo_ReceivedRtp))
|
if (result.exists(SessionInfo_ReceivedRtp))
|
||||||
answer["rtp_received"] = result[SessionInfo_ReceivedRtp].asInt();
|
answer["rtp_received"] = result[SessionInfo_ReceivedRtp].asInt();
|
||||||
// Raw byte total; consumers divide by rtp_received for the average packet size.
|
|
||||||
if (result.exists(SessionInfo_ReceivedRtpTraffic))
|
|
||||||
answer["rtp_received_bytes"] = result[SessionInfo_ReceivedRtpTraffic].asInt();
|
|
||||||
if (result.exists(SessionInfo_Duration))
|
if (result.exists(SessionInfo_Duration))
|
||||||
answer["duration"] = result[SessionInfo_Duration].asInt();
|
answer["duration"] = result[SessionInfo_Duration].asInt();
|
||||||
if (result.exists(SessionInfo_Jitter))
|
if (result.exists(SessionInfo_Jitter))
|
||||||
@@ -525,13 +558,79 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
|
|||||||
answer["rtt"] = result[SessionInfo_Rtt].asFloat();
|
answer["rtt"] = result[SessionInfo_Rtt].asFloat();
|
||||||
if (result.exists(SessionInfo_BitrateSwitchCounter))
|
if (result.exists(SessionInfo_BitrateSwitchCounter))
|
||||||
answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt();
|
answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt();
|
||||||
if (result.exists(SessionInfo_CngCounter))
|
|
||||||
answer["cng_counter"] = result[SessionInfo_CngCounter].asInt();
|
|
||||||
if (result.exists(SessionInfo_SSRC))
|
if (result.exists(SessionInfo_SSRC))
|
||||||
answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt();
|
answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt();
|
||||||
if (result.exists(SessionInfo_RemotePeer))
|
if (result.exists(SessionInfo_RemotePeer))
|
||||||
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
|
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
if (includeAqua)
|
||||||
|
{
|
||||||
|
answer["incoming_audio"] = mAquaIncoming.hexstring();
|
||||||
|
answer["incoming_audio_samplerate"] = AUDIO_SAMPLERATE;
|
||||||
|
answer["incoming_audio_channels"] = AUDIO_CHANNELS;
|
||||||
|
|
||||||
|
ICELogInfo(<< "Running AQuA analyzer.");
|
||||||
|
ByteBuffer referenceAudio;
|
||||||
|
// Read AQuA reference audio from file if available
|
||||||
|
if (aquaReference.empty())
|
||||||
|
{
|
||||||
|
ICELogCritical(<< "AQuA reference audio file is not set, skipping analyzing.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto sa = mAquaMap[sessionIter->first];
|
||||||
|
if (sa) {
|
||||||
|
Audio::WavFileReader reader;
|
||||||
|
reader.open(StringHelper::makeTstring(aquaReference));
|
||||||
|
|
||||||
|
if (reader.isOpened()) {
|
||||||
|
char buffer[1024];
|
||||||
|
int wasRead = 0;
|
||||||
|
do {
|
||||||
|
wasRead = reader.read(buffer, 1024);
|
||||||
|
if (wasRead > 0)
|
||||||
|
referenceAudio.appendBuffer(buffer, wasRead);
|
||||||
|
} while (wasRead == 1024);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ICELogCritical(<< "Failed to read AQuA reference audio, error code: " << reader.lastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
sevana::aqua::audio_buffer test(mAquaIncoming.data(), mAquaIncoming.size()),
|
||||||
|
reference(referenceAudio.data(), referenceAudio.size());
|
||||||
|
test.mRate = AUDIO_SAMPLERATE;
|
||||||
|
reference.mRate = AUDIO_SAMPLERATE;
|
||||||
|
test.mChannels = AUDIO_CHANNELS;
|
||||||
|
reference.mChannels = AUDIO_CHANNELS;
|
||||||
|
ICELogInfo(
|
||||||
|
<< "Comparing test audio " << mAquaIncoming.size() << " bytes with reference audio " << referenceAudio.size() << " bytes.");
|
||||||
|
auto r = sa->compare(reference, test);
|
||||||
|
if (r.mErrorCode) {
|
||||||
|
ICELogInfo(
|
||||||
|
<< "Error code: " << r.mErrorCode << ", msg: " << r.mErrorMessage);
|
||||||
|
} else {
|
||||||
|
ICELogInfo(<< "MOS: " << r.mMos << ", faults: " << r.mFaultsText);
|
||||||
|
}
|
||||||
|
answer["aqua_mos"] = r.mMos;
|
||||||
|
answer["aqua_report"] = r.mFaultsText;
|
||||||
|
/*std::string aqua_audio_text;
|
||||||
|
if (Base64::Encode(std::string(reinterpret_cast<const char*>(mAquaIncoming.data()), mAquaIncoming.size()), &aqua_audio_text))
|
||||||
|
{
|
||||||
|
answer["aqua_audio"] = aqua_audio_text;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (r.mErrorCode) {
|
||||||
|
answer["aqua_error_code"] = r.mErrorCode;
|
||||||
|
answer["aqua_error_message"] = r.mErrorMessage;
|
||||||
|
}
|
||||||
|
closeAqua(sessionIter->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove test audio
|
||||||
|
mAquaIncoming.clear(); mAquaOutgoing.clear();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -552,18 +651,18 @@ void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp
|
|||||||
std::string pem = request["cert"].asString();
|
std::string pem = request["cert"].asString();
|
||||||
|
|
||||||
std::string::size_type pb = 0, pe = 0;
|
std::string::size_type pb = 0, pe = 0;
|
||||||
while (pb != std::string::npos && pe != std::string::npos)
|
|
||||||
{
|
|
||||||
pb = pem.find(BeginCertificate, pb);
|
|
||||||
pe = pem.find(EndCertificate, pe);
|
|
||||||
|
|
||||||
if (pb != std::string::npos && pe != std::string::npos && pe > pb)
|
for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe);
|
||||||
|
pb != std::string::npos && pe != std::string::npos;
|
||||||
|
pb = pem.find(BeginCertificate, pb + BeginCertificate.size()), pe = pem.find(EndCertificate, pe + EndCertificate.size()))
|
||||||
{
|
{
|
||||||
std::string cert = pem.substr(pb, pe - pb + EndCertificate.size());
|
// Get single certificate
|
||||||
|
std::string cert = pem.substr(pb, pe + EndCertificate.size());
|
||||||
|
//int size = cert.size();
|
||||||
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
|
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
|
||||||
|
|
||||||
pb = ++pe;
|
// Delete processed part
|
||||||
}
|
pem.erase(0, pe + EndCertificate.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
@@ -588,8 +687,7 @@ void AgentImpl::stopAgentAndThread()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
// Stop worker thread
|
// Stop worker thread
|
||||||
if (mThread)
|
if (mThread)
|
||||||
@@ -633,8 +731,8 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
|||||||
std::string actionText = request["media_action"].asString(),
|
std::string actionText = request["media_action"].asString(),
|
||||||
directionText = request["media_direction"].asString();
|
directionText = request["media_direction"].asString();
|
||||||
|
|
||||||
MT::Stream::MediaDirection direction =
|
MT::Stream::MediaDirection direction = directionText == "incoming" ? MT::Stream::MediaDirection::Incoming
|
||||||
directionText == "incoming" ? MT::Stream::MediaDirection::Incoming : MT::Stream::MediaDirection::Outgoing;
|
: MT::Stream::MediaDirection::Outgoing;
|
||||||
std::string path = request["path"].asString();
|
std::string path = request["path"].asString();
|
||||||
|
|
||||||
// Try to open file
|
// Try to open file
|
||||||
@@ -652,7 +750,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
|
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
|
||||||
if (!reader->open(strx::makeTstring(path)))
|
if (!reader->open(StringHelper::makeTstring(path)))
|
||||||
answer["status"] = Status_FailedToOpenFile;
|
answer["status"] = Status_FailedToOpenFile;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -661,7 +759,8 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (actionText == "write")
|
else
|
||||||
|
if (actionText == "write")
|
||||||
{
|
{
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
{
|
{
|
||||||
@@ -672,7 +771,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
|
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
|
||||||
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
|
if (!writer->open(StringHelper::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
|
||||||
answer["status"] = Status_FailedToOpenFile;
|
answer["status"] = Status_FailedToOpenFile;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -681,44 +780,51 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (actionText == "mirror")
|
else
|
||||||
|
if (actionText == "mirror")
|
||||||
{
|
{
|
||||||
prov->setupMirror(request["enable"].asBool());
|
prov->setupMirror(request["enable"].asBool());
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
answer["status"] = Status_NoCommand;
|
answer["status"] = Status_AccountNotFound;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
answer["status"] = Status_NoMediaAction;
|
answer["status"] = Status_NoMediaAction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context,
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
void* userTag)
|
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
|
||||||
{
|
{
|
||||||
/*switch (direction)
|
/* if (mIncomingAudioDump && direction == MT::Stream::MediaDirection::Incoming)
|
||||||
|
mIncomingAudioDump->write(data, length);
|
||||||
|
|
||||||
|
if (mOutgoingAudioDump && direction == MT::Stream::MediaDirection::Outgoing)
|
||||||
|
mOutgoingAudioDump->write(data, length);*/
|
||||||
|
|
||||||
|
// User tag points to accumulator object which includes
|
||||||
|
// auto* sa = reinterpret_cast<sevana::aqua*>(userTag);
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
{
|
{
|
||||||
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
|
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
|
||||||
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
|
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Called on new incoming session; providers shoukld
|
// Called on new incoming session; providers shoukld
|
||||||
#define EVENT_WITH_NAME(X) \
|
#define EVENT_WITH_NAME(X) JsonCpp::Value v; v["event_name"] = X;
|
||||||
JsonCpp::Value v; \
|
|
||||||
v["event_name"] = X;
|
|
||||||
|
|
||||||
PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
|
PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
|
||||||
{
|
{
|
||||||
assert(mTerminal);
|
|
||||||
|
|
||||||
EVENT_WITH_NAME("provider_needed");
|
EVENT_WITH_NAME("provider_needed");
|
||||||
v["provider_name"] = name;
|
v["provider_name"] = name;
|
||||||
addEvent(v);
|
addEvent(v);
|
||||||
|
|
||||||
return std::make_shared<AudioProvider>(*this, *mTerminal);
|
return PDataProvider(new AudioProvider(*this, *mTerminal));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called on new session offer
|
// Called on new session offer
|
||||||
@@ -740,7 +846,6 @@ void AgentImpl::onSessionTerminated(PSession s, int responsecode, int reason)
|
|||||||
if (mOutgoingAudioDump)
|
if (mOutgoingAudioDump)
|
||||||
mOutgoingAudioDump->close();
|
mOutgoingAudioDump->close();
|
||||||
*/
|
*/
|
||||||
if (mAudioManager)
|
|
||||||
mAudioManager->stop(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
mAudioManager->stop(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
||||||
// Gather statistics before
|
// Gather statistics before
|
||||||
EVENT_WITH_NAME("session_terminated");
|
EVENT_WITH_NAME("session_terminated");
|
||||||
@@ -846,7 +951,8 @@ void AgentImpl::onCheckFinished(PSession s, const char* description)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called when log message must be recorded
|
// Called when log message must be recorded
|
||||||
void AgentImpl::onLog(const char* /*msg*/) {}
|
void AgentImpl::onLog(const char* /*msg*/)
|
||||||
|
{}
|
||||||
|
|
||||||
// Called when problem with SIP connection(s) detected
|
// Called when problem with SIP connection(s) detected
|
||||||
void AgentImpl::onSipConnectionFailed()
|
void AgentImpl::onSipConnectionFailed()
|
||||||
@@ -863,12 +969,12 @@ void AgentImpl::addEvent(const JsonCpp::Value& v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_AQUA_LIBRARY)
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
/*void AgentImpl::closeAqua(int sessionId)
|
void AgentImpl::closeAqua(int sessionId)
|
||||||
{
|
{
|
||||||
auto aquaIter = mAquaMap.find(sessionId);
|
auto aquaIter = mAquaMap.find(sessionId);
|
||||||
if (aquaIter != mAquaMap.end()) {
|
if (aquaIter != mAquaMap.end()) {
|
||||||
aquaIter->second->close();
|
aquaIter->second->close();
|
||||||
mAquaMap.erase(aquaIter);
|
mAquaMap.erase(aquaIter);
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,10 +13,18 @@
|
|||||||
#include "Agent_AudioManager.h"
|
#include "Agent_AudioManager.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <atomic>
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
# include "aqua++.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_PVQA_LIBRARY)
|
||||||
|
# include "pvqa++.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
class AgentImpl : public UserAgent, public MT::Stream::MediaObserver
|
class AgentImpl: public UserAgent
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
, public MT::Stream::MediaObserver
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::recursive_mutex mAgentMutex;
|
std::recursive_mutex mAgentMutex;
|
||||||
@@ -31,12 +39,19 @@ protected:
|
|||||||
typedef std::map<int, PSession> SessionMap;
|
typedef std::map<int, PSession> SessionMap;
|
||||||
SessionMap mSessionMap;
|
SessionMap mSessionMap;
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
|
// Keys are the same as used in mSessionMap
|
||||||
|
typedef std::map<int, std::shared_ptr<sevana::aqua>> AquaMap;
|
||||||
|
AquaMap mAquaMap;
|
||||||
|
ByteBuffer mAquaIncoming, mAquaOutgoing;
|
||||||
|
void closeAqua(int sessionId);
|
||||||
|
#endif
|
||||||
|
|
||||||
std::shared_ptr<std::thread> mThread;
|
std::shared_ptr<std::thread> mThread;
|
||||||
std::atomic<bool> mShutdown;
|
volatile bool mShutdown;
|
||||||
std::shared_ptr<MT::Terminal> mTerminal;
|
std::shared_ptr<MT::Terminal> mTerminal;
|
||||||
std::shared_ptr<AudioManager> mAudioManager;
|
std::shared_ptr<AudioManager> mAudioManager;
|
||||||
Audio::DataConnection* mAudioMonitoring = nullptr;
|
//Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void addEvent(const JsonCpp::Value& v);
|
void addEvent(const JsonCpp::Value& v);
|
||||||
@@ -67,12 +82,6 @@ public:
|
|||||||
bool waitForData(int milliseconds);
|
bool waitForData(int milliseconds);
|
||||||
std::string read();
|
std::string read();
|
||||||
|
|
||||||
// Get access to internal audio manager. Value can be nullptr.
|
|
||||||
const std::shared_ptr<AudioManager>& audioManager() const;
|
|
||||||
|
|
||||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
|
||||||
Audio::DataConnection* monitoring() const;
|
|
||||||
|
|
||||||
// UserAgent overrides
|
// UserAgent overrides
|
||||||
// Called on new incoming session; providers shoukld
|
// Called on new incoming session; providers shoukld
|
||||||
PDataProvider onProviderNeeded(const std::string& name) override;
|
PDataProvider onProviderNeeded(const std::string& name) override;
|
||||||
@@ -122,9 +131,10 @@ public:
|
|||||||
// Called when problem with SIP connection(s) detected
|
// Called when problem with SIP connection(s) detected
|
||||||
void onSipConnectionFailed() override;
|
void onSipConnectionFailed() override;
|
||||||
|
|
||||||
|
#if defined(USE_AQUA_LIBRARY)
|
||||||
// Called on incoming & outgoing audio for voice sessions
|
// Called on incoming & outgoing audio for voice sessions
|
||||||
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context,
|
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
||||||
void* userTag) override;
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
#include "Agent_Interface.h"
|
#include "Agent_Interface.h"
|
||||||
#include "Agent_Impl.h"
|
#include "Agent_Impl.h"
|
||||||
Agent::Agent() : mContext(new AgentImpl());
|
Agent::Agent()
|
||||||
|
:mContext(new AgentImpl());
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Agent::~Agent() {}
|
Agent::~Agent()
|
||||||
|
{
|
||||||
|
|
||||||
void Agent::write(const std::string& command) {}
|
}
|
||||||
|
|
||||||
|
void Agent::write(const std::string& command)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool Agent::waitForData(int milliseconds)
|
bool Agent::waitForData(int milliseconds)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Agent::read() {}
|
std::string Agent::read()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ using namespace Audio;
|
|||||||
|
|
||||||
// -------------------- AndroidEnumerator -----------------------------
|
// -------------------- AndroidEnumerator -----------------------------
|
||||||
|
|
||||||
AndroidEnumerator::AndroidEnumerator() {}
|
AndroidEnumerator::AndroidEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
AndroidEnumerator::~AndroidEnumerator() {}
|
AndroidEnumerator::~AndroidEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
int AndroidEnumerator::indexOfDefaultDevice()
|
int AndroidEnumerator::indexOfDefaultDevice()
|
||||||
{
|
{
|
||||||
@@ -38,21 +40,26 @@ std::string AndroidEnumerator::nameAt(int index)
|
|||||||
return "Audio";
|
return "Audio";
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidEnumerator::open(int direction) {}
|
void AndroidEnumerator::open(int direction)
|
||||||
|
{}
|
||||||
|
|
||||||
void AndroidEnumerator::close() {}
|
void AndroidEnumerator::close()
|
||||||
|
{}
|
||||||
|
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
|
||||||
OpenSLEngine::OpenSLEngine() {}
|
OpenSLEngine::OpenSLEngine()
|
||||||
|
{}
|
||||||
|
|
||||||
OpenSLEngine::~OpenSLEngine() {}
|
OpenSLEngine::~OpenSLEngine()
|
||||||
|
{}
|
||||||
|
|
||||||
void OpenSLEngine::open()
|
void OpenSLEngine::open()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(mMutex);
|
std::unique_lock<std::mutex> l(mMutex);
|
||||||
if (++mUsageCounter == 1)
|
if (++mUsageCounter == 1)
|
||||||
internalOpen();
|
internalOpen();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenSLEngine::close()
|
void OpenSLEngine::close()
|
||||||
@@ -65,9 +72,7 @@ void OpenSLEngine::close()
|
|||||||
internalClose();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_OPENSLES_ERROR \
|
#define CHECK_OPENSLES_ERROR if (resultCode != SL_RESULT_SUCCESS) throw Exception(ERR_OPENSLES, (int)resultCode)
|
||||||
if (resultCode != SL_RESULT_SUCCESS) \
|
|
||||||
throw Exception(ERR_OPENSLES, (int)resultCode)
|
|
||||||
|
|
||||||
void OpenSLEngine::internalOpen()
|
void OpenSLEngine::internalOpen()
|
||||||
{
|
{
|
||||||
@@ -113,15 +118,25 @@ OpenSLEngine& OpenSLEngine::instance()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------- Input implementation ----------------
|
// --------------- Input implementation ----------------
|
||||||
AndroidInputDevice::AndroidInputDevice(int devId) {}
|
AndroidInputDevice::AndroidInputDevice(int devId)
|
||||||
|
{}
|
||||||
|
|
||||||
AndroidInputDevice::~AndroidInputDevice() {}
|
AndroidInputDevice::~AndroidInputDevice()
|
||||||
|
{}
|
||||||
|
|
||||||
static int RateToProbe[12][2] = {
|
static int RateToProbe[12][2] = {
|
||||||
{SL_SAMPLINGRATE_16, 16000}, {SL_SAMPLINGRATE_8, 8000}, {SL_SAMPLINGRATE_32, 32000},
|
{ SL_SAMPLINGRATE_16, 16000 },
|
||||||
{SL_SAMPLINGRATE_44_1, 44100}, {SL_SAMPLINGRATE_11_025, 10025}, {SL_SAMPLINGRATE_22_05, 22050},
|
{ SL_SAMPLINGRATE_8, 8000 },
|
||||||
{SL_SAMPLINGRATE_24, 24000}, {SL_SAMPLINGRATE_48, 48000}, {SL_SAMPLINGRATE_64, 64000},
|
{ SL_SAMPLINGRATE_32, 32000 },
|
||||||
{SL_SAMPLINGRATE_88_2, 88200}, {SL_SAMPLINGRATE_96, 96000}, {SL_SAMPLINGRATE_192, 192000}};
|
{ SL_SAMPLINGRATE_44_1, 44100 },
|
||||||
|
{ SL_SAMPLINGRATE_11_025, 10025 },
|
||||||
|
{ SL_SAMPLINGRATE_22_05, 22050 },
|
||||||
|
{ SL_SAMPLINGRATE_24, 24000 },
|
||||||
|
{ SL_SAMPLINGRATE_48, 48000 },
|
||||||
|
{ SL_SAMPLINGRATE_64, 64000 },
|
||||||
|
{ SL_SAMPLINGRATE_88_2, 88200 },
|
||||||
|
{ SL_SAMPLINGRATE_96, 96000 },
|
||||||
|
{ SL_SAMPLINGRATE_192, 192000} };
|
||||||
|
|
||||||
bool AndroidInputDevice::open()
|
bool AndroidInputDevice::open()
|
||||||
{
|
{
|
||||||
@@ -177,7 +192,8 @@ bool AndroidInputDevice::fakeMode()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidInputDevice::setFakeMode(bool fakemode) {}
|
void AndroidInputDevice::setFakeMode(bool fakemode)
|
||||||
|
{}
|
||||||
|
|
||||||
int AndroidInputDevice::readBuffer(void* buffer)
|
int AndroidInputDevice::readBuffer(void* buffer)
|
||||||
{
|
{
|
||||||
@@ -190,13 +206,7 @@ int AndroidInputDevice::readBuffer(void* buffer)
|
|||||||
return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE);
|
return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_SL_INTERFACE(INTF, ERR) \
|
#define CHECK_SL_INTERFACE(INTF, ERR) {if (!INTF) throw Exception(ERR_OPENSLES, ERR); if (!(*INTF)) throw Exception(ERR_OPENSLES, ERR);}
|
||||||
{ \
|
|
||||||
if (!INTF) \
|
|
||||||
throw Exception(ERR_OPENSLES, ERR); \
|
|
||||||
if (!(*INTF)) \
|
|
||||||
throw Exception(ERR_OPENSLES, ERR); \
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidInputDevice::internalOpen(int rateCode, int rate)
|
void AndroidInputDevice::internalOpen(int rateCode, int rate)
|
||||||
{
|
{
|
||||||
@@ -204,22 +214,18 @@ void AndroidInputDevice::internalOpen(int rateCode, int rate)
|
|||||||
SLuint32 nrOfChannels = 1;
|
SLuint32 nrOfChannels = 1;
|
||||||
|
|
||||||
// Prepare audio source
|
// Prepare audio source
|
||||||
SLDataLocator_IODevice devDescription = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
SLDataLocator_IODevice devDescription = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
|
||||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
|
|
||||||
SLDataSource audioSource = { &devDescription, NULL };
|
SLDataSource audioSource = { &devDescription, NULL };
|
||||||
|
|
||||||
// Source flags
|
// Source flags
|
||||||
SLuint32 speakersFlags =
|
SLuint32 speakersFlags = nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
||||||
nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
|
||||||
|
|
||||||
// Buffer queue
|
// Buffer queue
|
||||||
SLDataLocator_AndroidSimpleBufferQueue queueDescription = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
|
SLDataLocator_AndroidSimpleBufferQueue queueDescription = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
|
||||||
|
|
||||||
// Audio format
|
// Audio format
|
||||||
SLDataFormat_PCM formatDescription = {SL_DATAFORMAT_PCM, nrOfChannels,
|
SLDataFormat_PCM formatDescription = { SL_DATAFORMAT_PCM, nrOfChannels, (SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||||
(SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16,
|
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags, SL_BYTEORDER_LITTLEENDIAN };
|
||||||
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags,
|
|
||||||
SL_BYTEORDER_LITTLEENDIAN};
|
|
||||||
|
|
||||||
SLDataSink audioSink = { &queueDescription, &formatDescription };
|
SLDataSink audioSink = { &queueDescription, &formatDescription };
|
||||||
|
|
||||||
@@ -232,9 +238,9 @@ void AndroidInputDevice::internalOpen(int rateCode, int rate)
|
|||||||
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
|
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
|
||||||
CHECK_SL_INTERFACE(engine_interface, -1);
|
CHECK_SL_INTERFACE(engine_interface, -1);
|
||||||
|
|
||||||
resultCode = (*engine_interface)
|
resultCode = (*engine_interface)->CreateAudioRecorder(
|
||||||
->CreateAudioRecorder(OpenSLEngine::instance().getNativeEngine(), &mRecorderObject, &audioSource,
|
OpenSLEngine::instance().getNativeEngine(),
|
||||||
&audioSink, 2, interfacesList, interfacesRequirements);
|
&mRecorderObject, &audioSource, &audioSink, 2, interfacesList, interfacesRequirements);
|
||||||
CHECK_OPENSLES_ERROR;
|
CHECK_OPENSLES_ERROR;
|
||||||
CHECK_SL_INTERFACE(mRecorderObject, -2);
|
CHECK_SL_INTERFACE(mRecorderObject, -2);
|
||||||
|
|
||||||
@@ -252,8 +258,7 @@ void AndroidInputDevice::internalOpen(int rateCode, int rate)
|
|||||||
CHECK_SL_INTERFACE(mRecorderInterface, -3);
|
CHECK_SL_INTERFACE(mRecorderInterface, -3);
|
||||||
|
|
||||||
// Now buffer queue interface...
|
// Now buffer queue interface...
|
||||||
resultCode =
|
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
|
||||||
(*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
|
|
||||||
CHECK_OPENSLES_ERROR;
|
CHECK_OPENSLES_ERROR;
|
||||||
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
|
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
|
||||||
|
|
||||||
@@ -272,8 +277,7 @@ void AndroidInputDevice::internalOpen(int rateCode, int rate)
|
|||||||
|
|
||||||
// Setup buffers
|
// Setup buffers
|
||||||
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
|
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
|
||||||
(*mRecorderBufferInterface)
|
(*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
|
||||||
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
|
|
||||||
|
|
||||||
// Start finally
|
// Start finally
|
||||||
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
|
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
|
||||||
@@ -298,7 +302,8 @@ void AndroidInputDevice::internalClose()
|
|||||||
{
|
{
|
||||||
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
|
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
|
||||||
SyncHelper::delay(1);
|
SyncHelper::delay(1);
|
||||||
} while (state == SL_RECORDSTATE_RECORDING);
|
}
|
||||||
|
while (state == SL_RECORDSTATE_RECORDING);
|
||||||
}
|
}
|
||||||
(*mRecorderObject)->Destroy(mRecorderObject);
|
(*mRecorderObject)->Destroy(mRecorderObject);
|
||||||
}
|
}
|
||||||
@@ -326,8 +331,9 @@ void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
|
|||||||
{
|
{
|
||||||
char* resampled = (char*)alloca(Format().sizeFromTime(10));
|
char* resampled = (char*)alloca(Format().sizeFromTime(10));
|
||||||
int processed = 0;
|
int processed = 0;
|
||||||
int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled,
|
int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled, Format().sizeFromTime(10));
|
||||||
Format().sizeFromTime(10)); if (outlen > 0) mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10));
|
if (outlen > 0)
|
||||||
|
mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10));
|
||||||
mDeviceRateCache.erase(tenMsSize);
|
mDeviceRateCache.erase(tenMsSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,8 +346,7 @@ void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// Re-enqueue used buffer
|
// Re-enqueue used buffer
|
||||||
(*mRecorderBufferInterface)
|
(*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
|
||||||
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
|
|
||||||
mRecorderBufferIndex++;
|
mRecorderBufferIndex++;
|
||||||
mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT;
|
mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT;
|
||||||
}
|
}
|
||||||
@@ -354,16 +359,14 @@ void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void*
|
|||||||
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
|
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------ AndroidOutputDevice -----------------
|
// ------------ AndroidOutputDevice -----------------
|
||||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidOutputDevice::~AndroidOutputDevice()
|
AndroidOutputDevice::~AndroidOutputDevice()
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||||
@@ -410,7 +413,9 @@ bool AndroidOutputDevice::fakeMode()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidOutputDevice::setFakeMode(bool fakemode) {}
|
void AndroidOutputDevice::setFakeMode(bool fakemode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
||||||
{
|
{
|
||||||
@@ -424,8 +429,8 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
|||||||
|
|
||||||
const SLInterfaceID interfacesList[] = { SL_IID_VOLUME };
|
const SLInterfaceID interfacesList[] = { SL_IID_VOLUME };
|
||||||
const SLboolean interfaceRequirements[] = { SL_BOOLEAN_FALSE };
|
const SLboolean interfaceRequirements[] = { SL_BOOLEAN_FALSE };
|
||||||
resultCode = (*OpenSLEngine::instance().getNativeEngine())
|
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateOutputMix(
|
||||||
->CreateOutputMix(OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
|
OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
|
||||||
interfaceRequirements);
|
interfaceRequirements);
|
||||||
CHECK_OPENSLES_ERROR;
|
CHECK_OPENSLES_ERROR;
|
||||||
|
|
||||||
@@ -434,12 +439,13 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
|||||||
CHECK_OPENSLES_ERROR;
|
CHECK_OPENSLES_ERROR;
|
||||||
|
|
||||||
// Prepare mixer configuration
|
// Prepare mixer configuration
|
||||||
SLuint32 speakers = channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
SLuint32 speakers =
|
||||||
|
channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
||||||
|
|
||||||
// Describe audio format
|
// Describe audio format
|
||||||
SLDataFormat_PCM pcm_format = {
|
SLDataFormat_PCM pcm_format = {SL_DATAFORMAT_PCM, channels, (SLuint32) rateId,
|
||||||
SL_DATAFORMAT_PCM, channels, (SLuint32)rateId, SL_PCMSAMPLEFORMAT_FIXED_16,
|
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||||
SL_PCMSAMPLEFORMAT_FIXED_16, speakers, SL_BYTEORDER_LITTLEENDIAN};
|
speakers, SL_BYTEORDER_LITTLEENDIAN};
|
||||||
|
|
||||||
// Describe audio source - buffers + audio format
|
// Describe audio source - buffers + audio format
|
||||||
SLDataSource audio_source = { &queue_desc, &pcm_format };
|
SLDataSource audio_source = { &queue_desc, &pcm_format };
|
||||||
@@ -449,13 +455,14 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
|||||||
SLDataSink audio_sink = { &mixer_desc, NULL };
|
SLDataSink audio_sink = { &mixer_desc, NULL };
|
||||||
|
|
||||||
// Create player instance
|
// Create player instance
|
||||||
const SLInterfaceID playerInterfaces[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME,
|
const SLInterfaceID playerInterfaces[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
|
SL_IID_VOLUME,
|
||||||
SL_IID_ANDROIDCONFIGURATION };
|
SL_IID_ANDROIDCONFIGURATION };
|
||||||
const SLboolean playerInterfacesReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
const SLboolean playerInterfacesReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||||
|
|
||||||
resultCode = (*OpenSLEngine::instance().getNativeEngine())
|
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateAudioPlayer(
|
||||||
->CreateAudioPlayer(OpenSLEngine::instance().getNativeEngine(), &mPlayer, &audio_source,
|
OpenSLEngine::instance().getNativeEngine(), &mPlayer,
|
||||||
&audio_sink, 3, playerInterfaces, playerInterfacesReqs);
|
&audio_source, &audio_sink, 3, playerInterfaces, playerInterfacesReqs);
|
||||||
CHECK_OPENSLES_ERROR;
|
CHECK_OPENSLES_ERROR;
|
||||||
|
|
||||||
// Get android config interface
|
// Get android config interface
|
||||||
@@ -464,8 +471,8 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
|||||||
if (resultCode == SL_RESULT_SUCCESS)
|
if (resultCode == SL_RESULT_SUCCESS)
|
||||||
{
|
{
|
||||||
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
|
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
|
||||||
resultCode = (*mAndroidConfig)
|
resultCode = (*mAndroidConfig)->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE,
|
||||||
->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
|
&streamType, sizeof(SLint32));
|
||||||
if (resultCode != SL_RESULT_SUCCESS)
|
if (resultCode != SL_RESULT_SUCCESS)
|
||||||
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
|
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
|
||||||
}
|
}
|
||||||
@@ -481,7 +488,8 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
|||||||
CHECK_OPENSLES_ERROR;
|
CHECK_OPENSLES_ERROR;
|
||||||
|
|
||||||
// Get the buffer queue interface
|
// Get the buffer queue interface
|
||||||
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mBufferQueue);
|
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
|
&mBufferQueue);
|
||||||
CHECK_OPENSLES_ERROR;
|
CHECK_OPENSLES_ERROR;
|
||||||
|
|
||||||
// Setup callback
|
// Setup callback
|
||||||
@@ -494,7 +502,8 @@ void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
|
|||||||
|
|
||||||
mBufferIndex = 0;
|
mBufferIndex = 0;
|
||||||
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
|
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
|
||||||
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize, (SLuint32)mBufferSize);
|
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize,
|
||||||
|
(SLuint32)mBufferSize);
|
||||||
|
|
||||||
// Set the player's state to playing
|
// Set the player's state to playing
|
||||||
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
|
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
|
||||||
@@ -511,15 +520,12 @@ void AndroidOutputDevice::internalClose()
|
|||||||
{
|
{
|
||||||
mInShutdown = true;
|
mInShutdown = true;
|
||||||
ICELogInfo(<< "Stop player");
|
ICELogInfo(<< "Stop player");
|
||||||
if (mPlayerControl)
|
if (mPlayerControl) {
|
||||||
{
|
if (*mPlayerControl) {
|
||||||
if (*mPlayerControl)
|
|
||||||
{
|
|
||||||
SLuint32 state = SL_PLAYSTATE_PLAYING;
|
SLuint32 state = SL_PLAYSTATE_PLAYING;
|
||||||
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
|
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
|
||||||
|
|
||||||
while (state != SL_PLAYSTATE_STOPPED)
|
while (state != SL_PLAYSTATE_STOPPED) {
|
||||||
{
|
|
||||||
(*mPlayerControl)->GetPlayState(mPlayerControl, &state);
|
(*mPlayerControl)->GetPlayState(mPlayerControl, &state);
|
||||||
SyncHelper::delay(1);
|
SyncHelper::delay(1);
|
||||||
}
|
}
|
||||||
@@ -586,8 +592,7 @@ void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void*
|
|||||||
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
|
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // TARGET_ANDROID
|
#endif // TARGET_ANDROID
|
||||||
@@ -111,6 +111,7 @@ protected:
|
|||||||
|
|
||||||
void handleCallback(SLAndroidSimpleBufferQueueItf bq);
|
void handleCallback(SLAndroidSimpleBufferQueueItf bq);
|
||||||
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpenSLEngine: public OsEngine
|
class OpenSLEngine: public OsEngine
|
||||||
@@ -138,7 +139,7 @@ protected:
|
|||||||
void internalOpen();
|
void internalOpen();
|
||||||
void internalClose();
|
void internalClose();
|
||||||
};
|
};
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif // TARGET_ANDROID
|
#endif // TARGET_ANDROID
|
||||||
|
|
||||||
|
|||||||
@@ -4,23 +4,24 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "../helper/HL_String.h"
|
#include "../helper/HL_String.h"
|
||||||
#include "../helper/HL_Time.h"
|
#include "../helper/HL_Time.h"
|
||||||
|
|
||||||
#ifdef TARGET_ANDROID
|
#ifdef TARGET_ANDROID
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "Audio"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
|
|
||||||
// -------------------- AndroidEnumerator -----------------------------
|
// -------------------- AndroidEnumerator -----------------------------
|
||||||
|
|
||||||
AndroidEnumerator::AndroidEnumerator() {}
|
AndroidEnumerator::AndroidEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
AndroidEnumerator::~AndroidEnumerator() {}
|
AndroidEnumerator::~AndroidEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
int AndroidEnumerator::indexOfDefaultDevice()
|
int AndroidEnumerator::indexOfDefaultDevice()
|
||||||
{
|
{
|
||||||
@@ -42,12 +43,15 @@ std::string AndroidEnumerator::nameAt(int index)
|
|||||||
return "Audio";
|
return "Audio";
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidEnumerator::open(int direction) {}
|
void AndroidEnumerator::open(int direction)
|
||||||
|
{}
|
||||||
|
|
||||||
void AndroidEnumerator::close() {}
|
void AndroidEnumerator::close()
|
||||||
|
{}
|
||||||
|
|
||||||
// --------------- Input implementation ----------------
|
// --------------- Input implementation ----------------
|
||||||
AndroidInputDevice::AndroidInputDevice(int devId) {}
|
AndroidInputDevice::AndroidInputDevice(int devId)
|
||||||
|
{}
|
||||||
|
|
||||||
AndroidInputDevice::~AndroidInputDevice()
|
AndroidInputDevice::~AndroidInputDevice()
|
||||||
{
|
{
|
||||||
@@ -89,14 +93,13 @@ void AndroidInputDevice::close()
|
|||||||
if (mRecordingStream != nullptr)
|
if (mRecordingStream != nullptr)
|
||||||
{
|
{
|
||||||
mRecordingStream->close();
|
mRecordingStream->close();
|
||||||
delete mRecordingStream;
|
delete mRecordingStream; mRecordingStream = nullptr;
|
||||||
mRecordingStream = nullptr;
|
|
||||||
}
|
}
|
||||||
mActive = false;
|
mActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
oboe::DataCallbackResult AndroidInputDevice::onAudioReady(oboe::AudioStream* audioStream, void* audioData,
|
oboe::DataCallbackResult
|
||||||
int32_t numFrames)
|
AndroidInputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(mMutex);
|
std::unique_lock<std::mutex> l(mMutex);
|
||||||
|
|
||||||
@@ -122,7 +125,8 @@ bool AndroidInputDevice::fakeMode()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidInputDevice::setFakeMode(bool fakemode) {}
|
void AndroidInputDevice::setFakeMode(bool fakemode)
|
||||||
|
{}
|
||||||
|
|
||||||
int AndroidInputDevice::readBuffer(void* buffer)
|
int AndroidInputDevice::readBuffer(void* buffer)
|
||||||
{
|
{
|
||||||
@@ -132,14 +136,11 @@ int AndroidInputDevice::readBuffer(void* buffer)
|
|||||||
// ------------ AndroidOutputDevice -----------------
|
// ------------ AndroidOutputDevice -----------------
|
||||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
|
||||||
}
|
}
|
||||||
AndroidOutputDevice::~AndroidOutputDevice()
|
AndroidOutputDevice::~AndroidOutputDevice()
|
||||||
{
|
{
|
||||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||||
// Mark shutdown before closing so a disconnect callback racing with teardown
|
|
||||||
// does not resurrect the stream via onErrorAfterClose()'s restart.
|
|
||||||
mInShutdown = true;
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +151,6 @@ bool AndroidOutputDevice::open()
|
|||||||
if (mActive)
|
if (mActive)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
mInShutdown = false;
|
|
||||||
mRequestedFrames = 0;
|
mRequestedFrames = 0;
|
||||||
mStartTime = 0.0;
|
mStartTime = 0.0;
|
||||||
mEndTime = 0.0;
|
mEndTime = 0.0;
|
||||||
@@ -161,10 +161,6 @@ bool AndroidOutputDevice::open()
|
|||||||
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||||
builder.setFormat(oboe::AudioFormat::I16);
|
builder.setFormat(oboe::AudioFormat::I16);
|
||||||
builder.setChannelCount(oboe::ChannelCount::Mono);
|
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||||
// Route through the platform voice-call path: correct device selection/volume
|
|
||||||
// and platform voice tuning for a softphone.
|
|
||||||
builder.setUsage(oboe::Usage::VoiceCommunication);
|
|
||||||
builder.setContentType(oboe::ContentType::Speech);
|
|
||||||
// builder.setDataCallback(this);
|
// builder.setDataCallback(this);
|
||||||
builder.setCallback(this);
|
builder.setCallback(this);
|
||||||
//builder.setErrorCallback(this)
|
//builder.setErrorCallback(this)
|
||||||
@@ -174,7 +170,7 @@ bool AndroidOutputDevice::open()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
mDeviceRate = mPlayingStream->getSampleRate();
|
mDeviceRate = mPlayingStream->getSampleRate();
|
||||||
ICELogInfo(<< "Output opened with rate " << mDeviceRate);
|
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
|
||||||
mActive = true;
|
mActive = true;
|
||||||
|
|
||||||
rescode = mPlayingStream->requestStart();
|
rescode = mPlayingStream->requestStart();
|
||||||
@@ -182,18 +178,7 @@ bool AndroidOutputDevice::open()
|
|||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
mActive = false;
|
mActive = false;
|
||||||
return mActive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latch the burst size and start from a two-burst buffer. onAudioReady() grows
|
|
||||||
// this on XRuns (up to a cap) so we keep low latency when the device can sustain
|
|
||||||
// it and trade a little latency for glitch-free playback when it can't.
|
|
||||||
mBurstFrames = mPlayingStream->getFramesPerBurst();
|
|
||||||
mXRunLast = 0;
|
|
||||||
mHeartbeatLast = 0.0f;
|
|
||||||
if (mBurstFrames > 0)
|
|
||||||
mPlayingStream->setBufferSizeInFrames(mBurstFrames * 2);
|
|
||||||
|
|
||||||
return mActive;
|
return mActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +191,7 @@ void AndroidOutputDevice::close()
|
|||||||
if (mPlayingStream != nullptr)
|
if (mPlayingStream != nullptr)
|
||||||
{
|
{
|
||||||
mPlayingStream->close();
|
mPlayingStream->close();
|
||||||
delete mPlayingStream;
|
delete mPlayingStream; mPlayingStream = nullptr;
|
||||||
mPlayingStream = nullptr;
|
|
||||||
}
|
}
|
||||||
mEndTime = now_ms();
|
mEndTime = now_ms();
|
||||||
mActive = false;
|
mActive = false;
|
||||||
@@ -226,10 +210,11 @@ bool AndroidOutputDevice::fakeMode()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidOutputDevice::setFakeMode(bool /*fakemode*/) {}
|
void AndroidOutputDevice::setFakeMode(bool /*fakemode*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream* audioStream, void* audioData,
|
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
|
||||||
int32_t numFrames)
|
|
||||||
{
|
{
|
||||||
if (mInShutdown)
|
if (mInShutdown)
|
||||||
return oboe::DataCallbackResult::Stop;
|
return oboe::DataCallbackResult::Stop;
|
||||||
@@ -247,50 +232,14 @@ oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream* au
|
|||||||
}
|
}
|
||||||
mRequestedFrames += numFrames;
|
mRequestedFrames += numFrames;
|
||||||
|
|
||||||
// Adaptive buffer sizing: on new XRuns (device-side underruns, i.e. we missed a
|
|
||||||
// callback deadline), grow the buffer one burst at a time up to a cap. Both calls
|
|
||||||
// are documented as safe from within the data callback.
|
|
||||||
auto xrun = audioStream->getXRunCount();
|
|
||||||
if (xrun && xrun.value() > mXRunLast)
|
|
||||||
{
|
|
||||||
mXRunLast = xrun.value();
|
|
||||||
if (mBurstFrames > 0)
|
|
||||||
{
|
|
||||||
int32_t cap = mBurstFrames * 8;
|
|
||||||
int32_t cur = audioStream->getBufferSizeInFrames();
|
|
||||||
int32_t next = std::min(cur + mBurstFrames, cap);
|
|
||||||
if (next > cur)
|
|
||||||
audioStream->setBufferSizeInFrames(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device-side heartbeat (Step 0): surfaces XRuns/buffer growth so device glitches
|
|
||||||
// can be told apart from network/jitter impairments.
|
|
||||||
float t = now_ms();
|
|
||||||
if (mHeartbeatLast == 0.0f)
|
|
||||||
mHeartbeatLast = t;
|
|
||||||
else if (t - mHeartbeatLast >= 5000.0f)
|
|
||||||
{
|
|
||||||
ICELogInfo(<< "[spk-heartbeat] xruns=" << mXRunLast << " bufFrames=" << audioStream->getBufferSizeInFrames()
|
|
||||||
<< " burst=" << mBurstFrames << " rate=" << mDeviceRate);
|
|
||||||
mHeartbeatLast = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
return oboe::DataCallbackResult::Continue;
|
return oboe::DataCallbackResult::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect recovery: on a route change (headset/BT plug/unplug) AAudio tears the
|
// TODO - special case https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
|
||||||
// stream down and calls this on its own thread after the stream is closed. Rebuild
|
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result) {
|
||||||
// on the new default route so audio doesn't silently die mid-call.
|
if (result == oboe::Result::ErrorDisconnected) {
|
||||||
// See https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
|
// LOGI("Restarting AudioStream after disconnect");
|
||||||
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream* stream, oboe::Result result)
|
// soundEngine.restart(); // please check oboe samples for soundEngine.restart(); call
|
||||||
{
|
|
||||||
if (result == oboe::Result::ErrorDisconnected && !mInShutdown)
|
|
||||||
{
|
|
||||||
ICELogInfo(<< "Output stream disconnected; restarting on the new route");
|
|
||||||
// close() and open() each take mMutex internally; this callback holds none.
|
|
||||||
close();
|
|
||||||
open();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // TARGET_ANDROID
|
#endif // TARGET_ANDROID
|
||||||
@@ -57,7 +57,8 @@ public:
|
|||||||
int readBuffer(void* buffer);
|
int readBuffer(void* buffer);
|
||||||
bool active() const;
|
bool active() const;
|
||||||
|
|
||||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames);
|
oboe::DataCallbackResult
|
||||||
|
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -98,15 +99,10 @@ protected:
|
|||||||
bool mInShutdown = false;
|
bool mInShutdown = false;
|
||||||
bool mActive = false;
|
bool mActive = false;
|
||||||
|
|
||||||
// Adaptive buffer sizing + device-side diagnostics (Step 0 / #5).
|
|
||||||
int32_t mBurstFrames = 0; // frames per burst, latched at open()
|
|
||||||
int32_t mXRunLast = 0; // last observed cumulative XRun count
|
|
||||||
float mHeartbeatLast = 0.0f; // now_ms() of last device heartbeat log
|
|
||||||
|
|
||||||
// Statistics
|
// Statistics
|
||||||
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
|
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
|
||||||
};
|
};
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif // TARGET_ANDROID
|
#endif // TARGET_ANDROID
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "CoreAudio"
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@@ -27,7 +27,8 @@ static inline short Float32ToInt16(Float32 v)
|
|||||||
int r = int(v * 32768);
|
int r = int(v * 32768);
|
||||||
if (r >= 32768)
|
if (r >= 32768)
|
||||||
return (short)32767;
|
return (short)32767;
|
||||||
else if (r < -32768)
|
else
|
||||||
|
if (r < -32768)
|
||||||
return (short)-32768;
|
return (short)-32768;
|
||||||
else
|
else
|
||||||
return (short)r;
|
return (short)r;
|
||||||
@@ -72,7 +73,10 @@ static void propertyListenerCallback(void* inUserData, AudioQueueRef queueObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CoreAudioUnit::CoreAudioUnit() : mUnit(0) {}
|
CoreAudioUnit::CoreAudioUnit()
|
||||||
|
:mUnit(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void CoreAudioUnit::open(bool voice)
|
void CoreAudioUnit::open(bool voice)
|
||||||
{
|
{
|
||||||
@@ -80,7 +84,9 @@ void CoreAudioUnit::open(bool voice)
|
|||||||
#ifdef TARGET_IOS
|
#ifdef TARGET_IOS
|
||||||
UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
|
UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
|
||||||
/* We want to be able to open playback and recording streams */
|
/* We want to be able to open playback and recording streams */
|
||||||
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory);
|
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
|
||||||
|
sizeof(audioCategory),
|
||||||
|
&audioCategory);
|
||||||
if (ostatus != kAudioSessionNoError)
|
if (ostatus != kAudioSessionNoError)
|
||||||
{
|
{
|
||||||
ICELogError(<< "Cannot set audio session to PlaybackAndRecord category, error" << ostatus);
|
ICELogError(<< "Cannot set audio session to PlaybackAndRecord category, error" << ostatus);
|
||||||
@@ -95,8 +101,7 @@ void CoreAudioUnit::open(bool voice)
|
|||||||
#ifdef TARGET_IOS
|
#ifdef TARGET_IOS
|
||||||
desc.componentSubType = voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_RemoteIO;
|
desc.componentSubType = voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_RemoteIO;
|
||||||
#else
|
#else
|
||||||
desc.componentSubType =
|
desc.componentSubType = kAudioUnitSubType_HALOutput;//voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_DefaultOutput;
|
||||||
kAudioUnitSubType_HALOutput; // voice ? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_DefaultOutput;
|
|
||||||
#endif
|
#endif
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
desc.componentFlags = 0;
|
desc.componentFlags = 0;
|
||||||
@@ -148,8 +153,12 @@ AudioStreamBasicDescription CoreAudioUnit::getFormat(int scope, int bus)
|
|||||||
|
|
||||||
void CoreAudioUnit::setFormat(AudioStreamBasicDescription& format, int scope, int bus)
|
void CoreAudioUnit::setFormat(AudioStreamBasicDescription& format, int scope, int bus)
|
||||||
{
|
{
|
||||||
OSStatus ostatus =
|
OSStatus ostatus = AudioUnitSetProperty(mUnit,
|
||||||
AudioUnitSetProperty(mUnit, kAudioUnitProperty_StreamFormat, scope, bus, &format, sizeof(format));
|
kAudioUnitProperty_StreamFormat,
|
||||||
|
scope,
|
||||||
|
bus,
|
||||||
|
&format,
|
||||||
|
sizeof(format));
|
||||||
if (ostatus != noErr)
|
if (ostatus != noErr)
|
||||||
{
|
{
|
||||||
ICELogError(<< "Cannot set stream format, error " << int(ostatus));
|
ICELogError(<< "Cannot set stream format, error " << int(ostatus));
|
||||||
@@ -172,8 +181,12 @@ bool CoreAudioUnit::getEnabled(int scope, int bus)
|
|||||||
void CoreAudioUnit::setEnabled(bool enabled, int scope, int bus)
|
void CoreAudioUnit::setEnabled(bool enabled, int scope, int bus)
|
||||||
{
|
{
|
||||||
UInt32 enable = enabled ? 1 : 0;
|
UInt32 enable = enabled ? 1 : 0;
|
||||||
OSStatus ostatus =
|
OSStatus ostatus = AudioUnitSetProperty(mUnit,
|
||||||
AudioUnitSetProperty(mUnit, kAudioOutputUnitProperty_EnableIO, scope, bus, &enable, sizeof(enable));
|
kAudioOutputUnitProperty_EnableIO,
|
||||||
|
scope,
|
||||||
|
bus,
|
||||||
|
&enable,
|
||||||
|
sizeof(enable));
|
||||||
|
|
||||||
if (ostatus != noErr)
|
if (ostatus != noErr)
|
||||||
{
|
{
|
||||||
@@ -184,8 +197,12 @@ void CoreAudioUnit::setEnabled(bool enabled, int scope, int bus)
|
|||||||
|
|
||||||
void CoreAudioUnit::makeCurrent(AudioDeviceID deviceId, int scope, int bus)
|
void CoreAudioUnit::makeCurrent(AudioDeviceID deviceId, int scope, int bus)
|
||||||
{
|
{
|
||||||
OSStatus ostatus =
|
OSStatus ostatus = AudioUnitSetProperty(mUnit,
|
||||||
AudioUnitSetProperty(mUnit, kAudioOutputUnitProperty_CurrentDevice, scope, bus, &deviceId, sizeof(deviceId));
|
kAudioOutputUnitProperty_CurrentDevice,
|
||||||
|
scope,
|
||||||
|
bus,
|
||||||
|
&deviceId,
|
||||||
|
sizeof(deviceId));
|
||||||
if (ostatus != noErr)
|
if (ostatus != noErr)
|
||||||
{
|
{
|
||||||
ICELogError(<< "Cannot make device " << int(deviceId) << " current, error " << ostatus);
|
ICELogError(<< "Cannot make device " << int(deviceId) << " current, error " << ostatus);
|
||||||
@@ -196,7 +213,12 @@ void CoreAudioUnit::makeCurrent(AudioDeviceID deviceId, int scope, int bus)
|
|||||||
|
|
||||||
void CoreAudioUnit::setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus)
|
void CoreAudioUnit::setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus)
|
||||||
{
|
{
|
||||||
OSStatus ostatus = AudioUnitSetProperty(mUnit, callbackType, scope, bus, &cb, sizeof(cb));
|
OSStatus ostatus = AudioUnitSetProperty(mUnit,
|
||||||
|
callbackType,
|
||||||
|
scope,
|
||||||
|
bus,
|
||||||
|
&cb,
|
||||||
|
sizeof(cb));
|
||||||
if (ostatus != noErr)
|
if (ostatus != noErr)
|
||||||
{
|
{
|
||||||
ICELogError(<< "Cannot set callback pointer, error " << int(ostatus));
|
ICELogError(<< "Cannot set callback pointer, error " << int(ostatus));
|
||||||
@@ -208,8 +230,7 @@ void CoreAudioUnit::setBufferFrameSizeInMilliseconds(int ms)
|
|||||||
{
|
{
|
||||||
#ifdef TARGET_IOS
|
#ifdef TARGET_IOS
|
||||||
Float32 preferredBufferSize = Float32(ms) / 1000; // in seconds
|
Float32 preferredBufferSize = Float32(ms) / 1000; // in seconds
|
||||||
OSStatus ostatus = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
|
OSStatus ostatus = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredBufferSize), &preferredBufferSize);
|
||||||
sizeof(preferredBufferSize), &preferredBufferSize);
|
|
||||||
if (ostatus != noErr)
|
if (ostatus != noErr)
|
||||||
{
|
{
|
||||||
ICELogError(<< "Cannot set audio buffer length to " << ms << " milliseconds");
|
ICELogError(<< "Cannot set audio buffer length to " << ms << " milliseconds");
|
||||||
@@ -224,8 +245,12 @@ int CoreAudioUnit::getBufferFrameSize()
|
|||||||
{
|
{
|
||||||
UInt32 bufsize = 0;
|
UInt32 bufsize = 0;
|
||||||
UInt32 size = sizeof(UInt32);
|
UInt32 size = sizeof(UInt32);
|
||||||
OSStatus ostatus = AudioUnitGetProperty(mUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global,
|
OSStatus ostatus = AudioUnitGetProperty(mUnit,
|
||||||
Bus_Speaker, &bufsize, &size);
|
kAudioDevicePropertyBufferFrameSize,
|
||||||
|
kAudioUnitScope_Global,
|
||||||
|
Bus_Speaker,
|
||||||
|
&bufsize,
|
||||||
|
&size);
|
||||||
if (ostatus != noErr)
|
if (ostatus != noErr)
|
||||||
{
|
{
|
||||||
ICELogError(<< "Cannot obtain input buffer size , error " << int(ostatus));
|
ICELogError(<< "Cannot obtain input buffer size , error " << int(ostatus));
|
||||||
@@ -249,8 +274,11 @@ AudioUnit CoreAudioUnit::getHandle()
|
|||||||
return mUnit;
|
return mUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSStatus MacDevice::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
OSStatus MacDevice::outputCallback( void *inRefCon,
|
||||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
|
const AudioTimeStamp *inTimeStamp,
|
||||||
|
UInt32 inBusNumber,
|
||||||
|
UInt32 inNumberFrames,
|
||||||
AudioBufferList *ioData )
|
AudioBufferList *ioData )
|
||||||
{
|
{
|
||||||
MacDevice* d = reinterpret_cast<MacDevice*>(inRefCon);
|
MacDevice* d = reinterpret_cast<MacDevice*>(inRefCon);
|
||||||
@@ -265,7 +293,8 @@ OSStatus MacDevice::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* i
|
|||||||
AudioBuffer& ab = ioData->mBuffers[0];
|
AudioBuffer& ab = ioData->mBuffers[0];
|
||||||
if (ab.mNumberChannels == 1)
|
if (ab.mNumberChannels == 1)
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "CoreAudio output callback for mono " << (int)ab.mDataByteSize << " bytes");
|
ICELogMedia(<< "CoreAudio output callback for mono " << (int)ab.mDataByteSize <<
|
||||||
|
" bytes");
|
||||||
memset(ab.mData, 0, ab.mDataByteSize);
|
memset(ab.mData, 0, ab.mDataByteSize);
|
||||||
d->provideAudioToSpeaker(ab.mNumberChannels, ab.mData, ab.mDataByteSize);
|
d->provideAudioToSpeaker(ab.mNumberChannels, ab.mData, ab.mDataByteSize);
|
||||||
|
|
||||||
@@ -274,7 +303,8 @@ OSStatus MacDevice::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* i
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "CoreAudio output callback for stereo " << (int)ab.mDataByteSize << " bytes")
|
ICELogMedia(<< "CoreAudio output callback for stereo " << (int)ab.mDataByteSize <<
|
||||||
|
" bytes")
|
||||||
// Iterate requested buffers
|
// Iterate requested buffers
|
||||||
for (unsigned i=0; i<ioData->mNumberBuffers; i++)
|
for (unsigned i=0; i<ioData->mNumberBuffers; i++)
|
||||||
{
|
{
|
||||||
@@ -295,8 +325,11 @@ OSStatus MacDevice::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* i
|
|||||||
|
|
||||||
static char GlobalInputBuffer[AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE];
|
static char GlobalInputBuffer[AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE];
|
||||||
|
|
||||||
OSStatus MacDevice::inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
OSStatus MacDevice::inputCallback(void *inRefCon,
|
||||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
|
const AudioTimeStamp *inTimeStamp,
|
||||||
|
UInt32 inBusNumber,
|
||||||
|
UInt32 inNumberFrames,
|
||||||
AudioBufferList *ioData)
|
AudioBufferList *ioData)
|
||||||
{
|
{
|
||||||
//ICELogDebug(<< "CoreAudio input callback");
|
//ICELogDebug(<< "CoreAudio input callback");
|
||||||
@@ -313,7 +346,11 @@ OSStatus MacDevice::inputCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|||||||
b.mDataByteSize = inNumberFrames * d->mStreamFormat.mChannelsPerFrame;
|
b.mDataByteSize = inNumberFrames * d->mStreamFormat.mChannelsPerFrame;
|
||||||
|
|
||||||
// Render the unit to get input data
|
// Render the unit to get input data
|
||||||
ostatus = AudioUnitRender(d->mAudioUnit.getHandle(), ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames,
|
ostatus = AudioUnitRender(d->mAudioUnit.getHandle(),
|
||||||
|
ioActionFlags,
|
||||||
|
inTimeStamp,
|
||||||
|
inBusNumber,
|
||||||
|
inNumberFrames,
|
||||||
d->mInputBufferList);
|
d->mInputBufferList);
|
||||||
|
|
||||||
if (ostatus != noErr)
|
if (ostatus != noErr)
|
||||||
@@ -328,7 +365,10 @@ OSStatus MacDevice::inputCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TARGET_IOS
|
#ifdef TARGET_IOS
|
||||||
void MacDevice::propListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inData)
|
void MacDevice::propListener(void *inClientData,
|
||||||
|
AudioSessionPropertyID inID,
|
||||||
|
UInt32 inDataSize,
|
||||||
|
const void * inData)
|
||||||
{
|
{
|
||||||
MacDevice* d = reinterpret_cast<MacDevice*>(inClientData);
|
MacDevice* d = reinterpret_cast<MacDevice*>(inClientData);
|
||||||
CFDictionaryRef routeDictionary;
|
CFDictionaryRef routeDictionary;
|
||||||
@@ -348,6 +388,7 @@ void MacDevice::propListener(void* inClientData, AudioSessionPropertyID inID, UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Audio route changed. Nothing to do in this implementation.
|
// Audio route changed. Nothing to do in this implementation.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacDevice::interruptionListener(void *inClientData, UInt32 inInterruption)
|
void MacDevice::interruptionListener(void *inClientData, UInt32 inInterruption)
|
||||||
@@ -368,7 +409,9 @@ void MacDevice::interruptionListener(void* inClientData, UInt32 inInterruption)
|
|||||||
*/
|
*/
|
||||||
/* Make sure we set the correct audio category before restarting */
|
/* Make sure we set the correct audio category before restarting */
|
||||||
audioCategory = kAudioSessionCategory_PlayAndRecord;
|
audioCategory = kAudioSessionCategory_PlayAndRecord;
|
||||||
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory);
|
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
|
||||||
|
sizeof(audioCategory),
|
||||||
|
&audioCategory);
|
||||||
if (ostatus != kAudioSessionNoError)
|
if (ostatus != kAudioSessionNoError)
|
||||||
{
|
{
|
||||||
ICELogError(<<"Cannot set the audio session category, error " << ostatus);
|
ICELogError(<<"Cannot set the audio session category, error " << ostatus);
|
||||||
@@ -377,7 +420,8 @@ void MacDevice::interruptionListener(void* inClientData, UInt32 inInterruption)
|
|||||||
// Start stream
|
// Start stream
|
||||||
d->startStream();
|
d->startStream();
|
||||||
}
|
}
|
||||||
else if (inInterruption == kAudioSessionBeginInterruption)
|
else
|
||||||
|
if (inInterruption == kAudioSessionBeginInterruption)
|
||||||
{
|
{
|
||||||
d->stopStream();
|
d->stopStream();
|
||||||
}
|
}
|
||||||
@@ -385,13 +429,16 @@ void MacDevice::interruptionListener(void* inClientData, UInt32 inInterruption)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
MacDevice::MacDevice(int devId)
|
MacDevice::MacDevice(int devId)
|
||||||
: mDeviceId(devId), mCapture(false), mRender(false), mActive(false), mConnection(nullptr), mUsageCount(0)
|
:mDeviceId(devId), mCapture(false), mRender(false), mActive(false),
|
||||||
|
mConnection(nullptr), mUsageCount(0)
|
||||||
{
|
{
|
||||||
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE);
|
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE);
|
||||||
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE);
|
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
MacDevice::~MacDevice() {}
|
MacDevice::~MacDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
DataConnection* MacDevice::connection()
|
DataConnection* MacDevice::connection()
|
||||||
{
|
{
|
||||||
@@ -535,8 +582,7 @@ bool MacDevice::createUnit(bool voice)
|
|||||||
// Start resample
|
// Start resample
|
||||||
mRenderResampler.start(mStreamFormat.mChannelsPerFrame, AUDIO_SAMPLERATE, mStreamFormat.mSampleRate);
|
mRenderResampler.start(mStreamFormat.mChannelsPerFrame, AUDIO_SAMPLERATE, mStreamFormat.mSampleRate);
|
||||||
|
|
||||||
// Set current render format - it is format required by unit from application; scope is Input and bus is 0
|
// Set current render format - it is format required by unit from application; scope is Input and bus is 0 (speaker)
|
||||||
// (speaker)
|
|
||||||
//mAudioUnit.setFormat(mRenderInputFormat, kAudioUnitScope_Input, Bus_Speaker);
|
//mAudioUnit.setFormat(mRenderInputFormat, kAudioUnitScope_Input, Bus_Speaker);
|
||||||
|
|
||||||
// Configure callback
|
// Configure callback
|
||||||
@@ -551,8 +597,7 @@ bool MacDevice::createUnit(bool voice)
|
|||||||
AURenderCallbackStruct cb;
|
AURenderCallbackStruct cb;
|
||||||
cb.inputProc = inputCallback;
|
cb.inputProc = inputCallback;
|
||||||
cb.inputProcRefCon = this;
|
cb.inputProcRefCon = this;
|
||||||
mAudioUnit.setCallback(cb, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,
|
mAudioUnit.setCallback(cb, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, Bus_Microphone); //!!!
|
||||||
Bus_Microphone); //!!!
|
|
||||||
#ifdef TARGET_OSX
|
#ifdef TARGET_OSX
|
||||||
AudioBuffer *ab;
|
AudioBuffer *ab;
|
||||||
UInt32 size, bufsize;
|
UInt32 size, bufsize;
|
||||||
@@ -618,8 +663,7 @@ void MacDevice::startStream()
|
|||||||
|
|
||||||
// Share audio chain
|
// Share audio chain
|
||||||
//UInt32 allowMixing = YES;
|
//UInt32 allowMixing = YES;
|
||||||
// AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof (allowMixing),
|
//AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof (allowMixing), &allowMixing);
|
||||||
// &allowMixing);
|
|
||||||
|
|
||||||
// Activate audio chain
|
// Activate audio chain
|
||||||
AudioSessionSetActive(true);
|
AudioSessionSetActive(true);
|
||||||
@@ -677,9 +721,15 @@ protected:
|
|||||||
|
|
||||||
MacDeviceList* MacDeviceList::mInstance = NULL;
|
MacDeviceList* MacDeviceList::mInstance = NULL;
|
||||||
|
|
||||||
MacDeviceList::MacDeviceList() {}
|
MacDeviceList::MacDeviceList()
|
||||||
|
{
|
||||||
|
|
||||||
MacDeviceList::~MacDeviceList() {}
|
}
|
||||||
|
|
||||||
|
MacDeviceList::~MacDeviceList()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
MacDeviceList& MacDeviceList::instance()
|
MacDeviceList& MacDeviceList::instance()
|
||||||
{
|
{
|
||||||
@@ -704,7 +754,8 @@ PMacDevice MacDeviceList::findDevice(int devId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Share list of opened devices
|
// Share list of opened devices
|
||||||
MacInputDevice::MacInputDevice(int devId) : InputDevice()
|
MacInputDevice::MacInputDevice(int devId)
|
||||||
|
:InputDevice()
|
||||||
{
|
{
|
||||||
// Look for MacDevice
|
// Look for MacDevice
|
||||||
mDevice = MacDeviceList::instance().findDevice(devId);
|
mDevice = MacDeviceList::instance().findDevice(devId);
|
||||||
@@ -733,7 +784,8 @@ Format MacInputDevice::getFormat()
|
|||||||
return mDevice->getFormat();
|
return mDevice->getFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
MacOutputDevice::MacOutputDevice(int devId) : OutputDevice()
|
MacOutputDevice::MacOutputDevice(int devId)
|
||||||
|
:OutputDevice()
|
||||||
{
|
{
|
||||||
// Look for MacDevice
|
// Look for MacDevice
|
||||||
mDevice = MacDeviceList::instance().findDevice(devId);
|
mDevice = MacDeviceList::instance().findDevice(devId);
|
||||||
@@ -762,9 +814,16 @@ Format MacOutputDevice::getFormat()
|
|||||||
return mDevice->getFormat();
|
return mDevice->getFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
MacEnumerator::MacEnumerator() : mDefaultInput(0), mDefaultOutput(0) {}
|
MacEnumerator::MacEnumerator()
|
||||||
|
:mDefaultInput(0), mDefaultOutput(0)
|
||||||
|
{
|
||||||
|
|
||||||
MacEnumerator::~MacEnumerator() {}
|
}
|
||||||
|
|
||||||
|
MacEnumerator::~MacEnumerator()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void MacEnumerator::open(int direction)
|
void MacEnumerator::open(int direction)
|
||||||
{
|
{
|
||||||
@@ -782,7 +841,8 @@ void MacEnumerator::open(int direction)
|
|||||||
addr.mSelector = kAudioHardwarePropertyDevices;
|
addr.mSelector = kAudioHardwarePropertyDevices;
|
||||||
addr.mScope = kAudioObjectPropertyScopeGlobal;
|
addr.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
addr.mElement = kAudioObjectPropertyElementMaster;
|
addr.mElement = kAudioObjectPropertyElementMaster;
|
||||||
osstatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &devSize);
|
osstatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
|
||||||
|
0, NULL, &devSize);
|
||||||
if (osstatus != noErr)
|
if (osstatus != noErr)
|
||||||
{
|
{
|
||||||
devSize = 0;
|
devSize = 0;
|
||||||
@@ -794,8 +854,8 @@ void MacEnumerator::open(int direction)
|
|||||||
deviceIds.resize(devCount);
|
deviceIds.resize(devCount);
|
||||||
|
|
||||||
// Get actual list
|
// Get actual list
|
||||||
osstatus =
|
osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
|
||||||
AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &devSize, (void*)&deviceIds.front());
|
0, NULL, &devSize, (void *)&deviceIds.front());
|
||||||
if (osstatus != noErr)
|
if (osstatus != noErr)
|
||||||
return;
|
return;
|
||||||
for (unsigned i=0; i<deviceIds.size(); i++)
|
for (unsigned i=0; i<deviceIds.size(); i++)
|
||||||
@@ -827,12 +887,13 @@ void MacEnumerator::open(int direction)
|
|||||||
addr.mElement = kAudioObjectPropertyElementMaster;
|
addr.mElement = kAudioObjectPropertyElementMaster;
|
||||||
size = sizeof(devId);
|
size = sizeof(devId);
|
||||||
|
|
||||||
osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void*)&devId);
|
osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
||||||
|
&addr, 0, NULL, &size, (void *)&devId);
|
||||||
|
|
||||||
if (osstatus == noErr)
|
if (osstatus == noErr)
|
||||||
{
|
{
|
||||||
std::vector<DeviceInfo>::iterator r = std::find_if(
|
std::vector<DeviceInfo>::iterator r = std::find_if(mDeviceList.begin(), mDeviceList.end(),
|
||||||
mDeviceList.begin(), mDeviceList.end(), [devId](const DeviceInfo& di) { return di.mId == devId; });
|
[devId] (const DeviceInfo& di) { return di.mId == devId;});
|
||||||
if (r != mDeviceList.end())
|
if (r != mDeviceList.end())
|
||||||
mDefaultInput = r - mDeviceList.begin();
|
mDefaultInput = r - mDeviceList.begin();
|
||||||
}
|
}
|
||||||
@@ -842,11 +903,13 @@ void MacEnumerator::open(int direction)
|
|||||||
if (direction == mySpeaker)
|
if (direction == mySpeaker)
|
||||||
{
|
{
|
||||||
addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void*)&devId);
|
osstatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
||||||
|
&addr, 0, NULL,
|
||||||
|
&size, (void *)&devId);
|
||||||
if (osstatus == noErr)
|
if (osstatus == noErr)
|
||||||
{
|
{
|
||||||
std::vector<DeviceInfo>::iterator r = std::find_if(
|
std::vector<DeviceInfo>::iterator r = std::find_if(mDeviceList.begin(), mDeviceList.end(),
|
||||||
mDeviceList.begin(), mDeviceList.end(), [devId](const DeviceInfo& di) { return di.mId == devId; });
|
[devId](const DeviceInfo& di) { return di.mId == devId;});
|
||||||
if (r != mDeviceList.end())
|
if (r != mDeviceList.end())
|
||||||
mDefaultOutput = r - mDeviceList.begin();
|
mDefaultOutput = r - mDeviceList.begin();
|
||||||
}
|
}
|
||||||
@@ -921,8 +984,7 @@ void MacEnumerator::getInfo(DeviceInfo& di)
|
|||||||
di.mOutputCount += buf->mBuffers[idx].mNumberChannels;
|
di.mOutputCount += buf->mBuffers[idx].mNumberChannels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(buf);
|
free(buf); buf = NULL;
|
||||||
buf = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get default sample rate */
|
/* Get default sample rate */
|
||||||
@@ -947,7 +1009,10 @@ void MacEnumerator::getInfo(DeviceInfo& di)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacEnumerator::close() {}
|
void MacEnumerator::close()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int MacEnumerator::count()
|
int MacEnumerator::count()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ namespace Audio
|
|||||||
class AudioException: public Exception
|
class AudioException: public Exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioException(int code, OSStatus subcode) : Exception(code, int(subcode)) {}
|
AudioException(int code, OSStatus subcode)
|
||||||
|
:Exception(code, int(subcode))
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
//#ifndef AudioDeviceID
|
//#ifndef AudioDeviceID
|
||||||
@@ -57,11 +59,7 @@ protected:
|
|||||||
bool mCanChangeInputVolume;
|
bool mCanChangeInputVolume;
|
||||||
int mInputCount, mOutputCount;
|
int mInputCount, mOutputCount;
|
||||||
int mDefaultRate;
|
int mDefaultRate;
|
||||||
DeviceInfo()
|
DeviceInfo(): mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0), mDefaultRate(16000) {}
|
||||||
: mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0),
|
|
||||||
mDefaultRate(16000)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
std::vector<DeviceInfo> mDeviceList;
|
std::vector<DeviceInfo> mDeviceList;
|
||||||
unsigned mDefaultInput, mDefaultOutput;
|
unsigned mDefaultInput, mDefaultOutput;
|
||||||
@@ -119,8 +117,7 @@ protected:
|
|||||||
|
|
||||||
CoreAudioUnit mAudioUnit;
|
CoreAudioUnit mAudioUnit;
|
||||||
AudioComponent mComponent;
|
AudioComponent mComponent;
|
||||||
AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat,
|
AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat, mStreamFormat;
|
||||||
mStreamFormat;
|
|
||||||
AudioBufferList* mInputBufferList;
|
AudioBufferList* mInputBufferList;
|
||||||
DataConnection* mConnection;
|
DataConnection* mConnection;
|
||||||
SpeexResampler mCaptureResampler, mRenderResampler;
|
SpeexResampler mCaptureResampler, mRenderResampler;
|
||||||
@@ -133,17 +130,27 @@ protected:
|
|||||||
void setupStreamFormat();
|
void setupStreamFormat();
|
||||||
bool createResampleUnit(AudioStreamBasicDescription format);
|
bool createResampleUnit(AudioStreamBasicDescription format);
|
||||||
|
|
||||||
static OSStatus outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
static OSStatus outputCallback( void *inRefCon,
|
||||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
|
const AudioTimeStamp *inTimeStamp,
|
||||||
|
UInt32 inBusNumber,
|
||||||
|
UInt32 inNumberFrames,
|
||||||
AudioBufferList *ioData );
|
AudioBufferList *ioData );
|
||||||
|
|
||||||
static OSStatus inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
static OSStatus inputCallback(void *inRefCon,
|
||||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
|
const AudioTimeStamp *inTimeStamp,
|
||||||
|
UInt32 inBusNumber,
|
||||||
|
UInt32 inNumberFrames,
|
||||||
AudioBufferList *ioData);
|
AudioBufferList *ioData);
|
||||||
#ifdef TARGET_IOS
|
#ifdef TARGET_IOS
|
||||||
static void propListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inData);
|
static void propListener(void *inClientData,
|
||||||
|
AudioSessionPropertyID inID,
|
||||||
|
UInt32 inDataSize,
|
||||||
|
const void * inData);
|
||||||
static void interruptionListener(void *inClientData, UInt32 inInterruption);
|
static void interruptionListener(void *inClientData, UInt32 inInterruption);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<MacDevice> PMacDevice;
|
typedef std::shared_ptr<MacDevice> PMacDevice;
|
||||||
@@ -161,9 +168,9 @@ public:
|
|||||||
bool fakeMode();
|
bool fakeMode();
|
||||||
void setFakeMode(bool fakemode);
|
void setFakeMode(bool fakemode);
|
||||||
int readBuffer(void* buffer);
|
int readBuffer(void* buffer);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PMacDevice mDevice;
|
PMacDevice mDevice;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MacOutputDevice: public OutputDevice
|
class MacOutputDevice: public OutputDevice
|
||||||
@@ -183,7 +190,7 @@ protected:
|
|||||||
PMacDevice mDevice;
|
PMacDevice mDevice;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif // TARGET_OSX
|
#endif // TARGET_OSX
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2018 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -8,40 +8,30 @@
|
|||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
DataWindow::DataWindow() {}
|
DataWindow::DataWindow()
|
||||||
|
{
|
||||||
|
mFilled = 0;
|
||||||
|
mData = NULL;
|
||||||
|
mCapacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
DataWindow::~DataWindow()
|
DataWindow::~DataWindow()
|
||||||
{
|
{
|
||||||
if (mData)
|
if (mData)
|
||||||
{
|
|
||||||
free(mData);
|
free(mData);
|
||||||
mData = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataWindow::setCapacity(size_t capacity)
|
void DataWindow::setCapacity(int capacity)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
|
int tail = capacity - mCapacity;
|
||||||
// The window only ever grows; a smaller request keeps the current buffer.
|
|
||||||
if (capacity <= mCapacity)
|
|
||||||
return;
|
|
||||||
|
|
||||||
size_t tail = capacity - mCapacity;
|
|
||||||
char* buffer = mData;
|
|
||||||
mData = (char*)realloc(mData, capacity);
|
mData = (char*)realloc(mData, capacity);
|
||||||
if (!mData)
|
|
||||||
{
|
|
||||||
// Realloc failed
|
|
||||||
mData = buffer;
|
|
||||||
throw std::bad_alloc();
|
|
||||||
}
|
|
||||||
if (tail > 0)
|
if (tail > 0)
|
||||||
memset(mData + mCapacity, 0, tail);
|
memset(mData + mCapacity, 0, tail);
|
||||||
mCapacity = capacity;
|
mCapacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataWindow::addZero(size_t length)
|
void DataWindow::addZero(int length)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
|
|
||||||
@@ -60,7 +50,7 @@ void DataWindow::addZero(size_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DataWindow::add(const void* data, size_t length)
|
void DataWindow::add(const void* data, int length)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
|
|
||||||
@@ -94,7 +84,7 @@ void DataWindow::add(short sample)
|
|||||||
add(&sample, sizeof sample);
|
add(&sample, sizeof sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataWindow::erase(size_t length)
|
void DataWindow::erase(int length)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
if (length > mFilled)
|
if (length > mFilled)
|
||||||
@@ -120,21 +110,21 @@ void DataWindow::clear()
|
|||||||
mFilled = 0;
|
mFilled = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
short DataWindow::shortAt(size_t index) const
|
short DataWindow::shortAt(int index) const
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
assert(index < mFilled / 2);
|
assert(index < mFilled / 2);
|
||||||
return ((short*)mData)[index];
|
return ((short*)mData)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataWindow::setShortAt(short value, size_t index)
|
void DataWindow::setShortAt(short value, int index)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
assert(index < mFilled / 2);
|
assert(index < mFilled / 2);
|
||||||
((short*)mData)[index] = value;
|
((short*)mData)[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DataWindow::read(void* buffer, size_t length)
|
int DataWindow::read(void* buffer, int length)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
if (length > mFilled)
|
if (length > mFilled)
|
||||||
@@ -150,27 +140,25 @@ size_t DataWindow::read(void* buffer, size_t length)
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DataWindow::filled() const
|
int DataWindow::filled() const
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
return mFilled;
|
return mFilled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataWindow::setFilled(size_t filled)
|
void DataWindow::setFilled(int filled)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
if (filled > mCapacity)
|
|
||||||
throw std::bad_alloc();
|
|
||||||
mFilled = filled;
|
mFilled = filled;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DataWindow::capacity() const
|
int DataWindow::capacity() const
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
return mCapacity;
|
return mCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataWindow::zero(size_t length)
|
void DataWindow::zero(int length)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
Lock l(mMutex);
|
||||||
assert(length <= mCapacity);
|
assert(length <= mCapacity);
|
||||||
@@ -178,25 +166,6 @@ void DataWindow::zero(size_t length)
|
|||||||
memset(mData, 0, mFilled);
|
memset(mData, 0, mFilled);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DataWindow::moveTo(DataWindow& dst, size_t size)
|
|
||||||
{
|
|
||||||
Lock l(mMutex);
|
|
||||||
|
|
||||||
size_t avail = std::min(size, (size_t)filled());
|
|
||||||
if (avail != 0)
|
|
||||||
{
|
|
||||||
dst.add(mData, avail);
|
|
||||||
erase(avail);
|
|
||||||
}
|
|
||||||
return avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::milliseconds DataWindow::getTimeLength(const Audio::Format& fmt) const
|
|
||||||
{
|
|
||||||
Lock l(mMutex);
|
|
||||||
return std::chrono::milliseconds(mFilled / sizeof(short) / fmt.channels() / (fmt.rate() / 1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
|
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
|
||||||
{
|
{
|
||||||
Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
|
Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "Audio_Interface.h"
|
|
||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
{
|
{
|
||||||
@@ -18,34 +17,31 @@ public:
|
|||||||
DataWindow();
|
DataWindow();
|
||||||
~DataWindow();
|
~DataWindow();
|
||||||
|
|
||||||
void setCapacity(size_t capacity);
|
void setCapacity(int capacity);
|
||||||
size_t capacity() const;
|
int capacity() const;
|
||||||
|
|
||||||
void addZero(size_t length);
|
void addZero(int length);
|
||||||
void add(const void* data, size_t length);
|
void add(const void* data, int length);
|
||||||
void add(short sample);
|
void add(short sample);
|
||||||
size_t read(void* buffer, size_t length);
|
int read(void* buffer, int length);
|
||||||
void erase(size_t length);
|
void erase(int length = -1);
|
||||||
const char* data() const;
|
const char* data() const;
|
||||||
char* mutableData();
|
char* mutableData();
|
||||||
size_t filled() const;
|
int filled() const;
|
||||||
void setFilled(size_t filled);
|
void setFilled(int filled);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
short shortAt(size_t index) const;
|
short shortAt(int index) const;
|
||||||
void setShortAt(short value, size_t index);
|
void setShortAt(short value, int index);
|
||||||
void zero(size_t length);
|
void zero(int length);
|
||||||
size_t moveTo(DataWindow& dst, size_t size /* in bytes*/);
|
|
||||||
|
|
||||||
std::chrono::milliseconds getTimeLength(const Format& fmt) const;
|
|
||||||
|
|
||||||
static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
|
static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
mutable Mutex mMutex;
|
mutable Mutex mMutex;
|
||||||
char* mData = nullptr;
|
char* mData;
|
||||||
size_t mFilled = 0;
|
int mFilled;
|
||||||
size_t mCapacity = 0;
|
int mCapacity;
|
||||||
};
|
};
|
||||||
} // namespace Audio
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
#include "Audio_DevicePair.h"
|
#include "Audio_DevicePair.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "Audio"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
// --- DevicePair ---
|
// --- DevicePair ---
|
||||||
DevicePair::DevicePair()
|
DevicePair::DevicePair(bool aec, bool agc)
|
||||||
: mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false),
|
:mConfig(NULL), mDelegate(NULL), mAec(aec), mAgc(agc), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS)
|
||||||
mAecFilter(AUDIO_MIC_BUFFER_LENGTH * 10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS),
|
|
||||||
mMonitoring(nullptr)
|
|
||||||
{
|
{
|
||||||
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
||||||
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
||||||
@@ -29,50 +28,26 @@ DevicePair::~DevicePair()
|
|||||||
if (mInput)
|
if (mInput)
|
||||||
{
|
{
|
||||||
if (mInput->connection() == this)
|
if (mInput->connection() == this)
|
||||||
mInput->setConnection(nullptr);
|
mInput->setConnection(NULL);
|
||||||
mInput.reset();
|
mInput.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mOutput)
|
if (mOutput)
|
||||||
{
|
{
|
||||||
if (mOutput->connection() == this)
|
if (mOutput->connection() == this)
|
||||||
mOutput->setConnection(nullptr);
|
mOutput->setConnection(NULL);
|
||||||
mOutput.reset();
|
mOutput.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setAec(bool aec)
|
|
||||||
{
|
|
||||||
mAec = aec;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DevicePair::aec()
|
|
||||||
{
|
|
||||||
return mAec;
|
|
||||||
}
|
|
||||||
|
|
||||||
DevicePair& DevicePair::setAgc(bool agc)
|
|
||||||
{
|
|
||||||
mAgc = agc;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DevicePair::agc()
|
|
||||||
{
|
|
||||||
return mAgc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
VariantMap* DevicePair::config()
|
VariantMap* DevicePair::config()
|
||||||
{
|
{
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setConfig(VariantMap* config)
|
void DevicePair::setConfig(VariantMap* config)
|
||||||
{
|
{
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PInputDevice DevicePair::input()
|
PInputDevice DevicePair::input()
|
||||||
@@ -80,17 +55,15 @@ PInputDevice DevicePair::input()
|
|||||||
return mInput;
|
return mInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setInput(PInputDevice input)
|
void DevicePair::setInput(PInputDevice input)
|
||||||
{
|
{
|
||||||
if (mInput == input)
|
if (mInput == input)
|
||||||
return *this;
|
return;
|
||||||
|
|
||||||
mInput = input;
|
mInput = input;
|
||||||
mInput->setConnection(this);
|
mInput->setConnection(this);
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->deviceChanged(this);
|
mDelegate->deviceChanged(this);
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
POutputDevice DevicePair::output()
|
POutputDevice DevicePair::output()
|
||||||
@@ -98,17 +71,14 @@ POutputDevice DevicePair::output()
|
|||||||
return mOutput;
|
return mOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setOutput(POutputDevice output)
|
void DevicePair::setOutput(POutputDevice output)
|
||||||
{
|
{
|
||||||
if (output == mOutput)
|
if (output == mOutput)
|
||||||
return *this;
|
return;
|
||||||
|
|
||||||
mOutput = output;
|
mOutput = output;
|
||||||
mOutput->setConnection(this);
|
mOutput->setConnection(this);
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->deviceChanged(this);
|
mDelegate->deviceChanged(this);
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DevicePair::start()
|
bool DevicePair::start()
|
||||||
@@ -118,7 +88,6 @@ bool DevicePair::start()
|
|||||||
result = mInput->open();
|
result = mInput->open();
|
||||||
if (mOutput && result)
|
if (mOutput && result)
|
||||||
result &= mOutput->open();
|
result &= mOutput->open();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,10 +99,9 @@ void DevicePair::stop()
|
|||||||
mOutput->close();
|
mOutput->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setDelegate(Delegate* dc)
|
void DevicePair::setDelegate(Delegate* dc)
|
||||||
{
|
{
|
||||||
mDelegate = dc;
|
mDelegate = dc;
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair::Delegate* DevicePair::delegate()
|
DevicePair::Delegate* DevicePair::delegate()
|
||||||
@@ -141,17 +109,6 @@ DevicePair::Delegate* DevicePair::delegate()
|
|||||||
return mDelegate;
|
return mDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
|
|
||||||
{
|
|
||||||
mMonitoring = monitoring;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataConnection* DevicePair::monitoring()
|
|
||||||
{
|
|
||||||
return mMonitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player& DevicePair::player()
|
Player& DevicePair::player()
|
||||||
{
|
{
|
||||||
return mPlayer;
|
return mPlayer;
|
||||||
@@ -177,6 +134,7 @@ void DevicePair::onMicData(const Format& f, const void* buffer, int length)
|
|||||||
|
|
||||||
for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
|
for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
|
||||||
{
|
{
|
||||||
|
|
||||||
size_t wasProcessed = 0;
|
size_t wasProcessed = 0;
|
||||||
|
|
||||||
size_t wasProduced = mMicResampler.resample(f.mRate, // Source rate
|
size_t wasProduced = mMicResampler.resample(f.mRate, // Source rate
|
||||||
@@ -193,6 +151,7 @@ void DevicePair::onMicData(const Format& f, const void* buffer, int length)
|
|||||||
|
|
||||||
mInputBuffer.erase((int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
|
mInputBuffer.erase((int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||||
@@ -214,6 +173,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
|||||||
int required = length - mOutputNativeData.filled();
|
int required = length - mOutputNativeData.filled();
|
||||||
if (required > 0)
|
if (required > 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Find how much blocks must be received from RTP/decoder side
|
// Find how much blocks must be received from RTP/decoder side
|
||||||
int nativeBufferSize = (int)nativeFormat.sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
|
int nativeBufferSize = (int)nativeFormat.sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
|
||||||
int blocks = required / nativeBufferSize;
|
int blocks = required / nativeBufferSize;
|
||||||
@@ -225,7 +185,6 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
|||||||
{
|
{
|
||||||
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
|
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
|
||||||
|
|
||||||
// Ask audio data on main AUDIO_SAMPLERATE frequency
|
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
|
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
|
||||||
|
|
||||||
@@ -238,10 +197,8 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
|||||||
|
|
||||||
// Resample these 10 milliseconds it to native format
|
// Resample these 10 milliseconds it to native format
|
||||||
size_t wasProcessed = 0;
|
size_t wasProcessed = 0;
|
||||||
size_t wasProduced = mSpkResampler.resample(Format().mRate, mOutput10msBuffer.data(),
|
size_t wasProduced = mSpkResampler.resample(nativeFormat.mRate, mOutput10msBuffer.data(), mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
|
||||||
mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
|
mOutputNativeData.mutableData() + mOutputNativeData.filled(), mOutputNativeData.capacity() - mOutputNativeData.filled());
|
||||||
mOutputNativeData.mutableData() + mOutputNativeData.filled(),
|
|
||||||
mOutputNativeData.capacity() - mOutputNativeData.filled());
|
|
||||||
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
|
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
|
||||||
#ifdef CONSOLE_LOGGING
|
#ifdef CONSOLE_LOGGING
|
||||||
printf("Resampled %d to %d\n", wasProcessed, wasProduced);
|
printf("Resampled %d to %d\n", wasProcessed, wasProduced);
|
||||||
@@ -257,10 +214,6 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
|||||||
|
|
||||||
mOutputNativeData.read(buffer, length);
|
mOutputNativeData.read(buffer, length);
|
||||||
|
|
||||||
// Send data to monitoring if needed
|
|
||||||
if (mMonitoring)
|
|
||||||
mMonitoring->onSpkData(f, buffer, length);
|
|
||||||
|
|
||||||
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
|
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
|
||||||
|
|
||||||
// AEC filter wants frames.
|
// AEC filter wants frames.
|
||||||
@@ -271,6 +224,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
|||||||
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
|
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
|
||||||
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
|
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
|
||||||
}
|
}
|
||||||
|
//ICELogMedia(<< "Audio::DevicePair::onSpkData() end")
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePair::processMicData(const Format& f, void* buffer, int length)
|
void DevicePair::processMicData(const Format& f, void* buffer, int length)
|
||||||
|
|||||||
@@ -26,32 +26,29 @@ public:
|
|||||||
virtual void deviceChanged(DevicePair* dpair) = 0;
|
virtual void deviceChanged(DevicePair* dpair) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
DevicePair();
|
DevicePair(bool aec = true, bool agc = true);
|
||||||
virtual ~DevicePair();
|
virtual ~DevicePair();
|
||||||
|
|
||||||
DevicePair& setAec(bool aec);
|
void setAec(bool aec);
|
||||||
bool aec();
|
bool aec();
|
||||||
DevicePair& setAgc(bool agc);
|
void setAgc(bool agc);
|
||||||
bool agc();
|
bool agc();
|
||||||
|
|
||||||
VariantMap* config();
|
VariantMap* config();
|
||||||
DevicePair& setConfig(VariantMap* config);
|
void setConfig(VariantMap* config);
|
||||||
|
|
||||||
PInputDevice input();
|
PInputDevice input();
|
||||||
DevicePair& setInput(PInputDevice input);
|
void setInput(PInputDevice input);
|
||||||
|
|
||||||
POutputDevice output();
|
POutputDevice output();
|
||||||
DevicePair& setOutput(POutputDevice output);
|
void setOutput(POutputDevice output);
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
DevicePair& setDelegate(Delegate* dc);
|
void setDelegate(Delegate* dc);
|
||||||
Delegate* delegate();
|
Delegate* delegate();
|
||||||
|
|
||||||
DevicePair& setMonitoring(DataConnection* monitoring);
|
|
||||||
DataConnection* monitoring();
|
|
||||||
|
|
||||||
Player& player();
|
Player& player();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -66,7 +63,6 @@ protected:
|
|||||||
Player mPlayer;
|
Player mPlayer;
|
||||||
UniversalResampler mMicResampler, mSpkResampler;
|
UniversalResampler mMicResampler, mSpkResampler;
|
||||||
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
||||||
DataConnection* mMonitoring;
|
|
||||||
|
|
||||||
#ifdef DUMP_NATIVEOUTPUT
|
#ifdef DUMP_NATIVEOUTPUT
|
||||||
std::shared_ptr<WavFileWriter> mNativeOutputDump;
|
std::shared_ptr<WavFileWriter> mNativeOutputDump;
|
||||||
@@ -80,6 +76,6 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<DevicePair> PDevicePair;
|
typedef std::shared_ptr<DevicePair> PDevicePair;
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
|
#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
|
||||||
#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)
|
#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "DirectSound"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
@@ -57,7 +57,10 @@ protected:
|
|||||||
|
|
||||||
DSoundInit gDSoundInit;
|
DSoundInit gDSoundInit;
|
||||||
|
|
||||||
DSoundInit::DSoundInit() : mRefCount(0) {}
|
DSoundInit::DSoundInit()
|
||||||
|
:mRefCount(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
DSoundInit::~DSoundInit()
|
DSoundInit::~DSoundInit()
|
||||||
{
|
{
|
||||||
@@ -79,15 +82,10 @@ void DSoundInit::load()
|
|||||||
if (!mRoutines.mInstance)
|
if (!mRoutines.mInstance)
|
||||||
throw std::logic_error("Cannot load dsound.dll");
|
throw std::logic_error("Cannot load dsound.dll");
|
||||||
|
|
||||||
mRoutines.DirectSoundCaptureCreate8 =
|
mRoutines.DirectSoundCaptureCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureCreate8");
|
||||||
(HRESULT(WINAPI*)(LPGUID, LPDIRECTSOUNDCAPTURE8*, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance,
|
mRoutines.DirectSoundCaptureEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureEnumerateW");
|
||||||
"DirectSoundCaptureCreate8");
|
mRoutines.DirectSoundCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCreate8");
|
||||||
mRoutines.DirectSoundCaptureEnumerateW = (HRESULT(WINAPI*)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(
|
mRoutines.DirectSoundEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW");
|
||||||
mRoutines.mInstance, "DirectSoundCaptureEnumerateW");
|
|
||||||
mRoutines.DirectSoundCreate8 = (HRESULT(WINAPI*)(LPGUID, LPDIRECTSOUND8*, LPUNKNOWN))::GetProcAddress(
|
|
||||||
mRoutines.mInstance, "DirectSoundCreate8");
|
|
||||||
mRoutines.DirectSoundEnumerateW =
|
|
||||||
(HRESULT(WINAPI*)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW");
|
|
||||||
mRoutines.GetDeviceID = (HRESULT (WINAPI*) (LPCGUID, LPGUID)) GetProcAddress(mRoutines.mInstance, "GetDeviceID");
|
mRoutines.GetDeviceID = (HRESULT (WINAPI*) (LPCGUID, LPGUID)) GetProcAddress(mRoutines.mInstance, "GetDeviceID");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +106,10 @@ void DSoundInit::unload()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------- VistaEnumerator ---------------------
|
// --------------- VistaEnumerator ---------------------
|
||||||
VistaEnumerator::VistaEnumerator() : mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture) {}
|
VistaEnumerator::VistaEnumerator()
|
||||||
|
:mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
VistaEnumerator::~VistaEnumerator()
|
VistaEnumerator::~VistaEnumerator()
|
||||||
{
|
{
|
||||||
@@ -122,8 +123,10 @@ void VistaEnumerator::open(int direction)
|
|||||||
|
|
||||||
mDirection = (direction == myMicrophone) ? eCapture : eRender;
|
mDirection = (direction == myMicrophone) ? eCapture : eRender;
|
||||||
|
|
||||||
HRESULT hr =
|
HRESULT hr = CoCreateInstance(
|
||||||
CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mEnumerator);
|
CLSID_MMDeviceEnumerator, NULL,
|
||||||
|
CLSCTX_ALL, IID_IMMDeviceEnumerator,
|
||||||
|
(void**)&mEnumerator);
|
||||||
if (!mEnumerator)
|
if (!mEnumerator)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -255,23 +258,26 @@ int VistaEnumerator::indexOfDefaultDevice()
|
|||||||
|
|
||||||
if (mDirection == mySpeaker)
|
if (mDirection == mySpeaker)
|
||||||
{
|
{
|
||||||
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
|
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
||||||
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
|
||||||
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
|
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
||||||
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
|
||||||
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
||||||
}
|
}
|
||||||
return devID;
|
return devID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------- XpEnumerator ---------------
|
// -------------- XpEnumerator ---------------
|
||||||
XpEnumerator::XpEnumerator() : mDirection(-1) {}
|
XpEnumerator::XpEnumerator()
|
||||||
|
:mDirection(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
XpEnumerator::~XpEnumerator() {}
|
XpEnumerator::~XpEnumerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void XpEnumerator::open(int direction)
|
void XpEnumerator::open(int direction)
|
||||||
{
|
{
|
||||||
@@ -302,7 +308,9 @@ void XpEnumerator::open(int direction)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XpEnumerator::close() {}
|
void XpEnumerator::close()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
int XpEnumerator::count()
|
int XpEnumerator::count()
|
||||||
{
|
{
|
||||||
@@ -325,14 +333,12 @@ int XpEnumerator::indexOfDefaultDevice()
|
|||||||
|
|
||||||
if (mDirection == mySpeaker)
|
if (mDirection == mySpeaker)
|
||||||
{
|
{
|
||||||
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
|
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
||||||
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
|
||||||
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
|
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
||||||
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
|
|
||||||
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
|
||||||
}
|
}
|
||||||
return devID;
|
return devID;
|
||||||
@@ -340,11 +346,10 @@ int XpEnumerator::indexOfDefaultDevice()
|
|||||||
|
|
||||||
// -------- DSoundInputDevice ---------------
|
// -------- DSoundInputDevice ---------------
|
||||||
DSoundInputDevice::DSoundInputDevice(GUID deviceId)
|
DSoundInputDevice::DSoundInputDevice(GUID deviceId)
|
||||||
: mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE),
|
:mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE), mEnableDenoiser(true),
|
||||||
mEnableDenoiser(true), mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT)
|
mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT)
|
||||||
#ifdef AUDIO_DUMPINPUT
|
#ifdef AUDIO_DUMPINPUT
|
||||||
,
|
,mDump(AUDIO_SAMPLERATE)
|
||||||
mDump(AUDIO_SAMPLERATE)
|
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
gDSoundInit.load();
|
gDSoundInit.load();
|
||||||
@@ -384,7 +389,8 @@ bool DSoundInputDevice::isSimulate() const
|
|||||||
|
|
||||||
void DSoundInputDevice::openDevice()
|
void DSoundInputDevice::openDevice()
|
||||||
{
|
{
|
||||||
ICELogInfo(<< "Open DirectSound audio input.")::CoInitialize(NULL);
|
ICELogInfo(<< "Open DirectSound audio input.")
|
||||||
|
::CoInitialize(NULL);
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
// Ensure if GUID is not null
|
// Ensure if GUID is not null
|
||||||
if (IsEqualGUID(mGUID, GUID_NULL))
|
if (IsEqualGUID(mGUID, GUID_NULL))
|
||||||
@@ -398,10 +404,7 @@ void DSoundInputDevice::openDevice()
|
|||||||
mDump.open(L"audioinput.wav");
|
mDump.open(L"audioinput.wav");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mNextBuffer = 0;
|
mNextBuffer = 0; mDevice = NULL; IUnknown* unk = NULL; mBuffer = NULL;
|
||||||
mDevice = NULL;
|
|
||||||
IUnknown* unk = NULL;
|
|
||||||
mBuffer = NULL;
|
|
||||||
DSoundHelper::checkComResult(gDSoundInit.mRoutines.DirectSoundCaptureCreate8(&mGUID, &mDevice, NULL));
|
DSoundHelper::checkComResult(gDSoundInit.mRoutines.DirectSoundCaptureCreate8(&mGUID, &mDevice, NULL));
|
||||||
|
|
||||||
WAVEFORMATEX wfx;
|
WAVEFORMATEX wfx;
|
||||||
@@ -411,8 +414,7 @@ void DSoundInputDevice::openDevice()
|
|||||||
wfx.nChannels = AUDIO_CHANNELS;
|
wfx.nChannels = AUDIO_CHANNELS;
|
||||||
wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
|
wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
|
||||||
wfx.wBitsPerSample = 16;
|
wfx.wBitsPerSample = 16;
|
||||||
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
|
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;;
|
||||||
;
|
|
||||||
wfx.nAvgBytesPerSec = AUDIO_SAMPLERATE * 2 * AUDIO_CHANNELS;
|
wfx.nAvgBytesPerSec = AUDIO_SAMPLERATE * 2 * AUDIO_CHANNELS;
|
||||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
|
||||||
@@ -522,8 +524,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (::WaitForSingleObject(mEventArray[mNextBuffer].hEventNotify,
|
if (::WaitForSingleObject(mEventArray[mNextBuffer].hEventNotify, AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_LENGTH * 4) != WAIT_OBJECT_0)
|
||||||
AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_LENGTH * 4) != WAIT_OBJECT_0)
|
|
||||||
{
|
{
|
||||||
setSimulate( true );
|
setSimulate( true );
|
||||||
return false;
|
return false;
|
||||||
@@ -546,8 +547,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
|
|||||||
if (++mNextBuffer == AUDIO_MIC_BUFFER_COUNT)
|
if (++mNextBuffer == AUDIO_MIC_BUFFER_COUNT)
|
||||||
mNextBuffer = 0;
|
mNextBuffer = 0;
|
||||||
|
|
||||||
LPVOID ptr1 = NULL, ptr2 = NULL;
|
LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0;
|
||||||
DWORD len1 = 0, len2 = 0;
|
|
||||||
DSoundHelper::checkComResult(mBuffer->Lock(mReadOffset, AUDIO_MIC_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
|
DSoundHelper::checkComResult(mBuffer->Lock(mReadOffset, AUDIO_MIC_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
|
||||||
|
|
||||||
// Copy&Enqueue captured data to mQueue
|
// Copy&Enqueue captured data to mQueue
|
||||||
@@ -585,7 +585,8 @@ void DSoundInputDevice::setSimulate(bool s)
|
|||||||
{
|
{
|
||||||
if (!mSimulate && s)
|
if (!mSimulate && s)
|
||||||
mNullAudio.start();
|
mNullAudio.start();
|
||||||
else if (mSimulate && !s)
|
else
|
||||||
|
if (mSimulate && !s)
|
||||||
mNullAudio.stop();
|
mNullAudio.stop();
|
||||||
|
|
||||||
mSimulate = s;
|
mSimulate = s;
|
||||||
@@ -641,8 +642,10 @@ void DSoundInputDevice::threadProc(void* arg)
|
|||||||
|
|
||||||
|
|
||||||
DSoundOutputDevice::DSoundOutputDevice(GUID deviceId)
|
DSoundOutputDevice::DSoundOutputDevice(GUID deviceId)
|
||||||
: mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL), mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0),
|
:mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL),
|
||||||
mThreadHandle(0), mSimulate(false), mGUID(deviceId), mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT)
|
mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0),
|
||||||
|
mThreadHandle(0), mSimulate(false), mGUID(deviceId),
|
||||||
|
mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT)
|
||||||
{
|
{
|
||||||
gDSoundInit.load();
|
gDSoundInit.load();
|
||||||
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
@@ -748,10 +751,8 @@ void DSoundOutputDevice::openDevice()
|
|||||||
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL));
|
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL));
|
||||||
|
|
||||||
// Fill the buffer with silence
|
// Fill the buffer with silence
|
||||||
LPVOID ptr1 = NULL, ptr2 = NULL;
|
LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0;
|
||||||
DWORD len1 = 0, len2 = 0;
|
DSoundHelper::checkComResult(mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0));
|
||||||
DSoundHelper::checkComResult(
|
|
||||||
mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0));
|
|
||||||
if (len1 && ptr1)
|
if (len1 && ptr1)
|
||||||
memset(ptr1, 0, len1);
|
memset(ptr1, 0, len1);
|
||||||
if (len2 && ptr2)
|
if (len2 && ptr2)
|
||||||
@@ -804,8 +805,7 @@ bool DSoundOutputDevice::getMediaFrame()
|
|||||||
mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame);
|
mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -833,8 +833,7 @@ bool DSoundOutputDevice::process()
|
|||||||
offset %= mBufferSize;
|
offset %= mBufferSize;
|
||||||
|
|
||||||
// See what we can write
|
// See what we can write
|
||||||
LPVOID ptr1 = NULL, ptr2 = NULL;
|
LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0;
|
||||||
DWORD len1 = 0, len2 = 0;
|
|
||||||
DSoundHelper::checkComResult(mBuffer->Lock(offset, AUDIO_SPK_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
|
DSoundHelper::checkComResult(mBuffer->Lock(offset, AUDIO_SPK_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
|
||||||
|
|
||||||
assert(ptr2 == NULL);
|
assert(ptr2 == NULL);
|
||||||
@@ -947,7 +946,8 @@ HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET* ppKsPropertySet
|
|||||||
// Find DllGetClassObject
|
// Find DllGetClassObject
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
pfnDllGetClassObject = (LPFNDLLGETCLASSOBJECT)GetProcAddress(hLibDsound, "DllGetClassObject");
|
pfnDllGetClassObject =
|
||||||
|
(LPFNDLLGETCLASSOBJECT)GetProcAddress ( hLibDsound, "DllGetClassObject" );
|
||||||
|
|
||||||
|
|
||||||
if(!pfnDllGetClassObject)
|
if(!pfnDllGetClassObject)
|
||||||
@@ -1008,9 +1008,14 @@ BOOL GetInfoFromDSoundGUID(GUID i_sGUID, int& dwWaveID)
|
|||||||
|
|
||||||
// On the first call the final size is unknown so pass the size of the struct in order to receive
|
// On the first call the final size is unknown so pass the size of the struct in order to receive
|
||||||
// "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings.
|
// "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings.
|
||||||
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0,
|
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
|
||||||
&sDirectSoundDeviceDescription, sizeof(sDirectSoundDeviceDescription),
|
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
|
||||||
&ulBytesReturned);
|
NULL,
|
||||||
|
0,
|
||||||
|
&sDirectSoundDeviceDescription,
|
||||||
|
sizeof(sDirectSoundDeviceDescription),
|
||||||
|
&ulBytesReturned
|
||||||
|
);
|
||||||
|
|
||||||
if (ulBytesReturned)
|
if (ulBytesReturned)
|
||||||
{
|
{
|
||||||
@@ -1019,8 +1024,14 @@ BOOL GetInfoFromDSoundGUID(GUID i_sGUID, int& dwWaveID)
|
|||||||
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA)new BYTE[ulBytesReturned];
|
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA)new BYTE[ulBytesReturned];
|
||||||
*psDirectSoundDeviceDescription = sDirectSoundDeviceDescription;
|
*psDirectSoundDeviceDescription = sDirectSoundDeviceDescription;
|
||||||
|
|
||||||
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0,
|
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
|
||||||
psDirectSoundDeviceDescription, ulBytesReturned, &ulBytesReturned);
|
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
psDirectSoundDeviceDescription,
|
||||||
|
ulBytesReturned,
|
||||||
|
&ulBytesReturned
|
||||||
|
);
|
||||||
|
|
||||||
dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId;
|
dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId;
|
||||||
/*Description = psDirectSoundDeviceDescription->Description;
|
/*Description = psDirectSoundDeviceDescription->Description;
|
||||||
@@ -1042,10 +1053,16 @@ struct EnumResult
|
|||||||
GUID mGuid;
|
GUID mGuid;
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
|
BOOL CALLBACK DSEnumCallback(
|
||||||
|
LPGUID lpGuid,
|
||||||
|
LPCTSTR lpcstrDescription,
|
||||||
|
LPCTSTR lpcstrModule,
|
||||||
|
LPVOID lpContext
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (lpGuid)
|
if (lpGuid)
|
||||||
{
|
{
|
||||||
|
|
||||||
int devId = -1;
|
int devId = -1;
|
||||||
GetInfoFromDSoundGUID(*lpGuid, devId);
|
GetInfoFromDSoundGUID(*lpGuid, devId);
|
||||||
EnumResult* er = (EnumResult*)lpContext;
|
EnumResult* er = (EnumResult*)lpContext;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2025 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#ifndef __AUDIO_DSOUND_H
|
#ifndef __AUDIO_DSOUND_H
|
||||||
#define __AUDIO_DSOUND_H
|
#define __AUDIO_DSOUND_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -182,6 +182,6 @@ protected:
|
|||||||
|
|
||||||
static void threadProc(void* arg);
|
static void threadProc(void* arg);
|
||||||
};
|
};
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ TimeSource::TimeSource(int quantTime, int nrOfQuants)
|
|||||||
mTailTime = 0;
|
mTailTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeSource::~TimeSource()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void TimeSource::start()
|
void TimeSource::start()
|
||||||
{
|
{
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
@@ -39,7 +43,9 @@ void TimeSource::start()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeSource::stop() {}
|
void TimeSource::stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
unsigned TimeSource::time()
|
unsigned TimeSource::time()
|
||||||
{
|
{
|
||||||
@@ -96,13 +102,11 @@ unsigned TimeSource::time()
|
|||||||
#if defined(TARGET_ANDROID)
|
#if defined(TARGET_ANDROID)
|
||||||
assert(0);
|
assert(0);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- StubTimer ---
|
// --- StubTimer ---
|
||||||
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
StubTimer::StubTimer(int bufferTime, int bufferCount)
|
||||||
: mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false),
|
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false)
|
||||||
mCurrentTime(0)
|
|
||||||
{
|
{
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
TimeSource(int quantTime, int nrOfQuants);
|
TimeSource(int quantTime, int nrOfQuants);
|
||||||
~TimeSource() = default;
|
~TimeSource();
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
@@ -73,6 +73,6 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
bool mActive;
|
bool mActive;
|
||||||
};
|
};
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -24,9 +24,14 @@
|
|||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
Device::Device() : mConnection(nullptr) {}
|
Device::Device()
|
||||||
|
:mConnection(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Device::~Device() {}
|
Device::~Device()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Device::setConnection(DataConnection* connection)
|
void Device::setConnection(DataConnection* connection)
|
||||||
@@ -39,9 +44,13 @@ DataConnection* Device::connection()
|
|||||||
return mConnection;
|
return mConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDevice::InputDevice() {}
|
InputDevice::InputDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
InputDevice::~InputDevice() {}
|
InputDevice::~InputDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
InputDevice* InputDevice::make(int devId)
|
InputDevice* InputDevice::make(int devId)
|
||||||
{
|
{
|
||||||
@@ -62,9 +71,13 @@ InputDevice* InputDevice::make(int devId)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputDevice::OutputDevice() {}
|
OutputDevice::OutputDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
OutputDevice::~OutputDevice() {}
|
OutputDevice::~OutputDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
OutputDevice* OutputDevice::make(int devId)
|
OutputDevice* OutputDevice::make(int devId)
|
||||||
{
|
{
|
||||||
@@ -87,9 +100,13 @@ OutputDevice* OutputDevice::make(int devId)
|
|||||||
|
|
||||||
|
|
||||||
// --- Enumerator ---
|
// --- Enumerator ---
|
||||||
Enumerator::Enumerator() {}
|
Enumerator::Enumerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Enumerator::~Enumerator() {}
|
Enumerator::~Enumerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
int Enumerator::nameToIndex(const std::tstring& name)
|
int Enumerator::nameToIndex(const std::tstring& name)
|
||||||
{
|
{
|
||||||
@@ -102,6 +119,7 @@ int Enumerator::nameToIndex(const std::tstring& name)
|
|||||||
|
|
||||||
Enumerator* Enumerator::make(bool useNull)
|
Enumerator* Enumerator::make(bool useNull)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (useNull)
|
if (useNull)
|
||||||
return new NullEnumerator();
|
return new NullEnumerator();
|
||||||
#ifndef USE_NULL_AUDIO
|
#ifndef USE_NULL_AUDIO
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#define __AUDIO_INTERFACE_H
|
#define __AUDIO_INTERFACE_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
#include "../helper/HL_VariantMap.h"
|
#include "../helper/HL_VariantMap.h"
|
||||||
#include "../helper/HL_Pointer.h"
|
#include "../helper/HL_Pointer.h"
|
||||||
@@ -27,33 +27,36 @@ struct Format
|
|||||||
int mRate;
|
int mRate;
|
||||||
int mChannels;
|
int mChannels;
|
||||||
|
|
||||||
Format() : mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS) {}
|
Format()
|
||||||
|
:mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS)
|
||||||
|
{}
|
||||||
|
|
||||||
Format(int rate, int channels) : mRate(rate), mChannels(channels) {}
|
Format(int rate, int channels)
|
||||||
|
:mRate(rate), mChannels(channels)
|
||||||
|
{}
|
||||||
|
|
||||||
size_t samplesFromSize(size_t length) const { return length / 2 / mChannels; }
|
size_t samplesFromSize(size_t length) const
|
||||||
|
{
|
||||||
|
return length / 2 / mChannels;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns milliseconds
|
// Returns milliseconds
|
||||||
float timeFromSize(size_t length) const { return float(samplesFromSize(length) / (mRate / 1000.0)); }
|
float timeFromSize(size_t length) const
|
||||||
|
{
|
||||||
|
return float(samplesFromSize(length) / (mRate / 1000.0));
|
||||||
|
}
|
||||||
|
|
||||||
float sizeFromTime(size_t milliseconds) const { return float((milliseconds * mRate) / 500.0 * mChannels); }
|
float sizeFromTime(size_t milliseconds) const
|
||||||
|
{
|
||||||
size_t sizeFromTime(std::chrono::milliseconds ms) const { return sizeFromTime(ms.count()); }
|
return float((milliseconds * mRate) / 500.0 * mChannels);
|
||||||
|
}
|
||||||
|
|
||||||
std::string toString()
|
std::string toString()
|
||||||
{
|
{
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
std::snprintf(buffer, sizeof(buffer), "%dHz %dch", mRate, mChannels);
|
sprintf(buffer, "%dHz %dch", mRate, mChannels);
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const Format& rhs) const { return mRate == rhs.mRate && mChannels == rhs.mChannels; }
|
|
||||||
|
|
||||||
bool operator!=(const Format& rhs) const { return mRate != rhs.mRate || mChannels != rhs.mChannels; }
|
|
||||||
|
|
||||||
int rate() const { return mRate; }
|
|
||||||
|
|
||||||
int channels() const { return mChannels; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataConnection
|
class DataConnection
|
||||||
@@ -76,7 +79,6 @@ public:
|
|||||||
virtual bool open() = 0;
|
virtual bool open() = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
virtual Format getFormat() = 0;
|
virtual Format getFormat() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DataConnection* mConnection;
|
DataConnection* mConnection;
|
||||||
};
|
};
|
||||||
@@ -128,6 +130,6 @@ public:
|
|||||||
|
|
||||||
static OsEngine* instance();
|
static OsEngine* instance();
|
||||||
};
|
};
|
||||||
}; // namespace Audio
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#include "Audio_Mixer.h"
|
#include "Audio_Mixer.h"
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "Mixer"
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
Mixer::Stream::Stream()
|
Mixer::Stream::Stream()
|
||||||
@@ -28,7 +28,9 @@ Mixer::Stream::Stream()
|
|||||||
mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT);
|
mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mixer::Stream::~Stream() {}
|
Mixer::Stream::~Stream()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Mixer::Stream::setSsrc(unsigned ssrc)
|
void Mixer::Stream::setSsrc(unsigned ssrc)
|
||||||
{
|
{
|
||||||
@@ -71,9 +73,7 @@ void Mixer::Stream::addPcm(int rate, const void* input, int length)
|
|||||||
if (mTempBuffer.size() < outputSize)
|
if (mTempBuffer.size() < outputSize)
|
||||||
mTempBuffer.resize(outputSize);
|
mTempBuffer.resize(outputSize);
|
||||||
|
|
||||||
Resampler* resampler = (rate == 8000)
|
Resampler* resampler = (rate == 8000) ? &mResampler8 : ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
|
||||||
? &mResampler8
|
|
||||||
: ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
|
|
||||||
size_t inputProcessed = 0;
|
size_t inputProcessed = 0;
|
||||||
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize);
|
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize);
|
||||||
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length
|
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length
|
||||||
@@ -88,11 +88,12 @@ Mixer::Mixer()
|
|||||||
mOutput.setCapacity(32768);
|
mOutput.setCapacity(32768);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mixer::~Mixer() {}
|
Mixer::~Mixer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Mixer::unregisterChannel(void* channel)
|
void Mixer::unregisterChannel(void* channel)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
|
||||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||||
{
|
{
|
||||||
Stream& c = mChannelList[i];
|
Stream& c = mChannelList[i];
|
||||||
@@ -107,7 +108,6 @@ void Mixer::unregisterChannel(void* channel)
|
|||||||
|
|
||||||
void Mixer::clear(void* context, unsigned ssrc)
|
void Mixer::clear(void* context, unsigned ssrc)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
|
||||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||||
{
|
{
|
||||||
Stream& c = mChannelList[i];
|
Stream& c = mChannelList[i];
|
||||||
@@ -141,11 +141,12 @@ Mixer::Stream* Mixer::allocateChannel(void* context, unsigned ssrc)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mixer::addPcm(void* context, unsigned ssrc, const void* inputData, int inputLength, int inputRate, bool fadeOut)
|
void Mixer::addPcm(void* context, unsigned ssrc,
|
||||||
|
const void* inputData, int inputLength,
|
||||||
|
int inputRate, bool fadeOut)
|
||||||
{
|
{
|
||||||
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
|
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
|
||||||
|
|
||||||
Lock l(mMutex);
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// Locate a channel
|
// Locate a channel
|
||||||
@@ -171,7 +172,6 @@ void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate,
|
|||||||
{
|
{
|
||||||
assert(rate == 8000 || rate == 16000 || rate == 32000 || rate == 48000);
|
assert(rate == 8000 || rate == 16000 || rate == 32000 || rate == 48000);
|
||||||
|
|
||||||
Lock l(mMutex);
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// Locate a channel
|
// Locate a channel
|
||||||
@@ -196,8 +196,6 @@ void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate,
|
|||||||
|
|
||||||
void Mixer::mix()
|
void Mixer::mix()
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
|
||||||
|
|
||||||
// Current sample
|
// Current sample
|
||||||
int sample = 0;
|
int sample = 0;
|
||||||
|
|
||||||
@@ -237,7 +235,8 @@ void Mixer::mix()
|
|||||||
audio.data().erase(audio.data().filled());
|
audio.data().erase(audio.data().filled());
|
||||||
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
|
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
|
||||||
}
|
}
|
||||||
else if (activeCounter == 2)
|
else
|
||||||
|
if (activeCounter == 2)
|
||||||
{
|
{
|
||||||
Stream& audio1 = *channelList[0];
|
Stream& audio1 = *channelList[0];
|
||||||
Stream& audio2 = *channelList[1];
|
Stream& audio2 = *channelList[1];
|
||||||
@@ -296,7 +295,8 @@ void Mixer::mix()
|
|||||||
mOutput.add(outputBuffer, outputCounter * 2);
|
mOutput.add(outputBuffer, outputCounter * 2);
|
||||||
outputCounter = 0;
|
outputCounter = 0;
|
||||||
}
|
}
|
||||||
} while (sourceCounter);
|
}
|
||||||
|
while (sourceCounter);
|
||||||
|
|
||||||
processed = 0;
|
processed = 0;
|
||||||
for (int i=0; i<activeCounter; i++)
|
for (int i=0; i<activeCounter; i++)
|
||||||
@@ -310,8 +310,6 @@ void Mixer::mix()
|
|||||||
|
|
||||||
int Mixer::getPcm(void* outputData, int outputLength)
|
int Mixer::getPcm(void* outputData, int outputLength)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
|
||||||
|
|
||||||
if (mOutput.filled() < outputLength)
|
if (mOutput.filled() < outputLength)
|
||||||
mix();
|
mix();
|
||||||
|
|
||||||
@@ -322,29 +320,18 @@ int Mixer::getPcm(void* outputData, int outputLength)
|
|||||||
|
|
||||||
int Mixer::mixAndGetPcm(Audio::DataWindow& output)
|
int Mixer::mixAndGetPcm(Audio::DataWindow& output)
|
||||||
{
|
{
|
||||||
Lock l(mMutex);
|
|
||||||
|
|
||||||
// Mix
|
// Mix
|
||||||
mix();
|
mix();
|
||||||
|
|
||||||
size_t avail = mOutput.filled();
|
// Set output space
|
||||||
if (!avail)
|
output.setCapacity(mOutput.filled());
|
||||||
{
|
|
||||||
output.setFilled(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure output has enough space (setCapacity only ever grows the window)
|
// Read mixed data to output
|
||||||
if (output.capacity() < avail)
|
return mOutput.read(output.mutableData(), output.capacity());
|
||||||
output.setCapacity(avail);
|
|
||||||
|
|
||||||
// Read mixed data to output and publish the real byte count
|
|
||||||
size_t got = mOutput.read(output.mutableData(), avail);
|
|
||||||
output.setFilled(got);
|
|
||||||
return static_cast<int>(got);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Mixer::available()
|
int Mixer::available()
|
||||||
{
|
{
|
||||||
return mOutput.filled();
|
return mOutput.filled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#ifndef _RX_MIXER_H
|
#ifndef _RX_MIXER_H
|
||||||
#define _RX_MIXER_H
|
#define _RX_MIXER_H
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_ByteBuffer.h"
|
#include "../helper/HL_ByteBuffer.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "Audio_Resampler.h"
|
#include "Audio_Resampler.h"
|
||||||
@@ -23,7 +23,10 @@ protected:
|
|||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
DataWindow mData;
|
DataWindow mData;
|
||||||
Resampler mResampler8, mResampler16, mResampler32, mResampler48;
|
Resampler mResampler8,
|
||||||
|
mResampler16,
|
||||||
|
mResampler32,
|
||||||
|
mResampler48;
|
||||||
bool mActive;
|
bool mActive;
|
||||||
void* mContext;
|
void* mContext;
|
||||||
unsigned mSSRC;
|
unsigned mSSRC;
|
||||||
@@ -64,6 +67,6 @@ public:
|
|||||||
int mixAndGetPcm(Audio::DataWindow& output);
|
int mixAndGetPcm(Audio::DataWindow& output);
|
||||||
int available();
|
int available();
|
||||||
};
|
};
|
||||||
} // namespace Audio
|
} //end of namespace
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
#include "Audio_Null.h"
|
#include "Audio_Null.h"
|
||||||
#include "helper/HL_Log.h"
|
#include "helper/HL_Log.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <chrono>
|
#define LOG_SUBSYSTEM "NULL audio"
|
||||||
#define LOG_SUBSYSTEM "audio"
|
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
NullTimer::NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name)
|
NullTimer::NullTimer(int interval, Delegate *delegate, const char* name)
|
||||||
:mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
|
:mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
|
||||||
{
|
{
|
||||||
start();
|
start();
|
||||||
@@ -33,51 +31,49 @@ void NullTimer::stop()
|
|||||||
|
|
||||||
void NullTimer::run()
|
void NullTimer::run()
|
||||||
{
|
{
|
||||||
mTail = 0us;
|
mTail = 0;
|
||||||
while (!mShutdown)
|
while (!mShutdown)
|
||||||
{
|
{
|
||||||
// Get current timestamp
|
// Get current timestamp
|
||||||
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
|
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
|
||||||
|
|
||||||
while (mTail >= mInterval)
|
while (mTail >= mInterval * 1000)
|
||||||
{
|
{
|
||||||
if (mDelegate)
|
if (mDelegate)
|
||||||
mDelegate->onTimerSignal(*this);
|
mDelegate->onTimerSignal(*this);
|
||||||
mTail -= mInterval;
|
mTail -= mInterval * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep for mInterval - mTail milliseconds
|
// Sleep for mInterval - mTail milliseconds
|
||||||
std::this_thread::sleep_for(mInterval - mTail);
|
std::this_thread::sleep_for(std::chrono::microseconds(mInterval * 1000 - mTail));
|
||||||
|
|
||||||
mTail =
|
mTail += (int)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp).count();
|
||||||
mTail + std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------- NullInputDevice -------------------------
|
// --------------------- NullInputDevice -------------------------
|
||||||
NullInputDevice::NullInputDevice() : mBuffer(nullptr) {}
|
NullInputDevice::NullInputDevice()
|
||||||
|
:mBuffer(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
NullInputDevice::~NullInputDevice()
|
NullInputDevice::~NullInputDevice()
|
||||||
{
|
{
|
||||||
internalClose();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullInputDevice::open()
|
bool NullInputDevice::open()
|
||||||
{
|
{
|
||||||
ICELogInfo(<< "Starting NullInputDevice for " << AUDIO_MIC_BUFFER_LENGTH << "ms buffers");
|
|
||||||
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
|
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
|
||||||
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
|
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
|
||||||
mTimeCounter = 0;
|
mTimeCounter = 0; mDataCounter = 0;
|
||||||
mDataCounter = 0;
|
|
||||||
|
|
||||||
// Creation of timer starts it also. So first onTimerSignal can come even before open() returns.
|
// Creation of timer starts it also. So first onTimerSignal can come even before open() returns.
|
||||||
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_MIC_BUFFER_LENGTH), this, "null_mic");
|
mTimer = std::make_shared<NullTimer>(AUDIO_MIC_BUFFER_LENGTH, this, "NullMicrophoneThread");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::internalClose()
|
void NullInputDevice::close()
|
||||||
{
|
{
|
||||||
ICELogInfo(<< "Stopping NullInputDevice");
|
|
||||||
mTimer.reset();
|
mTimer.reset();
|
||||||
if (mBuffer)
|
if (mBuffer)
|
||||||
{
|
{
|
||||||
@@ -87,16 +83,10 @@ void NullInputDevice::internalClose()
|
|||||||
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::close()
|
|
||||||
{
|
|
||||||
internalClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Format NullInputDevice::getFormat()
|
Format NullInputDevice::getFormat()
|
||||||
{
|
{
|
||||||
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
||||||
|
return Format();
|
||||||
return {}; // Return library-define default format
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::onTimerSignal(NullTimer& timer)
|
void NullInputDevice::onTimerSignal(NullTimer& timer)
|
||||||
@@ -108,35 +98,31 @@ void NullInputDevice::onTimerSignal(NullTimer& timer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------- NullOutputDevice --------------------------
|
// --------------------- NullOutputDevice --------------------------
|
||||||
NullOutputDevice::NullOutputDevice() : mBuffer(nullptr) {}
|
NullOutputDevice::NullOutputDevice()
|
||||||
|
:mBuffer(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
NullOutputDevice::~NullOutputDevice()
|
NullOutputDevice::~NullOutputDevice()
|
||||||
{
|
{
|
||||||
internalClose();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool NullOutputDevice::open()
|
bool NullOutputDevice::open()
|
||||||
{
|
{
|
||||||
mTimeCounter = 0;
|
mTimeCounter = 0; mDataCounter = 0;
|
||||||
mDataCounter = 0;
|
|
||||||
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
|
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
|
||||||
// Creation of timer starts it also. So first onSpkData() can come before open() returns even.
|
// Creation of timer starts it also. So first onSpkData() can come before open() returns even.
|
||||||
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_SPK_BUFFER_LENGTH), this, "null_spk");
|
mTimer = std::make_shared<NullTimer>(AUDIO_SPK_BUFFER_LENGTH, this, "NullSpeakerThread");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullOutputDevice::internalClose()
|
|
||||||
{
|
|
||||||
mTimer.reset();
|
|
||||||
free(mBuffer);
|
|
||||||
mBuffer = nullptr;
|
|
||||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void NullOutputDevice::close()
|
void NullOutputDevice::close()
|
||||||
{
|
{
|
||||||
internalClose();
|
mTimer.reset();
|
||||||
|
free(mBuffer); mBuffer = nullptr;
|
||||||
|
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Format NullOutputDevice::getFormat()
|
Format NullOutputDevice::getFormat()
|
||||||
@@ -154,13 +140,17 @@ void NullOutputDevice::onTimerSignal(NullTimer& timer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------- NullEnumerator --------------------------
|
// ---------------------- NullEnumerator --------------------------
|
||||||
NullEnumerator::NullEnumerator() {}
|
NullEnumerator::NullEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
NullEnumerator::~NullEnumerator() {}
|
NullEnumerator::~NullEnumerator()
|
||||||
|
{}
|
||||||
|
|
||||||
void NullEnumerator::open(int direction) {}
|
void NullEnumerator::open(int direction)
|
||||||
|
{}
|
||||||
|
|
||||||
void NullEnumerator::close() {}
|
void NullEnumerator::close()
|
||||||
|
{}
|
||||||
|
|
||||||
int NullEnumerator::count()
|
int NullEnumerator::count()
|
||||||
{
|
{
|
||||||
@@ -185,3 +175,4 @@ int NullEnumerator::indexOfDefaultDevice()
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#define __AUDIO_NULL_H
|
#define __AUDIO_NULL_H
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
|
||||||
#include "Audio_Interface.h"
|
#include "Audio_Interface.h"
|
||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
@@ -18,19 +17,18 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::thread mWorkerThread;
|
std::thread mWorkerThread;
|
||||||
std::atomic_bool mShutdown = {false};
|
volatile bool mShutdown;
|
||||||
Delegate* mDelegate = nullptr;
|
Delegate* mDelegate;
|
||||||
std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds
|
int mInterval, // Interval - wanted number of milliseconds
|
||||||
std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink
|
mTail; // Number of milliseconds that can be sent immediately to sink
|
||||||
std::string mThreadName;
|
std::string mThreadName;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* Interval is in milliseconds. */
|
/* Interval is in milliseconds. */
|
||||||
NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name = nullptr);
|
NullTimer(int interval, Delegate* delegate, const char* name = nullptr);
|
||||||
~NullTimer();
|
~NullTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,8 +38,6 @@ protected:
|
|||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||||
void internalClose();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NullInputDevice();
|
NullInputDevice();
|
||||||
virtual ~NullInputDevice();
|
virtual ~NullInputDevice();
|
||||||
@@ -59,9 +55,6 @@ protected:
|
|||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||||
|
|
||||||
void internalClose();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NullOutputDevice();
|
NullOutputDevice();
|
||||||
virtual ~NullOutputDevice();
|
virtual ~NullOutputDevice();
|
||||||
@@ -86,8 +79,8 @@ public:
|
|||||||
std::tstring nameAt(int index) override;
|
std::tstring nameAt(int index) override;
|
||||||
int idAt(int index) override;
|
int idAt(int index) override;
|
||||||
int indexOfDefaultDevice() override;
|
int indexOfDefaultDevice() override;
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Audio
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,13 +7,18 @@
|
|||||||
|
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "Player"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
// -------------- Player -----------
|
// -------------- Player -----------
|
||||||
Player::Player() : mDelegate(nullptr), mPlayedTime(0) {}
|
Player::Player()
|
||||||
|
:mDelegate(nullptr), mPlayedTime(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Player::~Player() {}
|
Player::~Player()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Player::setDelegate(EndOfAudioDelegate* d)
|
void Player::setDelegate(EndOfAudioDelegate* d)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ namespace Audio
|
|||||||
class Player: public DataConnection
|
class Player: public DataConnection
|
||||||
{
|
{
|
||||||
friend class DevicePair;
|
friend class DevicePair;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct PlaylistItem
|
struct PlaylistItem
|
||||||
{
|
{
|
||||||
@@ -68,5 +67,5 @@ public:
|
|||||||
int releasePlayed();
|
int releasePlayed();
|
||||||
void retrieveUsageIds(std::vector<int>& ids);
|
void retrieveUsageIds(std::vector<int>& ids);
|
||||||
};
|
};
|
||||||
} // namespace Audio
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "Audio_Quality.h"
|
#include "Audio_Quality.h"
|
||||||
#include "../helper/HL_Exception.h"
|
#include "../helper/HL_Exception.h"
|
||||||
#include "../helper/HL_Types.h"
|
#include "../helper/HL_Types.h"
|
||||||
@@ -17,10 +17,7 @@
|
|||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
#ifndef SHRT_MAX
|
|
||||||
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||||
#endif
|
|
||||||
|
|
||||||
AgcFilter::AgcFilter(int channels)
|
AgcFilter::AgcFilter(int channels)
|
||||||
{
|
{
|
||||||
static const float DefaultLevel = 0.8f;
|
static const float DefaultLevel = 0.8f;
|
||||||
@@ -34,7 +31,8 @@ AgcFilter::AgcFilter(int channels)
|
|||||||
c.mIgain = 65536;
|
c.mIgain = 65536;
|
||||||
if (level > 1.0f)
|
if (level > 1.0f)
|
||||||
level = 1.0f;
|
level = 1.0f;
|
||||||
else if (level < 0.5f)
|
else
|
||||||
|
if (level < 0.5f)
|
||||||
level = 0.5f;
|
level = 0.5f;
|
||||||
|
|
||||||
c.mIpeak = (int)(SHRT_MAX * level * 65536);
|
c.mIpeak = (int)(SHRT_MAX * level * 65536);
|
||||||
@@ -44,7 +42,9 @@ AgcFilter::AgcFilter(int channels)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AgcFilter::~AgcFilter() {}
|
AgcFilter::~AgcFilter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void AgcFilter::process(void *pcm, int length)
|
void AgcFilter::process(void *pcm, int length)
|
||||||
{
|
{
|
||||||
@@ -136,12 +136,12 @@ static void CheckWRACode(unsigned errorcode)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AecFilter::AecFilter(int tailTime, int frameTime, int rate) : mCtx(nullptr), mFrameTime(frameTime), mRate(rate)
|
AecFilter::AecFilter(int tailTime, int frameTime, int rate)
|
||||||
|
:mCtx(nullptr), mFrameTime(frameTime), mRate(rate)
|
||||||
{
|
{
|
||||||
#ifdef USE_SPEEX_AEC
|
#ifdef USE_SPEEX_AEC
|
||||||
if (AUDIO_CHANNELS == 2)
|
if (AUDIO_CHANNELS == 2)
|
||||||
mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS,
|
mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS, AUDIO_CHANNELS );
|
||||||
AUDIO_CHANNELS);
|
|
||||||
else
|
else
|
||||||
mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000));
|
mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000));
|
||||||
int tmp = rate;
|
int tmp = rate;
|
||||||
@@ -181,8 +181,7 @@ void AecFilter::fromMic(void* data)
|
|||||||
#ifdef USE_WEBRTC_AEC
|
#ifdef USE_WEBRTC_AEC
|
||||||
short* inputframe = (short*)ALLOCA(framesize);
|
short* inputframe = (short*)ALLOCA(framesize);
|
||||||
memcpy(inputframe, (char*)data+framesize*i, framesize);
|
memcpy(inputframe, (char*)data+framesize*i, framesize);
|
||||||
CheckWRACode(WebRtcAec_Process(mCtx, (short*)inputframe, NULL, (short*)data + framesize / 2 * i, NULL,
|
CheckWRACode(WebRtcAec_Process(mCtx, (short*)inputframe, NULL, (short*)data+framesize/2*i, NULL, mFrameTime * mRate / 1000, 0,0));
|
||||||
mFrameTime * mRate / 1000, 0, 0));
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +202,8 @@ int AecFilter::frametime()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DenoiseFilter::DenoiseFilter(int rate) : mRate(rate)
|
DenoiseFilter::DenoiseFilter(int rate)
|
||||||
|
:mRate(rate)
|
||||||
{
|
{
|
||||||
mCtx = speex_preprocess_state_init(mRate/100, mRate);
|
mCtx = speex_preprocess_state_init(mRate/100, mRate);
|
||||||
}
|
}
|
||||||
@@ -232,3 +232,4 @@ int DenoiseFilter::rate()
|
|||||||
{
|
{
|
||||||
return mRate;
|
return mRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#ifndef __AUDIO_QUALITY_H
|
#ifndef __AUDIO_QUALITY_H
|
||||||
#define __AUDIO_QUALITY_H
|
#define __AUDIO_QUALITY_H
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -24,7 +24,6 @@ protected:
|
|||||||
};
|
};
|
||||||
std::vector<Channel> mChannelList;
|
std::vector<Channel> mChannelList;
|
||||||
void processChannel(short* pcm, int nrOfSamples, int channelIndex);
|
void processChannel(short* pcm, int nrOfSamples, int channelIndex);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AgcFilter(int channels);
|
AgcFilter(int channels);
|
||||||
~AgcFilter();
|
~AgcFilter();
|
||||||
@@ -65,6 +64,6 @@ protected:
|
|||||||
int mRate; /// Duration of single audio frame (in milliseconds)
|
int mRate; /// Duration of single audio frame (in milliseconds)
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "Audio_Resampler.h"
|
#include "Audio_Resampler.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -17,7 +17,10 @@ namespace Audio
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
SpeexResampler::SpeexResampler() {}
|
SpeexResampler::SpeexResampler()
|
||||||
|
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void SpeexResampler::start(int channels, int sourceRate, int destRate)
|
void SpeexResampler::start(int channels, int sourceRate, int destRate)
|
||||||
{
|
{
|
||||||
@@ -48,35 +51,28 @@ void SpeexResampler::stop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpeexResampler::isOpened() const
|
|
||||||
{
|
|
||||||
return mContext != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeexResampler::~SpeexResampler()
|
SpeexResampler::~SpeexResampler()
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed, void* dest,
|
size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed,
|
||||||
size_t destCapacity)
|
void* dest, size_t destCapacity)
|
||||||
{
|
{
|
||||||
assert(mSourceRate != 0 && mDestRate != 0);
|
assert(mSourceRate != 0 && mDestRate != 0);
|
||||||
|
|
||||||
if (mDestRate == mSourceRate)
|
if (mDestRate == mSourceRate)
|
||||||
{
|
{
|
||||||
// Pass-through, but never write past the caller's buffer: clamp to its
|
|
||||||
// capacity instead of trusting sourceLength (which is caller/file driven).
|
|
||||||
assert(destCapacity >= sourceLength);
|
assert(destCapacity >= sourceLength);
|
||||||
size_t copied = std::min(sourceLength, destCapacity);
|
memcpy(dest, src, sourceLength);
|
||||||
memcpy(dest, src, copied);
|
sourceProcessed = sourceLength;
|
||||||
sourceProcessed = copied;
|
return sourceLength;
|
||||||
return copied;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mContext)
|
if (!mContext)
|
||||||
{
|
{
|
||||||
mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
|
mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate,
|
||||||
|
AUDIO_RESAMPLER_QUALITY, &mErrorCode);
|
||||||
if (!mContext)
|
if (!mContext)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -101,9 +97,11 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
|
|||||||
unsigned inLen = sourceLength / (sizeof(short) * mChannels);
|
unsigned inLen = sourceLength / (sizeof(short) * mChannels);
|
||||||
outLen /= sizeof(short) * mChannels;
|
outLen /= sizeof(short) * mChannels;
|
||||||
assert(mContext != NULL);
|
assert(mContext != NULL);
|
||||||
spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen), out_len = static_cast<spx_uint32_t>(outLen);
|
spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen),
|
||||||
|
out_len = static_cast<spx_uint32_t>(outLen);
|
||||||
|
|
||||||
int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState*)mContext, (spx_int16_t*)src, &in_len,
|
int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState *)mContext,
|
||||||
|
(spx_int16_t*)src, &in_len,
|
||||||
(spx_int16_t*)dest, &out_len);
|
(spx_int16_t*)dest, &out_len);
|
||||||
assert(speexCode == RESAMPLER_ERR_SUCCESS);
|
assert(speexCode == RESAMPLER_ERR_SUCCESS);
|
||||||
|
|
||||||
@@ -115,25 +113,24 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
|
|||||||
return outLen * sizeof(short) * mChannels;
|
return outLen * sizeof(short) * mChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpeexResampler::sourceRate() const
|
int SpeexResampler::sourceRate()
|
||||||
{
|
{
|
||||||
return mSourceRate;
|
return mSourceRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpeexResampler::destRate() const
|
int SpeexResampler::destRate()
|
||||||
{
|
{
|
||||||
return mDestRate;
|
return mDestRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SpeexResampler::getDestLength(size_t sourceLen) const
|
size_t SpeexResampler::getDestLength(size_t sourceLen)
|
||||||
{
|
{
|
||||||
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f);
|
return size_t(sourceLen * (float(mDestRate) / mSourceRate) + 0.5f) / 2 * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SpeexResampler::getSourceLength(size_t destLen) const
|
size_t SpeexResampler::getSourceLength(size_t destLen)
|
||||||
{
|
{
|
||||||
// Here we want to get 'destLen' number of samples
|
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f) / 2 * 2;
|
||||||
return size_t(destLen * (float(mSourceRate) / mDestRate) + 0.5f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns instance + speex resampler size in bytes
|
// Returns instance + speex resampler size in bytes
|
||||||
@@ -182,8 +179,7 @@ Resampler48kTo16k::~Resampler48kTo16k()
|
|||||||
|
|
||||||
int Resampler48kTo16k::process(const void *source, int sourceLen, void *dest, int destLen)
|
int Resampler48kTo16k::process(const void *source, int sourceLen, void *dest, int destLen)
|
||||||
{
|
{
|
||||||
const short* input = (const short*)source;
|
const short* input = (const short*)source; int inputLen = sourceLen / 2;
|
||||||
int inputLen = sourceLen / 2;
|
|
||||||
short* output = (short*)dest; //int outputCapacity = destLen / 2;
|
short* output = (short*)dest; //int outputCapacity = destLen / 2;
|
||||||
assert(inputLen % 480 == 0);
|
assert(inputLen % 480 == 0);
|
||||||
int frames = inputLen / 480;
|
int frames = inputLen / 480;
|
||||||
@@ -206,8 +202,7 @@ Resampler16kto48k::~Resampler16kto48k()
|
|||||||
|
|
||||||
int Resampler16kto48k::process(const void *source, int sourceLen, void *dest, int destLen)
|
int Resampler16kto48k::process(const void *source, int sourceLen, void *dest, int destLen)
|
||||||
{
|
{
|
||||||
const WebRtc_Word16* input = (const WebRtc_Word16*)source;
|
const WebRtc_Word16* input = (const WebRtc_Word16*)source; int inputLen = sourceLen / 2;
|
||||||
int inputLen = sourceLen / 2;
|
|
||||||
WebRtc_Word16* output = (WebRtc_Word16*)dest; //int outputCapacity = destLen / 2;
|
WebRtc_Word16* output = (WebRtc_Word16*)dest; //int outputCapacity = destLen / 2;
|
||||||
assert(inputLen % 160 == 0);
|
assert(inputLen % 160 == 0);
|
||||||
int frames = inputLen / 160;
|
int frames = inputLen / 160;
|
||||||
@@ -220,9 +215,15 @@ int Resampler16kto48k::process(const void* source, int sourceLen, void* dest, in
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ---------------- UniversalResampler -------------------
|
// ---------------- UniversalResampler -------------------
|
||||||
UniversalResampler::UniversalResampler() {}
|
UniversalResampler::UniversalResampler()
|
||||||
|
{
|
||||||
|
|
||||||
UniversalResampler::~UniversalResampler() {}
|
}
|
||||||
|
|
||||||
|
UniversalResampler::~UniversalResampler()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
size_t UniversalResampler::resample(int sourceRate, const void *sourceBuffer, size_t sourceLength,
|
size_t UniversalResampler::resample(int sourceRate, const void *sourceBuffer, size_t sourceLength,
|
||||||
size_t& sourceProcessed, int destRate, void *destBuffer, size_t destCapacity)
|
size_t& sourceProcessed, int destRate, void *destBuffer, size_t destCapacity)
|
||||||
@@ -244,7 +245,10 @@ size_t UniversalResampler::resample(int sourceRate, const void* sourceBuffer, si
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniversalResampler::preload() {}
|
void UniversalResampler::preload()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
size_t UniversalResampler::getDestLength(int sourceRate, int destRate, size_t sourceLength)
|
size_t UniversalResampler::getDestLength(int sourceRate, int destRate, size_t sourceLength)
|
||||||
{
|
{
|
||||||
@@ -269,7 +273,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
|
|||||||
PResampler r;
|
PResampler r;
|
||||||
if (resamplerIter == mResamplerMap.end())
|
if (resamplerIter == mResamplerMap.end())
|
||||||
{
|
{
|
||||||
r = std::make_shared<Resampler>();
|
r = PResampler(new Resampler());
|
||||||
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
r->start(AUDIO_CHANNELS, sourceRate, destRate);
|
||||||
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
mResamplerMap[RatePair(sourceRate, destRate)] = r;
|
||||||
}
|
}
|
||||||
@@ -278,4 +282,4 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Audio
|
} // end of namespace
|
||||||
|
|||||||
@@ -24,23 +24,23 @@ public:
|
|||||||
|
|
||||||
void start(int channels, int sourceRate, int destRate);
|
void start(int channels, int sourceRate, int destRate);
|
||||||
void stop();
|
void stop();
|
||||||
bool isOpened() const;
|
size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed,
|
||||||
|
void* dest, size_t destCapacity);
|
||||||
size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed, void* dest,
|
int sourceRate();
|
||||||
size_t destCapacity);
|
int destRate();
|
||||||
int sourceRate() const;
|
size_t getDestLength(size_t sourceLen);
|
||||||
int destRate() const;
|
size_t getSourceLength(size_t destLen);
|
||||||
size_t getDestLength(size_t sourceLen) const;
|
|
||||||
size_t getSourceLength(size_t destLen) const;
|
|
||||||
|
|
||||||
// Returns instance + speex encoder size in bytes
|
// Returns instance + speex encoder size in bytes
|
||||||
size_t getSize() const;
|
size_t getSize() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* mContext = nullptr;
|
void* mContext;
|
||||||
int mErrorCode = 0;
|
int mErrorCode;
|
||||||
int mSourceRate = 0, mDestRate = 0, mChannels = 0;
|
int mSourceRate,
|
||||||
short mLastSample = 0;
|
mDestRate,
|
||||||
|
mChannels;
|
||||||
|
short mLastSample;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef SpeexResampler Resampler;
|
typedef SpeexResampler Resampler;
|
||||||
@@ -82,7 +82,6 @@ public:
|
|||||||
Resampler48kTo16k();
|
Resampler48kTo16k();
|
||||||
~Resampler48kTo16k();
|
~Resampler48kTo16k();
|
||||||
int process(const void* source, int sourceLen, void* dest, int destLen);
|
int process(const void* source, int sourceLen, void* dest, int destLen);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WebRtc_Word32 mTemp[496];
|
WebRtc_Word32 mTemp[496];
|
||||||
WebRtcSpl_State48khzTo16khz mContext;
|
WebRtcSpl_State48khzTo16khz mContext;
|
||||||
@@ -100,6 +99,6 @@ protected:
|
|||||||
WebRtcSpl_State16khzTo48khz mContext;
|
WebRtcSpl_State16khzTo48khz mContext;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
} // namespace Audio
|
} // end of namespace
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+150
-188
@@ -7,10 +7,9 @@
|
|||||||
#include "helper/HL_Exception.h"
|
#include "helper/HL_Exception.h"
|
||||||
#include "helper/HL_String.h"
|
#include "helper/HL_String.h"
|
||||||
#include "helper/HL_Log.h"
|
#include "helper/HL_Log.h"
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifndef WORD
|
#ifndef WORD
|
||||||
# define WORD unsigned short
|
# define WORD unsigned short
|
||||||
@@ -19,8 +18,7 @@
|
|||||||
# define DWORD unsigned int
|
# define DWORD unsigned int
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
WORD wFormatTag;
|
WORD wFormatTag;
|
||||||
WORD nChannels;
|
WORD nChannels;
|
||||||
DWORD nSamplesPerSec;
|
DWORD nSamplesPerSec;
|
||||||
@@ -28,67 +26,64 @@ typedef struct
|
|||||||
WORD nBlockAlign;
|
WORD nBlockAlign;
|
||||||
WORD wBitsPerSample;
|
WORD wBitsPerSample;
|
||||||
WORD cbSize;
|
WORD cbSize;
|
||||||
} WaveFormatEx;
|
}
|
||||||
|
WaveFormatEx;
|
||||||
|
|
||||||
#define WAVE_FORMAT_PCM 1
|
#define WAVE_FORMAT_PCM 1
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "WavFileReader"
|
||||||
|
|
||||||
#define LOCK std::unique_lock<std::recursive_mutex> lock(mFileMtx);
|
#define LOCK std::unique_lock<std::recursive_mutex> lock(mFileMtx);
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
|
||||||
// ---------------------- WavFileReader -------------------------
|
// ---------------------- WavFileReader -------------------------
|
||||||
WavFileReader::WavFileReader() : mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
|
WavFileReader::WavFileReader()
|
||||||
|
:mHandle(nullptr), mRate(0), mLastError(0)
|
||||||
{
|
{
|
||||||
mDataOffset = 0;
|
mDataOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WavFileReader::~WavFileReader() {}
|
WavFileReader::~WavFileReader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED);
|
#define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED);
|
||||||
|
|
||||||
std::string WavFileReader::readChunk()
|
std::string WavFileReader::readChunk()
|
||||||
{
|
{
|
||||||
char name[5] = {0};
|
char name[5];
|
||||||
readBuffer(name, 4);
|
if (fread(name, 1, 4, mHandle) != 4)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
|
name[4] = 0;
|
||||||
std::string result = name;
|
std::string result = name;
|
||||||
uint32_t size = 0;
|
unsigned size;
|
||||||
readBuffer(&size, 4);
|
if (fread(&size, 4, 1, mHandle) != 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
if (result == "data")
|
if (result == "fact")
|
||||||
mDataLength = size;
|
fread(&mDataLength, 4, 1, mHandle);
|
||||||
else
|
else
|
||||||
// Skip the chunk body; RIFF chunks are word-aligned, so odd sizes carry a pad byte
|
if (result != "data")
|
||||||
mInput->seekg(std::streamoff(size + (size & 1)), std::ios_base::cur);
|
fseek(mHandle, size, SEEK_CUR);
|
||||||
|
else
|
||||||
|
mDataLength = size;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WavFileReader::readBuffer(void* buffer, size_t sz)
|
bool WavFileReader::open(const std::tstring& filename)
|
||||||
{
|
|
||||||
auto p = mInput->tellg();
|
|
||||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
|
||||||
if (mInput->tellg() - p != sz)
|
|
||||||
throw Exception(ERR_WAVFILE_FAILED);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WavFileReader::tryReadBuffer(void* buffer, size_t sz)
|
|
||||||
{
|
|
||||||
auto p = mInput->tellg();
|
|
||||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
|
||||||
return mInput->tellg() - p;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WavFileReader::open(const std::filesystem::path& p)
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mPath = p;
|
#ifdef WIN32
|
||||||
mInput = std::make_unique<std::ifstream>(p, std::ios::binary | std::ios::in);
|
mHandle = _wfopen(filename.c_str(), L"rb");
|
||||||
if (!mInput->is_open())
|
#else
|
||||||
|
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "rb");
|
||||||
|
#endif
|
||||||
|
if (NULL == mHandle)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
mLastError = errno;
|
mLastError = errno;
|
||||||
@@ -102,72 +97,76 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
|||||||
|
|
||||||
// Read the .WAV header
|
// Read the .WAV header
|
||||||
char riff[4];
|
char riff[4];
|
||||||
readBuffer(riff, sizeof riff);
|
if (fread(riff, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
|
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
// Read the file size
|
// Read the file size
|
||||||
uint32_t filesize = 0;
|
unsigned int filesize = 0;
|
||||||
readBuffer(&filesize, sizeof(filesize));
|
if (fread(&filesize, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
char wavefmt[9] = {0};
|
char wavefmt[9];
|
||||||
readBuffer(wavefmt, 8);
|
if (fread(wavefmt, 8, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
|
wavefmt[8] = 0;
|
||||||
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
if (strcmp(wavefmt, "WAVEfmt ") != 0)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
uint32_t fmtSize = 0;
|
unsigned fmtSize = 0;
|
||||||
readBuffer(&fmtSize, sizeof(fmtSize));
|
if (fread(&fmtSize, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
auto fmtStart = mInput->tellg();
|
unsigned fmtStart = ftell(mHandle);
|
||||||
|
|
||||||
uint16_t formattag = 0;
|
unsigned short formattag = 0;
|
||||||
readBuffer(&formattag, sizeof(formattag));
|
if (fread(&formattag, 2, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
mChannels = 0;
|
mChannels = 0;
|
||||||
readBuffer(&mChannels, sizeof(mChannels));
|
if (fread(&mChannels, 2, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
mSamplerate = 0;
|
mRate = 0;
|
||||||
readBuffer(&mSamplerate, sizeof(mSamplerate));
|
if (fread(&mRate, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
uint32_t avgbytespersec = 0;
|
unsigned int avgbytespersec = 0;
|
||||||
readBuffer(&avgbytespersec, sizeof(avgbytespersec));
|
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
uint16_t blockalign = 0;
|
unsigned short blockalign = 0;
|
||||||
readBuffer(&blockalign, sizeof(blockalign));
|
if (fread(&blockalign, 2, 1, mHandle) < 1)
|
||||||
|
THROW_READERROR;
|
||||||
|
|
||||||
mBits = 0;
|
mBits = 0;
|
||||||
readBuffer(&mBits, sizeof(mBits));
|
if (fread(&mBits, 2, 1, mHandle) < 1)
|
||||||
|
|
||||||
// Only 16-bit PCM is supported: the read path feeds the data
|
|
||||||
// directly into a 16-bit resampler.
|
|
||||||
if (mBits != 16)
|
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
// The read path is mono-only: it divides byte counts by AUDIO_CHANNELS
|
if (mBits !=8 && mBits != 16)
|
||||||
// and starts the resampler with AUDIO_CHANNELS. Reject anything else -
|
|
||||||
// a multi-channel file would make read() copy more source bytes than the
|
|
||||||
// caller's (mono-sized) output buffer can hold. Also reject a zero rate,
|
|
||||||
// which would divide by zero in readRaw() / poison the resampler ratio.
|
|
||||||
if (mChannels != AUDIO_CHANNELS || mSamplerate == 0)
|
|
||||||
THROW_READERROR;
|
THROW_READERROR;
|
||||||
|
|
||||||
// Look for the chunk 'data'
|
// Read the "chunk"
|
||||||
mInput->seekg(fmtStart + std::streampos(fmtSize));
|
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
|
||||||
|
//unsigned pos = ftell(mHandle);
|
||||||
mDataLength = 0;
|
mDataLength = 0;
|
||||||
while (readChunk() != "data")
|
while (readChunk() != "data")
|
||||||
;
|
;
|
||||||
|
|
||||||
mDataOffset = mInput->tellg();
|
mFileName = filename;
|
||||||
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
|
mDataOffset = ftell(mHandle);
|
||||||
|
|
||||||
|
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
mInput.reset();
|
fclose(mHandle); mHandle = nullptr;
|
||||||
mLastError = static_cast<unsigned>(-1);
|
mLastError = static_cast<unsigned>(-1);
|
||||||
}
|
}
|
||||||
return isOpened();
|
return isOpened();
|
||||||
@@ -176,118 +175,78 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
|||||||
void WavFileReader::close()
|
void WavFileReader::close()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
mInput.reset();
|
|
||||||
|
if (nullptr != mHandle)
|
||||||
|
fclose(mHandle);
|
||||||
|
mHandle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WavFileReader::samplerate() const
|
int WavFileReader::rate() const
|
||||||
{
|
{
|
||||||
return mSamplerate;
|
return mRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WavFileReader::channels() const
|
unsigned WavFileReader::read(void* buffer, unsigned bytes)
|
||||||
{
|
|
||||||
return mChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WavFileReader::read(void* buffer, size_t bytes)
|
|
||||||
{
|
{
|
||||||
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
|
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WavFileReader::readRaw(void* buffer, size_t bytes)
|
unsigned WavFileReader::read(short* buffer, unsigned samples)
|
||||||
{
|
|
||||||
return readRaw((short*)buffer, bytes / channels() / sizeof(short)) * channels() * sizeof(short);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WavFileReader::read(short* buffer, size_t samples)
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
if (!mInput)
|
if (!mHandle)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Get number of samples that must be read from source file
|
// Get number of samples that must be read from source file
|
||||||
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
||||||
bool useHeap = requiredBytes > sizeof mTempBuffer;
|
void* temp = alloca(requiredBytes);
|
||||||
void* temp;
|
|
||||||
if (useHeap)
|
|
||||||
temp = malloc(requiredBytes);
|
|
||||||
else
|
|
||||||
temp = mTempBuffer;
|
|
||||||
|
|
||||||
memset(temp, 0, requiredBytes);
|
memset(temp, 0, requiredBytes);
|
||||||
|
|
||||||
// Find required size of input buffer
|
// Find required size of input buffer
|
||||||
if (mDataLength)
|
if (mDataLength)
|
||||||
{
|
{
|
||||||
auto filePosition = mInput->tellg();
|
unsigned filePosition = ftell(mHandle);
|
||||||
|
|
||||||
// Check how much data we can read
|
// Check how much data we can read
|
||||||
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset;
|
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
|
||||||
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
|
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
|
||||||
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t readBytes = tryReadBuffer(temp, requiredBytes);
|
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
|
||||||
|
|
||||||
size_t processedBytes = 0;
|
size_t processedBytes = 0;
|
||||||
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes, buffer, samples * 2 * AUDIO_CHANNELS);
|
size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
|
||||||
|
buffer, samples * 2 * AUDIO_CHANNELS);
|
||||||
|
|
||||||
if (useHeap)
|
|
||||||
free(temp);
|
|
||||||
return result / 2 / AUDIO_CHANNELS;
|
return result / 2 / AUDIO_CHANNELS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t WavFileReader::readRaw(short* buffer, size_t samples)
|
|
||||||
{
|
|
||||||
LOCK;
|
|
||||||
|
|
||||||
if (!mInput)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Get number of samples that must be read from source file
|
|
||||||
size_t requiredBytes = samples * channels() * sizeof(short);
|
|
||||||
|
|
||||||
// Find required size of input buffer
|
|
||||||
if (mDataLength)
|
|
||||||
{
|
|
||||||
auto filePosition = mInput->tellg();
|
|
||||||
|
|
||||||
// Check how much data we can read
|
|
||||||
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset;
|
|
||||||
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
|
|
||||||
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t readBytes = tryReadBuffer(buffer, requiredBytes);
|
|
||||||
return readBytes / channels() / sizeof(short);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WavFileReader::isOpened()
|
bool WavFileReader::isOpened()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
if (!mInput)
|
|
||||||
return false;
|
return (mHandle != 0);
|
||||||
return mInput->is_open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WavFileReader::rewind()
|
void WavFileReader::rewind()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
if (mInput)
|
|
||||||
mInput->seekg(mDataOffset);
|
if (mHandle)
|
||||||
|
fseek(mHandle, mDataOffset, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path WavFileReader::path() const
|
std::tstring WavFileReader::filename() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mPath;
|
|
||||||
|
return mFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WavFileReader::size() const
|
unsigned WavFileReader::size() const
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
return mDataLength;
|
return mDataLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +259,10 @@ unsigned WavFileReader::lastError() const
|
|||||||
|
|
||||||
#define BITS_PER_CHANNEL 16
|
#define BITS_PER_CHANNEL 16
|
||||||
|
|
||||||
WavFileWriter::WavFileWriter() : mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0) {}
|
WavFileWriter::WavFileWriter()
|
||||||
|
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
WavFileWriter::~WavFileWriter()
|
WavFileWriter::~WavFileWriter()
|
||||||
{
|
{
|
||||||
@@ -313,76 +275,68 @@ void WavFileWriter::checkWriteResult(int result)
|
|||||||
throw Exception(ERR_WAVFILE_FAILED, errno);
|
throw Exception(ERR_WAVFILE_FAILED, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WavFileWriter::writeBuffer(const void* buffer, size_t sz)
|
bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
|
||||||
{
|
|
||||||
if (!mOutput)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto p = mOutput->tellp();
|
|
||||||
mOutput->write(reinterpret_cast<const char*>(buffer), sz);
|
|
||||||
if (mOutput->tellp() - p != sz)
|
|
||||||
throw Exception(ERR_WAVFILE_FAILED);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int channels)
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
close();
|
close();
|
||||||
mSamplerate = samplerate;
|
|
||||||
|
mRate = rate;
|
||||||
mChannels = channels;
|
mChannels = channels;
|
||||||
|
|
||||||
mOutput = std::make_unique<std::ofstream>(p, std::ios::binary | std::ios::trunc);
|
#ifdef WIN32
|
||||||
if (!mOutput->is_open())
|
mHandle = _wfopen(filename.c_str(), L"wb");
|
||||||
|
#else
|
||||||
|
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "wb");
|
||||||
|
#endif
|
||||||
|
if (nullptr == mHandle)
|
||||||
{
|
{
|
||||||
int errorcode = errno;
|
ICELogError(<< "Failed to create .wav file: filename = " << StringHelper::makeUtf8(filename) << " , error = " << errno);
|
||||||
ICELogError(<< "Failed to create .wav file: filename = " << p << " , error = " << errorcode);
|
|
||||||
mOutput.reset();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the .WAV header
|
// Write the .WAV header
|
||||||
const char* riff = "RIFF";
|
const char* riff = "RIFF";
|
||||||
writeBuffer(riff, 4);
|
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
|
||||||
|
|
||||||
// Write the file size
|
// Write the file size
|
||||||
uint32_t filesize = 0;
|
unsigned int filesize = 0;
|
||||||
writeBuffer(&filesize, sizeof filesize);
|
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
|
||||||
|
|
||||||
const char* wavefmt = "WAVEfmt ";
|
const char* wavefmt = "WAVEfmt ";
|
||||||
writeBuffer(wavefmt, 8);
|
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
|
||||||
|
|
||||||
// Set the format description
|
// Set the format description
|
||||||
uint32_t dwFmtSize = 16; /*= 16L*/
|
DWORD dwFmtSize = 16; /*= 16L*/;
|
||||||
;
|
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
|
||||||
writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
|
|
||||||
|
|
||||||
WaveFormatEx format;
|
WaveFormatEx format;
|
||||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
writeBuffer(&format.wFormatTag, sizeof(format.wFormatTag));
|
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
|
||||||
|
|
||||||
format.nChannels = mChannels;
|
format.nChannels = mChannels;
|
||||||
writeBuffer(&format.nChannels, sizeof(format.nChannels));
|
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
|
||||||
|
|
||||||
format.nSamplesPerSec = mSamplerate;
|
format.nSamplesPerSec = mRate;
|
||||||
writeBuffer(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec));
|
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
|
||||||
|
|
||||||
format.nAvgBytesPerSec = mSamplerate * 2 * mChannels;
|
format.nAvgBytesPerSec = mRate * 2 * mChannels;
|
||||||
writeBuffer(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec));
|
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
|
||||||
|
|
||||||
format.nBlockAlign = 2 * mChannels;
|
format.nBlockAlign = 2 * mChannels;
|
||||||
writeBuffer(&format.nBlockAlign, sizeof(format.nBlockAlign));
|
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
|
||||||
|
|
||||||
format.wBitsPerSample = BITS_PER_CHANNEL;
|
format.wBitsPerSample = BITS_PER_CHANNEL;
|
||||||
writeBuffer(&format.wBitsPerSample, sizeof(format.wBitsPerSample));
|
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
|
||||||
|
|
||||||
const char* data = "data";
|
const char* data = "data";
|
||||||
writeBuffer(data, 4);
|
checkWriteResult( fwrite(data, 4, 1, mHandle));
|
||||||
|
|
||||||
mPath = p;
|
mFileName = filename;
|
||||||
mWritten = 0;
|
mWritten = 0;
|
||||||
|
|
||||||
mLengthOffset = mOutput->tellp();
|
mLengthOffset = ftell(mHandle);
|
||||||
writeBuffer(&mWritten, sizeof mWritten);
|
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||||
|
|
||||||
return isOpened();
|
return isOpened();
|
||||||
}
|
}
|
||||||
@@ -390,43 +344,51 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
|
|||||||
void WavFileWriter::close()
|
void WavFileWriter::close()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
mOutput.reset();
|
|
||||||
|
if (mHandle)
|
||||||
|
{
|
||||||
|
fclose(mHandle);
|
||||||
|
mHandle = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
size_t WavFileWriter::write(const void* buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
if (!mOutput)
|
if (!mHandle)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Seek the end of file - here new data will be written
|
// Seek the end of file
|
||||||
mOutput->seekp(0, std::ios_base::end);
|
fseek(mHandle, 0, SEEK_END);
|
||||||
mWritten += bytes;
|
mWritten += bytes;
|
||||||
|
|
||||||
// Write the data
|
// Write the data
|
||||||
writeBuffer(buffer, bytes);
|
fwrite(buffer, bytes, 1, mHandle);
|
||||||
|
|
||||||
// Write file length
|
// Write file length
|
||||||
mOutput->seekp(4, std::ios_base::beg);
|
fseek(mHandle, 4, SEEK_SET);
|
||||||
uint32_t fl = mWritten + 36;
|
int32_t fl = mWritten + 36;
|
||||||
writeBuffer(&fl, sizeof(fl));
|
fwrite(&fl, sizeof(fl), 1, mHandle);
|
||||||
|
|
||||||
// Write data length
|
// Write data length
|
||||||
mOutput->seekp(mLengthOffset, std::ios_base::beg);
|
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
|
||||||
writeBuffer(&mWritten, sizeof(mWritten));
|
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WavFileWriter::isOpened() const
|
bool WavFileWriter::isOpened()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mOutput && mOutput->is_open();
|
|
||||||
|
return (mHandle != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path WavFileWriter::path() const
|
std::tstring WavFileWriter::filename()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mPath;
|
|
||||||
|
return mFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
@@ -22,43 +20,35 @@ namespace Audio
|
|||||||
class WavFileReader
|
class WavFileReader
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
uint16_t mChannels = 0;
|
FILE* mHandle;
|
||||||
uint16_t mBits = 0;
|
short mChannels;
|
||||||
int mSamplerate = 0;
|
short mBits;
|
||||||
std::filesystem::path mPath;
|
int mRate;
|
||||||
|
std::tstring mFileName;
|
||||||
mutable std::recursive_mutex mFileMtx;
|
mutable std::recursive_mutex mFileMtx;
|
||||||
size_t mDataOffset = 0;
|
unsigned mDataOffset;
|
||||||
size_t mDataLength = 0;
|
unsigned mDataLength;
|
||||||
Resampler mResampler;
|
Resampler mResampler;
|
||||||
unsigned mLastError = 0;
|
unsigned mLastError;
|
||||||
std::unique_ptr<std::ifstream> mInput;
|
|
||||||
uint8_t mTempBuffer[16384];
|
|
||||||
|
|
||||||
std::string readChunk();
|
std::string readChunk();
|
||||||
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
|
|
||||||
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WavFileReader();
|
WavFileReader();
|
||||||
~WavFileReader();
|
~WavFileReader();
|
||||||
|
|
||||||
bool open(const std::filesystem::path& p);
|
bool open(const std::tstring& filename);
|
||||||
void close();
|
void close();
|
||||||
bool isOpened();
|
bool isOpened();
|
||||||
void rewind();
|
void rewind();
|
||||||
int samplerate() const;
|
int rate() const;
|
||||||
int channels() const;
|
|
||||||
|
|
||||||
// This method returns number of read bytes
|
// This method returns number of read bytes
|
||||||
size_t read(void* buffer, size_t bytes);
|
unsigned read(void* buffer, unsigned bytes);
|
||||||
size_t readRaw(void* buffer, size_t bytes);
|
|
||||||
|
|
||||||
// This method returns number of read samples
|
// This method returns number of read samples
|
||||||
size_t read(short* buffer, size_t samples);
|
unsigned read(short* buffer, unsigned samples);
|
||||||
size_t readRaw(short* buffer, size_t samples);
|
std::tstring filename() const;
|
||||||
|
unsigned size() const;
|
||||||
std::filesystem::path path() const;
|
|
||||||
size_t size() const;
|
|
||||||
|
|
||||||
unsigned lastError() const;
|
unsigned lastError() const;
|
||||||
};
|
};
|
||||||
@@ -68,29 +58,29 @@ typedef std::shared_ptr<WavFileReader> PWavFileReader;
|
|||||||
class WavFileWriter
|
class WavFileWriter
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
|
FILE* mHandle; /// Handle of audio file.
|
||||||
std::filesystem::path mPath; /// Path to requested audio file.
|
std::tstring mFileName; /// Path to requested audio file.
|
||||||
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||||
size_t mWritten = 0; /// Amount of written data (in bytes)
|
int mWritten; /// Amount of written data (in bytes)
|
||||||
size_t mLengthOffset = 0; /// Position of length field.
|
int mLengthOffset; /// Position of length field.
|
||||||
int mSamplerate = 0, mChannels = 0;
|
int mRate,
|
||||||
|
mChannels;
|
||||||
|
|
||||||
void checkWriteResult(int result);
|
void checkWriteResult(int result);
|
||||||
void writeBuffer(const void* buffer, size_t sz);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WavFileWriter();
|
WavFileWriter();
|
||||||
~WavFileWriter();
|
~WavFileWriter();
|
||||||
|
|
||||||
bool open(const std::filesystem::path& p, int samplerate, int channels);
|
bool open(const std::tstring& filename, int rate, int channels);
|
||||||
void close();
|
void close();
|
||||||
bool isOpened() const;
|
bool isOpened();
|
||||||
size_t write(const void* buffer, size_t bytes);
|
size_t write(const void* buffer, size_t bytes);
|
||||||
std::filesystem::path path() const;
|
std::tstring filename();
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
||||||
|
|
||||||
} // namespace Audio
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ WmmeInputDevice::Buffer::~Buffer()
|
|||||||
GlobalUnlock(mHeaderHandle);
|
GlobalUnlock(mHeaderHandle);
|
||||||
GlobalFree(mHeaderHandle);
|
GlobalFree(mHeaderHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WmmeInputDevice::Buffer::prepare(HWAVEIN device)
|
bool WmmeInputDevice::Buffer::prepare(HWAVEIN device)
|
||||||
@@ -92,8 +93,8 @@ void* WmmeInputDevice::Buffer::data()
|
|||||||
|
|
||||||
|
|
||||||
WmmeInputDevice::WmmeInputDevice(int deviceId)
|
WmmeInputDevice::WmmeInputDevice(int deviceId)
|
||||||
: mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false), mBufferIndex(0), mDeviceIndex(deviceId),
|
:mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false),
|
||||||
mThreadHandle(0)
|
mBufferIndex(0), mDeviceIndex(deviceId), mThreadHandle(0)
|
||||||
{
|
{
|
||||||
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
@@ -112,8 +113,7 @@ bool WmmeInputDevice::fakeMode()
|
|||||||
return mFakeMode;
|
return mFakeMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CALLBACK WmmeInputDevice::callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
|
void CALLBACK WmmeInputDevice::callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||||
DWORD_PTR dwParam2)
|
|
||||||
{
|
{
|
||||||
WmmeInputDevice* impl;
|
WmmeInputDevice* impl;
|
||||||
switch(uMsg)
|
switch(uMsg)
|
||||||
@@ -147,8 +147,7 @@ void WmmeInputDevice::openDevice()
|
|||||||
|
|
||||||
// Open wavein
|
// Open wavein
|
||||||
|
|
||||||
MMRESULT mmres =
|
MMRESULT mmres = waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||||
waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
|
||||||
if (mmres != MMSYSERR_NOERROR)
|
if (mmres != MMSYSERR_NOERROR)
|
||||||
{
|
{
|
||||||
mFakeMode = true;
|
mFakeMode = true;
|
||||||
@@ -217,6 +216,7 @@ void WmmeInputDevice::close()
|
|||||||
::SetEvent(mShutdownSignal);
|
::SetEvent(mShutdownSignal);
|
||||||
::WaitForSingleObject(mThreadHandle, INFINITE);
|
::WaitForSingleObject(mThreadHandle, INFINITE);
|
||||||
mThreadHandle = 0;
|
mThreadHandle = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WmmeInputDevice::tryReadBuffer(void* buffer)
|
bool WmmeInputDevice::tryReadBuffer(void* buffer)
|
||||||
@@ -287,7 +287,8 @@ void WmmeInputDevice::threadProc(void* arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- WmmeOutputDevice ---
|
// --- WmmeOutputDevice ---
|
||||||
WmmeOutputDevice::Buffer::Buffer() : mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL)
|
WmmeOutputDevice::Buffer::Buffer()
|
||||||
|
:mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL)
|
||||||
{
|
{
|
||||||
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
|
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
|
||||||
if (!mHeaderHandle)
|
if (!mHeaderHandle)
|
||||||
@@ -353,8 +354,7 @@ WmmeOutputDevice::~WmmeOutputDevice()
|
|||||||
close();
|
close();
|
||||||
|
|
||||||
// Destroy used signals
|
// Destroy used signals
|
||||||
CloseHandle(mDoneSignal);
|
CloseHandle(mDoneSignal); CloseHandle(mShutdownSignal);
|
||||||
CloseHandle(mShutdownSignal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WmmeOutputDevice::open()
|
bool WmmeOutputDevice::open()
|
||||||
@@ -432,7 +432,8 @@ bool WmmeOutputDevice::areBuffersFinished()
|
|||||||
bool result = true;
|
bool result = true;
|
||||||
for (unsigned i=0; i<AUDIO_SPK_BUFFER_COUNT && result; i++)
|
for (unsigned i=0; i<AUDIO_SPK_BUFFER_COUNT && result; i++)
|
||||||
{
|
{
|
||||||
bool finished = mBufferList[i].mHeader->dwFlags & WHDR_DONE || !mBufferList[i].mHeader->dwFlags;
|
bool finished = mBufferList[i].mHeader->dwFlags & WHDR_DONE ||
|
||||||
|
!mBufferList[i].mHeader->dwFlags;
|
||||||
if (finished)
|
if (finished)
|
||||||
{
|
{
|
||||||
/* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED)
|
/* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED)
|
||||||
@@ -487,7 +488,8 @@ void WmmeOutputDevice::threadProc(void* arg)
|
|||||||
exitCount++;
|
exitCount++;
|
||||||
}
|
}
|
||||||
impl->mBufferIndex = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
|
impl->mBufferIndex = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
|
||||||
} while (!exitSignal || exitCount < AUDIO_SPK_BUFFER_COUNT);
|
}
|
||||||
|
while (!exitSignal || exitCount < AUDIO_SPK_BUFFER_COUNT);
|
||||||
impl->closeDevice();
|
impl->closeDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,8 +540,7 @@ bool WmmeOutputDevice::closing()
|
|||||||
return mClosing;
|
return mClosing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CALLBACK WmmeOutputDevice::callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
|
void CALLBACK WmmeOutputDevice::callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||||
DWORD_PTR dwParam2)
|
|
||||||
{
|
{
|
||||||
WmmeOutputDevice* impl;
|
WmmeOutputDevice* impl;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -79,8 +79,7 @@ protected:
|
|||||||
void openDevice();
|
void openDevice();
|
||||||
void closeDevice();
|
void closeDevice();
|
||||||
|
|
||||||
static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
|
static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
|
||||||
DWORD_PTR dwParam2);
|
|
||||||
static void threadProc(void* arg);
|
static void threadProc(void* arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,14 +102,12 @@ protected:
|
|||||||
class Buffer
|
class Buffer
|
||||||
{
|
{
|
||||||
friend class WmmeOutputDevice;
|
friend class WmmeOutputDevice;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Buffer();
|
Buffer();
|
||||||
~Buffer();
|
~Buffer();
|
||||||
bool prepare(HWAVEOUT device);
|
bool prepare(HWAVEOUT device);
|
||||||
bool unprepare(HWAVEOUT device);
|
bool unprepare(HWAVEOUT device);
|
||||||
bool write(HWAVEOUT device);
|
bool write(HWAVEOUT device);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WAVEHDR* mHeader;
|
WAVEHDR* mHeader;
|
||||||
void* mData;
|
void* mData;
|
||||||
@@ -124,7 +121,9 @@ protected:
|
|||||||
Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT];
|
Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT];
|
||||||
unsigned mPlayedTime; /// Amount of played time in milliseconds
|
unsigned mPlayedTime; /// Amount of played time in milliseconds
|
||||||
bool mClosing;
|
bool mClosing;
|
||||||
HANDLE mDoneSignal, mShutdownSignal, mThreadHandle;
|
HANDLE mDoneSignal,
|
||||||
|
mShutdownSignal,
|
||||||
|
mThreadHandle;
|
||||||
volatile bool mShutdownMarker;
|
volatile bool mShutdownMarker;
|
||||||
|
|
||||||
volatile LONG mPlayedCount;
|
volatile LONG mPlayedCount;
|
||||||
@@ -135,13 +134,14 @@ protected:
|
|||||||
void closeDevice();
|
void closeDevice();
|
||||||
bool areBuffersFinished();
|
bool areBuffersFinished();
|
||||||
|
|
||||||
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
|
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
|
||||||
DWORD_PTR dwParam2);
|
|
||||||
static void threadProc(void* arg);
|
static void threadProc(void* arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace Audio
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
#include "Audio_iOS.h"
|
#include "Audio_iOS.h"
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
class IosInputDevice: public InputDevice
|
class IosInputDevice: public InputDevice
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IosInputDevice();
|
IosInputDevice();
|
||||||
~IosInputDevice();
|
~IosInputDevice();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void open();
|
void open();
|
||||||
void close();
|
void close();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,42 +1,27 @@
|
|||||||
project (audio_lib)
|
project (audio_lib)
|
||||||
|
|
||||||
# Rely on C++ 11
|
# Rely on C++ 11
|
||||||
set (CMAKE_CXX_STANDARD 20)
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
set (AUDIOLIB_SOURCES
|
set (AUDIOLIB_SOURCES
|
||||||
Audio_Resampler.cpp
|
Audio_Resampler.cpp
|
||||||
Audio_Resampler.h
|
|
||||||
Audio_Quality.cpp
|
Audio_Quality.cpp
|
||||||
Audio_Quality.h
|
|
||||||
Audio_Mixer.cpp
|
Audio_Mixer.cpp
|
||||||
Audio_Mixer.h
|
|
||||||
Audio_Interface.cpp
|
Audio_Interface.cpp
|
||||||
Audio_Interface.h
|
|
||||||
Audio_Helper.cpp
|
Audio_Helper.cpp
|
||||||
Audio_Helper.h
|
|
||||||
Audio_DataWindow.cpp
|
Audio_DataWindow.cpp
|
||||||
Audio_DataWindow.h
|
|
||||||
Audio_DevicePair.cpp
|
Audio_DevicePair.cpp
|
||||||
Audio_DevicePair.h
|
|
||||||
Audio_Player.cpp
|
Audio_Player.cpp
|
||||||
Audio_Player.h
|
|
||||||
Audio_Null.cpp
|
Audio_Null.cpp
|
||||||
Audio_Null.h
|
|
||||||
Audio_CoreAudio.cpp
|
Audio_CoreAudio.cpp
|
||||||
Audio_CoreAudio.h
|
|
||||||
Audio_DirectSound.cpp
|
Audio_DirectSound.cpp
|
||||||
Audio_DirectSound.h
|
|
||||||
Audio_AndroidOboe.cpp
|
|
||||||
Audio_AndroidOboe.h
|
|
||||||
Audio_WavFile.cpp
|
Audio_WavFile.cpp
|
||||||
Audio_WavFile.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(audio_lib ${AUDIOLIB_SOURCES})
|
add_library(audio_lib ${AUDIOLIB_SOURCES})
|
||||||
set_property(TARGET audio_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
|
||||||
|
|
||||||
##
|
##
|
||||||
target_include_directories(audio_lib
|
target_include_directories(audio_lib
|
||||||
|
|||||||
@@ -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/Pidf.hxx>
|
||||||
#include <resip/stack/PlainContents.hxx>
|
#include <resip/stack/PlainContents.hxx>
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "engine"
|
#define LOG_SUBSYSTEM "Account"
|
||||||
|
|
||||||
#define CONFIG(X) mConfig->at(X)
|
#define CONFIG(X) mConfig->at(X)
|
||||||
#define CONFIG_EXISTS(X) mConfig->exists(X)
|
#define CONFIG_EXISTS(X) mConfig->exists(X)
|
||||||
@@ -35,19 +35,22 @@ public:
|
|||||||
NATDecorator(UserAgent& endpoint);
|
NATDecorator(UserAgent& endpoint);
|
||||||
virtual ~NATDecorator();
|
virtual ~NATDecorator();
|
||||||
|
|
||||||
virtual void decorateMessage(resip::SipMessage& msg, const resip::Tuple& source, const resip::Tuple& destination,
|
virtual void decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId);
|
||||||
const resip::Data& sigcompId);
|
|
||||||
virtual void rollbackMessage(resip::SipMessage& msg);
|
virtual void rollbackMessage(resip::SipMessage& msg);
|
||||||
virtual MessageDecorator* clone() const;
|
virtual MessageDecorator* clone() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
NATDecorator::NATDecorator(UserAgent& ua) : mUserAgent(ua), mViaPort(0), mContactsPort(0) {}
|
NATDecorator::NATDecorator(UserAgent& ua)
|
||||||
|
:mUserAgent(ua), mViaPort(0), mContactsPort(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
NATDecorator::~NATDecorator() {}
|
NATDecorator::~NATDecorator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void NATDecorator::decorateMessage(resip::SipMessage& msg, const resip::Tuple& source, const resip::Tuple& destination,
|
void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId)
|
||||||
const resip::Data& sigcompId)
|
|
||||||
{
|
{
|
||||||
// Make a copy to allow rollback
|
// Make a copy to allow rollback
|
||||||
mMessage = msg;
|
mMessage = msg;
|
||||||
@@ -150,14 +153,17 @@ resip::MessageDecorator* NATDecorator::clone() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
Account::Account(PVariantMap config, UserAgent& agent)
|
Account::Account(PVariantMap config, UserAgent& agent)
|
||||||
: mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None), mRegistration(NULL)
|
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
|
||||||
|
mRegistration(NULL)
|
||||||
{
|
{
|
||||||
mProfile = std::make_shared<resip::UserProfile>(agent.mProfile);
|
mProfile = resip::SharedPtr<resip::UserProfile>(new resip::UserProfile(agent.mProfile));
|
||||||
mId = Account::generateId();
|
mId = Account::generateId();
|
||||||
setup(*config);
|
setup(*config);
|
||||||
}
|
}
|
||||||
|
|
||||||
Account::~Account() {}
|
Account::~Account()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Account::setup(VariantMap &config)
|
void Account::setup(VariantMap &config)
|
||||||
{
|
{
|
||||||
@@ -170,8 +176,8 @@ void Account::setup(VariantMap& config)
|
|||||||
mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()),
|
mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()),
|
||||||
resip::Data(config[CONFIG_USERNAME].asStdString()),
|
resip::Data(config[CONFIG_USERNAME].asStdString()),
|
||||||
resip::Data(config[CONFIG_PASSWORD].asStdString()));
|
resip::Data(config[CONFIG_PASSWORD].asStdString()));
|
||||||
ICELogInfo(<< "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() << ", username to "
|
ICELogInfo( << "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() <<
|
||||||
<< config[CONFIG_USERNAME].asStdString());
|
", username to " << config[CONFIG_USERNAME].asStdString());
|
||||||
|
|
||||||
// Proxy
|
// Proxy
|
||||||
mProfile->unsetOutboundProxy();
|
mProfile->unsetOutboundProxy();
|
||||||
@@ -195,7 +201,7 @@ void Account::setup(VariantMap& config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NAT decorator
|
// NAT decorator
|
||||||
mProfile->setOutboundDecorator(std::make_shared<NATDecorator>(mAgent));
|
mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent)));
|
||||||
|
|
||||||
// Rinstance
|
// Rinstance
|
||||||
if (config.exists(CONFIG_INSTANCE_ID))
|
if (config.exists(CONFIG_INSTANCE_ID))
|
||||||
@@ -260,12 +266,10 @@ void Account::start()
|
|||||||
|
|
||||||
// Create registration
|
// Create registration
|
||||||
mRegistration = new ResipSession(*mAgent.mDum);
|
mRegistration = new ResipSession(*mAgent.mDum);
|
||||||
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile,
|
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
||||||
mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
|
|
||||||
|
|
||||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str()))
|
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||||
.push_back(resip::StringCategory(iter->second.c_str()));
|
|
||||||
|
|
||||||
mRegistrationState = RegistrationState::Registering;
|
mRegistrationState = RegistrationState::Registering;
|
||||||
|
|
||||||
@@ -301,7 +305,8 @@ void Account::stop()
|
|||||||
mRegistrationHandle->removeAll();
|
mRegistrationHandle->removeAll();
|
||||||
mRegistrationHandle = resip::ClientRegistrationHandle();
|
mRegistrationHandle = resip::ClientRegistrationHandle();
|
||||||
}
|
}
|
||||||
else if (mRegistration)
|
else
|
||||||
|
if (mRegistration)
|
||||||
{
|
{
|
||||||
mRegistration->end();
|
mRegistration->end();
|
||||||
}
|
}
|
||||||
@@ -356,8 +361,7 @@ void Account::publishPresence(bool online, const std::string& content, int secon
|
|||||||
if (mPublication.isValid())
|
if (mPublication.isValid())
|
||||||
mPublication->update(&p);
|
mPublication->update(&p);
|
||||||
else
|
else
|
||||||
mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p,
|
mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p, resip::Symbols::Presence, seconds));
|
||||||
resip::Symbols::Presence, seconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::stopPublish()
|
void Account::stopPublish()
|
||||||
@@ -376,15 +380,15 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
|
|||||||
observer->mSessionId = observer->mSession->sessionId();
|
observer->mSessionId = observer->mSession->sessionId();
|
||||||
observer->mPeer = target;
|
observer->mPeer = target;
|
||||||
|
|
||||||
std::shared_ptr<resip::SipMessage> msg;
|
resip::SharedPtr<resip::SipMessage> msg;
|
||||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
|
||||||
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
||||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
|
if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
|
||||||
refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt();
|
refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt();
|
||||||
|
|
||||||
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile, resip::Data(package), expires,
|
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile,
|
||||||
refresh, observer->mSession);
|
resip::Data(package), expires, refresh, observer->mSession);
|
||||||
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
|
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
|
||||||
|
|
||||||
mAgent.mClientObserverMap[observer->mSessionId] = observer;
|
mAgent.mClientObserverMap[observer->mSessionId] = observer;
|
||||||
@@ -409,10 +413,8 @@ int Account::sendMsg(const std::string& peer, const void* ptr, unsigned length,
|
|||||||
else
|
else
|
||||||
type = resip::Mime(resip::Data(mime), resip::Data());
|
type = resip::Mime(resip::Data(mime), resip::Data());
|
||||||
|
|
||||||
resip::ClientPagerMessageHandle msgHandle =
|
resip::ClientPagerMessageHandle msgHandle = mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s);
|
||||||
mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s);
|
unique_ptr<resip::Contents> contentPtr(new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)),type));
|
||||||
unique_ptr<resip::Contents> contentPtr(
|
|
||||||
new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)), type));
|
|
||||||
int result = s->sessionId();
|
int result = s->sessionId();
|
||||||
msgHandle->page(std::move(contentPtr));
|
msgHandle->page(std::move(contentPtr));
|
||||||
|
|
||||||
@@ -454,8 +456,7 @@ void Account::queryStunServerIp()
|
|||||||
if (!mConfig->exists(CONFIG_STUNSERVER_NAME))
|
if (!mConfig->exists(CONFIG_STUNSERVER_NAME))
|
||||||
{
|
{
|
||||||
// Send request to find STUN or TURN service
|
// Send request to find STUN or TURN service
|
||||||
std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." +
|
std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." + mConfig->at(CONFIG_DOMAIN).asStdString();
|
||||||
mConfig->at(CONFIG_DOMAIN).asStdString();
|
|
||||||
|
|
||||||
// Start lookup
|
// Start lookup
|
||||||
mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this);
|
mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this);
|
||||||
@@ -497,7 +498,7 @@ void Account::prepareIceStack(Session* session, ice::AgentRole icerole)
|
|||||||
//config.mDetectNetworkChange = true;
|
//config.mDetectNetworkChange = true;
|
||||||
//config.mNetworkCheckInterval = 5000;
|
//config.mNetworkCheckInterval = 5000;
|
||||||
|
|
||||||
session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config));
|
session->mIceStack = resip::SharedPtr<ice::Stack>(ice::Stack::makeICEBox(config));
|
||||||
session->mIceStack->setEventHandler(session, this);
|
session->mIceStack->setEventHandler(session, this);
|
||||||
session->mIceStack->setRole(icerole);
|
session->mIceStack->setRole(icerole);
|
||||||
}
|
}
|
||||||
@@ -551,12 +552,12 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
|
|||||||
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
|
||||||
}
|
}
|
||||||
|
|
||||||
mUsedTransport = response.getReceivedTransportTuple().getType();
|
const resip::Transport* transport = response.getReceivedTransport();
|
||||||
// bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS;
|
mUsedTransport = transport->transport();
|
||||||
|
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
|
||||||
|
|
||||||
// Retry registration for stream based transport too
|
// Retry registration for stream based transport too
|
||||||
if ((hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ &&
|
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
|
||||||
mConfig->at(CONFIG_EXTERNALIP).asBool())
|
|
||||||
{
|
{
|
||||||
//mRegistrationHandle->requestRefresh();
|
//mRegistrationHandle->requestRefresh();
|
||||||
// Unregister at first
|
// Unregister at first
|
||||||
@@ -585,12 +586,8 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
|
|||||||
const char* transportName = nullptr;
|
const char* transportName = nullptr;
|
||||||
switch (mUsedTransport)
|
switch (mUsedTransport)
|
||||||
{
|
{
|
||||||
case resip::TCP:
|
case resip::TCP: transportName = "tcp"; break;
|
||||||
transportName = "tcp";
|
case resip::TLS: transportName = "tls"; break;
|
||||||
break;
|
|
||||||
case resip::TLS:
|
|
||||||
transportName = "tls";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hostport.param(resip::p_transport) = resip::Data(transportName);
|
hostport.param(resip::p_transport) = resip::Data(transportName);
|
||||||
@@ -599,10 +596,9 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
|
|||||||
//mProfile->setDefaultFrom(from);
|
//mProfile->setDefaultFrom(from);
|
||||||
}
|
}
|
||||||
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
||||||
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
resip::SharedPtr<resip::SipMessage> regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
||||||
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
|
||||||
regmessage->header(resip::ExtensionHeader(iter->first.c_str()))
|
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str()));
|
||||||
.push_back(resip::StringCategory(iter->second.c_str()));
|
|
||||||
|
|
||||||
mAgent.mDum->send(regmessage);
|
mAgent.mDum->send(regmessage);
|
||||||
return;
|
return;
|
||||||
@@ -633,8 +629,7 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
|
|||||||
ICELogInfo( << "Success to resolve STUN/TURN address to " << foundAddress.c_str());
|
ICELogInfo( << "Success to resolve STUN/TURN address to " << foundAddress.c_str());
|
||||||
mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str());
|
mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str());
|
||||||
|
|
||||||
// Here the IP address of STUN/TURN server is found. If account is registered already - it means account is
|
// Here the IP address of STUN/TURN server is found. If account is registered already - it means account is ready.
|
||||||
// ready.
|
|
||||||
if (mRegistrationState == RegistrationState::Registered)
|
if (mRegistrationState == RegistrationState::Registered)
|
||||||
mAgent.onAccountStart(mAgent.getAccount(this));
|
mAgent.onAccountStart(mAgent.getAccount(this));
|
||||||
}
|
}
|
||||||
@@ -649,7 +644,10 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&) {}
|
void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
||||||
{
|
{
|
||||||
@@ -677,8 +675,8 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
|||||||
|
|
||||||
const char* host = result.records[index].target().c_str();
|
const char* host = result.records[index].target().c_str();
|
||||||
|
|
||||||
ICELogInfo(<< "Success to find STUN/TURN server on " << result.records[index].target().c_str() << ":"
|
ICELogInfo( << "Success to find STUN/TURN server on " << result.records[index].target().c_str() <<
|
||||||
<< (int)result.records[index].port());
|
":" << (int)result.records[index].port());
|
||||||
|
|
||||||
|
|
||||||
if (inet_addr(host) == INADDR_NONE)
|
if (inet_addr(host) == INADDR_NONE)
|
||||||
@@ -697,11 +695,18 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
|||||||
ICELogError( << "Failed to find STUN or TURN service for specified domain.");
|
ICELogError( << "Failed to find STUN or TURN service for specified domain.");
|
||||||
//mAgent::shutdown();
|
//mAgent::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&) {}
|
void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&)
|
||||||
|
{
|
||||||
|
|
||||||
void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&) {}
|
}
|
||||||
|
|
||||||
|
void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool Account::isResponsibleFor(const resip::NameAddr &addr)
|
bool Account::isResponsibleFor(const resip::NameAddr &addr)
|
||||||
{
|
{
|
||||||
@@ -735,8 +740,8 @@ Account::UserInfo Account::getUserInfo() const
|
|||||||
return mUserInfo;
|
return mUserInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic_int Account::IdGenerator;
|
resip::AtomicCounter Account::IdGenerator;
|
||||||
int Account::generateId()
|
int Account::generateId()
|
||||||
{
|
{
|
||||||
return ++IdGenerator;
|
return IdGenerator.increment();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class Account : public resip::DnsResultSink
|
|||||||
{
|
{
|
||||||
friend class UserAgent;
|
friend class UserAgent;
|
||||||
friend class NATDecorator;
|
friend class NATDecorator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Account(PVariantMap config, UserAgent& agent);
|
Account(PVariantMap config, UserAgent& agent);
|
||||||
~Account();
|
~Account();
|
||||||
@@ -66,7 +65,7 @@ public:
|
|||||||
void setup(VariantMap& config);
|
void setup(VariantMap& config);
|
||||||
|
|
||||||
/* Returns corresponding resiprocate profile */
|
/* Returns corresponding resiprocate profile */
|
||||||
std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; }
|
resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; }
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> UserInfo;
|
typedef std::map<std::string, std::string> UserInfo;
|
||||||
void setUserInfo(const UserInfo& info);
|
void setUserInfo(const UserInfo& info);
|
||||||
@@ -84,7 +83,7 @@ protected:
|
|||||||
RegistrationState mRegistrationState;
|
RegistrationState mRegistrationState;
|
||||||
|
|
||||||
ice::NetworkAddress mExternalAddress;
|
ice::NetworkAddress mExternalAddress;
|
||||||
std::shared_ptr<resip::UserProfile> mProfile;
|
resip::SharedPtr<resip::UserProfile> mProfile;
|
||||||
UserAgent& mAgent;
|
UserAgent& mAgent;
|
||||||
bool mPresenceOnline;
|
bool mPresenceOnline;
|
||||||
std::string mPresenceContent;
|
std::string mPresenceContent;
|
||||||
@@ -136,7 +135,7 @@ protected:
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
static int generateId();
|
static int generateId();
|
||||||
static std::atomic_int IdGenerator;
|
static resip::AtomicCounter IdGenerator;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Account> PAccount;
|
typedef std::shared_ptr<Account> PAccount;
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_String.h"
|
#include "../helper/HL_String.h"
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "engine"
|
#define LOG_SUBSYSTEM "AudioProvider"
|
||||||
|
|
||||||
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
|
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
|
||||||
: mUserAgent(agent), mTerminal(terminal), mState(0), mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
|
:mUserAgent(agent), mTerminal(terminal), mState(0),
|
||||||
|
mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
|
||||||
{
|
{
|
||||||
mActive = mfActive;
|
mActive = mfActive;
|
||||||
mRemoteState = msSendRecv;
|
mRemoteState = msSendRecv;
|
||||||
@@ -25,11 +26,12 @@ AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
|
|||||||
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
|
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
|
||||||
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
|
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
|
||||||
mSrtpSuite = SRTP_NONE;
|
mSrtpSuite = SRTP_NONE;
|
||||||
setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving |
|
setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending);
|
||||||
(int)StreamState::Sending);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioProvider::~AudioProvider() {}
|
AudioProvider::~AudioProvider()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::string AudioProvider::streamName()
|
std::string AudioProvider::streamName()
|
||||||
{
|
{
|
||||||
@@ -62,7 +64,7 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver* observer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Processes incoming data
|
// Processes incoming data
|
||||||
void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source)
|
void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source)
|
||||||
{
|
{
|
||||||
if (!mActiveStream)
|
if (!mActiveStream)
|
||||||
return;
|
return;
|
||||||
@@ -75,8 +77,7 @@ void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer,
|
void AudioProvider::sendData(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size)
|
||||||
unsigned int size)
|
|
||||||
{
|
{
|
||||||
s->sendDatagram(destination, buffer, size);
|
s->sendDatagram(destination, buffer, size);
|
||||||
}
|
}
|
||||||
@@ -92,14 +93,13 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
|||||||
// Check if SRTP suite is found already or not
|
// Check if SRTP suite is found already or not
|
||||||
if (mSrtpSuite == SRTP_NONE)
|
if (mSrtpSuite == SRTP_NONE)
|
||||||
{
|
{
|
||||||
// RFC 4568 requires a unique tag per crypto attribute; use the suite id.
|
|
||||||
for (int suite = SRTP_AES_128_AUTH_80; suite <= SRTP_LAST; suite++)
|
for (int suite = SRTP_AES_128_AUTH_80; suite <= SRTP_LAST; suite++)
|
||||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite, suite)));
|
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// Answer/re-offer: echo the tag of the negotiated attribute.
|
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
|
||||||
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite, mSrtpTag)));
|
|
||||||
}
|
}
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
|
|
||||||
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
||||||
if (mAvailableCodecs.empty())
|
if (mAvailableCodecs.empty())
|
||||||
@@ -114,7 +114,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
|||||||
if (mRemoteTelephoneCodec)
|
if (mRemoteTelephoneCodec)
|
||||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Publish stream state
|
// Publish stream state
|
||||||
const char* attr = nullptr;
|
const char* attr = nullptr;
|
||||||
@@ -123,33 +123,18 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
|||||||
case mfActive:
|
case mfActive:
|
||||||
switch(mRemoteState)
|
switch(mRemoteState)
|
||||||
{
|
{
|
||||||
case msSendonly:
|
case msSendonly: attr = "recvonly"; break;
|
||||||
attr = "recvonly";
|
case msInactive: attr = "recvonly"; break;
|
||||||
break;
|
|
||||||
case msInactive:
|
|
||||||
attr = "recvonly";
|
|
||||||
break;
|
|
||||||
case msRecvonly:
|
|
||||||
case msSendRecv:
|
|
||||||
break; // Do nothing here
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case mfPaused:
|
case mfPaused:
|
||||||
switch (mRemoteState)
|
switch (mRemoteState)
|
||||||
{
|
{
|
||||||
case msRecvonly:
|
case msRecvonly: attr = "sendonly"; break;
|
||||||
attr = "sendonly";
|
case msSendonly: attr = "inactive"; break;
|
||||||
break;
|
case msInactive: attr = "inactive"; break;
|
||||||
case msSendonly:
|
case msSendRecv: attr = "sendonly"; break;
|
||||||
attr = "inactive";
|
|
||||||
break;
|
|
||||||
case msInactive:
|
|
||||||
attr = "inactive";
|
|
||||||
break;
|
|
||||||
case msSendRecv:
|
|
||||||
attr = "sendonly";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -242,8 +227,10 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
|||||||
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++)
|
||||||
{
|
{
|
||||||
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
|
||||||
|
#if defined(USE_RESIP_INTEGRATION)
|
||||||
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
|
||||||
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mAvailableCodecs.size())
|
if (!mAvailableCodecs.size())
|
||||||
@@ -260,13 +247,11 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
|||||||
{
|
{
|
||||||
const resip::Data& attr = *attrIter;
|
const resip::Data& attr = *attrIter;
|
||||||
ByteBuffer tempkey;
|
ByteBuffer tempkey;
|
||||||
int tag = 1;
|
SrtpSuite suite = processCryptoAttribute(attr, tempkey);
|
||||||
SrtpSuite suite = processCryptoAttribute(attr, tempkey, &tag);
|
if (suite > ss)
|
||||||
if (srtpSuiteStrength(suite) > srtpSuiteStrength(ss))
|
|
||||||
{
|
{
|
||||||
ss = suite;
|
ss = suite;
|
||||||
mSrtpSuite = suite;
|
mSrtpSuite = suite;
|
||||||
mSrtpTag = tag;
|
|
||||||
key = tempkey;
|
key = tempkey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,30 +296,40 @@ MT::PStream AudioProvider::activeStream()
|
|||||||
return mActiveStream;
|
return mActiveStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AudioProvider::createCryptoAttribute(SrtpSuite suite, int tag)
|
std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
|
||||||
{
|
{
|
||||||
if (!mActiveStream)
|
if (!mActiveStream)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
|
// Use tag 1 - it is ok, as we use only single crypto attribute
|
||||||
|
int srtpTag = 1;
|
||||||
|
|
||||||
// Print key to base64 string
|
// Print key to base64 string
|
||||||
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
|
||||||
if (!keyBuffer)
|
|
||||||
return "";
|
|
||||||
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
resip::Data d(keyBuffer->data(), keyBuffer->size());
|
||||||
resip::Data keyText = d.base64encode();
|
resip::Data keyText = d.base64encode();
|
||||||
|
|
||||||
return std::format("{} {} inline:{}", tag, toString(suite), keyText.c_str());
|
// Create "crypto" attribute value
|
||||||
|
char buffer[512];
|
||||||
|
const char* suiteName = NULL;
|
||||||
|
switch (suite)
|
||||||
|
{
|
||||||
|
case SRTP_AES_128_AUTH_80: suiteName = SRTP_SUITE_NAME_1; break;
|
||||||
|
case SRTP_AES_256_AUTH_80: suiteName = SRTP_SUITE_NAME_2; break;
|
||||||
|
default: assert(0);
|
||||||
|
}
|
||||||
|
sprintf(buffer, "%d %s inline:%s", srtpTag, suiteName, keyText.c_str());
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag)
|
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
|
||||||
{
|
{
|
||||||
int srtpTag = 0;
|
int srtpTag = 0;
|
||||||
char suite[64], keyChunk[256];
|
char suite[64], keyChunk[256];
|
||||||
int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk);
|
int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk);
|
||||||
if (components != 3)
|
if (components != 3)
|
||||||
return SRTP_NONE;
|
return SRTP_NONE;
|
||||||
if (tag)
|
|
||||||
*tag = srtpTag;
|
|
||||||
|
|
||||||
const char* delimiter = strchr(keyChunk, '|');
|
const char* delimiter = strchr(keyChunk, '|');
|
||||||
resip::Data keyText;
|
resip::Data keyText;
|
||||||
@@ -346,7 +341,15 @@ SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBu
|
|||||||
resip::Data rawkey = keyText.base64decode();
|
resip::Data rawkey = keyText.base64decode();
|
||||||
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
key = ByteBuffer(rawkey.c_str(), rawkey.size());
|
||||||
|
|
||||||
return toSrtpSuite(suite);
|
// Open srtp
|
||||||
|
SrtpSuite result = SRTP_NONE;
|
||||||
|
if (strcmp(suite, SRTP_SUITE_NAME_1) == 0)
|
||||||
|
result = SRTP_AES_128_AUTH_80;
|
||||||
|
else
|
||||||
|
if (strcmp(suite, SRTP_SUITE_NAME_2) == 0)
|
||||||
|
result = SRTP_AES_256_AUTH_80;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
|
||||||
@@ -379,9 +382,9 @@ void AudioProvider::setupMirror(bool enable)
|
|||||||
mActiveStream->setupMirror(enable);
|
mActiveStream->setupMirror(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProvider::setStateImpl(unsigned int state)
|
void AudioProvider::setStateImpl(unsigned int state) {
|
||||||
{
|
|
||||||
mState = state;
|
mState = state;
|
||||||
if (mActiveStream)
|
if (mActiveStream)
|
||||||
mActiveStream->setState(state);
|
mActiveStream->setState(state);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -12,7 +12,9 @@
|
|||||||
#include "../media/MT_Box.h"
|
#include "../media/MT_Box.h"
|
||||||
#include "../media/MT_Stream.h"
|
#include "../media/MT_Stream.h"
|
||||||
#include "../media/MT_Codec.h"
|
#include "../media/MT_Codec.h"
|
||||||
|
#include "../audio/Audio_Interface.h"
|
||||||
|
|
||||||
|
#include "rutil/ThreadIf.hxx"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ class UserAgent;
|
|||||||
class AudioProvider: public DataProvider
|
class AudioProvider: public DataProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AudioProvider(UserAgent& agent, MT::Terminal& terminal);
|
AudioProvider(UserAgent& agent, MT::Terminal& terminal);
|
||||||
virtual ~AudioProvider();
|
virtual ~AudioProvider();
|
||||||
|
|
||||||
@@ -34,11 +37,10 @@ public:
|
|||||||
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
||||||
|
|
||||||
// Processes incoming data
|
// Processes incoming data
|
||||||
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
|
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer,
|
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
|
||||||
unsigned int datasize) override;
|
|
||||||
|
|
||||||
// Updates SDP offer
|
// Updates SDP offer
|
||||||
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
||||||
@@ -74,7 +76,6 @@ public:
|
|||||||
void setupMirror(bool enable);
|
void setupMirror(bool enable);
|
||||||
|
|
||||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||||
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag = nullptr);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// SDP's stream name
|
// SDP's stream name
|
||||||
@@ -93,12 +94,11 @@ protected:
|
|||||||
|
|
||||||
unsigned mState;
|
unsigned mState;
|
||||||
SrtpSuite mSrtpSuite;
|
SrtpSuite mSrtpSuite;
|
||||||
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
|
|
||||||
struct RemoteCodec
|
struct RemoteCodec
|
||||||
{
|
{
|
||||||
RemoteCodec(MT::Codec::Factory* factory, int payloadType) : mFactory(factory), mRemotePayloadType(payloadType)
|
RemoteCodec(MT::Codec::Factory* factory, int payloadType)
|
||||||
{
|
:mFactory(factory), mRemotePayloadType(payloadType)
|
||||||
}
|
{ }
|
||||||
|
|
||||||
MT::Codec::Factory* mFactory;
|
MT::Codec::Factory* mFactory;
|
||||||
int mRemotePayloadType;
|
int mRemotePayloadType;
|
||||||
@@ -110,11 +110,13 @@ protected:
|
|||||||
MT::Stream::MediaObserver* mMediaObserver = nullptr;
|
MT::Stream::MediaObserver* mMediaObserver = nullptr;
|
||||||
void* mMediaObserverTag = nullptr;
|
void* mMediaObserverTag = nullptr;
|
||||||
|
|
||||||
std::string createCryptoAttribute(SrtpSuite suite, int tag);
|
std::string createCryptoAttribute(SrtpSuite suite);
|
||||||
|
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
|
||||||
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
||||||
|
|
||||||
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
||||||
void setStateImpl(unsigned state);
|
void setStateImpl(unsigned state);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -44,12 +44,14 @@ bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& me
|
|||||||
mRemoteState = msSendonly;
|
mRemoteState = msSendonly;
|
||||||
setState(state() & ~(int)StreamState::Sending);
|
setState(state() & ~(int)StreamState::Sending);
|
||||||
}
|
}
|
||||||
else if (media.exists("recvonly"))
|
else
|
||||||
|
if (media.exists("recvonly"))
|
||||||
{
|
{
|
||||||
mRemoteState = msRecvonly;
|
mRemoteState = msRecvonly;
|
||||||
setState(state() & ~(int)StreamState::Receiving);
|
setState(state() & ~(int)StreamState::Receiving);
|
||||||
}
|
}
|
||||||
else if (media.exists("inactive"))
|
else
|
||||||
|
if (media.exists("inactive"))
|
||||||
{
|
{
|
||||||
mRemoteState = msInactive;
|
mRemoteState = msInactive;
|
||||||
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving) );
|
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving) );
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "resip/stack/SdpContents.hxx"
|
#include "resip/stack/SdpContents.hxx"
|
||||||
|
#include "rutil/SharedPtr.hxx"
|
||||||
|
|
||||||
#include "../helper/HL_InternetAddress.h"
|
#include "../helper/HL_InternetAddress.h"
|
||||||
#include "../helper/HL_NetworkSocket.h"
|
#include "../helper/HL_NetworkSocket.h"
|
||||||
@@ -45,12 +46,10 @@ public:
|
|||||||
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
||||||
|
|
||||||
// Processes incoming data
|
// Processes incoming data
|
||||||
virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize,
|
virtual void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0;
|
||||||
InternetAddress& address) = 0;
|
|
||||||
|
|
||||||
// This method is called by user agent to send ICE packet from mediasocket
|
// This method is called by user agent to send ICE packet from mediasocket
|
||||||
virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer,
|
virtual void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0;
|
||||||
unsigned int datasize) = 0;
|
|
||||||
|
|
||||||
// Updates SDP offer
|
// Updates SDP offer
|
||||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
|
||||||
|
|||||||
+188
-250
@@ -37,76 +37,13 @@
|
|||||||
# include "resip/stack/ssl/WinSecurity.hxx"
|
# include "resip/stack/ssl/WinSecurity.hxx"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "engine"
|
#define LOG_SUBSYSTEM "[Engine]"
|
||||||
#define LOCK Lock l(mGuard)
|
#define LOCK Lock l(mGuard)
|
||||||
#define CAST2RESIPSESSION(x) \
|
#define CAST2RESIPSESSION(x) (x.isValid() ? (x->getAppDialogSet().isValid() ? dynamic_cast<ResipSession*>(x->getAppDialogSet().get()) : NULL) : NULL)
|
||||||
(x.isValid() ? (x->getAppDialogSet().isValid() ? dynamic_cast<ResipSession*>(x->getAppDialogSet().get()) : NULL) \
|
|
||||||
: NULL)
|
|
||||||
|
|
||||||
typedef resip::SdpContents::Session::Medium Medium;
|
typedef resip::SdpContents::Session::Medium Medium;
|
||||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||||
|
|
||||||
class TransportLogger : public resip::Transport::SipMessageLoggingHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void outboundMessage(const resip::Tuple& source, const resip::Tuple& destination,
|
|
||||||
const resip::SipMessage& msg) override
|
|
||||||
{
|
|
||||||
std::ostringstream dest_buffer;
|
|
||||||
dest_buffer << destination;
|
|
||||||
std::ostringstream msg_buffer;
|
|
||||||
msg_buffer << msg;
|
|
||||||
std::string msg_text = msg_buffer.str();
|
|
||||||
#if defined(TARGET_ANDROID)
|
|
||||||
if (msg_text.size() > 512)
|
|
||||||
{
|
|
||||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << " :");
|
|
||||||
msg_text = strx::prefixLines(msg_text, "<---");
|
|
||||||
|
|
||||||
auto lines = strx::split(msg_text);
|
|
||||||
for (const auto& l : lines)
|
|
||||||
ICELogDebug(<< l);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
|
||||||
#else
|
|
||||||
ICELogDebug(<< "Sent to " << dest_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: retransmissions store already encoded messages, so callback doesn't send SipMessage it sends
|
|
||||||
// the encoded version of the SipMessage instead. If you need a SipMessage you will need to
|
|
||||||
// re-parse back into a SipMessage in the callback handler.
|
|
||||||
void outboundRetransmit(const resip::Tuple& source, const resip::Tuple& destination,
|
|
||||||
const resip::SendData& data) override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void inboundMessage(const resip::Tuple& source, const resip::Tuple& destination,
|
|
||||||
const resip::SipMessage& msg) override
|
|
||||||
{
|
|
||||||
std::ostringstream source_buffer;
|
|
||||||
source_buffer << source;
|
|
||||||
std::ostringstream msg_buffer;
|
|
||||||
msg_buffer << msg;
|
|
||||||
std::string msg_text = msg_buffer.str();
|
|
||||||
#if defined(TARGET_ANDROID)
|
|
||||||
if (msg_text.size() > 512)
|
|
||||||
{
|
|
||||||
ICELogDebug(<< "Received from " << source_buffer.str() << " :");
|
|
||||||
msg_text = strx::prefixLines(msg_text, "--->");
|
|
||||||
auto lines = strx::split(msg_text);
|
|
||||||
for (const auto& l : lines)
|
|
||||||
ICELogDebug(<< l);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_text, "<---"));
|
|
||||||
#else
|
|
||||||
ICELogDebug(<< "Received from " << source_buffer.str() << "\n" << strx::prefixLines(msg_buffer.str(), "--->"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//-------------- UserAgent -----------------------
|
//-------------- UserAgent -----------------------
|
||||||
UserAgent::UserAgent()
|
UserAgent::UserAgent()
|
||||||
{
|
{
|
||||||
@@ -117,7 +54,7 @@ UserAgent::UserAgent()
|
|||||||
mConfig[CONFIG_RTCP_ATTR] = true;
|
mConfig[CONFIG_RTCP_ATTR] = true;
|
||||||
|
|
||||||
// Create master profile
|
// Create master profile
|
||||||
mProfile = std::make_shared<resip::MasterProfile>();
|
mProfile = resip::SharedPtr<resip::MasterProfile>(new resip::MasterProfile());
|
||||||
mProfile->clearSupportedMethods();
|
mProfile->clearSupportedMethods();
|
||||||
mProfile->addSupportedMethod(resip::INVITE);
|
mProfile->addSupportedMethod(resip::INVITE);
|
||||||
mProfile->addSupportedMethod(resip::BYE);
|
mProfile->addSupportedMethod(resip::BYE);
|
||||||
@@ -156,7 +93,7 @@ void UserAgent::start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize resip loggег
|
// Initialize resip loggег
|
||||||
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Info, "Client", *this);
|
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Debug, "Client", *this);
|
||||||
|
|
||||||
// Build list of nameservers if specified
|
// Build list of nameservers if specified
|
||||||
resip::DnsStub::NameserverList nslist;
|
resip::DnsStub::NameserverList nslist;
|
||||||
@@ -168,17 +105,12 @@ void UserAgent::start()
|
|||||||
|
|
||||||
while (std::getline(ss, line))
|
while (std::getline(ss, line))
|
||||||
{
|
{
|
||||||
line = strx::trim(line);
|
line = StringHelper::trim(line);
|
||||||
ice::NetworkAddress addr(line.c_str(), 0);
|
ice::NetworkAddress addr(line.c_str(), 0); addr.setPort(80); // Fake port to make ICEAddress initialized
|
||||||
addr.setPort(80); // Fake port to make ICEAddress initialized
|
|
||||||
switch (addr.family())
|
switch (addr.family())
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET: nslist.push_back(resip::GenericIPAddress(*addr.sockaddr4())); break;
|
||||||
nslist.push_back(resip::GenericIPAddress(*addr.sockaddr4()));
|
case AF_INET6: nslist.push_back(resip::GenericIPAddress(*addr.sockaddr6())); break;
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
nslist.push_back(resip::GenericIPAddress(*addr.sockaddr6()));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +120,7 @@ void UserAgent::start()
|
|||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
s = new resip::WinSecurity();
|
s = new resip::WinSecurity();
|
||||||
#elif defined(TARGET_OSX)
|
#elif defined(TARGET_OSX)
|
||||||
s = new resip::Security();
|
s = new resip::MacSecurity();
|
||||||
#elif defined(TARGET_LINUX)
|
#elif defined(TARGET_LINUX)
|
||||||
s = new resip::Security("/etc/ssl/certs");
|
s = new resip::Security("/etc/ssl/certs");
|
||||||
#elif defined(TARGET_ANDROID)
|
#elif defined(TARGET_ANDROID)
|
||||||
@@ -210,22 +142,12 @@ void UserAgent::start()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mStack->setTransportSipMessageLoggingHandler(std::make_shared<TransportLogger>());
|
|
||||||
|
|
||||||
// Add transports
|
// Add transports
|
||||||
mTransportList.clear();
|
mTransportList.clear();
|
||||||
resip::InternalTransport* t;
|
resip::InternalTransport* t;
|
||||||
|
|
||||||
#define ADD_TRANSPORT4(X) \
|
#define ADD_TRANSPORT4(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
||||||
if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V4)))) \
|
#define ADD_TRANSPORT6(X) if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) { t->setTransportLogger(this); mTransportList.push_back(t);}
|
||||||
{ /*t->setTransportLogger(this);*/ \
|
|
||||||
mTransportList.push_back(t); \
|
|
||||||
}
|
|
||||||
#define ADD_TRANSPORT6(X) \
|
|
||||||
if ((t = dynamic_cast<resip::InternalTransport*>(mStack->addTransport(X, 0, resip::V6)))) \
|
|
||||||
{ /*t->setTransportLogger(this);*/ \
|
|
||||||
mTransportList.push_back(t); \
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (mConfig[CONFIG_TRANSPORT].asInt())
|
switch (mConfig[CONFIG_TRANSPORT].asInt())
|
||||||
{
|
{
|
||||||
@@ -340,16 +262,16 @@ void UserAgent::shutdown()
|
|||||||
|
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
for (auto& observerIter : mClientObserverMap)
|
for (auto observerIter: mClientObserverMap)
|
||||||
observerIter.second->stop();
|
observerIter.second->stop();
|
||||||
|
|
||||||
for (auto& observerIter : mServerObserverMap)
|
for (auto observerIter: mServerObserverMap)
|
||||||
observerIter.second->stop();
|
observerIter.second->stop();
|
||||||
|
|
||||||
for (auto& sessionIter : mSessionMap)
|
for (auto sessionIter: mSessionMap)
|
||||||
sessionIter.second->stop();
|
sessionIter.second->stop();
|
||||||
|
|
||||||
for (auto& accountIter : mAccountSet)
|
for (auto accountIter: mAccountSet)
|
||||||
accountIter->stop();
|
accountIter->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,26 +279,24 @@ void UserAgent::shutdown()
|
|||||||
bool UserAgent::active()
|
bool UserAgent::active()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
return mStack != nullptr;
|
return mStack != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::refresh()
|
void UserAgent::refresh()
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
for (auto& acc : mAccountSet)
|
for (auto acc: mAccountSet)
|
||||||
acc->refresh();
|
acc->refresh();
|
||||||
|
|
||||||
for (auto& observer : mClientObserverMap)
|
for (auto observer: mClientObserverMap)
|
||||||
observer.second->refresh();
|
observer.second->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onDumCanBeDeleted()
|
void UserAgent::onDumCanBeDeleted()
|
||||||
{
|
{
|
||||||
delete mDum;
|
delete mDum; mDum = NULL;
|
||||||
mDum = nullptr;
|
delete mStack; mStack = NULL;
|
||||||
delete mStack;
|
|
||||||
mStack = nullptr;
|
|
||||||
|
|
||||||
mClientObserverMap.clear();
|
mClientObserverMap.clear();
|
||||||
mServerObserverMap.clear();
|
mServerObserverMap.clear();
|
||||||
@@ -395,12 +315,11 @@ void UserAgent::stop()
|
|||||||
mTransportList.clear();
|
mTransportList.clear();
|
||||||
|
|
||||||
// Dump statistics here
|
// Dump statistics here
|
||||||
ICELogInfo(<< "Remaining " << Session::InstanceCounter.load() << " session(s), "
|
ICELogInfo(<< "Remaining " << Session::InstanceCounter.value() << " session(s), " << ResipSession::InstanceCounter.value() << " resip DialogSet(s), " << resip::ClientRegistration::InstanceCounter.value() << " ClientRegistration(s)");
|
||||||
<< ResipSession::InstanceCounter.load() << " resip DialogSet(s), "
|
|
||||||
<< resip::ClientRegistration::InstanceCounter.load() << " ClientRegistration(s)");
|
|
||||||
|
|
||||||
mDum->shutdown(this);
|
mDum->shutdown(this);
|
||||||
onDumCanBeDeleted();
|
onDumCanBeDeleted();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -439,8 +358,7 @@ void UserAgent::process()
|
|||||||
this->onSipConnectionFailed();
|
this->onSipConnectionFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erase one terminated session. The rule is : seession must not have references from resiprocate and reference
|
// Erase one terminated session. The rule is : seession must not have references from resiprocate and reference count has to be 1.
|
||||||
// count has to be 1.
|
|
||||||
{
|
{
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
SessionMap::iterator sessionIter;
|
SessionMap::iterator sessionIter;
|
||||||
@@ -461,14 +379,15 @@ void UserAgent::process()
|
|||||||
|
|
||||||
// Find all sessions
|
// Find all sessions
|
||||||
std::set<int> idSet;
|
std::set<int> idSet;
|
||||||
for (auto sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
SessionMap::iterator sessionIter;
|
||||||
|
for (sessionIter = mSessionMap.begin(); sessionIter != mSessionMap.end(); ++sessionIter)
|
||||||
idSet.insert(sessionIter->first);
|
idSet.insert(sessionIter->first);
|
||||||
|
|
||||||
// Now process session one by one checking if current is available yet
|
// Now process session one by one checking if current is available yet
|
||||||
std::set<int>::iterator resipIter;
|
std::set<int>::iterator resipIter;
|
||||||
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
|
for (resipIter = idSet.begin(); resipIter != idSet.end(); ++resipIter)
|
||||||
{
|
{
|
||||||
auto sessionIter = mSessionMap.find(*resipIter);
|
SessionMap::iterator sessionIter = mSessionMap.find(*resipIter);
|
||||||
if (sessionIter == mSessionMap.end())
|
if (sessionIter == mSessionMap.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -479,9 +398,7 @@ void UserAgent::process()
|
|||||||
session.processQueuedOffer();
|
session.processQueuedOffer();
|
||||||
|
|
||||||
// Generate outgoing data while available
|
// Generate outgoing data while available
|
||||||
int iceStreamId = -1, iceComponentId = -1;
|
int iceStreamId = -1, iceComponentId = -1; void* iceTag = NULL; bool iceResponse;
|
||||||
void* iceTag = NULL;
|
|
||||||
bool iceResponse;
|
|
||||||
ice::PByteBuffer buffer;
|
ice::PByteBuffer buffer;
|
||||||
while ((buffer = session.mIceStack->generateOutgoingData(iceResponse, iceStreamId, iceComponentId, iceTag)))
|
while ((buffer = session.mIceStack->generateOutgoingData(iceResponse, iceStreamId, iceComponentId, iceTag)))
|
||||||
{
|
{
|
||||||
@@ -493,13 +410,9 @@ void UserAgent::process()
|
|||||||
if (stream.provider() && stream.iceInfo().mStreamId == iceStreamId)
|
if (stream.provider() && stream.iceInfo().mStreamId == iceStreamId)
|
||||||
{
|
{
|
||||||
// Send generated packet via provider's method to allow custom scheme of encryption
|
// Send generated packet via provider's method to allow custom scheme of encryption
|
||||||
ICELogDebug(<< "Sending ICE packet to " << buffer->remoteAddress().toStdString() << " with "
|
ICELogDebug(<<"Sending ICE packet to " << buffer->remoteAddress().toStdString() << " with " << buffer->comment());
|
||||||
<< buffer->comment());
|
|
||||||
|
|
||||||
RtpPair<PDatagramSocket>& pair =
|
PDatagramSocket s = iceComponentId == ICE_RTP_ID ? stream.socket4().mRtp : stream.socket4().mRtcp;
|
||||||
buffer->remoteAddress().family() == AF_INET6 ? stream.socket6() : stream.socket4();
|
|
||||||
PDatagramSocket s = iceComponentId == ICE_RTP_ID ? pair.mRtp : pair.mRtcp;
|
|
||||||
if (s)
|
|
||||||
stream.provider()->sendData(s, buffer->remoteAddress(), buffer->data(), buffer->size());
|
stream.provider()->sendData(s, buffer->remoteAddress(), buffer->data(), buffer->size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -515,18 +428,9 @@ void UserAgent::process()
|
|||||||
void UserAgent::addRootCert(const ByteBuffer& data)
|
void UserAgent::addRootCert(const ByteBuffer& data)
|
||||||
{
|
{
|
||||||
LOCK;
|
LOCK;
|
||||||
if (!mStack)
|
|
||||||
return;
|
|
||||||
resip::Data b(data.data(), data.size());
|
resip::Data b(data.data(), data.size());
|
||||||
try
|
|
||||||
{
|
|
||||||
mStack->getSecurity()->addRootCertPEM(b);
|
mStack->getSecurity()->addRootCertPEM(b);
|
||||||
}
|
}
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
// Ignore silently
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PAccount UserAgent::createAccount(PVariantMap config)
|
PAccount UserAgent::createAccount(PVariantMap config)
|
||||||
{
|
{
|
||||||
@@ -560,14 +464,15 @@ PSession UserAgent::createSession(PAccount account)
|
|||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UserAgent::formatSipAddress(const std::string& sip)
|
std::string UserAgent::formatSipAddress(std::string sip)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
if (sip.size())
|
if (sip.size())
|
||||||
{
|
{
|
||||||
if (sip.find("sip:") == std::string::npos && sip.find("sips:") == std::string::npos)
|
if (sip.find("sip:") == std::string::npos && sip.find("sips:") == std::string::npos)
|
||||||
result = "<sip:" + sip + ">";
|
result = "<sip:" + sip + ">";
|
||||||
else if (sip[0] != '<' && sip.find('<') == std::string::npos)
|
else
|
||||||
|
if (sip[0] != '<' && sip.find('<') == std::string::npos)
|
||||||
result = "<" + sip + ">";
|
result = "<" + sip + ">";
|
||||||
else
|
else
|
||||||
result = sip;
|
result = sip;
|
||||||
@@ -575,18 +480,17 @@ std::string UserAgent::formatSipAddress(const std::string& sip)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserAgent::isSipAddressValid(const std::string& sip)
|
bool UserAgent::isSipAddressValid(std::string sip)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string s = sip;
|
if (sip.find('<') == std::string::npos)
|
||||||
if (s.find('<') == std::string::npos)
|
sip = "<" + sip;
|
||||||
s = "<" + s;
|
if (sip.find('>') == std::string::npos)
|
||||||
if (s.find('>') == std::string::npos)
|
sip += ">";
|
||||||
s += ">";
|
|
||||||
|
|
||||||
resip::Data d(formatSipAddress(s));
|
resip::Data d(formatSipAddress(sip));
|
||||||
resip::Uri uri(d);
|
resip::Uri uri(d);
|
||||||
result = uri.isWellFormed();
|
result = uri.isWellFormed();
|
||||||
if (result)
|
if (result)
|
||||||
@@ -621,7 +525,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
|
|||||||
if (nameaddr.uri().port())
|
if (nameaddr.uri().port())
|
||||||
{
|
{
|
||||||
char porttext[32];
|
char porttext[32];
|
||||||
std::snprintf(porttext, sizeof(porttext), ":%u", (unsigned)nameaddr.uri().port());
|
sprintf(porttext, ":%u", (unsigned)nameaddr.uri().port());
|
||||||
result.mDomain += porttext;
|
result.mDomain += porttext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,7 +540,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserAgent::compareSipAddresses(const std::string& sip1, const std::string& sip2)
|
bool UserAgent::compareSipAddresses(std::string sip1, std::string sip2)
|
||||||
{
|
{
|
||||||
if (sip1.empty() || sip2.empty())
|
if (sip1.empty() || sip2.empty())
|
||||||
return false;
|
return false;
|
||||||
@@ -661,9 +565,14 @@ void UserAgent::onCandidateGathered(PSession s, const char* address)
|
|||||||
|
|
||||||
|
|
||||||
// Called when new connectivity check is finished
|
// Called when new connectivity check is finished
|
||||||
void UserAgent::onCheckFinished(PSession s, const char* description) {}
|
void UserAgent::onCheckFinished(PSession s, const char* description)
|
||||||
|
{
|
||||||
|
|
||||||
void UserAgent::onLog(const char* msg) {}
|
}
|
||||||
|
|
||||||
|
void UserAgent::onLog(const char* msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::sendOffer(Session* session)
|
void UserAgent::sendOffer(Session* session)
|
||||||
{
|
{
|
||||||
@@ -676,12 +585,10 @@ void UserAgent::sendOffer(Session* session)
|
|||||||
if (session->mOriginVersion == 1)
|
if (session->mOriginVersion == 1)
|
||||||
{
|
{
|
||||||
// Construct INVITE session
|
// Construct INVITE session
|
||||||
auto msg =
|
resip::SharedPtr<resip::SipMessage> msg = mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
||||||
mDum->makeInviteSession(session->mRemotePeer, session->account()->mProfile, &sdp, session->mResipSession);
|
|
||||||
|
|
||||||
// Include user headers
|
// Include user headers
|
||||||
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin();
|
for (Session::UserHeaders::const_iterator iter = session->mUserHeaders.begin(); iter != session->mUserHeaders.end(); iter++)
|
||||||
iter != session->mUserHeaders.end(); iter++)
|
|
||||||
{
|
{
|
||||||
const std::string& name = iter->first;
|
const std::string& name = iter->first;
|
||||||
const std::string& value = iter->second;
|
const std::string& value = iter->second;
|
||||||
@@ -748,31 +655,35 @@ void UserAgent::onFailure(resip::ClientRegistrationHandle h, const resip::SipMes
|
|||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
bool UserAgent::operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName,
|
bool UserAgent::operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName, const char* file,
|
||||||
const char* file, int line, const resip::Data& message,
|
int line, const resip::Data& message, const resip::Data& messageWithHeaders)
|
||||||
const resip::Data& messageWithHeaders, const resip::Data& instanceName)
|
|
||||||
{
|
{
|
||||||
std::string filename = file;
|
std::string filename = file;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
ss << "File " << strx::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
ss << "File " << StringHelper::extractFilename(filename).c_str() << ", line " << line << ": " << message.c_str();
|
||||||
if (level <= resip::Log::Crit)
|
if (level <= resip::Log::Crit)
|
||||||
ICELogCritical(<< ss.str()) else if (level <= resip::Log::Warning)
|
ICELogCritical(<< ss.str())
|
||||||
ICELogError(<< ss.str().c_str()) else if (level < resip::Log::Debug)
|
else
|
||||||
ICELogInfo(<< ss.str().c_str()) else ICELogDebug(<< ss.str().c_str()) return false;
|
if (level <= resip::Log::Warning)
|
||||||
|
ICELogError(<< ss.str().c_str())
|
||||||
|
else
|
||||||
|
if (level < resip::Log::Debug)
|
||||||
|
ICELogInfo(<< ss.str().c_str())
|
||||||
|
else
|
||||||
|
ICELogDebug(<< ss.str().c_str())
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma region INVITE handler
|
#pragma region INVITE handler
|
||||||
|
|
||||||
|
|
||||||
/// called when an initial INVITE or the intial response to an outoing invite
|
/// called when an initial INVITE or the intial response to an outoing invite
|
||||||
void UserAgent::onNewSession(resip::ClientInviteSessionHandle h, resip::InviteSession::OfferAnswerType oat,
|
void UserAgent::onNewSession(resip::ClientInviteSessionHandle h, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg)
|
||||||
const resip::SipMessage& msg)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onNewSession(resip::ServerInviteSessionHandle h, resip::InviteSession::OfferAnswerType oat,
|
void UserAgent::onNewSession(resip::ServerInviteSessionHandle h, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg)
|
||||||
const resip::SipMessage& msg)
|
|
||||||
{
|
{
|
||||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
ResipSession* rs = CAST2RESIPSESSION(h);
|
||||||
if (!rs)
|
if (!rs)
|
||||||
@@ -808,20 +719,21 @@ void UserAgent::onNewSession(resip::ServerInviteSessionHandle h, resip::InviteSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Received a failure response from UAS
|
/// Received a failure response from UAS
|
||||||
void UserAgent::onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// called when an in-dialog provisional response is received that contains an SDP body
|
/// called when an in-dialog provisional response is received that contains an SDP body
|
||||||
void UserAgent::onEarlyMedia(resip::ClientInviteSessionHandle h, const resip::SipMessage&, const resip::SdpContents&) {}
|
void UserAgent::onEarlyMedia(resip::ClientInviteSessionHandle h, const resip::SipMessage&, const resip::SdpContents&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// called when dialog enters the Early state - typically after getting 18x
|
/// called when dialog enters the Early state - typically after getting 18x
|
||||||
void UserAgent::onProvisional(resip::ClientInviteSessionHandle h, const resip::SipMessage& msg)
|
void UserAgent::onProvisional(resip::ClientInviteSessionHandle h, const resip::SipMessage& msg)
|
||||||
{
|
{
|
||||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||||
if (!rs)
|
|
||||||
return;
|
|
||||||
PSession s = getUserSession(rs->mSessionId);
|
|
||||||
if (!s)
|
if (!s)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -830,16 +742,14 @@ void UserAgent::onProvisional(resip::ClientInviteSessionHandle h, const resip::S
|
|||||||
int responseCode = msg.header(resip::h_StatusLine).statusCode();
|
int responseCode = msg.header(resip::h_StatusLine).statusCode();
|
||||||
onSessionProvisional(s, responseCode);
|
onSessionProvisional(s, responseCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// called when a dialog initiated as a UAC enters the connected state
|
/// called when a dialog initiated as a UAC enters the connected state
|
||||||
void UserAgent::onConnected(resip::ClientInviteSessionHandle h, const resip::SipMessage& msg)
|
void UserAgent::onConnected(resip::ClientInviteSessionHandle h, const resip::SipMessage& msg)
|
||||||
{
|
{
|
||||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||||
if (!rs)
|
|
||||||
return;
|
|
||||||
PSession s = getUserSession(rs->mSessionId);
|
|
||||||
if (!s)
|
if (!s)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -890,13 +800,9 @@ void UserAgent::onConnected(resip::InviteSessionHandle h, const resip::SipMessag
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UserAgent::onTerminated(resip::InviteSessionHandle h, resip::InviteSessionHandler::TerminatedReason reason,
|
void UserAgent::onTerminated(resip::InviteSessionHandle h, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related)
|
||||||
const resip::SipMessage* related)
|
|
||||||
{
|
{
|
||||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||||
if (!rs)
|
|
||||||
return;
|
|
||||||
PSession s = getUserSession(rs->mSessionId);
|
|
||||||
if (!s)
|
if (!s)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -918,7 +824,9 @@ void UserAgent::onTerminated(resip::InviteSessionHandle h, resip::InviteSessionH
|
|||||||
|
|
||||||
/// called when a fork that was created through a 1xx never receives a 2xx
|
/// called when a fork that was created through a 1xx never receives a 2xx
|
||||||
/// because another fork answered and this fork was canceled by a proxy.
|
/// because another fork answered and this fork was canceled by a proxy.
|
||||||
void UserAgent::onForkDestroyed(resip::ClientInviteSessionHandle) {}
|
void UserAgent::onForkDestroyed(resip::ClientInviteSessionHandle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// called when a 3xx with valid targets is encountered in an early dialog
|
/// called when a 3xx with valid targets is encountered in an early dialog
|
||||||
@@ -927,7 +835,9 @@ void UserAgent::onForkDestroyed(resip::ClientInviteSessionHandle) {}
|
|||||||
/// Basically an onTermintated that conveys more information.
|
/// Basically an onTermintated that conveys more information.
|
||||||
/// checking for 3xx respones in onTerminated will not work as there may
|
/// checking for 3xx respones in onTerminated will not work as there may
|
||||||
/// be no valid targets.
|
/// be no valid targets.
|
||||||
void UserAgent::onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when an SDP answer is received - has nothing to do with user
|
/// Called when an SDP answer is received - has nothing to do with user
|
||||||
/// answering the call
|
/// answering the call
|
||||||
@@ -938,8 +848,6 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
|||||||
if (!resipSession)
|
if (!resipSession)
|
||||||
return;
|
return;
|
||||||
Session* s = resipSession->session();
|
Session* s = resipSession->session();
|
||||||
if (!s)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool iceAvailable = true;
|
bool iceAvailable = true;
|
||||||
|
|
||||||
@@ -970,14 +878,14 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
|||||||
std::list<resip::SdpContents::Session::Medium>::const_iterator mediaIter;
|
std::list<resip::SdpContents::Session::Medium>::const_iterator mediaIter;
|
||||||
unsigned streamIndex = 0;
|
unsigned streamIndex = 0;
|
||||||
for (mediaIter = sdp.session().media().begin(), streamIndex = 0;
|
for (mediaIter = sdp.session().media().begin(), streamIndex = 0;
|
||||||
mediaIter != sdp.session().media().end() && streamIndex < s->mStreamList.size(); ++mediaIter, ++streamIndex)
|
mediaIter != sdp.session().media().end() && streamIndex < s->mStreamList.size();
|
||||||
|
++mediaIter, ++streamIndex)
|
||||||
{
|
{
|
||||||
Session::Stream& stream = s->mStreamList[streamIndex];
|
Session::Stream& stream = s->mStreamList[streamIndex];
|
||||||
const resip::SdpContents::Session::Medium& remoteStream = *mediaIter;
|
const resip::SdpContents::Session::Medium& remoteStream = *mediaIter;
|
||||||
|
|
||||||
// Update remote default ip if available
|
// Update remote default ip if available
|
||||||
const std::list<resip::SdpContents::Session::Connection>& streamConnections =
|
const std::list<resip::SdpContents::Session::Connection>& streamConnections = remoteStream.getMediumConnections();
|
||||||
remoteStream.getMediumConnections();
|
|
||||||
if (streamConnections.size())
|
if (streamConnections.size())
|
||||||
remoteDefaultIP = streamConnections.front().getAddress().c_str();
|
remoteDefaultIP = streamConnections.front().getAddress().c_str();
|
||||||
|
|
||||||
@@ -1023,8 +931,7 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
|||||||
// Corresponding turn allocation will be removed in this case.
|
// Corresponding turn allocation will be removed in this case.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!s->mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteDefaultIP,
|
if (!s->mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteDefaultIP, remoteDefaultPort, mConfig[CONFIG_DEFERRELAYED].asBool()))
|
||||||
remoteDefaultPort, mConfig[CONFIG_DEFERRELAYED].asBool()))
|
|
||||||
iceAvailable = false;
|
iceAvailable = false;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@@ -1041,8 +948,9 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
|||||||
|
|
||||||
// See if remote stream has "rtcp" or "rtcp-mux" attributes
|
// See if remote stream has "rtcp" or "rtcp-mux" attributes
|
||||||
if (remoteStream.exists("rtcp"))
|
if (remoteStream.exists("rtcp"))
|
||||||
addr2.setPort(strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort + 1));
|
addr2.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteDefaultPort+1) );
|
||||||
else if (remoteStream.exists("rtcp-mux"))
|
else
|
||||||
|
if (remoteStream.exists("rtcp-mux"))
|
||||||
addr2.setPort( remoteDefaultPort );
|
addr2.setPort( remoteDefaultPort );
|
||||||
stream.provider()->setDestinationAddress(RtpPair<InternetAddress>(addr, addr2));
|
stream.provider()->setDestinationAddress(RtpPair<InternetAddress>(addr, addr2));
|
||||||
mediasupported = true;
|
mediasupported = true;
|
||||||
@@ -1082,15 +990,14 @@ void UserAgent::onAnswer(resip::InviteSessionHandle h, const resip::SipMessage&
|
|||||||
s->mIceStack->clear();
|
s->mIceStack->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t version = sdp.session().origin().getVersion();
|
UInt64 version = sdp.session().origin().getVersion();
|
||||||
s->mRemoteOriginVersion = version;
|
s->mRemoteOriginVersion = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when an SDP offer is received - must send an answer soon after this
|
/// 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)
|
void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& msg, const resip::SdpContents& sdp)
|
||||||
{
|
{
|
||||||
ResipSession* rs = CAST2RESIPSESSION(h);
|
PSession s = getUserSession(CAST2RESIPSESSION(h)->mSessionId);
|
||||||
PSession s = rs ? getUserSession(rs->mSessionId) : PSession();
|
|
||||||
if (!s)
|
if (!s)
|
||||||
{
|
{
|
||||||
h->reject(488);
|
h->reject(488);
|
||||||
@@ -1108,17 +1015,17 @@ void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& m
|
|||||||
if (sdp.session().exists("ice-ufrag"))
|
if (sdp.session().exists("ice-ufrag"))
|
||||||
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
|
iceUfrag = sdp.session().getValues("ice-ufrag").front().c_str();
|
||||||
|
|
||||||
// ice::Stack& ice = *s->mIceStack;
|
ice::Stack& ice = *s->mIceStack;
|
||||||
|
|
||||||
uint64_t version = sdp.session().origin().getVersion();
|
UInt64 version = sdp.session().origin().getVersion();
|
||||||
std::string remoteIp = sdp.session().connection().getAddress().c_str();
|
std::string remoteIp = sdp.session().connection().getAddress().c_str();
|
||||||
// Default to 200: a retransmitted offer (same origin version) keeps the session.
|
int code;
|
||||||
int code = 200;
|
if ((UInt64)-1 == s->mRemoteOriginVersion)
|
||||||
if ((uint64_t)-1 == s->mRemoteOriginVersion)
|
|
||||||
{
|
{
|
||||||
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
|
code = s->processSdp(version, iceAvailable, icePwd, iceUfrag, remoteIp, sdp.session().media());
|
||||||
}
|
}
|
||||||
else if (version == s->mRemoteOriginVersion)
|
else
|
||||||
|
if (version == s->mRemoteOriginVersion)
|
||||||
{
|
{
|
||||||
// Timer, answer with previous SDP
|
// Timer, answer with previous SDP
|
||||||
//session->processTimer();
|
//session->processTimer();
|
||||||
@@ -1155,43 +1062,64 @@ void UserAgent::onOffer(resip::InviteSessionHandle h, const resip::SipMessage& m
|
|||||||
|
|
||||||
/// called when an Invite w/out SDP is sent, or any other context which
|
/// called when an Invite w/out SDP is sent, or any other context which
|
||||||
/// requires an SDP offer from the user
|
/// requires an SDP offer from the user
|
||||||
void UserAgent::onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// called if an offer in a UPDATE or re-INVITE was rejected - not real
|
/// called if an offer in a UPDATE or re-INVITE was rejected - not real
|
||||||
/// useful. A SipMessage is provided if one is available
|
/// useful. A SipMessage is provided if one is available
|
||||||
void UserAgent::onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage* msg) {}
|
void UserAgent::onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage* msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// called when INFO message is received
|
/// called when INFO message is received
|
||||||
void UserAgent::onInfo(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onInfo(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// called when response to INFO message is received
|
/// called when response to INFO message is received
|
||||||
void UserAgent::onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// called when MESSAGE message is received
|
/// called when MESSAGE message is received
|
||||||
void UserAgent::onMessage(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onMessage(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// called when response to MESSAGE message is received
|
/// called when response to MESSAGE message is received
|
||||||
void UserAgent::onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// called when an REFER message is received. The refer is accepted or
|
/// called when an REFER message is received. The refer is accepted or
|
||||||
/// rejected using the server subscription. If the offer is accepted,
|
/// rejected using the server subscription. If the offer is accepted,
|
||||||
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
|
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
|
||||||
/// InviteSession that will send notify messages using the ServerSubscription
|
/// InviteSession that will send notify messages using the ServerSubscription
|
||||||
void UserAgent::onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// called when an REFER message receives a failure response
|
/// called when an REFER message receives a failure response
|
||||||
void UserAgent::onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) {}
|
void UserAgent::onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// called when an REFER message receives an accepted response
|
/// called when an REFER message receives an accepted response
|
||||||
void UserAgent::onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle,
|
void UserAgent::onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg)
|
||||||
const resip::SipMessage& msg)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1212,9 +1140,13 @@ void UserAgent::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>& result) {}
|
void UserAgent::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>& result)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result) {}
|
void UserAgent::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>& result)
|
void UserAgent::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>& result)
|
||||||
{
|
{
|
||||||
@@ -1262,20 +1194,28 @@ int UserAgent::onRequestRetry(resip::ClientPublicationHandle, int retrySeconds,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UserAgent::onPublicationSuccess(PAccount account) {}
|
|
||||||
|
|
||||||
void UserAgent::onPublicationTerminated(PAccount account, int code) {}
|
void UserAgent::onPublicationSuccess(PAccount account)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserAgent::onPublicationTerminated(PAccount account, int code)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Subscriptions
|
#pragma region Subscriptions
|
||||||
|
|
||||||
void UserAgent::onClientObserverStart(PClientObserver observer) {}
|
void UserAgent::onClientObserverStart(PClientObserver observer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onClientObserverStop(PClientObserver observer, int code) {}
|
void UserAgent::onClientObserverStop(PClientObserver observer, int code)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online,
|
void UserAgent::onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, const std::string& content)
|
||||||
const std::string& content)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1287,8 +1227,6 @@ void UserAgent::onPresenceUpdate(PClientObserver observer, const std::string& pe
|
|||||||
void UserAgent::onNewSubscription(resip::ServerSubscriptionHandle h, const resip::SipMessage& sub)
|
void UserAgent::onNewSubscription(resip::ServerSubscriptionHandle h, const resip::SipMessage& sub)
|
||||||
{
|
{
|
||||||
ResipSession* s = CAST2RESIPSESSION(h);
|
ResipSession* s = CAST2RESIPSESSION(h);
|
||||||
if (!s)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get the event package name
|
// Get the event package name
|
||||||
const char* event = sub.header(resip::h_Event).value().c_str();
|
const char* event = sub.header(resip::h_Event).value().c_str();
|
||||||
@@ -1324,9 +1262,13 @@ void UserAgent::onTerminated(resip::ServerSubscriptionHandle h)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onServerObserverStart(PServerObserver observer) {}
|
void UserAgent::onServerObserverStart(PServerObserver observer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onServerObserverStop(PServerObserver observer, int code) {}
|
void UserAgent::onServerObserverStop(PServerObserver observer, int code)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMessage& notify)
|
void UserAgent::onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMessage& notify)
|
||||||
{
|
{
|
||||||
@@ -1362,7 +1304,8 @@ void UserAgent::onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMess
|
|||||||
bool online = pidf->getSimpleStatus(&body);
|
bool online = pidf->getSimpleStatus(&body);
|
||||||
onPresenceUpdate(observer, observer->peer(), online, std::string(body.c_str(), body.size()));
|
onPresenceUpdate(observer, observer->peer(), online, std::string(body.c_str(), body.size()));
|
||||||
}
|
}
|
||||||
else if (resip::MultipartRelatedContents* mr = dynamic_cast<resip::MultipartRelatedContents*>(contents))
|
else
|
||||||
|
if (resip::MultipartRelatedContents* mr = dynamic_cast<resip::MultipartRelatedContents*>(contents))
|
||||||
{
|
{
|
||||||
resip::MultipartRelatedContents::Parts& parts = mr->parts();
|
resip::MultipartRelatedContents::Parts& parts = mr->parts();
|
||||||
for( resip::MultipartRelatedContents::Parts::const_iterator i = parts.begin(); i != parts.end(); ++i)
|
for( resip::MultipartRelatedContents::Parts::const_iterator i = parts.begin(); i != parts.end(); ++i)
|
||||||
@@ -1416,7 +1359,8 @@ void UserAgent::onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (resip::Pidf* pidf = dynamic_cast<resip::Pidf*>(c))
|
else
|
||||||
|
if (resip::Pidf* pidf = dynamic_cast<resip::Pidf*>(c))
|
||||||
{
|
{
|
||||||
resip::Data body = pidf->getBodyData();
|
resip::Data body = pidf->getBodyData();
|
||||||
bool online = pidf->getSimpleStatus(&body);
|
bool online = pidf->getSimpleStatus(&body);
|
||||||
@@ -1426,8 +1370,7 @@ void UserAgent::onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMess
|
|||||||
onPresenceUpdate(observer, entity.c_str(), online, std::string(body.c_str(), body.size()));
|
onPresenceUpdate(observer, entity.c_str(), online, std::string(body.c_str(), body.size()));
|
||||||
|
|
||||||
// Drop corresponding record from availableContacts
|
// Drop corresponding record from availableContacts
|
||||||
std::vector<resip::Data>::iterator ci =
|
std::vector<resip::Data>::iterator ci = std::find(availableContacts.begin(), availableContacts.end(), entity);
|
||||||
std::find(availableContacts.begin(), availableContacts.end(), entity);
|
|
||||||
if (ci != availableContacts.end())
|
if (ci != availableContacts.end())
|
||||||
availableContacts.erase(ci);
|
availableContacts.erase(ci);
|
||||||
}
|
}
|
||||||
@@ -1499,16 +1442,22 @@ void UserAgent::onNewSubscription(resip::ClientSubscriptionHandle h, const resip
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// called to allow app to adorn a message.
|
/// called to allow app to adorn a message.
|
||||||
void UserAgent::onReadyToSend(resip::ClientSubscriptionHandle, resip::SipMessage& msg) {}
|
void UserAgent::onReadyToSend(resip::ClientSubscriptionHandle, resip::SipMessage& msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void UserAgent::onNotifyNotReceived(resip::ClientSubscriptionHandle) {}
|
void UserAgent::onNotifyNotReceived(resip::ClientSubscriptionHandle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when a TCP or TLS flow to the server has terminated. This can be caused by socket
|
/// Called when a TCP or TLS flow to the server has terminated. This can be caused by socket
|
||||||
/// errors, or missing CRLF keep alives pong responses from the server.
|
/// errors, or missing CRLF keep alives pong responses from the server.
|
||||||
// Called only if clientOutbound is enabled on the UserProfile and the first hop server
|
// Called only if clientOutbound is enabled on the UserProfile and the first hop server
|
||||||
/// supports RFC5626 (outbound).
|
/// supports RFC5626 (outbound).
|
||||||
/// Default implementation is to re-form the subscription using a new flow
|
/// Default implementation is to re-form the subscription using a new flow
|
||||||
void UserAgent::onFlowTerminated(resip::ClientSubscriptionHandle) {}
|
void UserAgent::onFlowTerminated(resip::ClientSubscriptionHandle)
|
||||||
|
{
|
||||||
|
}
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region PagerHandler
|
#pragma region PagerHandler
|
||||||
@@ -1522,16 +1471,14 @@ void UserAgent::onSuccess(resip::ClientPagerMessageHandle h, const resip::SipMes
|
|||||||
onMessageSent(getAccount(status.header(resip::h_From)), s->sessionId(), s->remoteAddress(), s->tag());
|
onMessageSent(getAccount(status.header(resip::h_From)), s->sessionId(), s->remoteAddress(), s->tag());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onFailure(resip::ClientPagerMessageHandle h, const resip::SipMessage& status,
|
void UserAgent::onFailure(resip::ClientPagerMessageHandle h, const resip::SipMessage& status, std::unique_ptr<resip::Contents> contents)
|
||||||
std::unique_ptr<resip::Contents> contents)
|
|
||||||
{
|
{
|
||||||
if (!h.isValid())
|
if (!h.isValid())
|
||||||
return;
|
return;
|
||||||
ResipSession* s = CAST2RESIPSESSION(h);
|
ResipSession* s = CAST2RESIPSESSION(h);
|
||||||
if (!s)
|
if (!s)
|
||||||
return;
|
return;
|
||||||
onMessageFailed(getAccount(status.header(resip::h_From)), s->sessionId(), s->remoteAddress(),
|
onMessageFailed(getAccount(status.header(resip::h_From)), s->sessionId(), s->remoteAddress(), status.header(resip::h_StatusLine).statusCode(), s->tag());
|
||||||
status.header(resip::h_StatusLine).statusCode(), s->tag());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onMessageArrived(resip::ServerPagerMessageHandle h, const resip::SipMessage& message)
|
void UserAgent::onMessageArrived(resip::ServerPagerMessageHandle h, const resip::SipMessage& message)
|
||||||
@@ -1557,24 +1504,24 @@ void UserAgent::updateInterfaceList()
|
|||||||
//ICEImpl::ICENetworkHelper::instance().reload();
|
//ICEImpl::ICENetworkHelper::instance().reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onMessageArrived(PAccount /*account*/, const std::string& /*peer*/, const void* /*ptr*/,
|
void UserAgent::onMessageArrived(PAccount /*account*/, const std::string& /*peer*/, const void* /*ptr*/, unsigned /*length*/)
|
||||||
unsigned /*length*/)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onMessageFailed(PAccount /*account*/, int /*id*/, const std::string& /*peer*/, int /*code*/,
|
void UserAgent::onMessageFailed(PAccount /*account*/, int /*id*/, const std::string& /*peer*/, int /*code*/, void* /*tag*/)
|
||||||
void* /*tag*/)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAgent::onMessageSent(PAccount /*account*/, int /*id*/, const std::string& /*peer*/, void* /*tag*/) {}
|
void UserAgent::onMessageSent(PAccount /*account*/, int /*id*/, const std::string& /*peer*/, void* /*tag*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
VariantMap& UserAgent::config()
|
VariantMap& UserAgent::config()
|
||||||
{
|
{
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
static void splitToHeaders(resip::Data& content, std::vector<resip::Data>& output)
|
||||||
{
|
{
|
||||||
resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
|
resip::Data::size_type startLine = 0, endLine = content.find("\r\n");
|
||||||
while (endLine != resip::Data::npos)
|
while (endLine != resip::Data::npos)
|
||||||
@@ -1586,9 +1533,9 @@ VariantMap& UserAgent::config()
|
|||||||
}
|
}
|
||||||
if (0 == startLine)
|
if (0 == startLine)
|
||||||
output.push_back(content);
|
output.push_back(content);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/*static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
static bool parseHeader(resip::Data& input, resip::Data& name, resip::Data& value)
|
||||||
{
|
{
|
||||||
resip::Data::size_type p = input.find(":");
|
resip::Data::size_type p = input.find(":");
|
||||||
if (p == resip::Data::npos)
|
if (p == resip::Data::npos)
|
||||||
@@ -1609,7 +1556,7 @@ VariantMap& UserAgent::config()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
|
void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen)
|
||||||
{
|
{
|
||||||
@@ -1617,17 +1564,17 @@ void UserAgent::onSipMessage(int flow, const char* msg, unsigned int length, con
|
|||||||
ice::NetworkAddress address(*addr, addrlen);
|
ice::NetworkAddress address(*addr, addrlen);
|
||||||
std::string addressText = address.toStdString();
|
std::string addressText = address.toStdString();
|
||||||
|
|
||||||
/*switch (flow)
|
switch (flow)
|
||||||
{
|
{
|
||||||
case resip::InternalTransport::TransportLogger::Flow_Received:
|
case resip::InternalTransport::TransportLogger::Flow_Received:
|
||||||
ICELogDebug(<< "Received from " << addressText << ":" << "\n"
|
ICELogDebug(<< "Received from " << addressText << ":" << "\n"
|
||||||
<< strx::prefixLines(d, "--->"));
|
<< StringHelper::prefixLines(d, "--->"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case resip::InternalTransport::TransportLogger::Flow_Sent:
|
case resip::InternalTransport::TransportLogger::Flow_Sent:
|
||||||
ICELogDebug(<< "Sent to " << addressText << "\n" << strx::prefixLines(d, "<---"));
|
ICELogDebug(<< "Sent to " << addressText << "\n" << StringHelper::prefixLines(d, "<---"));
|
||||||
break;
|
break;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PSession UserAgent::getUserSession(int sessionId)
|
PSession UserAgent::getUserSession(int sessionId)
|
||||||
@@ -1642,8 +1589,7 @@ PSession UserAgent::getUserSession(int sessionId)
|
|||||||
PAccount UserAgent::getAccount(const resip::NameAddr& myAddr)
|
PAccount UserAgent::getAccount(const resip::NameAddr& myAddr)
|
||||||
{
|
{
|
||||||
PAccount acc;
|
PAccount acc;
|
||||||
for (AccountSet::iterator accountIter = mAccountSet.begin(); accountIter != mAccountSet.end() && !acc;
|
for (AccountSet::iterator accountIter = mAccountSet.begin(); accountIter != mAccountSet.end() && !acc; accountIter++)
|
||||||
accountIter++)
|
|
||||||
if ((*accountIter)->isResponsibleFor(myAddr))
|
if ((*accountIter)->isResponsibleFor(myAddr))
|
||||||
acc = *accountIter;
|
acc = *accountIter;
|
||||||
|
|
||||||
@@ -1653,8 +1599,7 @@ PAccount UserAgent::getAccount(const resip::NameAddr& myAddr)
|
|||||||
PAccount UserAgent::getAccount(Account* account)
|
PAccount UserAgent::getAccount(Account* account)
|
||||||
{
|
{
|
||||||
PAccount acc;
|
PAccount acc;
|
||||||
for (AccountSet::iterator accountIter = mAccountSet.begin(); accountIter != mAccountSet.end() && !acc;
|
for (AccountSet::iterator accountIter = mAccountSet.begin(); accountIter != mAccountSet.end() && !acc; accountIter++)
|
||||||
accountIter++)
|
|
||||||
if (accountIter->get() == account)
|
if (accountIter->get() == account)
|
||||||
acc = *accountIter;
|
acc = *accountIter;
|
||||||
|
|
||||||
@@ -1663,14 +1608,7 @@ PAccount UserAgent::getAccount(Account* account)
|
|||||||
|
|
||||||
PAccount UserAgent::getAccount(int sessionId)
|
PAccount UserAgent::getAccount(int sessionId)
|
||||||
{
|
{
|
||||||
auto profileIter = std::find_if(mAccountSet.begin(), mAccountSet.end(),
|
auto profileIter = std::find_if(mAccountSet.begin(), mAccountSet.end(), [=](const AccountSet::value_type& v) {if (v->mRegistration) return v->mRegistration->sessionId() == sessionId; else return false;});
|
||||||
[=](const AccountSet::value_type& v)
|
|
||||||
{
|
|
||||||
if (v->mRegistration)
|
|
||||||
return v->mRegistration->sessionId() == sessionId;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
return (profileIter != mAccountSet.end()) ? *profileIter : PAccount();
|
return (profileIter != mAccountSet.end()) ? *profileIter : PAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
#include "../ice/ICETime.h"
|
#include "../ice/ICETime.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "EP_Session.h"
|
#include "EP_Session.h"
|
||||||
#include "EP_Observer.h"
|
#include "EP_Observer.h"
|
||||||
#include "EP_DataProvider.h"
|
#include "EP_DataProvider.h"
|
||||||
@@ -87,8 +87,7 @@ enum
|
|||||||
CONFIG_ICEUSERNAME, // Optional username for TURN server. String value.
|
CONFIG_ICEUSERNAME, // Optional username for TURN server. String value.
|
||||||
CONFIG_ICEPASSWORD, // Optional password for TURN server. String value.
|
CONFIG_ICEPASSWORD, // Optional password for TURN server. String value.
|
||||||
CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value.
|
CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value.
|
||||||
CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use
|
CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_NAME.
|
||||||
// CONFIG_STUNSERVER_NAME.
|
|
||||||
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
|
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
|
||||||
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
|
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
|
||||||
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
|
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
|
||||||
@@ -99,8 +98,7 @@ enum
|
|||||||
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
|
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
|
||||||
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
|
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
|
||||||
CONFIG_ROOTCERT, // Additional root cert in PEM format; string.
|
CONFIG_ROOTCERT, // Additional root cert in PEM format; string.
|
||||||
CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to
|
CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to reduce number of steps of SIP transaction
|
||||||
// reduce number of steps of SIP transaction
|
|
||||||
CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
|
CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
|
||||||
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
|
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
|
||||||
CONFIG_DEFERRELAYED, // Defer relayed media path
|
CONFIG_DEFERRELAYED, // Defer relayed media path
|
||||||
@@ -110,7 +108,7 @@ enum
|
|||||||
CONFIG_ACCOUNT, // VariantMap with account configuration
|
CONFIG_ACCOUNT, // VariantMap with account configuration
|
||||||
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
||||||
CONFIG_OWN_DNS, // Use predefined DNS servers
|
CONFIG_OWN_DNS, // Use predefined DNS servers
|
||||||
CONFIG_REGID // reg-id value from RFC5626,
|
CONFIG_REGID // reg-id value from RFC5626
|
||||||
};
|
};
|
||||||
|
|
||||||
// Conntype parameter for OnSessionEstablished event
|
// Conntype parameter for OnSessionEstablished event
|
||||||
@@ -154,21 +152,19 @@ class UserAgent : public resip::ClientRegistrationHandler,
|
|||||||
public resip::ServerSubscriptionHandler,
|
public resip::ServerSubscriptionHandler,
|
||||||
public resip::ClientPagerMessageHandler,
|
public resip::ClientPagerMessageHandler,
|
||||||
public resip::ServerPagerMessageHandler,
|
public resip::ServerPagerMessageHandler,
|
||||||
public resip::ClientPublicationHandler
|
public resip::ClientPublicationHandler,
|
||||||
// public resip::InternalTransport::TransportLogger
|
public resip::InternalTransport::TransportLogger
|
||||||
{
|
{
|
||||||
friend class Account;
|
friend class Account;
|
||||||
friend class Session;
|
friend class Session;
|
||||||
friend class ResipSession;
|
friend class ResipSession;
|
||||||
friend class NATDecorator;
|
friend class NATDecorator;
|
||||||
friend class WatcherQueue;
|
friend class WatcherQueue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same.
|
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
||||||
* Otherwise returns false. */
|
static bool compareSipAddresses(std::string sip1, std::string sip2);
|
||||||
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
|
static std::string formatSipAddress(std::string sip);
|
||||||
static std::string formatSipAddress(const std::string& sip);
|
static bool isSipAddressValid(std::string sip);
|
||||||
static bool isSipAddressValid(const std::string& sip);
|
|
||||||
struct SipAddress
|
struct SipAddress
|
||||||
{
|
{
|
||||||
bool mValid;
|
bool mValid;
|
||||||
@@ -191,8 +187,7 @@ public:
|
|||||||
This is asynchronous method. onStop() event will be called later */
|
This is asynchronous method. onStop() event will be called later */
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything.
|
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything. onStop() is called in context of this method. */
|
||||||
* onStop() is called in context of this method. */
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
/* Checks if user agent is active (started). */
|
/* Checks if user agent is active (started). */
|
||||||
@@ -273,8 +268,7 @@ public:
|
|||||||
virtual void onClientObserverStop(PClientObserver observer, int code);
|
virtual void onClientObserverStop(PClientObserver observer, int code);
|
||||||
virtual void onServerObserverStop(PServerObserver observer, int code);
|
virtual void onServerObserverStop(PServerObserver observer, int code);
|
||||||
|
|
||||||
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online,
|
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, const std::string& content);
|
||||||
const std::string& content);
|
|
||||||
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
|
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
|
||||||
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
|
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
|
||||||
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
|
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
|
||||||
@@ -286,17 +280,14 @@ public:
|
|||||||
// InviteSessionHandler implementation
|
// InviteSessionHandler implementation
|
||||||
#pragma region InviteSessionHandler implementation
|
#pragma region InviteSessionHandler implementation
|
||||||
/// called when an initial INVITE or the intial response to an outoing invite
|
/// called when an initial INVITE or the intial response to an outoing invite
|
||||||
virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat,
|
virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
|
||||||
const resip::SipMessage& msg) override;
|
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
|
||||||
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat,
|
|
||||||
const resip::SipMessage& msg) override;
|
|
||||||
|
|
||||||
/// Received a failure response from UAS
|
/// Received a failure response from UAS
|
||||||
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
|
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
|
||||||
|
|
||||||
/// called when an in-dialog provisional response is received that contains an SDP body
|
/// called when an in-dialog provisional response is received that contains an SDP body
|
||||||
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&,
|
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&, const resip::SdpContents&) override;
|
||||||
const resip::SdpContents&) override;
|
|
||||||
|
|
||||||
/// called when dialog enters the Early state - typically after getting 18x
|
/// called when dialog enters the Early state - typically after getting 18x
|
||||||
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
|
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
|
||||||
@@ -307,8 +298,7 @@ public:
|
|||||||
/// called when a dialog initiated as a UAS enters the connected state
|
/// called when a dialog initiated as a UAS enters the connected state
|
||||||
virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||||
|
|
||||||
virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason,
|
virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related=0) override;
|
||||||
const resip::SipMessage* related = 0) override;
|
|
||||||
|
|
||||||
/// called when a fork that was created through a 1xx never receives a 2xx
|
/// called when a fork that was created through a 1xx never receives a 2xx
|
||||||
/// because another fork answered and this fork was canceled by a proxy.
|
/// because another fork answered and this fork was canceled by a proxy.
|
||||||
@@ -355,8 +345,7 @@ public:
|
|||||||
/// rejected using the server subscription. If the offer is accepted,
|
/// rejected using the server subscription. If the offer is accepted,
|
||||||
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
|
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
|
||||||
/// InviteSession that will send notify messages using the ServerSubscription
|
/// InviteSession that will send notify messages using the ServerSubscription
|
||||||
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle,
|
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg) override;
|
||||||
const resip::SipMessage& msg) override;
|
|
||||||
|
|
||||||
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||||
|
|
||||||
@@ -364,8 +353,7 @@ public:
|
|||||||
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||||
|
|
||||||
/// called when an REFER message receives an accepted response
|
/// called when an REFER message receives an accepted response
|
||||||
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle,
|
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg) override;
|
||||||
const resip::SipMessage& msg) override;
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
// ClientRegistrationHandler implementation
|
// ClientRegistrationHandler implementation
|
||||||
@@ -389,9 +377,13 @@ public:
|
|||||||
|
|
||||||
#pragma region ExternalLogger implementation
|
#pragma region ExternalLogger implementation
|
||||||
/** return true to also do default logging, false to suppress default logging. */
|
/** return true to also do default logging, false to suppress default logging. */
|
||||||
virtual bool operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName,
|
virtual bool operator()(resip::Log::Level level,
|
||||||
const char* file, int line, const resip::Data& message,
|
const resip::Subsystem& subsystem,
|
||||||
const resip::Data& messageWithHeaders, const resip::Data& instanceName) override;
|
const resip::Data& appName,
|
||||||
|
const char* file,
|
||||||
|
int line,
|
||||||
|
const resip::Data& message,
|
||||||
|
const resip::Data& messageWithHeaders) override;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region DnsResultSink implementation
|
#pragma region DnsResultSink implementation
|
||||||
@@ -405,7 +397,7 @@ public:
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region TransportLogger implementation
|
#pragma region TransportLogger implementation
|
||||||
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
|
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region ClientPublicationHandler
|
#pragma region ClientPublicationHandler
|
||||||
@@ -445,19 +437,17 @@ public:
|
|||||||
|
|
||||||
#pragma region PagerHandler
|
#pragma region PagerHandler
|
||||||
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
|
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
|
||||||
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status,
|
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, std::unique_ptr<resip::Contents> contents) override;
|
||||||
std::unique_ptr<resip::Contents> contents) override;
|
|
||||||
void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override;
|
void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
void onDumCanBeDeleted() override;
|
void onDumCanBeDeleted() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Mutex to protect this instance
|
// Mutex to protect this instance
|
||||||
Mutex mGuard;
|
Mutex mGuard;
|
||||||
|
|
||||||
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
|
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
|
||||||
std::shared_ptr<resip::MasterProfile> mProfile;
|
resip::SharedPtr<resip::MasterProfile> mProfile;
|
||||||
|
|
||||||
// Resiprocate's SIP stack object pointer
|
// Resiprocate's SIP stack object pointer
|
||||||
resip::SipStack* mStack;
|
resip::SipStack* mStack;
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
#include "EP_NetworkQueue.h"
|
#include "EP_NetworkQueue.h"
|
||||||
#include "EP_Engine.h"
|
#include "EP_Engine.h"
|
||||||
|
|
||||||
WatcherQueue::WatcherQueue(UserAgent& ua) : mActiveId(0), mAgent(ua) {}
|
WatcherQueue::WatcherQueue(UserAgent& ua)
|
||||||
|
:mActiveId(0), mAgent(ua)
|
||||||
|
{}
|
||||||
|
|
||||||
WatcherQueue::~WatcherQueue() {}
|
WatcherQueue::~WatcherQueue()
|
||||||
|
{}
|
||||||
|
|
||||||
int WatcherQueue::add(const std::string& peer, const std::string& package, void* tag)
|
int WatcherQueue::add(std::string peer, std::string package, void* tag)
|
||||||
{
|
{
|
||||||
ice::Lock l(mGuard);
|
ice::Lock l(mGuard);
|
||||||
|
|
||||||
@@ -13,7 +16,8 @@ int WatcherQueue::add(const std::string& peer, const std::string& package, void*
|
|||||||
for (unsigned i=0; i<mItemList.size(); i++)
|
for (unsigned i=0; i<mItemList.size(); i++)
|
||||||
{
|
{
|
||||||
Item& item = mItemList[i];
|
Item& item = mItemList[i];
|
||||||
if (item.mTarget == peer && item.mPackage == package && item.mState != Item::State_Deleting)
|
if (item.mTarget == peer && item.mPackage == package &&
|
||||||
|
item.mState != Item::State_Deleting)
|
||||||
return item.mId;
|
return item.mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,9 +44,10 @@ void WatcherQueue::remove(int id)
|
|||||||
ice::Lock l(mGuard);
|
ice::Lock l(mGuard);
|
||||||
|
|
||||||
// Check if queue has similar item
|
// Check if queue has similar item
|
||||||
for (auto& item : mItemList)
|
for (unsigned i=0; i<mItemList.size(); i++)
|
||||||
{
|
{
|
||||||
if (item.mId == id && id)
|
Item& item = mItemList[i];
|
||||||
|
if (item.mId == id && !id)
|
||||||
{
|
{
|
||||||
if (item.mState != Item::State_Deleting)
|
if (item.mState != Item::State_Deleting)
|
||||||
item.mState = Item::State_ScheduledToDelete;
|
item.mState = Item::State_ScheduledToDelete;
|
||||||
@@ -57,9 +62,10 @@ void WatcherQueue::refresh(int id)
|
|||||||
ice::Lock l(mGuard);
|
ice::Lock l(mGuard);
|
||||||
|
|
||||||
// Check if queue has similar item
|
// Check if queue has similar item
|
||||||
for (auto& item : mItemList)
|
for (unsigned i=0; i<mItemList.size(); i++)
|
||||||
{
|
{
|
||||||
if (item.mId == id && id)
|
Item& item = mItemList[i];
|
||||||
|
if (item.mId == id && !id)
|
||||||
{
|
{
|
||||||
if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active)
|
if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active)
|
||||||
item.mState = Item::State_ScheduledToRefresh;
|
item.mState = Item::State_ScheduledToRefresh;
|
||||||
@@ -79,7 +85,7 @@ void WatcherQueue::process()
|
|||||||
if (i == mItemList.end())
|
if (i == mItemList.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::shared_ptr<resip::SipMessage> msg;
|
resip::SharedPtr<resip::SipMessage> msg;
|
||||||
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
|
||||||
|
|
||||||
switch (i->mState)
|
switch (i->mState)
|
||||||
@@ -136,9 +142,9 @@ void WatcherQueue::onTerminated(int id, int code)
|
|||||||
{
|
{
|
||||||
if (i->mSession)
|
if (i->mSession)
|
||||||
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
|
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
|
||||||
|
mItemList.erase(i);
|
||||||
if (i->mId == mActiveId)
|
if (i->mId == mActiveId)
|
||||||
mActiveId = 0;
|
mActiveId = 0;
|
||||||
mItemList.erase(i);
|
|
||||||
}
|
}
|
||||||
process();
|
process();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,25 +28,26 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
||||||
ResipSession* mSession = nullptr;
|
ResipSession* mSession;
|
||||||
State mState = State::State_None;
|
State mState;
|
||||||
std::string mTarget; // Target's address
|
std::string mTarget; // Target's address
|
||||||
std::string mPackage; // Event package
|
std::string mPackage; // Event package
|
||||||
void* mTag = nullptr; // User tag
|
void* mTag; // User tag
|
||||||
int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only)
|
int mId;
|
||||||
|
|
||||||
Item() {}
|
Item()
|
||||||
|
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
|
||||||
|
{}
|
||||||
|
|
||||||
bool scheduled()
|
bool scheduled()
|
||||||
{
|
{
|
||||||
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete ||
|
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || mState == State_ScheduledToRefresh;
|
||||||
mState == State_ScheduledToRefresh;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
WatcherQueue(UserAgent& agent);
|
WatcherQueue(UserAgent& agent);
|
||||||
~WatcherQueue();
|
~WatcherQueue();
|
||||||
|
|
||||||
int add(const std::string& peer, const std::string& package, void* tag);
|
int add(std::string peer, std::string package, void* tag);
|
||||||
void remove(int id);
|
void remove(int id);
|
||||||
void refresh(int id);
|
void refresh(int id);
|
||||||
void clear();
|
void clear();
|
||||||
|
|||||||
@@ -9,9 +9,13 @@
|
|||||||
#include <resip/stack/Pidf.hxx>
|
#include <resip/stack/Pidf.hxx>
|
||||||
#include <resip/dum/ClientSubscription.hxx>
|
#include <resip/dum/ClientSubscription.hxx>
|
||||||
|
|
||||||
ClientObserver::ClientObserver() {}
|
ClientObserver::ClientObserver()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ClientObserver::~ClientObserver() {}
|
ClientObserver::~ClientObserver()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void ClientObserver::refresh()
|
void ClientObserver::refresh()
|
||||||
{
|
{
|
||||||
@@ -23,7 +27,8 @@ void ClientObserver::stop()
|
|||||||
{
|
{
|
||||||
if (mHandle.isValid())
|
if (mHandle.isValid())
|
||||||
mHandle->end();
|
mHandle->end();
|
||||||
else if (mSession)
|
else
|
||||||
|
if (mSession)
|
||||||
{
|
{
|
||||||
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
|
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
|
||||||
if (mSession)
|
if (mSession)
|
||||||
@@ -37,7 +42,11 @@ std::string ClientObserver::peer()
|
|||||||
return mPeer;
|
return mPeer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerObserver::ServerObserver() : mState(State_Incoming) {}
|
ServerObserver::ServerObserver()
|
||||||
|
:mState(State_Incoming)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ServerObserver::~ServerObserver()
|
ServerObserver::~ServerObserver()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class ClientObserver
|
|||||||
{
|
{
|
||||||
friend class Account;
|
friend class Account;
|
||||||
friend class UserAgent;
|
friend class UserAgent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClientObserver();
|
ClientObserver();
|
||||||
~ClientObserver();
|
~ClientObserver();
|
||||||
@@ -44,7 +43,6 @@ typedef std::shared_ptr<ClientObserver> PClientObserver;
|
|||||||
class ServerObserver
|
class ServerObserver
|
||||||
{
|
{
|
||||||
friend class UserAgent;
|
friend class UserAgent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ServerObserver();
|
ServerObserver();
|
||||||
~ServerObserver();
|
~ServerObserver();
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ ReliableTunnel::ReliableTunnel(const char* streamname)
|
|||||||
mBandwidth = 0;
|
mBandwidth = 0;
|
||||||
mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReliableTunnel::~ReliableTunnel()
|
ReliableTunnel::~ReliableTunnel()
|
||||||
@@ -237,8 +238,7 @@ void ReliableTunnel::encrypt(void* dataPtr, int dataSize)
|
|||||||
|
|
||||||
#ifdef USE_OPENSSL
|
#ifdef USE_OPENSSL
|
||||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher,
|
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_ENCRYPT);
|
||||||
BF_ENCRYPT);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_CRYPTOPP
|
#ifdef USE_CRYPTOPP
|
||||||
@@ -254,8 +254,7 @@ void ReliableTunnel::decrypt(void* dataPtr, int dataSize)
|
|||||||
return;
|
return;
|
||||||
#ifdef USE_OPENSSL
|
#ifdef USE_OPENSSL
|
||||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher,
|
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_DECRYPT);
|
||||||
BF_DECRYPT);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_CRYPTOPP
|
#ifdef USE_CRYPTOPP
|
||||||
|
|||||||
@@ -111,7 +111,8 @@ protected:
|
|||||||
// Mutex to protect queuing/sending outgoing data
|
// Mutex to protect queuing/sending outgoing data
|
||||||
resip::Mutex mOutgoingMtx;
|
resip::Mutex mOutgoingMtx;
|
||||||
|
|
||||||
std::vector<std::string> mNewQueued;
|
std::vector<std::string>
|
||||||
|
mNewQueued;
|
||||||
resip::Mutex mNewQueuedGuard;
|
resip::Mutex mNewQueuedGuard;
|
||||||
resip::Mutex mStackGuard;
|
resip::Mutex mStackGuard;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -7,36 +7,43 @@
|
|||||||
#include "EP_Engine.h"
|
#include "EP_Engine.h"
|
||||||
#include "EP_AudioProvider.h"
|
#include "EP_AudioProvider.h"
|
||||||
#include "../media/MT_Stream.h"
|
#include "../media/MT_Stream.h"
|
||||||
|
#include "../media/MT_AudioStream.h"
|
||||||
|
#include "../media/MT_Dtmf.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
|
#include "../helper/HL_Exception.h"
|
||||||
|
#include "../helper/HL_StreamState.h"
|
||||||
#include "../helper/HL_Sync.h"
|
#include "../helper/HL_Sync.h"
|
||||||
#include "../helper/HL_String.h"
|
#include "../helper/HL_String.h"
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "engine"
|
#define LOG_SUBSYSTEM "[Engine]"
|
||||||
|
|
||||||
typedef resip::SdpContents::Session::Medium Medium;
|
typedef resip::SdpContents::Session::Medium Medium;
|
||||||
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
typedef resip::SdpContents::Session::MediumContainer MediumContainer;
|
||||||
|
|
||||||
#define IS_MULTIPLEX() \
|
#define DOMULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
||||||
mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
|
|
||||||
|
|
||||||
|
|
||||||
//------------ ResipSessionAppDialog ------------
|
//------------ ResipSessionAppDialog ------------
|
||||||
#pragma region ResipSessionAppDialog
|
#pragma region ResipSessionAppDialog
|
||||||
ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham) {}
|
ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ResipSessionAppDialog::~ResipSessionAppDialog() {}
|
ResipSessionAppDialog::~ResipSessionAppDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
|
||||||
#pragma region ResipSession
|
#pragma region ResipSession
|
||||||
|
|
||||||
std::atomic_int ResipSession::InstanceCounter;
|
resip::AtomicCounter ResipSession::InstanceCounter;
|
||||||
|
|
||||||
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
||||||
: resip::AppDialogSet(dum), mUserAgent(nullptr), mType(Type_None), mSessionId(0), mSession(0)
|
: resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
|
||||||
{
|
{
|
||||||
ResipSession::InstanceCounter++;
|
ResipSession::InstanceCounter.increment();
|
||||||
mTag = nullptr;
|
mTag = NULL;
|
||||||
mTerminated = false;
|
mTerminated = false;
|
||||||
mOnWatchingStartSent = false;
|
mOnWatchingStartSent = false;
|
||||||
mSessionId = Session::generateId();
|
mSessionId = Session::generateId();
|
||||||
@@ -55,7 +62,7 @@ ResipSession::~ResipSession()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ResipSession::InstanceCounter--;
|
ResipSession::InstanceCounter.decrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
|
resip::AppDialog* ResipSession::createAppDialog(const resip::SipMessage& msg)
|
||||||
@@ -91,8 +98,7 @@ void ResipSession::runTerminatedEvent(Type type, int code, int reason)
|
|||||||
case Type_Subscription:
|
case Type_Subscription:
|
||||||
if (mSession)
|
if (mSession)
|
||||||
{
|
{
|
||||||
UserAgent::ClientObserverMap::iterator observerIter =
|
UserAgent::ClientObserverMap::iterator observerIter = mUserAgent->mClientObserverMap.find(mSession->sessionId());
|
||||||
mUserAgent->mClientObserverMap.find(mSession->sessionId());
|
|
||||||
if (observerIter != mUserAgent->mClientObserverMap.end())
|
if (observerIter != mUserAgent->mClientObserverMap.end())
|
||||||
mUserAgent->onClientObserverStop(observerIter->second, code);
|
mUserAgent->onClientObserverStop(observerIter->second, code);
|
||||||
}
|
}
|
||||||
@@ -161,12 +167,12 @@ int ResipSession::sessionId()
|
|||||||
return mSessionId;
|
return mSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResipSession::setUASProfile(const std::shared_ptr<resip::UserProfile>& profile)
|
void ResipSession::setUASProfile(std::shared_ptr<resip::UserProfile> profile)
|
||||||
{
|
{
|
||||||
mUASProfile = profile;
|
mUASProfile = profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
|
resip::SharedPtr<resip::UserProfile> ResipSession::selectUASUserProfile(const resip::SipMessage& msg)
|
||||||
{
|
{
|
||||||
assert(mUserAgent != nullptr);
|
assert(mUserAgent != nullptr);
|
||||||
|
|
||||||
@@ -178,15 +184,20 @@ std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const res
|
|||||||
else
|
else
|
||||||
return mUserAgent->mProfile;
|
return mUserAgent->mProfile;
|
||||||
}
|
}
|
||||||
return std::shared_ptr<resip::UserProfile>();
|
return resip::SharedPtr<resip::UserProfile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Session::Stream
|
#pragma region Session::Stream
|
||||||
Session::Stream::Stream() : mRtcpAttr(false), mRtcpMuxAttr(false) {}
|
Session::Stream::Stream()
|
||||||
|
:mRtcpAttr(false), mRtcpMuxAttr(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Session::Stream::~Stream() {}
|
Session::Stream::~Stream()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Session::Stream::setProvider(PDataProvider provider)
|
void Session::Stream::setProvider(PDataProvider provider)
|
||||||
{
|
{
|
||||||
@@ -251,11 +262,11 @@ void Session::Stream::setRtcpMuxAttr(bool value)
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Session
|
#pragma region Session
|
||||||
std::atomic_int Session::InstanceCounter;
|
resip::AtomicCounter Session::InstanceCounter;
|
||||||
|
|
||||||
Session::Session(PAccount account)
|
Session::Session(PAccount account)
|
||||||
{
|
{
|
||||||
InstanceCounter++;
|
InstanceCounter.increment();
|
||||||
mAccount = account;
|
mAccount = account;
|
||||||
mSessionId = Session::generateId();
|
mSessionId = Session::generateId();
|
||||||
mTag = NULL;
|
mTag = NULL;
|
||||||
@@ -267,7 +278,7 @@ Session::Session(PAccount account)
|
|||||||
mRole = Acceptor;
|
mRole = Acceptor;
|
||||||
mGatheredCandidates = false;
|
mGatheredCandidates = false;
|
||||||
mTerminated = false;
|
mTerminated = false;
|
||||||
mRemoteOriginVersion = (uint64_t)-1;
|
mRemoteOriginVersion = (UInt64)-1;
|
||||||
mResipSession = NULL;
|
mResipSession = NULL;
|
||||||
mRefCount = 1;
|
mRefCount = 1;
|
||||||
mOfferAnswerCounter = 0;
|
mOfferAnswerCounter = 0;
|
||||||
@@ -284,9 +295,8 @@ Session::~Session()
|
|||||||
clearProvidersAndSockets();
|
clearProvidersAndSockets();
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{}
|
||||||
}
|
InstanceCounter.decrement();
|
||||||
InstanceCounter--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::start(const std::string& peer)
|
void Session::start(const std::string& peer)
|
||||||
@@ -340,10 +350,6 @@ void Session::stop()
|
|||||||
// Free socket
|
// Free socket
|
||||||
SocketHeap::instance().freeSocketPair( dataStream.socket4() );
|
SocketHeap::instance().freeSocketPair( dataStream.socket4() );
|
||||||
SocketHeap::instance().freeSocketPair( dataStream.socket6() );
|
SocketHeap::instance().freeSocketPair( dataStream.socket6() );
|
||||||
|
|
||||||
// Drop the references so the destructor's cleanup does not free them again
|
|
||||||
dataStream.setSocket4(RtpPair<PDatagramSocket>());
|
|
||||||
dataStream.setSocket6(RtpPair<PDatagramSocket>());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,8 +366,7 @@ void Session::accept()
|
|||||||
|
|
||||||
Lock locksession(mGuard);
|
Lock locksession(mGuard);
|
||||||
|
|
||||||
// If ICE candidate gathering is not finished - just mark session as accepted. It will be accepted in ICE handling
|
// If ICE candidate gathering is not finished - just mark session as accepted. It will be accepted in ICE handling code.
|
||||||
// code.
|
|
||||||
mAcceptedByUser = true;
|
mAcceptedByUser = true;
|
||||||
|
|
||||||
if (mGatheredCandidates || mIceStack->state() == ice::IceNone)
|
if (mGatheredCandidates || mIceStack->state() == ice::IceNone)
|
||||||
@@ -444,52 +449,56 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
|||||||
MT::Statistics stat;
|
MT::Statistics stat;
|
||||||
|
|
||||||
// Iterate all session providers
|
// Iterate all session providers
|
||||||
Stream* media = nullptr;
|
stat.reset();
|
||||||
for (Stream& stream : mStreamList)
|
Stream* media = NULL;
|
||||||
|
for (unsigned streamIndex = 0; streamIndex < mStreamList.size(); streamIndex++)
|
||||||
|
{
|
||||||
|
Stream& stream = mStreamList[streamIndex];
|
||||||
|
if (stream.provider())
|
||||||
{
|
{
|
||||||
if (!stream.provider())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
media = &stream;
|
media = &stream;
|
||||||
MT::Statistics s = stream.provider()->getStatistics();
|
MT::Statistics s = stream.provider()->getStatistics();
|
||||||
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos());
|
#if defined(USE_PVQA_LIBRARY) && !defined(TARGET_SERVER)
|
||||||
|
if (options != InfoOptions::Standard)
|
||||||
|
{
|
||||||
|
// This information is available AFTER audio stream is deleted
|
||||||
|
info[SessionInfo_PvqaMos] = s.mPvqaMos;
|
||||||
|
info[SessionInfo_PvqaReport] = s.mPvqaReport;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
|
||||||
info[SessionInfo_AudioCodec] = s.mCodecName;
|
info[SessionInfo_AudioCodec] = s.mCodecName;
|
||||||
|
|
||||||
stat += s;
|
stat += s;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
|
info[SessionInfo_ReceivedTraffic] = static_cast<int>(stat.mReceived);
|
||||||
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
|
info[SessionInfo_SentTraffic] = static_cast<int>(stat.mSent);
|
||||||
info[SessionInfo_ReceivedRtp] = static_cast<int>(stat.mReceivedRtp);
|
info[SessionInfo_ReceivedRtp] = static_cast<int>(stat.mReceivedRtp);
|
||||||
info[SessionInfo_ReceivedRtpTraffic] = static_cast<int>(stat.mReceivedRtpBytes);
|
|
||||||
info[SessionInfo_ReceivedRtcp] = static_cast<int>(stat.mReceivedRtcp);
|
info[SessionInfo_ReceivedRtcp] = static_cast<int>(stat.mReceivedRtcp);
|
||||||
info[SessionInfo_LostRtp] = static_cast<int>(stat.mPacketLoss);
|
info[SessionInfo_LostRtp] = static_cast<int>(stat.mPacketLoss);
|
||||||
info[SessionInfo_DroppedRtp] = static_cast<int>(stat.mPacketDropped);
|
info[SessionInfo_DroppedRtp] = static_cast<int>(stat.mPacketDropped);
|
||||||
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
||||||
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
||||||
if (stat.mFirstRtpTime)
|
if (stat.mFirstRtpTime)
|
||||||
info[SessionInfo_Duration] = static_cast<int>(
|
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - *(stat.mFirstRtpTime)).count());
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - *(stat.mFirstRtpTime))
|
|
||||||
.count());
|
|
||||||
else
|
else
|
||||||
info[SessionInfo_Duration] = 0;
|
info[SessionInfo_Duration] = 0;
|
||||||
|
|
||||||
if (stat.mReceivedRtp)
|
if (stat.mReceivedRtp)
|
||||||
info[SessionInfo_PacketLoss] = static_cast<int>((stat.mPacketLoss * 1000) / stat.mReceivedRtp);
|
info[SessionInfo_PacketLoss] = static_cast<int>((stat.mPacketLoss * 1000) / stat.mReceivedRtp);
|
||||||
|
|
||||||
if (media && mIceStack)
|
if (media)
|
||||||
info[SessionInfo_AudioPeer] =
|
info[SessionInfo_AudioPeer] = mIceStack->remoteAddress(media->iceInfo().mStreamId, media->iceInfo().mComponentId.mRtp).toStdString();
|
||||||
mIceStack->remoteAddress(media->iceInfo().mStreamId, media->iceInfo().mComponentId.mRtp).toStdString();
|
|
||||||
|
|
||||||
info[SessionInfo_Jitter] = stat.mJitter;
|
info[SessionInfo_Jitter] = stat.mJitter;
|
||||||
if (stat.mRttDelay.is_initialized())
|
if (stat.mRttDelay.is_initialized())
|
||||||
info[SessionInfo_Rtt] = static_cast<float>(stat.mRttDelay * 1000);
|
info[SessionInfo_Rtt] = static_cast<float>(stat.mRttDelay * 1000);
|
||||||
#if defined(USE_AMR_CODEC)
|
#if defined(USE_AMR_CODEC)
|
||||||
info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter;
|
info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter;
|
||||||
info[SessionInfo_CngCounter] = stat.mCng;
|
|
||||||
#endif
|
#endif
|
||||||
// Variant stores VTYPE_INT here; keep the 32 bits (consumers read it back with asInt()).
|
info[SessionInfo_SSRC] = stat.mSsrc;
|
||||||
info[SessionInfo_SSRC] = static_cast<int>(stat.mSsrc);
|
|
||||||
info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString();
|
info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,8 +512,7 @@ PAccount Session::account()
|
|||||||
return mAccount;
|
return mAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr,
|
void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize)
|
||||||
unsigned receivedSize)
|
|
||||||
{
|
{
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
|
|
||||||
@@ -541,11 +549,12 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
|
|||||||
{
|
{
|
||||||
// Try to process incoming data by ICE stack
|
// Try to process incoming data by ICE stack
|
||||||
int component = -1, stream = -1;
|
int component = -1, stream = -1;
|
||||||
|
bool processed;
|
||||||
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
|
if (mIceStack->findStreamAndComponent(socket->family(), socket->localport(), &stream, &component))
|
||||||
{
|
{
|
||||||
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
||||||
buffer.setRemoteAddress(src);
|
buffer.setRemoteAddress(src);
|
||||||
/*bool processed = */ mIceStack->processIncomingData(stream, component, buffer);
|
processed = mIceStack->processIncomingData(stream, component, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -593,7 +602,8 @@ void Session::onGathered(ice::Stack* stack, void* tag)
|
|||||||
|
|
||||||
if (mRole == Initiator)
|
if (mRole == Initiator)
|
||||||
mUserAgent->sendOffer(this);
|
mUserAgent->sendOffer(this);
|
||||||
else if (mRole == Acceptor)
|
else
|
||||||
|
if (mRole == Acceptor)
|
||||||
{
|
{
|
||||||
// Mark session as gathered ICE candidates
|
// Mark session as gathered ICE candidates
|
||||||
mGatheredCandidates = true;
|
mGatheredCandidates = true;
|
||||||
@@ -673,12 +683,8 @@ void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection)
|
|||||||
ice::NetworkAddress defaultAddr = mIceStack->defaultAddress(mStreamList.front().iceInfo().mStreamId, ICE_RTP_ID);
|
ice::NetworkAddress defaultAddr = mIceStack->defaultAddress(mStreamList.front().iceInfo().mStreamId, ICE_RTP_ID);
|
||||||
|
|
||||||
// Set IP address for origin and connection
|
// Set IP address for origin and connection
|
||||||
sdp.session().origin().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET
|
sdp.session().origin().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET ? resip::SdpContents::IP4 : resip::SdpContents::IP6);
|
||||||
? resip::SdpContents::IP4
|
sdp.session().connection().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET ? resip::SdpContents::IP4 : resip::SdpContents::IP6);
|
||||||
: resip::SdpContents::IP6);
|
|
||||||
sdp.session().connection().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET
|
|
||||||
? resip::SdpContents::IP4
|
|
||||||
: resip::SdpContents::IP6);
|
|
||||||
|
|
||||||
// Add ICE credentials
|
// Add ICE credentials
|
||||||
if (mIceStack->state() > ice::IceNone)
|
if (mIceStack->state() > ice::IceNone)
|
||||||
@@ -700,15 +706,15 @@ void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection)
|
|||||||
rtcpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTCP_ID);
|
rtcpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTCP_ID);
|
||||||
|
|
||||||
// Define media stream SDP's header
|
// Define media stream SDP's header
|
||||||
resip::SdpContents::Session::Medium media(resip::Data(provider.streamName()), rtpPort.port(), 0,
|
resip::SdpContents::Session::Medium media(resip::Data(provider.streamName()), rtpPort.port(), 0, resip::Data(provider.streamProfile()));
|
||||||
resip::Data(provider.streamProfile()));
|
|
||||||
|
|
||||||
// Add "rtcp" attribute
|
// Add "rtcp" attribute
|
||||||
if (mUserAgent->mConfig[CONFIG_RTCP_ATTR].asBool())
|
if (mUserAgent->mConfig[CONFIG_RTCP_ATTR].asBool())
|
||||||
{
|
{
|
||||||
if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
|
if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
|
||||||
rtcpPort = rtpPort;
|
rtcpPort = rtpPort;
|
||||||
else if (rtcpPort.isEmpty())
|
else
|
||||||
|
if (rtcpPort.isEmpty())
|
||||||
{
|
{
|
||||||
rtcpPort = rtpPort;
|
rtcpPort = rtpPort;
|
||||||
rtcpPort.setPort( rtpPort.port() + 1);
|
rtcpPort.setPort( rtpPort.port() + 1);
|
||||||
@@ -749,12 +755,9 @@ PDataProvider Session::findProviderByPort(int family, unsigned short port)
|
|||||||
{
|
{
|
||||||
Stream& s = mStreamList[i];
|
Stream& s = mStreamList[i];
|
||||||
|
|
||||||
// Sockets may not be allocated yet (stream created from SDP, sockets follow later)
|
if ((s.socket4().mRtp->localport() == port || s.socket4().mRtcp->localport() == port) && family == AF_INET)
|
||||||
if (family == AF_INET && s.socket4().mRtp && s.socket4().mRtcp &&
|
|
||||||
(s.socket4().mRtp->localport() == port || s.socket4().mRtcp->localport() == port))
|
|
||||||
return s.provider();
|
return s.provider();
|
||||||
if (family == AF_INET6 && s.socket6().mRtp && s.socket6().mRtcp &&
|
if ((s.socket6().mRtp->localport() == port || s.socket6().mRtcp->localport() == port) && family == AF_INET6)
|
||||||
(s.socket6().mRtp->localport() == port || s.socket6().mRtcp->localport() == port))
|
|
||||||
return s.provider();
|
return s.provider();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -789,8 +792,8 @@ void Session::addProvider(PDataProvider provider)
|
|||||||
s.setProvider( provider );
|
s.setProvider( provider );
|
||||||
|
|
||||||
// Allocate socket for provider
|
// Allocate socket for provider
|
||||||
s.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
|
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()) );
|
||||||
s.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
|
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()) );
|
||||||
s.provider()->setSocket(s.socket4(), s.socket6());
|
s.provider()->setSocket(s.socket4(), s.socket6());
|
||||||
|
|
||||||
// Create ICE stream/component
|
// Create ICE stream/component
|
||||||
@@ -799,11 +802,10 @@ void Session::addProvider(PDataProvider provider)
|
|||||||
ii.mPort4 = s.socket4().mRtp->localport();
|
ii.mPort4 = s.socket4().mRtp->localport();
|
||||||
ii.mPort6 = s.socket6().mRtp->localport();
|
ii.mPort6 = s.socket6().mRtp->localport();
|
||||||
|
|
||||||
ii.mComponentId.mRtp =
|
ii.mComponentId.mRtp = mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(),
|
||||||
mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(), s.socket6().mRtp->localport());
|
s.socket6().mRtp->localport());
|
||||||
if (!mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
|
if (!mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
|
||||||
ii.mComponentId.mRtcp =
|
ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtcp->localport(), s.socket6().mRtcp->localport());
|
||||||
mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtcp->localport(), s.socket6().mRtcp->localport());
|
|
||||||
|
|
||||||
s.setIceInfo(ii);
|
s.setIceInfo(ii);
|
||||||
|
|
||||||
@@ -827,10 +829,10 @@ int Session::sessionId()
|
|||||||
return mSessionId;
|
return mSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic_int Session::IdGenerator;
|
resip::AtomicCounter Session::IdGenerator;
|
||||||
int Session::generateId()
|
int Session::generateId()
|
||||||
{
|
{
|
||||||
return ++IdGenerator;
|
return (int)IdGenerator.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Session::remoteAddress() const
|
std::string Session::remoteAddress() const
|
||||||
@@ -894,8 +896,8 @@ void Session::refreshMediaPath()
|
|||||||
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
||||||
|
|
||||||
// Bring new socket to provider and stream
|
// Bring new socket to provider and stream
|
||||||
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()),
|
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX() ),
|
||||||
s6 = SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX());
|
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX());
|
||||||
|
|
||||||
p->setSocket(s4, s6);
|
p->setSocket(s4, s6);
|
||||||
s.setSocket4(s4);
|
s.setSocket4(s4);
|
||||||
@@ -912,7 +914,7 @@ void Session::refreshMediaPath()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Received offer with new SDP
|
// Received offer with new SDP
|
||||||
int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
int Session::processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
|
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
|
||||||
{
|
{
|
||||||
bool iceRestart = false;
|
bool iceRestart = false;
|
||||||
@@ -955,9 +957,9 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
|||||||
targetAddr.mRtcp.setIp(remoteIp);
|
targetAddr.mRtcp.setIp(remoteIp);
|
||||||
if (stream.rtcpMuxAttr())
|
if (stream.rtcpMuxAttr())
|
||||||
targetAddr.mRtcp.setPort( remoteStream.port() );
|
targetAddr.mRtcp.setPort( remoteStream.port() );
|
||||||
else if (stream.rtcpAttr())
|
else
|
||||||
targetAddr.mRtcp.setPort(
|
if (stream.rtcpAttr())
|
||||||
strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1));
|
targetAddr.mRtcp.setPort( StringHelper::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
|
||||||
else
|
else
|
||||||
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
||||||
|
|
||||||
@@ -974,8 +976,8 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
|
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, DOMULTIPLEX()));
|
||||||
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
|
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, DOMULTIPLEX()));
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
@@ -1008,15 +1010,13 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
|||||||
|
|
||||||
// See what remote peer offers - offer only single ice component if it relies on multiplexing
|
// See what remote peer offers - offer only single ice component if it relies on multiplexing
|
||||||
if (!targetAddr.multiplexed() && !mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
|
if (!targetAddr.multiplexed() && !mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
|
||||||
ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, stream.socket4().mRtcp->localport(),
|
ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, stream.socket4().mRtcp->localport(), stream.socket6().mRtcp->localport());
|
||||||
stream.socket6().mRtcp->localport());
|
|
||||||
stream.setIceInfo(ii);
|
stream.setIceInfo(ii);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iceAvailable)
|
if (iceAvailable)
|
||||||
{
|
{
|
||||||
if (mIceStack->remotePassword(stream.iceInfo().mStreamId) != icePwd ||
|
if (mIceStack->remotePassword(stream.iceInfo().mStreamId) != icePwd || mIceStack->remoteUfrag(stream.iceInfo().mStreamId) != iceUfrag)
|
||||||
mIceStack->remoteUfrag(stream.iceInfo().mStreamId) != iceUfrag)
|
|
||||||
{
|
{
|
||||||
iceRestart = true;
|
iceRestart = true;
|
||||||
mIceStack->setRemotePassword(icePwd, stream.iceInfo().mStreamId);
|
mIceStack->setRemotePassword(icePwd, stream.iceInfo().mStreamId);
|
||||||
@@ -1037,11 +1037,9 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
|||||||
if (candidateVector.empty())
|
if (candidateVector.empty())
|
||||||
iceAvailable = false;
|
iceAvailable = false;
|
||||||
|
|
||||||
// Ask ICE stack to process this information. This call will remove also second component if it is not defined
|
// Ask ICE stack to process this information. This call will remove also second component if it is not defined in remote sdp.
|
||||||
// in remote sdp.
|
|
||||||
if (iceAvailable)
|
if (iceAvailable)
|
||||||
iceAvailable = mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteIp, remotePort,
|
iceAvailable = mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteIp, remotePort, mUserAgent->mConfig[CONFIG_DEFERRELAYED].asBool());
|
||||||
mUserAgent->mConfig[CONFIG_DEFERRELAYED].asBool());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if there are compatible media streams
|
// See if there are compatible media streams
|
||||||
@@ -1139,10 +1137,11 @@ void Session::processQueuedOffer()
|
|||||||
//-------------- ResipSessionFactory ---------
|
//-------------- ResipSessionFactory ---------
|
||||||
#pragma region ResipSessionFactory
|
#pragma region ResipSessionFactory
|
||||||
|
|
||||||
ResipSessionFactory::ResipSessionFactory(UserAgent* agent) : mAgent(agent) {}
|
ResipSessionFactory::ResipSessionFactory(UserAgent* agent)
|
||||||
|
:mAgent(agent)
|
||||||
|
{}
|
||||||
|
|
||||||
resip::AppDialogSet* ResipSessionFactory::createAppDialogSet(resip::DialogUsageManager& dum,
|
resip::AppDialogSet* ResipSessionFactory::createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg)
|
||||||
const resip::SipMessage& msg)
|
|
||||||
{
|
{
|
||||||
ResipSession* s = new ResipSession(dum);
|
ResipSession* s = new ResipSession(dum);
|
||||||
s->setUa( mAgent );
|
s->setUa( mAgent );
|
||||||
|
|||||||
@@ -28,14 +28,15 @@
|
|||||||
#include "rutil/Logger.hxx"
|
#include "rutil/Logger.hxx"
|
||||||
#include "rutil/Random.hxx"
|
#include "rutil/Random.hxx"
|
||||||
#include "rutil/WinLeakCheck.hxx"
|
#include "rutil/WinLeakCheck.hxx"
|
||||||
|
#include "rutil/AtomicCounter.hxx"
|
||||||
|
|
||||||
#include "../ice/ICEBox.h"
|
#include "../ice/ICEBox.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <atomic>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
|
#include "EP_Session.h"
|
||||||
#include "EP_Account.h"
|
#include "EP_Account.h"
|
||||||
#include "EP_DataProvider.h"
|
#include "EP_DataProvider.h"
|
||||||
#include "EP_AudioProvider.h"
|
#include "EP_AudioProvider.h"
|
||||||
@@ -72,12 +73,12 @@ enum SessionInfo
|
|||||||
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
|
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
|
||||||
SessionInfo_RemotePeer,
|
SessionInfo_RemotePeer,
|
||||||
SessionInfo_SSRC,
|
SessionInfo_SSRC,
|
||||||
SessionInfo_CngCounter, // For AMR codecs only
|
|
||||||
SessionInfo_ReceivedRtpTraffic // amount of received RTP traffic in bytes, RTCP excluded
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Session : public SocketSink, public ice::StageHandler
|
class Session :
|
||||||
|
public SocketSink,
|
||||||
|
public ice::StageHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class Command
|
class Command
|
||||||
@@ -89,7 +90,8 @@ public:
|
|||||||
// Describes ice stream/component
|
// Describes ice stream/component
|
||||||
struct IceInfo
|
struct IceInfo
|
||||||
{
|
{
|
||||||
IceInfo() : mStreamId(-1)
|
IceInfo()
|
||||||
|
:mStreamId(-1)
|
||||||
{
|
{
|
||||||
mPort4 = mPort6 = 0;
|
mPort4 = mPort6 = 0;
|
||||||
mComponentId.mRtp = mComponentId.mRtcp = -1;
|
mComponentId.mRtp = mComponentId.mRtcp = -1;
|
||||||
@@ -212,8 +214,7 @@ public:
|
|||||||
void refreshMediaPath();
|
void refreshMediaPath();
|
||||||
|
|
||||||
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
||||||
// There are passing string objects by value; this is correct; this values will modified on the stack.
|
int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
|
||||||
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
|
|
||||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
||||||
|
|
||||||
// Session ID
|
// Session ID
|
||||||
@@ -223,7 +224,7 @@ public:
|
|||||||
std::vector<Stream> mStreamList;
|
std::vector<Stream> mStreamList;
|
||||||
|
|
||||||
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
||||||
std::shared_ptr<ice::Stack> mIceStack;
|
resip::SharedPtr<ice::Stack> mIceStack;
|
||||||
|
|
||||||
// Pointer to owner user agent instance
|
// Pointer to owner user agent instance
|
||||||
UserAgent* mUserAgent;
|
UserAgent* mUserAgent;
|
||||||
@@ -236,7 +237,7 @@ public:
|
|||||||
|
|
||||||
// SDP's origin version for sending
|
// SDP's origin version for sending
|
||||||
int mOriginVersion;
|
int mOriginVersion;
|
||||||
uint64_t mRemoteOriginVersion;
|
UInt64 mRemoteOriginVersion;
|
||||||
|
|
||||||
// SDP's session version
|
// SDP's session version
|
||||||
int mSessionVersion;
|
int mSessionVersion;
|
||||||
@@ -317,8 +318,8 @@ public:
|
|||||||
void enqueueOffer();
|
void enqueueOffer();
|
||||||
void processQueuedOffer();
|
void processQueuedOffer();
|
||||||
static int generateId();
|
static int generateId();
|
||||||
static std::atomic_int IdGenerator;
|
static resip::AtomicCounter IdGenerator;
|
||||||
static std::atomic_int InstanceCounter;
|
static resip::AtomicCounter InstanceCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Session> PSession;
|
typedef std::shared_ptr<Session> PSession;
|
||||||
@@ -353,13 +354,13 @@ public:
|
|||||||
Type_Call,
|
Type_Call,
|
||||||
Type_Auto
|
Type_Auto
|
||||||
};
|
};
|
||||||
static std::atomic_int InstanceCounter;
|
static resip::AtomicCounter InstanceCounter;
|
||||||
|
|
||||||
|
|
||||||
ResipSession(resip::DialogUsageManager& dum);
|
ResipSession(resip::DialogUsageManager& dum);
|
||||||
virtual ~ResipSession();
|
virtual ~ResipSession();
|
||||||
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
||||||
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||||
|
|
||||||
void setType(Type type);
|
void setType(Type type);
|
||||||
Type type();
|
Type type();
|
||||||
@@ -383,7 +384,7 @@ public:
|
|||||||
|
|
||||||
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
||||||
|
|
||||||
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
|
void setUASProfile(std::shared_ptr<resip::UserProfile> profile);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool mTerminated;
|
bool mTerminated;
|
||||||
@@ -403,7 +404,6 @@ class ResipSessionFactory : public resip::AppDialogSetFactory
|
|||||||
public:
|
public:
|
||||||
ResipSessionFactory(UserAgent* agent);
|
ResipSessionFactory(UserAgent* agent);
|
||||||
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
|
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UserAgent* mAgent;
|
UserAgent* mAgent;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
/* Copyright(C) 2007-2023 VoIP objects (voipobjects.com)
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#ifndef __TOOLKIT_CONFIG_H
|
|
||||||
#define __TOOLKIT_CONFIG_H
|
|
||||||
|
|
||||||
#define USE_SPEEX_AEC
|
|
||||||
|
|
||||||
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
|
|
||||||
// #define USE_WEBRTC_AEC
|
|
||||||
#define USER
|
|
||||||
|
|
||||||
|
|
||||||
#define AUDIO_SAMPLE_WIDTH 16
|
|
||||||
#define AUDIO_CHANNELS 1
|
|
||||||
|
|
||||||
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
|
|
||||||
#define AUDIO_SAMPLERATE 48000
|
|
||||||
#define AUDIO_MIC_BUFFER_COUNT 16
|
|
||||||
#define AUDIO_MIC_BUFFER_LENGTH 10
|
|
||||||
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
|
||||||
#define AUDIO_SPK_BUFFER_COUNT 16
|
|
||||||
#define AUDIO_SPK_BUFFER_LENGTH 10
|
|
||||||
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
|
||||||
#define AUDIO_MIX_CHANNEL_COUNT 16
|
|
||||||
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
|
|
||||||
|
|
||||||
// Resampler quality on the Speex 0..10 scale. Quality 1 upsampling (e.g. G.711 8k -> 48k)
|
|
||||||
// adds audible aliasing and hurts PVQA/AQuA scores; 7 is a good fidelity/CPU balance for a
|
|
||||||
// single mono stream on modern ARM64. Raise toward 10 for max fidelity if CPU allows.
|
|
||||||
#define AUDIO_RESAMPLER_QUALITY 7
|
|
||||||
#define AEC_FRAME_TIME 10
|
|
||||||
#define AEC_TAIL_TIME 160
|
|
||||||
|
|
||||||
|
|
||||||
// Defined these two lines to get dumping of audio input/output
|
|
||||||
// #define AUDIO_DUMPINPUT
|
|
||||||
// #define AUDIO_DUMPOUTPUT
|
|
||||||
|
|
||||||
|
|
||||||
#define UA_REGISTRATION_TIME 3600
|
|
||||||
#define UA_MEDIA_PORT_START 20000
|
|
||||||
#define UA_MEDIA_PORT_FINISH 30000
|
|
||||||
#define UA_MAX_UDP_PACKET_SIZE 576
|
|
||||||
#define UA_PUBLICATION_ID "314"
|
|
||||||
|
|
||||||
#define MT_SAMPLERATE AUDIO_SAMPLERATE
|
|
||||||
|
|
||||||
#define MT_MAXAUDIOFRAME 1440
|
|
||||||
#define MT_MAXRTPPACKET 1500
|
|
||||||
#define MT_DTMF_END_PACKETS 3
|
|
||||||
|
|
||||||
// Milliseconds before
|
|
||||||
#define RTP_BUFFER_HIGH (2000)
|
|
||||||
#define RTP_BUFFER_LOW (0)
|
|
||||||
#define RTP_BUFFER_PREBUFFER (100)
|
|
||||||
|
|
||||||
#define RTP_DECODED_CAPACITY 2048
|
|
||||||
|
|
||||||
#define DEFAULT_SUBSCRIPTION_TIME 1200
|
|
||||||
#define DEFAULT_SUBSCRIPTION_REFRESHTIME 500
|
|
||||||
|
|
||||||
#define PRESENCE_IN_REG_HEADER "PresenceInReg"
|
|
||||||
|
|
||||||
// Maximum UDP packet length
|
|
||||||
#define MAX_UDPPACKET_SIZE 65535
|
|
||||||
#define MAX_VALID_UDPPACKET_SIZE 2048
|
|
||||||
|
|
||||||
// AMR codec defines - it requires USE_AMR_CODEC defined
|
|
||||||
// #define USE_AMR_CODEC
|
|
||||||
#define MT_AMRNB_PAYLOADTYPE 112
|
|
||||||
#define MT_AMRNB_CODECNAME "amr"
|
|
||||||
|
|
||||||
#define MT_AMRNB_OCTET_PAYLOADTYPE 113
|
|
||||||
|
|
||||||
#define MT_AMRWB_PAYLOADTYPE 96
|
|
||||||
#define MT_AMRWB_CODECNAME "amr-wb"
|
|
||||||
|
|
||||||
#define MT_AMRWB_OCTET_PAYLOADTYPE 97
|
|
||||||
|
|
||||||
#define MT_GSMEFR_PAYLOADTYPE 126
|
|
||||||
#define MT_GSMEFR_CODECNAME "GERAN-EFR"
|
|
||||||
|
|
||||||
#define MT_EVS_PAYLOADTYPE 127
|
|
||||||
#define MT_EVS_CODECNAME "EVS"
|
|
||||||
|
|
||||||
// OPUS codec defines
|
|
||||||
#define MT_OPUS_CODEC_PT 106
|
|
||||||
|
|
||||||
// ILBC codec defines
|
|
||||||
#define MT_ILBC20_PAYLOADTYPE -1
|
|
||||||
#define MT_ILBC30_PAYLOADTYPE -1
|
|
||||||
|
|
||||||
// ISAC codec defines
|
|
||||||
#define MT_ISAC16K_PAYLOADTYPE -1
|
|
||||||
#define MT_ISAC32K_PAYLOADTYPE -1
|
|
||||||
|
|
||||||
// GSM HR payload type
|
|
||||||
#define MT_GSMHR_PAYLOADTYPE -1
|
|
||||||
|
|
||||||
// Mirror buffer capacity
|
|
||||||
#define MT_MIRROR_CAPACITY 32768
|
|
||||||
|
|
||||||
// Mirror buffer readiness threshold - 50 milliseconds
|
|
||||||
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
|
|
||||||
|
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
|
||||||
#define TEXT(X) X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// In milliseconds
|
|
||||||
#define MT_SEVANA_FRAME_TIME 680
|
|
||||||
|
|
||||||
// Number of samples
|
|
||||||
#define MT_MAX_DECODEBUFFER 32768
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
cmake_minimum_required (VERSION 3.15)
|
|
||||||
project (helper_lib)
|
project (helper_lib)
|
||||||
|
|
||||||
# Rely on C++ 11
|
# Rely on C++ 11
|
||||||
set (CMAKE_CXX_STANDARD 20)
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set (USE_NULL_UUID OFF CACHE BOOL "When enabled linking to libuuid is avoided")
|
||||||
|
|
||||||
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
file (GLOB HELPER_LIB_SOURCES "*.cpp" "*.h")
|
||||||
|
|
||||||
add_library(helper_lib ${HELPER_LIB_SOURCES})
|
add_library(helper_lib ${HELPER_LIB_SOURCES})
|
||||||
set_property(TARGET helper_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
|
||||||
|
|
||||||
# Private include directories
|
# Private include directories
|
||||||
target_include_directories(helper_lib PUBLIC ../../libs/ ../../engine ../ .)
|
target_include_directories(helper_lib PRIVATE ../../libs/ ../../engine ../)
|
||||||
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
target_compile_definitions(helper_lib PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_UNICODE)
|
||||||
if (TARGET_LINUX)
|
|
||||||
target_link_libraries (helper_lib PUBLIC uuid)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -5,6 +5,12 @@
|
|||||||
|
|
||||||
#include "HL_AsyncCommand.h"
|
#include "HL_AsyncCommand.h"
|
||||||
|
|
||||||
AsyncCommand::AsyncCommand() {}
|
AsyncCommand::AsyncCommand()
|
||||||
|
{
|
||||||
|
|
||||||
AsyncCommand::~AsyncCommand() {}
|
}
|
||||||
|
|
||||||
|
AsyncCommand::~AsyncCommand()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+60
-108
@@ -7,11 +7,9 @@ const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
"0123456789+/";
|
"0123456789+/";
|
||||||
|
|
||||||
class Base64
|
class Base64 {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
static bool Encode(const std::string& in, std::string* out)
|
static bool Encode(const std::string &in, std::string *out) {
|
||||||
{
|
|
||||||
int i = 0, j = 0;
|
int i = 0, j = 0;
|
||||||
size_t enc_len = 0;
|
size_t enc_len = 0;
|
||||||
unsigned char a3[3];
|
unsigned char a3[3];
|
||||||
@@ -22,15 +20,12 @@ public:
|
|||||||
int input_len = in.size();
|
int input_len = in.size();
|
||||||
std::string::const_iterator input = in.begin();
|
std::string::const_iterator input = in.begin();
|
||||||
|
|
||||||
while (input_len--)
|
while (input_len--) {
|
||||||
{
|
|
||||||
a3[i++] = *(input++);
|
a3[i++] = *(input++);
|
||||||
if (i == 3)
|
if (i == 3) {
|
||||||
{
|
|
||||||
a3_to_a4(a4, a3);
|
a3_to_a4(a4, a3);
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++) {
|
||||||
{
|
|
||||||
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
|
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,22 +33,18 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i)
|
if (i) {
|
||||||
{
|
for (j = i; j < 3; j++) {
|
||||||
for (j = i; j < 3; j++)
|
|
||||||
{
|
|
||||||
a3[j] = '\0';
|
a3[j] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
a3_to_a4(a4, a3);
|
a3_to_a4(a4, a3);
|
||||||
|
|
||||||
for (j = 0; j < i + 1; j++)
|
for (j = 0; j < i + 1; j++) {
|
||||||
{
|
|
||||||
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
|
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((i++ < 3))
|
while ((i++ < 3)) {
|
||||||
{
|
|
||||||
(*out)[enc_len++] = '=';
|
(*out)[enc_len++] = '=';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,8 +52,7 @@ public:
|
|||||||
return (enc_len == out->size());
|
return (enc_len == out->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Encode(const char* input, size_t input_length, char* out, size_t out_length)
|
static bool Encode(const char *input, size_t input_length, char *out, size_t out_length) {
|
||||||
{
|
|
||||||
int i = 0, j = 0;
|
int i = 0, j = 0;
|
||||||
char *out_begin = out;
|
char *out_begin = out;
|
||||||
unsigned char a3[3];
|
unsigned char a3[3];
|
||||||
@@ -70,18 +60,14 @@ public:
|
|||||||
|
|
||||||
size_t encoded_length = EncodedLength(input_length);
|
size_t encoded_length = EncodedLength(input_length);
|
||||||
|
|
||||||
if (out_length < encoded_length)
|
if (out_length < encoded_length) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
while (input_length--)
|
while (input_length--) {
|
||||||
{
|
|
||||||
a3[i++] = *input++;
|
a3[i++] = *input++;
|
||||||
if (i == 3)
|
if (i == 3) {
|
||||||
{
|
|
||||||
a3_to_a4(a4, a3);
|
a3_to_a4(a4, a3);
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++) {
|
||||||
{
|
|
||||||
*out++ = kBase64Alphabet[a4[i]];
|
*out++ = kBase64Alphabet[a4[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,22 +75,18 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i)
|
if (i) {
|
||||||
{
|
for (j = i; j < 3; j++) {
|
||||||
for (j = i; j < 3; j++)
|
|
||||||
{
|
|
||||||
a3[j] = '\0';
|
a3[j] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
a3_to_a4(a4, a3);
|
a3_to_a4(a4, a3);
|
||||||
|
|
||||||
for (j = 0; j < i + 1; j++)
|
for (j = 0; j < i + 1; j++) {
|
||||||
{
|
|
||||||
*out++ = kBase64Alphabet[a4[j]];
|
*out++ = kBase64Alphabet[a4[j]];
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((i++ < 3))
|
while ((i++ < 3)) {
|
||||||
{
|
|
||||||
*out++ = '=';
|
*out++ = '=';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,8 +94,7 @@ public:
|
|||||||
return (out == (out_begin + encoded_length));
|
return (out == (out_begin + encoded_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Decode(const std::string& in, std::string* out)
|
static bool Decode(const std::string &in, std::string *out) {
|
||||||
{
|
|
||||||
int i = 0, j = 0;
|
int i = 0, j = 0;
|
||||||
size_t dec_len = 0;
|
size_t dec_len = 0;
|
||||||
unsigned char a3[3];
|
unsigned char a3[3];
|
||||||
@@ -124,25 +105,20 @@ public:
|
|||||||
|
|
||||||
out->resize(DecodedLength(in));
|
out->resize(DecodedLength(in));
|
||||||
|
|
||||||
while (input_len--)
|
while (input_len--) {
|
||||||
{
|
if (*input == '=') {
|
||||||
if (*input == '=')
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
a4[i++] = *(input++);
|
a4[i++] = *(input++);
|
||||||
if (i == 4)
|
if (i == 4) {
|
||||||
{
|
for (i = 0; i <4; i++) {
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
a4[i] = b64_lookup(a4[i]);
|
a4[i] = b64_lookup(a4[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
a4_to_a3(a3,a4);
|
a4_to_a3(a3,a4);
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++) {
|
||||||
{
|
|
||||||
(*out)[dec_len++] = a3[i];
|
(*out)[dec_len++] = a3[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,22 +126,18 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i)
|
if (i) {
|
||||||
{
|
for (j = i; j < 4; j++) {
|
||||||
for (j = i; j < 4; j++)
|
|
||||||
{
|
|
||||||
a4[j] = '\0';
|
a4[j] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++) {
|
||||||
{
|
|
||||||
a4[j] = b64_lookup(a4[j]);
|
a4[j] = b64_lookup(a4[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
a4_to_a3(a3,a4);
|
a4_to_a3(a3,a4);
|
||||||
|
|
||||||
for (j = 0; j < i - 1; j++)
|
for (j = 0; j < i - 1; j++) {
|
||||||
{
|
|
||||||
(*out)[dec_len++] = a3[j];
|
(*out)[dec_len++] = a3[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,8 +145,7 @@ public:
|
|||||||
return (dec_len == out->size());
|
return (dec_len == out->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Decode(const char* input, size_t input_length, char* out, size_t out_length)
|
static bool Decode(const char *input, size_t input_length, char *out, size_t out_length) {
|
||||||
{
|
|
||||||
int i = 0, j = 0;
|
int i = 0, j = 0;
|
||||||
char *out_begin = out;
|
char *out_begin = out;
|
||||||
unsigned char a3[3];
|
unsigned char a3[3];
|
||||||
@@ -182,28 +153,22 @@ public:
|
|||||||
|
|
||||||
size_t decoded_length = DecodedLength(input, input_length);
|
size_t decoded_length = DecodedLength(input, input_length);
|
||||||
|
|
||||||
if (out_length < decoded_length)
|
if (out_length < decoded_length) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
while (input_length--)
|
while (input_length--) {
|
||||||
{
|
if (*input == '=') {
|
||||||
if (*input == '=')
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
a4[i++] = *(input++);
|
a4[i++] = *(input++);
|
||||||
if (i == 4)
|
if (i == 4) {
|
||||||
{
|
for (i = 0; i <4; i++) {
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
a4[i] = b64_lookup(a4[i]);
|
a4[i] = b64_lookup(a4[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
a4_to_a3(a3,a4);
|
a4_to_a3(a3,a4);
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++) {
|
||||||
{
|
|
||||||
*out++ = a3[i];
|
*out++ = a3[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,22 +176,18 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i)
|
if (i) {
|
||||||
{
|
for (j = i; j < 4; j++) {
|
||||||
for (j = i; j < 4; j++)
|
|
||||||
{
|
|
||||||
a4[j] = '\0';
|
a4[j] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++) {
|
||||||
{
|
|
||||||
a4[j] = b64_lookup(a4[j]);
|
a4[j] = b64_lookup(a4[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
a4_to_a3(a3,a4);
|
a4_to_a3(a3,a4);
|
||||||
|
|
||||||
for (j = 0; j < i - 1; j++)
|
for (j = 0; j < i - 1; j++) {
|
||||||
{
|
|
||||||
*out++ = a3[j];
|
*out++ = a3[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,71 +195,62 @@ public:
|
|||||||
return (out == (out_begin + decoded_length));
|
return (out == (out_begin + decoded_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int DecodedLength(const char* in, size_t in_length)
|
static int DecodedLength(const char *in, size_t in_length) {
|
||||||
{
|
|
||||||
int numEq = 0;
|
int numEq = 0;
|
||||||
|
|
||||||
const char *in_end = in + in_length;
|
const char *in_end = in + in_length;
|
||||||
while (*--in_end == '=')
|
while (*--in_end == '=') ++numEq;
|
||||||
++numEq;
|
|
||||||
|
|
||||||
return ((6 * in_length) / 8) - numEq;
|
return ((6 * in_length) / 8) - numEq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int DecodedLength(const std::string& in)
|
static int DecodedLength(const std::string &in) {
|
||||||
{
|
|
||||||
int numEq = 0;
|
int numEq = 0;
|
||||||
int n = in.size();
|
int n = in.size();
|
||||||
|
|
||||||
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it)
|
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) {
|
||||||
{
|
|
||||||
++numEq;
|
++numEq;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((6 * n) / 8) - numEq;
|
return ((6 * n) / 8) - numEq;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static int EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; }
|
inline static int EncodedLength(size_t length) {
|
||||||
|
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
|
||||||
|
}
|
||||||
|
|
||||||
inline static int EncodedLength(const std::string& in) { return EncodedLength(in.length()); }
|
inline static int EncodedLength(const std::string &in) {
|
||||||
|
return EncodedLength(in.length());
|
||||||
|
}
|
||||||
|
|
||||||
inline static void StripPadding(std::string* in)
|
inline static void StripPadding(std::string *in) {
|
||||||
{
|
while (!in->empty() && *(in->rbegin()) == '=') in->resize(in->size() - 1);
|
||||||
while (!in->empty() && *(in->rbegin()) == '=')
|
|
||||||
in->resize(in->size() - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline void a3_to_a4(unsigned char* a4, unsigned char* a3)
|
static inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
|
||||||
{
|
|
||||||
a4[0] = (a3[0] & 0xfc) >> 2;
|
a4[0] = (a3[0] & 0xfc) >> 2;
|
||||||
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
|
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
|
||||||
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
|
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
|
||||||
a4[3] = (a3[2] & 0x3f);
|
a4[3] = (a3[2] & 0x3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void a4_to_a3(unsigned char* a3, unsigned char* a4)
|
static inline void a4_to_a3(unsigned char * a3, unsigned char * a4) {
|
||||||
{
|
|
||||||
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
|
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
|
||||||
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
|
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
|
||||||
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
|
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned char b64_lookup(unsigned char c)
|
static inline unsigned char b64_lookup(unsigned char c) {
|
||||||
{
|
if(c >='A' && c <='Z') return c - 'A';
|
||||||
if (c >= 'A' && c <= 'Z')
|
if(c >='a' && c <='z') return c - 71;
|
||||||
return c - 'A';
|
if(c >='0' && c <='9') return c + 4;
|
||||||
if (c >= 'a' && c <= 'z')
|
if(c == '+') return 62;
|
||||||
return c - 71;
|
if(c == '/') return 63;
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
return c + 4;
|
|
||||||
if (c == '+')
|
|
||||||
return 62;
|
|
||||||
if (c == '/')
|
|
||||||
return 63;
|
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // HL_BASE64_H
|
#endif // HL_BASE64_H
|
||||||
+130
-143
@@ -40,14 +40,25 @@ typedef Item* PItem;
|
|||||||
class Item
|
class Item
|
||||||
{
|
{
|
||||||
friend class Calc::Parser;
|
friend class Calc::Parser;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool isVariable() const { return mType == Type::Var; }
|
bool isVariable() const
|
||||||
bool isFixed() const { return mType == Type::Number || mType == Type::String; }
|
{
|
||||||
|
return mType == Type::Var;
|
||||||
|
}
|
||||||
|
bool isFixed() const
|
||||||
|
{
|
||||||
|
return mType == Type::Number || mType == Type::String;
|
||||||
|
}
|
||||||
|
|
||||||
bool isOperation() const { return mType >= Type::And && mType <= Type::Div; }
|
bool isOperation() const
|
||||||
|
{
|
||||||
|
return mType >= Type::And && mType <= Type::Div;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasBrackets() const { return mHasBrackets; }
|
bool hasBrackets() const
|
||||||
|
{
|
||||||
|
return mHasBrackets;
|
||||||
|
}
|
||||||
|
|
||||||
int getOperatorLevel() const
|
int getOperatorLevel() const
|
||||||
{
|
{
|
||||||
@@ -83,13 +94,25 @@ public:
|
|||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Type getType() const { return mType; }
|
Type getType() const
|
||||||
|
{
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
std::string getName() const { return mName; }
|
std::string getName() const
|
||||||
|
{
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
Variant& value() { return mValue; }
|
Variant& value()
|
||||||
|
{
|
||||||
|
return mValue;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<PItem>& children() { return mChildren; }
|
std::vector<PItem>& children()
|
||||||
|
{
|
||||||
|
return mChildren;
|
||||||
|
}
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> NameMap;
|
typedef std::map<std::string, std::string> NameMap;
|
||||||
|
|
||||||
@@ -103,54 +126,21 @@ public:
|
|||||||
oss << " ";
|
oss << " ";
|
||||||
switch (mType)
|
switch (mType)
|
||||||
{
|
{
|
||||||
case Type::Number:
|
case Type::Number: oss << mValue.asStdString(); break;
|
||||||
oss << mValue.asStdString();
|
case Type::String: oss << '"' << mValue.asStdString() << '"'; break;
|
||||||
break;
|
case Type::Var: { NameMap::const_iterator iter = nm.find(mName); oss << ((iter != nm.end()) ? iter->second : mName);} break;
|
||||||
case Type::String:
|
case Type::Add: oss << "+"; break;
|
||||||
oss << '"' << mValue.asStdString() << '"';
|
case Type::Mul: oss << "*"; break;
|
||||||
break;
|
case Type::Div: oss << "/"; break;
|
||||||
case Type::Var:
|
case Type::Sub: oss << "-"; break;
|
||||||
{
|
case Type::Equal: oss << "=="; break;
|
||||||
NameMap::const_iterator iter = nm.find(mName);
|
case Type::NotEqual: oss << "!="; break;
|
||||||
oss << ((iter != nm.end()) ? iter->second : mName);
|
case Type::Less: oss << "<"; break;
|
||||||
}
|
case Type::LessOrEqual: oss << "<="; break;
|
||||||
break;
|
case Type::Greater: oss << ">"; break;
|
||||||
case Type::Add:
|
case Type::GreatorOrEqual: oss << ">="; break;
|
||||||
oss << "+";
|
case Type::Or: oss << "or"; break;
|
||||||
break;
|
case Type::And: oss << "and"; break;
|
||||||
case Type::Mul:
|
|
||||||
oss << "*";
|
|
||||||
break;
|
|
||||||
case Type::Div:
|
|
||||||
oss << "/";
|
|
||||||
break;
|
|
||||||
case Type::Sub:
|
|
||||||
oss << "-";
|
|
||||||
break;
|
|
||||||
case Type::Equal:
|
|
||||||
oss << "==";
|
|
||||||
break;
|
|
||||||
case Type::NotEqual:
|
|
||||||
oss << "!=";
|
|
||||||
break;
|
|
||||||
case Type::Less:
|
|
||||||
oss << "<";
|
|
||||||
break;
|
|
||||||
case Type::LessOrEqual:
|
|
||||||
oss << "<=";
|
|
||||||
break;
|
|
||||||
case Type::Greater:
|
|
||||||
oss << ">";
|
|
||||||
break;
|
|
||||||
case Type::GreatorOrEqual:
|
|
||||||
oss << ">=";
|
|
||||||
break;
|
|
||||||
case Type::Or:
|
|
||||||
oss << "or";
|
|
||||||
break;
|
|
||||||
case Type::And:
|
|
||||||
oss << "and";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("operator expected");
|
throw std::runtime_error("operator expected");
|
||||||
}
|
}
|
||||||
@@ -177,56 +167,23 @@ public:
|
|||||||
switch (mType)
|
switch (mType)
|
||||||
{
|
{
|
||||||
case Type::Number:
|
case Type::Number:
|
||||||
case Type::String:
|
case Type::String: result = mValue; break;
|
||||||
result = mValue;
|
|
||||||
|
case Type::Var: { auto iter = vm.find(mName); if (iter != vm.end()) return iter->second; else throw std::runtime_error("Variable " + mName + " did not find."); }
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type::Var:
|
case Type::Add: result = left + right; break;
|
||||||
{
|
case Type::Mul: result = left * right; break;
|
||||||
auto iter = vm.find(mName);
|
case Type::Div: result = left / right; break;
|
||||||
if (iter != vm.end())
|
case Type::Sub: result = left - right; break;
|
||||||
return iter->second;
|
case Type::Equal: result = left == right; break;
|
||||||
else
|
case Type::NotEqual: result = left != right; break;
|
||||||
throw std::runtime_error("Variable " + mName + " did not find.");
|
case Type::Less: result = left < right; break;
|
||||||
}
|
case Type::LessOrEqual: result = left <= right; break;
|
||||||
break;
|
case Type::Greater: result = left > right; break;
|
||||||
|
case Type::GreatorOrEqual: result = left >= right; break;
|
||||||
case Type::Add:
|
case Type::Or: result = left.asBool() || right.asBool(); break;
|
||||||
result = left + right;
|
case Type::And: result = left.asBool() && right.asBool(); break;
|
||||||
break;
|
|
||||||
case Type::Mul:
|
|
||||||
result = left * right;
|
|
||||||
break;
|
|
||||||
case Type::Div:
|
|
||||||
result = left / right;
|
|
||||||
break;
|
|
||||||
case Type::Sub:
|
|
||||||
result = left - right;
|
|
||||||
break;
|
|
||||||
case Type::Equal:
|
|
||||||
result = left == right;
|
|
||||||
break;
|
|
||||||
case Type::NotEqual:
|
|
||||||
result = left != right;
|
|
||||||
break;
|
|
||||||
case Type::Less:
|
|
||||||
result = left < right;
|
|
||||||
break;
|
|
||||||
case Type::LessOrEqual:
|
|
||||||
result = left <= right;
|
|
||||||
break;
|
|
||||||
case Type::Greater:
|
|
||||||
result = left > right;
|
|
||||||
break;
|
|
||||||
case Type::GreatorOrEqual:
|
|
||||||
result = left >= right;
|
|
||||||
break;
|
|
||||||
case Type::Or:
|
|
||||||
result = left.asBool() || right.asBool();
|
|
||||||
break;
|
|
||||||
case Type::And:
|
|
||||||
result = left.asBool() && right.asBool();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
@@ -247,7 +204,7 @@ private:
|
|||||||
std::vector<PItem> mChildren;
|
std::vector<PItem> mChildren;
|
||||||
bool mHasBrackets = false;
|
bool mHasBrackets = false;
|
||||||
};
|
};
|
||||||
} // namespace Ast
|
}
|
||||||
|
|
||||||
static bool ishex(int c)
|
static bool ishex(int c)
|
||||||
{
|
{
|
||||||
@@ -277,9 +234,15 @@ private:
|
|||||||
LexemType mType = LexemType::None;
|
LexemType mType = LexemType::None;
|
||||||
std::string mValue;
|
std::string mValue;
|
||||||
|
|
||||||
operator bool() const { return mType != LexemType::None; }
|
operator bool () const
|
||||||
|
{
|
||||||
|
return mType != LexemType::None;
|
||||||
|
}
|
||||||
|
|
||||||
std::string toString() const { return std::to_string((int)mType) + " : " + mValue; }
|
std::string toString() const
|
||||||
|
{
|
||||||
|
return std::to_string((int)mType) + " : " + mValue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Lexem mCurrentLexem;
|
Lexem mCurrentLexem;
|
||||||
|
|
||||||
@@ -289,16 +252,20 @@ private:
|
|||||||
|
|
||||||
if (c == '(')
|
if (c == '(')
|
||||||
mCurrentLexem.mType = LexemType::OpenBracket;
|
mCurrentLexem.mType = LexemType::OpenBracket;
|
||||||
else if (c == ')')
|
else
|
||||||
|
if (c == ')')
|
||||||
mCurrentLexem.mType = LexemType::CloseBracket;
|
mCurrentLexem.mType = LexemType::CloseBracket;
|
||||||
else if (isdigit(c))
|
else
|
||||||
|
if (isdigit(c))
|
||||||
mCurrentLexem.mType = LexemType::Dec;
|
mCurrentLexem.mType = LexemType::Dec;
|
||||||
else if (isalpha(c))
|
else
|
||||||
|
if (isalpha(c))
|
||||||
mCurrentLexem.mType = LexemType::Var;
|
mCurrentLexem.mType = LexemType::Var;
|
||||||
else if (c == '+' || c == '-' || c == '/' || c == '*' || c == '=' || c == '<' || c == '>' || c == '&' ||
|
else
|
||||||
c == '|')
|
if (c == '+' || c == '-' || c == '/' || c == '*' || c == '=' || c == '<' || c == '>' || c == '&' || c == '|')
|
||||||
mCurrentLexem.mType = LexemType::Oper;
|
mCurrentLexem.mType = LexemType::Oper;
|
||||||
else if (c == '"')
|
else
|
||||||
|
if (c == '"')
|
||||||
mCurrentLexem.mType = LexemType::Str;
|
mCurrentLexem.mType = LexemType::Str;
|
||||||
else
|
else
|
||||||
return Lexem();
|
return Lexem();
|
||||||
@@ -316,9 +283,15 @@ private:
|
|||||||
|
|
||||||
if (mCurrentLexem.mType == LexemType::Oper)
|
if (mCurrentLexem.mType == LexemType::Oper)
|
||||||
{
|
{
|
||||||
if (mCurrentLexem.mValue == "+" || mCurrentLexem.mValue == "-" || mCurrentLexem.mValue == "*" ||
|
if (mCurrentLexem.mValue == "+" ||
|
||||||
mCurrentLexem.mValue == "/" || mCurrentLexem.mValue == ">=" || mCurrentLexem.mValue == "<=" ||
|
mCurrentLexem.mValue == "-" ||
|
||||||
mCurrentLexem.mValue == "==" || mCurrentLexem.mValue == "||" || mCurrentLexem.mValue == "&&")
|
mCurrentLexem.mValue == "*" ||
|
||||||
|
mCurrentLexem.mValue == "/" ||
|
||||||
|
mCurrentLexem.mValue == ">=" ||
|
||||||
|
mCurrentLexem.mValue == "<=" ||
|
||||||
|
mCurrentLexem.mValue == "==" ||
|
||||||
|
mCurrentLexem.mValue == "||" ||
|
||||||
|
mCurrentLexem.mValue == "&&")
|
||||||
{
|
{
|
||||||
// Lexem finished
|
// Lexem finished
|
||||||
result = mCurrentLexem;
|
result = mCurrentLexem;
|
||||||
@@ -339,7 +312,7 @@ private:
|
|||||||
{
|
{
|
||||||
// Dot is here - is it float
|
// Dot is here - is it float
|
||||||
bool isFloat = false;
|
bool isFloat = false;
|
||||||
strx::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
|
StringHelper::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
|
||||||
if (isFloat)
|
if (isFloat)
|
||||||
mCurrentLexem.mType = LexemType::Float;
|
mCurrentLexem.mType = LexemType::Float;
|
||||||
else
|
else
|
||||||
@@ -384,7 +357,8 @@ private:
|
|||||||
case LexemType::Dec:
|
case LexemType::Dec:
|
||||||
if (c == 'x' && mCurrentLexem.mValue == "0")
|
if (c == 'x' && mCurrentLexem.mValue == "0")
|
||||||
mCurrentLexem.mType = LexemType::Hex;
|
mCurrentLexem.mType = LexemType::Hex;
|
||||||
else if (isdigit(c) || c == '.')
|
else
|
||||||
|
if (isdigit(c) || c == '.')
|
||||||
{
|
{
|
||||||
mCurrentLexem.mValue.push_back(c);
|
mCurrentLexem.mValue.push_back(c);
|
||||||
}
|
}
|
||||||
@@ -440,14 +414,14 @@ private:
|
|||||||
|
|
||||||
if (putback)
|
if (putback)
|
||||||
input.putback(c);
|
input.putback(c);
|
||||||
else if (!result)
|
else
|
||||||
|
if (!result)
|
||||||
c = input.get();
|
c = input.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNumericLexem();
|
checkNumericLexem();
|
||||||
|
|
||||||
// Recover partially processed lexem - maybe we finish processing at all but there is dec / float / string /
|
// Recover partially processed lexem - maybe we finish processing at all but there is dec / float / string / variable
|
||||||
// variable
|
|
||||||
if (mCurrentLexem.mType != LexemType::None && result.mType == LexemType::None)
|
if (mCurrentLexem.mType != LexemType::None && result.mType == LexemType::None)
|
||||||
result = mCurrentLexem;
|
result = mCurrentLexem;
|
||||||
|
|
||||||
@@ -467,27 +441,38 @@ private:
|
|||||||
case LexemType::Oper:
|
case LexemType::Oper:
|
||||||
if (l.mValue == "-")
|
if (l.mValue == "-")
|
||||||
result->mType = Ast::Type::Sub;
|
result->mType = Ast::Type::Sub;
|
||||||
else if (l.mValue == "+")
|
else
|
||||||
|
if (l.mValue == "+")
|
||||||
result->mType = Ast::Type::Add;
|
result->mType = Ast::Type::Add;
|
||||||
else if (l.mValue == "*")
|
else
|
||||||
|
if (l.mValue == "*")
|
||||||
result->mType = Ast::Type::Mul;
|
result->mType = Ast::Type::Mul;
|
||||||
else if (l.mValue == "/")
|
else
|
||||||
|
if (l.mValue == "/")
|
||||||
result->mType = Ast::Type::Div;
|
result->mType = Ast::Type::Div;
|
||||||
else if (l.mValue == "<")
|
else
|
||||||
|
if (l.mValue == "<")
|
||||||
result->mType = Ast::Type::Less;
|
result->mType = Ast::Type::Less;
|
||||||
else if (l.mValue == "<=")
|
else
|
||||||
|
if (l.mValue == "<=")
|
||||||
result->mType = Ast::Type::LessOrEqual;
|
result->mType = Ast::Type::LessOrEqual;
|
||||||
else if (l.mValue == ">")
|
else
|
||||||
|
if (l.mValue == ">")
|
||||||
result->mType = Ast::Type::Greater;
|
result->mType = Ast::Type::Greater;
|
||||||
else if (l.mValue == ">=")
|
else
|
||||||
|
if (l.mValue == ">=")
|
||||||
result->mType = Ast::Type::GreatorOrEqual;
|
result->mType = Ast::Type::GreatorOrEqual;
|
||||||
else if (l.mValue == "==")
|
else
|
||||||
|
if (l.mValue == "==")
|
||||||
result->mType = Ast::Type::Equal;
|
result->mType = Ast::Type::Equal;
|
||||||
else if (l.mValue == "!=")
|
else
|
||||||
|
if (l.mValue == "!=")
|
||||||
result->mType = Ast::Type::NotEqual;
|
result->mType = Ast::Type::NotEqual;
|
||||||
else if (l.mValue == "&&")
|
else
|
||||||
|
if (l.mValue == "&&")
|
||||||
result->mType = Ast::Type::And;
|
result->mType = Ast::Type::And;
|
||||||
else if (l.mValue == "||")
|
else
|
||||||
|
if (l.mValue == "||")
|
||||||
result->mType = Ast::Type::Or;
|
result->mType = Ast::Type::Or;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -503,7 +488,7 @@ private:
|
|||||||
|
|
||||||
case LexemType::Hex:
|
case LexemType::Hex:
|
||||||
result->mType = Ast::Type::Number;
|
result->mType = Ast::Type::Number;
|
||||||
result->mValue = strx::fromHex2Int(l.mValue);
|
result->mValue = StringHelper::fromHex2Int(l.mValue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LexemType::Float:
|
case LexemType::Float:
|
||||||
@@ -524,11 +509,11 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Lexem mLexem;
|
Lexem mLexem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Ast::PItem parseExpression(std::istream& input)
|
Ast::PItem parseExpression(std::istream& input)
|
||||||
{
|
{
|
||||||
Ast::PItem operationNode(nullptr), leftNode(nullptr), rightNode(nullptr), currentOperation(nullptr);
|
Ast::PItem operationNode(nullptr), leftNode(nullptr), rightNode(nullptr),
|
||||||
|
currentOperation(nullptr);
|
||||||
|
|
||||||
// While we have lexem
|
// While we have lexem
|
||||||
while (mLexem = getLexem(input))
|
while (mLexem = getLexem(input))
|
||||||
@@ -560,7 +545,8 @@ public:
|
|||||||
throw std::runtime_error("Open bracket or constant / number / string / variable expected.");
|
throw std::runtime_error("Open bracket or constant / number / string / variable expected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!operationNode)
|
else
|
||||||
|
if (!operationNode)
|
||||||
{
|
{
|
||||||
// Well, there is left node already
|
// Well, there is left node already
|
||||||
// See operation here
|
// See operation here
|
||||||
@@ -587,12 +573,10 @@ public:
|
|||||||
// Parse rest of expression
|
// Parse rest of expression
|
||||||
rightNode = parseExpression(input);
|
rightNode = parseExpression(input);
|
||||||
|
|
||||||
// If right part of expression is operation - make left side child of right part - to allow calculation
|
// If right part of expression is operation - make left side child of right part - to allow calculation in right order
|
||||||
// in right order
|
|
||||||
if (operationNode)
|
if (operationNode)
|
||||||
{
|
{
|
||||||
if (rightNode->isOperation() &&
|
if (rightNode->isOperation() && rightNode->getOperatorLevel() <= operationNode->getOperatorLevel() && !rightNode->hasBrackets())
|
||||||
rightNode->getOperatorLevel() <= operationNode->getOperatorLevel() && !rightNode->hasBrackets())
|
|
||||||
{
|
{
|
||||||
// Get left child of right expression - make it our right child
|
// Get left child of right expression - make it our right child
|
||||||
operationNode->children().push_back(leftNode);
|
operationNode->children().push_back(leftNode);
|
||||||
@@ -615,7 +599,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Ast::PItem parse(std::istream& input) { return nullptr; }
|
Ast::PItem parse(std::istream& input)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void testLexemParser(const std::string& test)
|
void testLexemParser(const std::string& test)
|
||||||
{
|
{
|
||||||
@@ -633,7 +620,7 @@ class Worker
|
|||||||
public:
|
public:
|
||||||
Variant eval(Ast::PItem ast);
|
Variant eval(Ast::PItem ast);
|
||||||
};
|
};
|
||||||
} // namespace Calc
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -49,10 +49,8 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
|
|||||||
|
|
||||||
CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW");
|
CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW");
|
||||||
CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall");
|
CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall");
|
||||||
CrInstallIntoCurrentThread =
|
CrInstallIntoCurrentThread = (CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2");
|
||||||
(CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2");
|
CrUninstallFromCurrentThread = (CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread");
|
||||||
CrUninstallFromCurrentThread =
|
|
||||||
(CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread");
|
|
||||||
CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW");
|
CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW");
|
||||||
#else
|
#else
|
||||||
CrInstall = &crInstallW;
|
CrInstall = &crInstallW;
|
||||||
@@ -72,7 +70,8 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
|
|||||||
{
|
{
|
||||||
std::wstring appname, version, url;
|
std::wstring appname, version, url;
|
||||||
} unicode;
|
} unicode;
|
||||||
unicode.appname = StringHelper::makeTstring(appname), unicode.version = StringHelper::makeTstring(version),
|
unicode.appname = StringHelper::makeTstring(appname),
|
||||||
|
unicode.version = StringHelper::makeTstring(version),
|
||||||
unicode.url = StringHelper::makeTstring(url);
|
unicode.url = StringHelper::makeTstring(url);
|
||||||
|
|
||||||
if (unicode.appname.empty())
|
if (unicode.appname.empty())
|
||||||
@@ -104,8 +103,7 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
|
|||||||
wchar_t errorMsg[512] = L"";
|
wchar_t errorMsg[512] = L"";
|
||||||
CrGetLastErrorMsg(errorMsg, 512);
|
CrGetLastErrorMsg(errorMsg, 512);
|
||||||
OutputDebugStringW(errorMsg);
|
OutputDebugStringW(errorMsg);
|
||||||
// LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " <<
|
//LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " << errorMsg);
|
||||||
// errorMsg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -122,8 +120,7 @@ void CrashReporter::free()
|
|||||||
CrUninstallFromCurrentThread = nullptr;
|
CrUninstallFromCurrentThread = nullptr;
|
||||||
CrGetLastErrorMsg = nullptr;
|
CrGetLastErrorMsg = nullptr;
|
||||||
#if defined(CRASHRPT_DYNAMIC)
|
#if defined(CRASHRPT_DYNAMIC)
|
||||||
::FreeLibrary(CrLibraryHandle);
|
::FreeLibrary(CrLibraryHandle); CrLibraryHandle = NULL;
|
||||||
CrLibraryHandle = NULL;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -132,8 +129,8 @@ void CrashReporter::free()
|
|||||||
bool CrashReporter::isLoaded()
|
bool CrashReporter::isLoaded()
|
||||||
{
|
{
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg || !CrInstallIntoCurrentThread ||
|
return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg ||
|
||||||
!CrUninstallFromCurrentThread);
|
!CrInstallIntoCurrentThread || !CrUninstallFromCurrentThread);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@@ -177,12 +174,20 @@ CrashReporterGuard::~CrashReporterGuard()
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
CrashReporterThreadPoint::CrashReporterThreadPoint() {}
|
CrashReporterThreadPoint::CrashReporterThreadPoint()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
CrashReporterThreadPoint::~CrashReporterThreadPoint() {}
|
CrashReporterThreadPoint::~CrashReporterThreadPoint()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
CrashReporterGuard::CrashReporterGuard() {}
|
CrashReporterGuard::CrashReporterGuard()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
CrashReporterGuard::~CrashReporterGuard() {}
|
CrashReporterGuard::~CrashReporterGuard()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class SE_Exception
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
unsigned int nSE;
|
unsigned int nSE;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SE_Exception() {}
|
SE_Exception() {}
|
||||||
SE_Exception(unsigned int n) : nSE(n) {}
|
SE_Exception(unsigned int n) : nSE(n) {}
|
||||||
|
|||||||
@@ -2,9 +2,12 @@
|
|||||||
#include "HL_String.h"
|
#include "HL_String.h"
|
||||||
|
|
||||||
// --------- CsvFile ----------------
|
// --------- CsvFile ----------------
|
||||||
CsvReader::CsvReader(std::istream& stream) : mInputStream(stream) {}
|
CsvReader::CsvReader(std::istream& stream)
|
||||||
|
:mInputStream(stream)
|
||||||
|
{}
|
||||||
|
|
||||||
CsvReader::~CsvReader() {}
|
CsvReader::~CsvReader()
|
||||||
|
{}
|
||||||
|
|
||||||
std::istream& CsvReader::stream() const
|
std::istream& CsvReader::stream() const
|
||||||
{
|
{
|
||||||
@@ -17,10 +20,10 @@ bool CsvReader::readLine(std::vector<std::string>& cells)
|
|||||||
std::string line;
|
std::string line;
|
||||||
if (!std::getline(mInputStream, line))
|
if (!std::getline(mInputStream, line))
|
||||||
return false;
|
return false;
|
||||||
strx::trim(line);
|
StringHelper::trim(line);
|
||||||
if (line.empty())
|
if (line.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
strx::split(line, cells, ",;");
|
StringHelper::split(line, cells, ",;");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <cstdio>
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
ERR_MEDIA_SOCKET_FAILED = 1, // Failed to create media socket
|
ERR_MEDIA_SOCKET_FAILED = 1, // Failed to create media socket
|
||||||
@@ -41,9 +41,10 @@ enum
|
|||||||
class Exception: public std::exception
|
class Exception: public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Exception(int code, int subcode = 0) : mCode(code), mSubcode(subcode)
|
Exception(int code, int subcode = 0)
|
||||||
|
:mCode(code), mSubcode(subcode)
|
||||||
{
|
{
|
||||||
std::snprintf(mMessage, sizeof(mMessage), "%d-%d", code, subcode);
|
sprintf(mMessage, "%d-%d", code, subcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Exception(int code, const char* message)
|
Exception(int code, const char* message)
|
||||||
@@ -52,22 +53,33 @@ public:
|
|||||||
strncpy(mMessage, message, (sizeof mMessage) - 1 );
|
strncpy(mMessage, message, (sizeof mMessage) - 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
Exception(const Exception& src) : mCode(src.mCode), mSubcode(src.mSubcode)
|
Exception(const Exception& src)
|
||||||
|
:mCode(src.mCode), mSubcode(src.mSubcode)
|
||||||
{
|
{
|
||||||
memcpy(mMessage, src.mMessage, sizeof mMessage);
|
memcpy(mMessage, src.mMessage, sizeof mMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Exception() {}
|
~Exception()
|
||||||
|
{ }
|
||||||
|
|
||||||
int code() const { return mCode; }
|
int code() const
|
||||||
|
{
|
||||||
|
return mCode;
|
||||||
|
}
|
||||||
|
|
||||||
int subcode() const { return mSubcode; }
|
int subcode() const
|
||||||
|
{
|
||||||
|
return mSubcode;
|
||||||
|
}
|
||||||
|
|
||||||
const char* what() const noexcept { return mMessage; }
|
const char* what() const noexcept
|
||||||
|
{
|
||||||
|
return mMessage;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int mCode = 0, mSubcode = 0;
|
int mCode, mSubcode;
|
||||||
char mMessage[256] = {0};
|
char mMessage[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "HL_File.h"
|
#include "HL_File.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <sys/statvfs.h>
|
# include <sys/statvfs.h>
|
||||||
# include <memory.h>
|
# include <memory.h>
|
||||||
@@ -32,24 +32,23 @@ void FileHelper::remove(const char* s)
|
|||||||
::remove(s);
|
::remove(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::string FileHelper::gettempname()
|
std::string FileHelper::gettempname()
|
||||||
// {
|
{
|
||||||
// #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
#if defined(TARGET_LINUX)
|
||||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||||
// int code = mkstemp(template_filename);
|
mkstemp(template_filename);
|
||||||
|
return template_filename;
|
||||||
|
#elif defined(TARGET_WIN)
|
||||||
|
char buffer[L_tmpnam];
|
||||||
|
tmpnam(buffer);
|
||||||
|
|
||||||
// return template_filename;
|
return buffer;
|
||||||
// #elif defined(TARGET_WIN)
|
#elif defined(TARGET_OSX)
|
||||||
// char buffer[L_tmpnam];
|
char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
||||||
// tmpnam(buffer);
|
mktemp(template_filename);
|
||||||
|
return template_filename;
|
||||||
// return buffer;
|
#endif
|
||||||
// #elif defined(TARGET_OSX)
|
}
|
||||||
// char template_filename[L_tmpnam] = "rtphone_XXXXXXX.tmp";
|
|
||||||
// mktemp(template_filename);
|
|
||||||
// return template_filename;
|
|
||||||
// #endif
|
|
||||||
// }
|
|
||||||
|
|
||||||
bool FileHelper::isAbsolute(const std::string& s)
|
bool FileHelper::isAbsolute(const std::string& s)
|
||||||
{
|
{
|
||||||
@@ -65,7 +64,7 @@ std::string FileHelper::getCurrentDir()
|
|||||||
return std::string();
|
return std::string();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||||
char buf[512];
|
char buf[512];
|
||||||
if (getcwd(buf, sizeof buf) != nullptr)
|
if (getcwd(buf, sizeof buf) != nullptr)
|
||||||
return buf;
|
return buf;
|
||||||
@@ -102,8 +101,7 @@ size_t FileHelper::getFreespace(const std::string& path)
|
|||||||
size_t r = static_cast<size_t>(-1);
|
size_t r = static_cast<size_t>(-1);
|
||||||
|
|
||||||
#if defined(TARGET_LINUX)
|
#if defined(TARGET_LINUX)
|
||||||
struct statvfs stats;
|
struct statvfs stats; memset(&stats, 0, sizeof stats);
|
||||||
memset(&stats, 0, sizeof stats);
|
|
||||||
|
|
||||||
int retcode = statvfs(path.c_str(), &stats);
|
int retcode = statvfs(path.c_str(), &stats);
|
||||||
if (retcode == 0)
|
if (retcode == 0)
|
||||||
@@ -111,33 +109,3 @@ size_t FileHelper::getFreespace(const std::string& path)
|
|||||||
#endif
|
#endif
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileHelper::expandUserHome(const std::string& path)
|
|
||||||
{
|
|
||||||
if (path.empty() || path[0] != '~')
|
|
||||||
return path; // No expansion needed
|
|
||||||
|
|
||||||
const char* home_dir = nullptr;
|
|
||||||
|
|
||||||
#ifdef TARGET_WIN
|
|
||||||
home_dir = std::getenv("USERPROFILE");
|
|
||||||
if (!home_dir)
|
|
||||||
{
|
|
||||||
home_dir = std::getenv("HOMEDRIVE");
|
|
||||||
const char* homepath = std::getenv("HOMEPATH");
|
|
||||||
if (home_dir && homepath)
|
|
||||||
{
|
|
||||||
std::string fullpath(home_dir);
|
|
||||||
fullpath += homepath;
|
|
||||||
return fullpath + path.substr(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
home_dir = std::getenv("HOME");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!home_dir)
|
|
||||||
throw std::runtime_error("Unable to determine the home directory");
|
|
||||||
|
|
||||||
return std::string(home_dir) + path.substr(1);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public:
|
|||||||
static void remove(const std::string& s);
|
static void remove(const std::string& s);
|
||||||
static void remove(const char* s);
|
static void remove(const char* s);
|
||||||
|
|
||||||
// static std::string gettempname();
|
static std::string gettempname();
|
||||||
static bool isAbsolute(const std::string& s);
|
static bool isAbsolute(const std::string& s);
|
||||||
|
|
||||||
static std::string getCurrentDir();
|
static std::string getCurrentDir();
|
||||||
@@ -23,7 +23,6 @@ public:
|
|||||||
// Returns free space on volume for path
|
// Returns free space on volume for path
|
||||||
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
|
||||||
static size_t getFreespace(const std::string& path);
|
static size_t getFreespace(const std::string& path);
|
||||||
static std::string expandUserHome(const std::string& path);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ bool Packet::parseV3(const ByteBuffer& packet)
|
|||||||
|
|
||||||
if (!sourceAddr4.isEmpty())
|
if (!sourceAddr4.isEmpty())
|
||||||
mSourceAddress = sourceAddr4;
|
mSourceAddress = sourceAddr4;
|
||||||
else if (!sourceAddr6.isEmpty())
|
else
|
||||||
|
if (!sourceAddr6.isEmpty())
|
||||||
mSourceAddress = sourceAddr6;
|
mSourceAddress = sourceAddr6;
|
||||||
|
|
||||||
if (!mSourceAddress.isEmpty())
|
if (!mSourceAddress.isEmpty())
|
||||||
@@ -110,7 +111,8 @@ bool Packet::parseV3(const ByteBuffer& packet)
|
|||||||
|
|
||||||
if (!destAddr4.isEmpty())
|
if (!destAddr4.isEmpty())
|
||||||
mDestinationAddress = destAddr4;
|
mDestinationAddress = destAddr4;
|
||||||
else if (!destAddr6.isEmpty())
|
else
|
||||||
|
if (!destAddr6.isEmpty())
|
||||||
mDestinationAddress = destAddr6;
|
mDestinationAddress = destAddr6;
|
||||||
|
|
||||||
if (!mDestinationAddress.isEmpty())
|
if (!mDestinationAddress.isEmpty())
|
||||||
@@ -146,53 +148,16 @@ bool Packet::parseV2(const ByteBuffer& packet)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WRITE_CHUNK_UCHAR(T, V) \
|
#define WRITE_CHUNK_UCHAR(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(1); w.writeUChar((uint8_t)V);}
|
||||||
{ \
|
#define WRITE_CHUNK_USHORT(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(2); w.writeUShort((uint16_t)V);}
|
||||||
w.writeUShort((uint16_t)mVendorId); \
|
#define WRITE_CHUNK_UINT(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(4); w.writeUInt((uint32_t)V);}
|
||||||
w.writeUShort((uint16_t)T); \
|
#define WRITE_CHUNK_IP4(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(4); w.writeIp(V);}
|
||||||
w.writeUShort(1); \
|
#define WRITE_CHUNK_IP6(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(8); w.writeIp(V);}
|
||||||
w.writeUChar((uint8_t)V); \
|
#define WRITE_CHUNK_BUFFER(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(8); w.writeBuffer(V.data(), V.size());}
|
||||||
}
|
|
||||||
#define WRITE_CHUNK_USHORT(T, V) \
|
|
||||||
{ \
|
|
||||||
w.writeUShort((uint16_t)mVendorId); \
|
|
||||||
w.writeUShort((uint16_t)T); \
|
|
||||||
w.writeUShort(2); \
|
|
||||||
w.writeUShort((uint16_t)V); \
|
|
||||||
}
|
|
||||||
#define WRITE_CHUNK_UINT(T, V) \
|
|
||||||
{ \
|
|
||||||
w.writeUShort((uint16_t)mVendorId); \
|
|
||||||
w.writeUShort((uint16_t)T); \
|
|
||||||
w.writeUShort(4); \
|
|
||||||
w.writeUInt((uint32_t)V); \
|
|
||||||
}
|
|
||||||
#define WRITE_CHUNK_IP4(T, V) \
|
|
||||||
{ \
|
|
||||||
w.writeUShort((uint16_t)mVendorId); \
|
|
||||||
w.writeUShort((uint16_t)T); \
|
|
||||||
w.writeUShort(4); \
|
|
||||||
w.writeIp(V); \
|
|
||||||
}
|
|
||||||
#define WRITE_CHUNK_IP6(T, V) \
|
|
||||||
{ \
|
|
||||||
w.writeUShort((uint16_t)mVendorId); \
|
|
||||||
w.writeUShort((uint16_t)T); \
|
|
||||||
w.writeUShort(8); \
|
|
||||||
w.writeIp(V); \
|
|
||||||
}
|
|
||||||
#define WRITE_CHUNK_BUFFER(T, V) \
|
|
||||||
{ \
|
|
||||||
w.writeUShort((uint16_t)mVendorId); \
|
|
||||||
w.writeUShort((uint16_t)T); \
|
|
||||||
w.writeUShort(8); \
|
|
||||||
w.writeBuffer(V.data(), V.size()); \
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer Packet::buildV3()
|
ByteBuffer Packet::buildV3()
|
||||||
{
|
{
|
||||||
ByteBuffer r;
|
ByteBuffer r; r.resize(mBody.size() + 512);
|
||||||
r.resize(mBody.size() + 512);
|
|
||||||
BufferWriter w(r);
|
BufferWriter w(r);
|
||||||
|
|
||||||
// Signature
|
// Signature
|
||||||
@@ -209,7 +174,8 @@ ByteBuffer Packet::buildV3()
|
|||||||
{
|
{
|
||||||
if (mSourceAddress.isV4())
|
if (mSourceAddress.isV4())
|
||||||
WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress)
|
WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress)
|
||||||
else if (mSourceAddress.isV6())
|
else
|
||||||
|
if (mSourceAddress.isV6())
|
||||||
WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
|
WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
|
||||||
|
|
||||||
WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port());
|
WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port());
|
||||||
@@ -220,7 +186,8 @@ ByteBuffer Packet::buildV3()
|
|||||||
{
|
{
|
||||||
if (mDestinationAddress.isV4())
|
if (mDestinationAddress.isV4())
|
||||||
WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress)
|
WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress)
|
||||||
else if (mDestinationAddress.isV6())
|
else
|
||||||
|
if (mDestinationAddress.isV6())
|
||||||
WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
|
WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
|
||||||
|
|
||||||
WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port());
|
WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port());
|
||||||
@@ -249,9 +216,7 @@ ByteBuffer Packet::buildV3()
|
|||||||
|
|
||||||
r.resize(w.offset());
|
r.resize(w.offset());
|
||||||
|
|
||||||
w.rewind();
|
w.rewind(); w.skip(4); w.writeUShort((uint16_t)r.size());
|
||||||
w.skip(4);
|
|
||||||
w.writeUShort((uint16_t)r.size());
|
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,13 @@ struct Packet
|
|||||||
bool parseV2(const ByteBuffer& packet);
|
bool parseV2(const ByteBuffer& packet);
|
||||||
ByteBuffer buildV3();
|
ByteBuffer buildV3();
|
||||||
|
|
||||||
uint8_t mIpProtocolFamily, mIpProtocolId;
|
uint8_t
|
||||||
|
mIpProtocolFamily,
|
||||||
|
mIpProtocolId;
|
||||||
|
|
||||||
InternetAddress mSourceAddress, mDestinationAddress;
|
InternetAddress
|
||||||
|
mSourceAddress,
|
||||||
|
mDestinationAddress;
|
||||||
|
|
||||||
timeval mTimestamp;
|
timeval mTimestamp;
|
||||||
ProtocolId mProtocolType;
|
ProtocolId mProtocolType;
|
||||||
@@ -76,6 +80,6 @@ struct Packet
|
|||||||
uint32_t mBodyOffset = 0;
|
uint32_t mBodyOffset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HEP
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1070
-903
File diff suppressed because it is too large
Load Diff
@@ -36,3 +36,4 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
#endif // HL_IUUP_H
|
#endif // HL_IUUP_H
|
||||||
|
|
||||||
|
|||||||
@@ -2,3 +2,4 @@
|
|||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
using ice::GLogger;
|
using ice::GLogger;
|
||||||
using ice::LogLock;
|
using ice::LogLock;
|
||||||
|
|
||||||
using ice::LL_CRITICAL;
|
|
||||||
using ice::LL_DEBUG;
|
|
||||||
using ice::LL_ERROR;
|
|
||||||
using ice::LL_INFO;
|
|
||||||
using ice::LL_MEDIA;
|
using ice::LL_MEDIA;
|
||||||
|
using ice::LL_DEBUG;
|
||||||
|
using ice::LL_INFO;
|
||||||
|
using ice::LL_CRITICAL;
|
||||||
using ice::LL_NONE;
|
using ice::LL_NONE;
|
||||||
using ice::LL_SPECIAL;
|
using ice::LL_SPECIAL;
|
||||||
|
using ice::LL_ERROR;
|
||||||
using ice::LogLevelHelper;
|
using ice::LogLevelHelper;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -11,36 +11,15 @@
|
|||||||
#define MPLS_STACK_MASK (0x00000100)
|
#define MPLS_STACK_MASK (0x00000100)
|
||||||
#define MPLS_STACK_SHIFT (8)
|
#define MPLS_STACK_SHIFT (8)
|
||||||
|
|
||||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForRaw(const Packet& data)
|
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||||
{
|
{
|
||||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
|
PacketData result(packet);
|
||||||
|
|
||||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(packet.mData);
|
||||||
return Payload();
|
|
||||||
|
|
||||||
|
|
||||||
switch (ip4->version())
|
|
||||||
{
|
|
||||||
case 4:
|
|
||||||
return GetUdpPayloadForIp4(data);
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
return GetUdpPayloadForIp6(data);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return Payload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
|
||||||
{
|
|
||||||
Packet result(data);
|
|
||||||
|
|
||||||
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(data.mData);
|
|
||||||
|
|
||||||
// Skip ethernet header
|
// Skip ethernet header
|
||||||
result.mData += sizeof(EthernetHeader);
|
packet.mData += sizeof(EthernetHeader);
|
||||||
result.mLength -= sizeof(EthernetHeader);
|
packet.mLength -= sizeof(EthernetHeader);
|
||||||
|
|
||||||
// See if there is Vlan header
|
// See if there is Vlan header
|
||||||
uint16_t proto = 0;
|
uint16_t proto = 0;
|
||||||
@@ -49,11 +28,12 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
|||||||
// Skip 1 or more VLAN headers
|
// Skip 1 or more VLAN headers
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(result.mData);
|
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData);
|
||||||
result.mData += sizeof(VlanHeader);
|
packet.mData += sizeof(VlanHeader);
|
||||||
result.mLength -= sizeof(VlanHeader);
|
packet.mLength -= sizeof(VlanHeader);
|
||||||
proto = ntohs(vlan->mData);
|
proto = ntohs(vlan->mData);
|
||||||
} while (proto == 0x8100);
|
}
|
||||||
|
while (proto == 0x8100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip MPLS headers
|
// Skip MPLS headers
|
||||||
@@ -63,10 +43,10 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
|||||||
case ETHERTYPE_MPLS_MC:
|
case ETHERTYPE_MPLS_MC:
|
||||||
// Parse MPLS here until marker "bottom of mpls stack"
|
// Parse MPLS here until marker "bottom of mpls stack"
|
||||||
for(bool bottomOfStack = false; !bottomOfStack;
|
for(bool bottomOfStack = false; !bottomOfStack;
|
||||||
bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
bottomOfStack = ((ntohl(*(uint32_t*)(packet.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
||||||
{
|
{
|
||||||
result.mData += 4;
|
packet.mData += 4;
|
||||||
result.mLength -= 4;
|
packet.mLength -=4;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -79,86 +59,86 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(result.mData);
|
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
|
||||||
|
|
||||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||||
return Payload();
|
return PacketData();
|
||||||
|
|
||||||
|
|
||||||
switch (ip4->version())
|
switch (ip4->version())
|
||||||
{
|
{
|
||||||
case 4:
|
case 4:
|
||||||
return GetUdpPayloadForIp4(result);
|
return GetUdpPayloadForIp4(packet, source, destination);
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
return GetUdpPayloadForIp6(result);
|
return GetUdpPayloadForIp6(packet, source, destination);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return Payload();
|
return PacketData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForSLL(const Packet& data)
|
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForSLL(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||||
{
|
{
|
||||||
Packet result(data);
|
PacketData result(packet);
|
||||||
|
|
||||||
if (result.mLength < 16)
|
if (packet.mLength < 16)
|
||||||
return Payload();
|
return PacketData();
|
||||||
|
|
||||||
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(result.mData);
|
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(packet.mData);
|
||||||
|
|
||||||
result.mData += sizeof(LinuxSllHeader);
|
packet.mData += sizeof(LinuxSllHeader);
|
||||||
result.mLength -= sizeof(LinuxSllHeader);
|
packet.mLength -= sizeof(LinuxSllHeader);
|
||||||
|
|
||||||
switch (ntohs(sll->mProtocolType))
|
switch (ntohs(sll->mProtocolType))
|
||||||
{
|
{
|
||||||
case 0x0800:
|
case 0x0800:
|
||||||
return GetUdpPayloadForIp4(result);
|
return GetUdpPayloadForIp4(packet, source, destination);
|
||||||
|
|
||||||
case 0x86DD:
|
case 0x86DD:
|
||||||
return GetUdpPayloadForIp6(result);
|
return GetUdpPayloadForIp6(packet, source, destination);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return Payload();
|
return PacketData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForLoopback(const Packet& data)
|
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForLoopback(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||||
{
|
{
|
||||||
Packet result(data);
|
PacketData result(packet);
|
||||||
|
|
||||||
if (result.mLength < 16)
|
if (packet.mLength < 16)
|
||||||
return Payload();
|
return PacketData();
|
||||||
|
|
||||||
struct LoopbackHeader
|
struct LoopbackHeader
|
||||||
{
|
{
|
||||||
uint32_t mProtocolType;
|
uint32_t mProtocolType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(result.mData);
|
const LoopbackHeader* lh = reinterpret_cast<const LoopbackHeader*>(packet.mData);
|
||||||
|
|
||||||
result.mData += sizeof(LoopbackHeader);
|
packet.mData += sizeof(LoopbackHeader);
|
||||||
result.mLength -= sizeof(LoopbackHeader);
|
packet.mLength -= sizeof(LoopbackHeader);
|
||||||
|
|
||||||
switch (lh->mProtocolType)
|
switch (lh->mProtocolType)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
return GetUdpPayloadForIp4(result);
|
return GetUdpPayloadForIp4(packet, source, destination);
|
||||||
|
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
return GetUdpPayloadForIp6(result);
|
return GetUdpPayloadForIp6(packet, source, destination);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return Payload();
|
return PacketData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
|
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||||
{
|
{
|
||||||
Packet result(data);
|
PacketData result(packet);
|
||||||
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
|
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
|
||||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||||
return Payload();
|
return PacketData(nullptr, 0);
|
||||||
|
|
||||||
result.mData += ip4->headerLength();
|
result.mData += ip4->headerLength();
|
||||||
result.mLength -= ip4->headerLength();
|
result.mLength -= ip4->headerLength();
|
||||||
@@ -172,27 +152,29 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
|
|||||||
if (length - sizeof(UdpHeader) < (size_t)result.mLength)
|
if (length - sizeof(UdpHeader) < (size_t)result.mLength)
|
||||||
result.mLength = length - sizeof(UdpHeader);
|
result.mLength = length - sizeof(UdpHeader);
|
||||||
|
|
||||||
InternetAddress addr_source;
|
source.setIp(ip4->mSource);
|
||||||
addr_source.setIp(ip4->mSource);
|
source.setPort(ntohs(udp->mSourcePort));
|
||||||
addr_source.setPort(ntohs(udp->mSourcePort));
|
|
||||||
|
|
||||||
InternetAddress addr_dest;
|
destination.setIp(ip4->mDestination);
|
||||||
addr_dest.setIp(ip4->mDestination);
|
destination.setPort(ntohs(udp->mDestinationPort));
|
||||||
addr_dest.setPort(ntohs(udp->mDestinationPort));
|
|
||||||
|
|
||||||
return {.data = result, .source = addr_source, .dest = addr_dest};
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Ip6Header
|
struct Ip6Header
|
||||||
{
|
{
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
uint8_t traffic_class_hi : 4, version : 4;
|
uint8_t traffic_class_hi:4,
|
||||||
uint8_t flow_label_hi : 4, traffic_class_lo : 4;
|
version:4;
|
||||||
|
uint8_t flow_label_hi:4,
|
||||||
|
traffic_class_lo:4;
|
||||||
uint16_t flow_label_lo;
|
uint16_t flow_label_lo;
|
||||||
|
|
||||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||||
uint8_t version : 4, traffic_class_hi : 4;
|
uint8_t version:4,
|
||||||
uint8_t traffic_class_lo : 4, flow_label_hi : 4;
|
traffic_class_hi:4;
|
||||||
|
uint8_t traffic_class_lo:4,
|
||||||
|
flow_label_hi:4;
|
||||||
uint16_t flow_label_lo;
|
uint16_t flow_label_lo;
|
||||||
#else
|
#else
|
||||||
# error "Please fix endianness defines"
|
# error "Please fix endianness defines"
|
||||||
@@ -206,10 +188,10 @@ struct Ip6Header
|
|||||||
struct in6_addr dst_ip;
|
struct in6_addr dst_ip;
|
||||||
};
|
};
|
||||||
|
|
||||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
|
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
|
||||||
{
|
{
|
||||||
Packet result(data);
|
PacketData result(packet);
|
||||||
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData);
|
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(packet.mData);
|
||||||
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||||
return PacketData(nullptr, 0);
|
return PacketData(nullptr, 0);
|
||||||
*/
|
*/
|
||||||
@@ -221,13 +203,18 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
|
|||||||
result.mData += sizeof(UdpHeader);
|
result.mData += sizeof(UdpHeader);
|
||||||
result.mLength -= sizeof(UdpHeader);
|
result.mLength -= sizeof(UdpHeader);
|
||||||
|
|
||||||
InternetAddress addr_source;
|
/*
|
||||||
addr_source.setIp(ip6->src_ip);
|
if (result.mLength != ntohs(udp->mDatagramLength))
|
||||||
addr_source.setPort(ntohs(udp->mSourcePort));
|
return PacketData(nullptr, 0);
|
||||||
|
*/
|
||||||
|
|
||||||
InternetAddress addr_dest;
|
source.setIp(ip6->src_ip);
|
||||||
addr_dest.setIp(ip6->dst_ip);
|
source.setPort(ntohs(udp->mSourcePort));
|
||||||
addr_dest.setPort(ntohs(udp->mDestinationPort));
|
//std::cout << source.toStdString() << " - ";
|
||||||
|
|
||||||
return {.data = result, .source = addr_source, .dest = addr_dest};
|
destination.setIp(ip6->dst_ip);
|
||||||
|
destination.setPort(ntohs(udp->mDestinationPort));
|
||||||
|
//std::cout << destination.toStdString() << std::endl;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,31 +7,25 @@
|
|||||||
class NetworkFrame
|
class NetworkFrame
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Packet
|
struct PacketData
|
||||||
{
|
{
|
||||||
const uint8_t* mData;
|
const uint8_t* mData;
|
||||||
size_t mLength;
|
size_t mLength;
|
||||||
|
|
||||||
Packet(const uint8_t* data, size_t length) : mData(data), mLength(length) {}
|
PacketData(const uint8_t* data, size_t length)
|
||||||
|
:mData(data), mLength(length)
|
||||||
|
{}
|
||||||
|
|
||||||
Packet() : mData(nullptr), mLength(0) {}
|
PacketData()
|
||||||
|
:mData(nullptr), mLength(0)
|
||||||
bool is_empty() const { return mData == nullptr || mLength == 0; }
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Payload
|
static PacketData GetUdpPayloadForEthernet(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||||
{
|
static PacketData GetUdpPayloadForIp4(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||||
Packet data;
|
static PacketData GetUdpPayloadForIp6(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||||
InternetAddress source;
|
static PacketData GetUdpPayloadForSLL(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||||
InternetAddress dest;
|
static PacketData GetUdpPayloadForLoopback(PacketData& packet, InternetAddress& source, InternetAddress& destination);
|
||||||
};
|
|
||||||
|
|
||||||
static Payload GetUdpPayloadForEthernet(const Packet& data);
|
|
||||||
static Payload GetUdpPayloadForIp4(const Packet& data);
|
|
||||||
static Payload GetUdpPayloadForIp6(const Packet& data);
|
|
||||||
static Payload GetUdpPayloadForSLL(const Packet& data);
|
|
||||||
static Payload GetUdpPayloadForLoopback(const Packet& data);
|
|
||||||
static Payload GetUdpPayloadForRaw(const Packet& data);
|
|
||||||
|
|
||||||
struct EthernetHeader
|
struct EthernetHeader
|
||||||
{
|
{
|
||||||
@@ -75,11 +69,18 @@ public:
|
|||||||
uint8_t mTtl; /* time to live */
|
uint8_t mTtl; /* time to live */
|
||||||
uint8_t mProtocol; /* protocol */
|
uint8_t mProtocol; /* protocol */
|
||||||
uint16_t mChecksum; /* checksum */
|
uint16_t mChecksum; /* checksum */
|
||||||
in_addr mSource, mDestination; /* source and dest address */
|
in_addr mSource,
|
||||||
|
mDestination; /* source and dest address */
|
||||||
|
|
||||||
int headerLength() const { return (mVhl & 0x0f) * 4; }
|
int headerLength() const
|
||||||
|
{
|
||||||
|
return (mVhl & 0x0f) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
int version() const { return mVhl >> 4; }
|
int version() const
|
||||||
|
{
|
||||||
|
return mVhl >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
const in_addr& source4() const { return mSource; }
|
const in_addr& source4() const { return mSource; }
|
||||||
const in_addr& dest4() const { return mDestination; }
|
const in_addr& dest4() const { return mDestination; }
|
||||||
@@ -118,5 +119,6 @@ public:
|
|||||||
uint16_t mChecksum; /* checksum */
|
uint16_t mChecksum; /* checksum */
|
||||||
uint16_t mUrgentPointer; /* urgent pointer */
|
uint16_t mUrgentPointer; /* urgent pointer */
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -7,9 +7,8 @@
|
|||||||
# include <asm/ioctls.h>
|
# include <asm/ioctls.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#include "HL_NetworkSocket.h"
|
#include "HL_NetworkSocket.h"
|
||||||
#include "HL_Log.h"
|
|
||||||
|
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
@@ -20,13 +19,15 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "network"
|
DatagramSocket::DatagramSocket()
|
||||||
|
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
|
||||||
|
{
|
||||||
|
|
||||||
DatagramSocket::DatagramSocket() : mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0) {}
|
}
|
||||||
|
|
||||||
DatagramSocket::~DatagramSocket()
|
DatagramSocket::~DatagramSocket()
|
||||||
{
|
{
|
||||||
internalClose();
|
closeSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::open(int family)
|
void DatagramSocket::open(int family)
|
||||||
@@ -39,10 +40,8 @@ void DatagramSocket::open(int family)
|
|||||||
mHandle = ::socket(mFamily, SOCK_DGRAM, IPPROTO_UDP);
|
mHandle = ::socket(mFamily, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if (mHandle != INVALID_SOCKET)
|
if (mHandle != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
sockaddr_in addr4;
|
sockaddr_in addr4; sockaddr_in6 addr6;
|
||||||
sockaddr_in6 addr6;
|
memset(&addr4, 0, sizeof(addr4)); memset(&addr6, 0, sizeof(addr6));
|
||||||
memset(&addr4, 0, sizeof(addr4));
|
|
||||||
memset(&addr6, 0, sizeof(addr6));
|
|
||||||
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
|
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
|
||||||
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
|
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
|
||||||
if (!retcode)
|
if (!retcode)
|
||||||
@@ -62,8 +61,7 @@ void DatagramSocket::sendDatagram(InternetAddress& dest, const void* packetData,
|
|||||||
if (mHandle == INVALID_SOCKET)
|
if (mHandle == INVALID_SOCKET)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*int sent = */ ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(),
|
int sent = ::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
||||||
dest.sockaddrLen());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
||||||
@@ -71,36 +69,23 @@ unsigned DatagramSocket::recvDatagram(InternetAddress& src, void* packetBuffer,
|
|||||||
if (mHandle == INVALID_SOCKET)
|
if (mHandle == INVALID_SOCKET)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sockaddr* addr = nullptr;
|
sockaddr_in sourceaddr;
|
||||||
socklen_t addrLen = 0;
|
#ifdef WIN32
|
||||||
sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}};
|
int addrlen = sizeof(sourceaddr);
|
||||||
sockaddr_in6 addr_6 = {AF_INET6, 0, 0, {0}, 0};
|
#else
|
||||||
switch (mFamily)
|
socklen_t addrlen = sizeof(sourceaddr);
|
||||||
{
|
#endif
|
||||||
case AF_INET:
|
int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, (sockaddr*)&sourceaddr, &addrlen);
|
||||||
addr = (sockaddr*)&addr_4;
|
|
||||||
addrLen = sizeof(addr_4);
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
addr = (sockaddr*)&addr_6;
|
|
||||||
addrLen = sizeof(addr_6);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
if (!addr || !addrLen)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, addr, &addrLen);
|
|
||||||
if (received > 0)
|
if (received > 0)
|
||||||
{
|
{
|
||||||
src = InternetAddress((sockaddr&)*addr, addrLen);
|
src = InternetAddress((sockaddr&)sourceaddr, addrlen);
|
||||||
return received;
|
return received;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::internalClose()
|
void DatagramSocket::closeSocket()
|
||||||
{
|
{
|
||||||
if (mHandle != INVALID_SOCKET)
|
if (mHandle != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
@@ -113,11 +98,6 @@ void DatagramSocket::internalClose()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::closeSocket()
|
|
||||||
{
|
|
||||||
internalClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DatagramSocket::isValid() const
|
bool DatagramSocket::isValid() const
|
||||||
{
|
{
|
||||||
return mHandle != INVALID_SOCKET;
|
return mHandle != INVALID_SOCKET;
|
||||||
@@ -159,19 +139,15 @@ DatagramAgreggator::DatagramAgreggator()
|
|||||||
mMaxHandle = 0;
|
mMaxHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DatagramAgreggator::~DatagramAgreggator() {}
|
DatagramAgreggator::~DatagramAgreggator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void DatagramAgreggator::addSocket(PDatagramSocket socket)
|
void DatagramAgreggator::addSocket(PDatagramSocket socket)
|
||||||
{
|
{
|
||||||
if (socket->mHandle == INVALID_SOCKET)
|
if (socket->mHandle == INVALID_SOCKET)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mSocketVector.size() >= 62)
|
|
||||||
{
|
|
||||||
ICELogError(<< "fd_set overflow; too much sockets");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FD_SET(socket->mHandle, &mReadSet);
|
FD_SET(socket->mHandle, &mReadSet);
|
||||||
if (socket->mHandle > mMaxHandle)
|
if (socket->mHandle > mMaxHandle)
|
||||||
mMaxHandle = socket->mHandle;
|
mMaxHandle = socket->mHandle;
|
||||||
@@ -187,7 +163,7 @@ unsigned DatagramAgreggator::count()
|
|||||||
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
|
||||||
{
|
{
|
||||||
PDatagramSocket socket = mSocketVector[index];
|
PDatagramSocket socket = mSocketVector[index];
|
||||||
return FD_ISSET(socket->mHandle, &mReadSet);
|
return (FD_ISSET(socket->mHandle, &mReadSet) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
||||||
@@ -195,12 +171,12 @@ PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
|||||||
return mSocketVector[index];
|
return mSocketVector[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatagramAgreggator::waitForData(std::chrono::milliseconds timeout)
|
bool DatagramAgreggator::waitForData(unsigned milliseconds)
|
||||||
{
|
{
|
||||||
timeval tv;
|
timeval tv;
|
||||||
tv.tv_sec = timeout.count() / 1000;
|
tv.tv_sec = milliseconds / 1000;
|
||||||
tv.tv_usec = (timeout.count() % 1000) * 1000;
|
tv.tv_usec = (milliseconds % 1000) * 1000;
|
||||||
|
|
||||||
int rescode = ::select(mMaxHandle + 1, &mReadSet, nullptr, nullptr, &tv);
|
int rescode = ::select(mMaxHandle, &mReadSet, NULL, NULL, &tv);
|
||||||
return rescode > 0;
|
return rescode > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -9,19 +9,18 @@
|
|||||||
#include "HL_InternetAddress.h"
|
#include "HL_InternetAddress.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
class NetworkSocket
|
class NetworkSocket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual int localport() = 0;
|
virtual int localport() = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DatagramSocket
|
class DatagramSocket
|
||||||
{
|
{
|
||||||
friend class SocketHeap;
|
friend class SocketHeap;
|
||||||
friend class DatagramAgreggator;
|
friend class DatagramAgreggator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DatagramSocket();
|
DatagramSocket();
|
||||||
virtual ~DatagramSocket();
|
virtual ~DatagramSocket();
|
||||||
@@ -37,12 +36,10 @@ public:
|
|||||||
virtual SOCKET socket() const;
|
virtual SOCKET socket() const;
|
||||||
|
|
||||||
virtual void open(int family);
|
virtual void open(int family);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int mFamily;
|
int mFamily;
|
||||||
SOCKET mHandle;
|
SOCKET mHandle;
|
||||||
int mLocalPort;
|
int mLocalPort;
|
||||||
void internalClose();
|
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
||||||
|
|
||||||
@@ -57,7 +54,7 @@ public:
|
|||||||
bool hasDataAtIndex(unsigned index);
|
bool hasDataAtIndex(unsigned index);
|
||||||
PDatagramSocket socketAt(unsigned index);
|
PDatagramSocket socketAt(unsigned index);
|
||||||
|
|
||||||
bool waitForData(std::chrono::milliseconds timeout);
|
bool waitForData(unsigned milliseconds);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::vector<PDatagramSocket> SocketList;
|
typedef std::vector<PDatagramSocket> SocketList;
|
||||||
|
|||||||
+181
-293
@@ -97,11 +97,9 @@
|
|||||||
# define OPTIONAL_MUTABLE_CONSTEXPR constexpr
|
# define OPTIONAL_MUTABLE_CONSTEXPR constexpr
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
namespace std
|
namespace std{
|
||||||
{
|
|
||||||
|
|
||||||
namespace experimental
|
namespace experimental{
|
||||||
{
|
|
||||||
|
|
||||||
// BEGIN workaround for missing is_trivially_destructible
|
// BEGIN workaround for missing is_trivially_destructible
|
||||||
# if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
|
# if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
|
||||||
@@ -141,17 +139,11 @@ template<class T, class U>
|
|||||||
struct is_assignable
|
struct is_assignable
|
||||||
{
|
{
|
||||||
template <class X, class Y>
|
template <class X, class Y>
|
||||||
constexpr static bool has_assign(...)
|
constexpr static bool has_assign(...) { return false; }
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class X, class Y, size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) >
|
template <class X, class Y, size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) >
|
||||||
// the comma operator is necessary for the cases where operator= returns void
|
// the comma operator is necessary for the cases where operator= returns void
|
||||||
constexpr static bool has_assign(bool)
|
constexpr static bool has_assign(bool) { return true; }
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr static bool value = has_assign<T, U>(true);
|
constexpr static bool value = has_assign<T, U>(true);
|
||||||
};
|
};
|
||||||
@@ -161,14 +153,12 @@ template<class T>
|
|||||||
struct is_nothrow_move_assignable
|
struct is_nothrow_move_assignable
|
||||||
{
|
{
|
||||||
template <class X, bool has_any_move_assign>
|
template <class X, bool has_any_move_assign>
|
||||||
struct has_nothrow_move_assign
|
struct has_nothrow_move_assign {
|
||||||
{
|
|
||||||
constexpr static bool value = false;
|
constexpr static bool value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class X>
|
template <class X>
|
||||||
struct has_nothrow_move_assign<X, true>
|
struct has_nothrow_move_assign<X, true> {
|
||||||
{
|
|
||||||
constexpr static bool value = noexcept( std::declval<X&>() = std::declval<X&&>() );
|
constexpr static bool value = noexcept( std::declval<X&>() = std::declval<X&&>() );
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,31 +170,27 @@ struct is_nothrow_move_assignable
|
|||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 20.5.4, optional for object types
|
// 20.5.4, optional for object types
|
||||||
template<class T>
|
template <class T> class optional;
|
||||||
class optional;
|
|
||||||
|
|
||||||
// 20.5.5, optional for lvalue reference types
|
// 20.5.5, optional for lvalue reference types
|
||||||
template<class T>
|
template <class T> class optional<T&>;
|
||||||
class optional<T&>;
|
|
||||||
|
|
||||||
|
|
||||||
// workaround: std utility functions aren't constexpr yet
|
// workaround: std utility functions aren't constexpr yet
|
||||||
template<class T>
|
template <class T> inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type& t) noexcept
|
||||||
inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type& t) noexcept
|
|
||||||
{
|
{
|
||||||
return static_cast<T&&>(t);
|
return static_cast<T&&>(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type&& t) noexcept
|
||||||
inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type&& t) noexcept
|
|
||||||
{
|
{
|
||||||
static_assert(!std::is_lvalue_reference<T>::value, "!!");
|
static_assert(!std::is_lvalue_reference<T>::value, "!!");
|
||||||
return static_cast<T&&>(t);
|
return static_cast<T&&>(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> inline constexpr typename std::remove_reference<T>::type&& constexpr_move(T&& t) noexcept
|
||||||
inline constexpr typename std::remove_reference<T>::type&& constexpr_move(T&& t) noexcept
|
|
||||||
{
|
{
|
||||||
return static_cast<typename std::remove_reference<T>::type&&>(t);
|
return static_cast<typename std::remove_reference<T>::type&&>(t);
|
||||||
}
|
}
|
||||||
@@ -225,16 +211,10 @@ template<typename T>
|
|||||||
struct has_overloaded_addressof
|
struct has_overloaded_addressof
|
||||||
{
|
{
|
||||||
template <class X>
|
template <class X>
|
||||||
constexpr static bool has_overload(...)
|
constexpr static bool has_overload(...) { return false; }
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class X, size_t S = sizeof(std::declval<X&>().operator&()) >
|
template <class X, size_t S = sizeof(std::declval<X&>().operator&()) >
|
||||||
constexpr static bool has_overload(bool)
|
constexpr static bool has_overload(bool) { return true; }
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr static bool value = has_overload<T>(true);
|
constexpr static bool value = has_overload<T>(true);
|
||||||
};
|
};
|
||||||
@@ -254,39 +234,29 @@ T* static_addressof(T& ref)
|
|||||||
|
|
||||||
// the call to convert<A>(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A
|
// the call to convert<A>(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A
|
||||||
template <class U>
|
template <class U>
|
||||||
constexpr U convert(U v)
|
constexpr U convert(U v) { return v; }
|
||||||
{
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
constexpr struct trivial_init_t
|
constexpr struct trivial_init_t{} trivial_init{};
|
||||||
{
|
|
||||||
} trivial_init{};
|
|
||||||
|
|
||||||
|
|
||||||
// 20.5.6, In-place construction
|
// 20.5.6, In-place construction
|
||||||
constexpr struct in_place_t
|
constexpr struct in_place_t{} in_place{};
|
||||||
{
|
|
||||||
} in_place{};
|
|
||||||
|
|
||||||
|
|
||||||
// 20.5.7, Disengaged state indicator
|
// 20.5.7, Disengaged state indicator
|
||||||
struct nullopt_t
|
struct nullopt_t
|
||||||
{
|
{
|
||||||
struct init
|
struct init{};
|
||||||
{
|
|
||||||
};
|
|
||||||
constexpr explicit nullopt_t(init){}
|
constexpr explicit nullopt_t(init){}
|
||||||
};
|
};
|
||||||
constexpr nullopt_t nullopt{nullopt_t::init()};
|
constexpr nullopt_t nullopt{nullopt_t::init()};
|
||||||
|
|
||||||
|
|
||||||
// 20.5.8, class bad_optional_access
|
// 20.5.8, class bad_optional_access
|
||||||
class bad_optional_access : public logic_error
|
class bad_optional_access : public logic_error {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
explicit bad_optional_access(const string& what_arg) : logic_error{what_arg} {}
|
explicit bad_optional_access(const string& what_arg) : logic_error{what_arg} {}
|
||||||
explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {}
|
explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {}
|
||||||
@@ -302,9 +272,7 @@ union storage_t
|
|||||||
constexpr storage_t( trivial_init_t ) noexcept : dummy_() {};
|
constexpr storage_t( trivial_init_t ) noexcept : dummy_() {};
|
||||||
|
|
||||||
template <class... Args>
|
template <class... Args>
|
||||||
constexpr storage_t(Args&&... args) : value_(constexpr_forward<Args>(args)...)
|
constexpr storage_t( Args&&... args ) : value_(constexpr_forward<Args>(args)...) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~storage_t(){}
|
~storage_t(){}
|
||||||
};
|
};
|
||||||
@@ -319,9 +287,7 @@ union constexpr_storage_t
|
|||||||
constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {};
|
constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {};
|
||||||
|
|
||||||
template <class... Args>
|
template <class... Args>
|
||||||
constexpr constexpr_storage_t(Args&&... args) : value_(constexpr_forward<Args>(args)...)
|
constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward<Args>(args)...) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~constexpr_storage_t() = default;
|
~constexpr_storage_t() = default;
|
||||||
};
|
};
|
||||||
@@ -339,22 +305,14 @@ struct optional_base
|
|||||||
|
|
||||||
explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
|
explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
|
||||||
|
|
||||||
template<class... Args>
|
template <class... Args> explicit optional_base(in_place_t, Args&&... args)
|
||||||
explicit optional_base(in_place_t, Args&&... args) : init_(true), storage_(constexpr_forward<Args>(args)...)
|
: init_(true), storage_(constexpr_forward<Args>(args)...) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
|
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
|
||||||
explicit optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
|
explicit optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||||
: init_(true), storage_(il, std::forward<Args>(args)...)
|
: init_(true), storage_(il, std::forward<Args>(args)...) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~optional_base()
|
~optional_base() { if (init_) storage_.value_.T::~T(); }
|
||||||
{
|
|
||||||
if (init_)
|
|
||||||
storage_.value_.T::~T();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -370,28 +328,23 @@ struct constexpr_optional_base
|
|||||||
|
|
||||||
explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
|
explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
|
||||||
|
|
||||||
template<class... Args>
|
template <class... Args> explicit constexpr constexpr_optional_base(in_place_t, Args&&... args)
|
||||||
explicit constexpr constexpr_optional_base(in_place_t, Args&&... args)
|
: init_(true), storage_(constexpr_forward<Args>(args)...) {}
|
||||||
: init_(true), storage_(constexpr_forward<Args>(args)...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
|
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
|
||||||
OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list<U> il,
|
OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||||
Args&&... args)
|
: init_(true), storage_(il, std::forward<Args>(args)...) {}
|
||||||
: init_(true), storage_(il, std::forward<Args>(args)...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~constexpr_optional_base() = default;
|
~constexpr_optional_base() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
using OptionalBase =
|
using OptionalBase = typename std::conditional<
|
||||||
typename std::conditional<is_trivially_destructible<T>::value, // if possible
|
is_trivially_destructible<T>::value, // if possible
|
||||||
constexpr_optional_base<typename std::remove_const<T>::type>, // use base with trivial
|
constexpr_optional_base<typename std::remove_const<T>::type>, // use base with trivial destructor
|
||||||
// destructor
|
optional_base<typename std::remove_const<T>::type>
|
||||||
optional_base<typename std::remove_const<T>::type>>::type;
|
>::type;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -419,10 +372,8 @@ class optional : private OptionalBase<T>
|
|||||||
T& contained_val() { return OptionalBase<T>::storage_.value_; }
|
T& contained_val() { return OptionalBase<T>::storage_.value_; }
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
void clear() noexcept
|
void clear() noexcept {
|
||||||
{
|
if (initialized()) dataptr()->T::~T();
|
||||||
if (initialized())
|
|
||||||
dataptr()->T::~T();
|
|
||||||
OptionalBase<T>::init_ = false;
|
OptionalBase<T>::init_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,19 +400,19 @@ public:
|
|||||||
constexpr optional() noexcept : OptionalBase<T>() {};
|
constexpr optional() noexcept : OptionalBase<T>() {};
|
||||||
constexpr optional(nullopt_t) noexcept : OptionalBase<T>() {};
|
constexpr optional(nullopt_t) noexcept : OptionalBase<T>() {};
|
||||||
|
|
||||||
optional(const optional& rhs) : OptionalBase<T>()
|
optional(const optional& rhs)
|
||||||
{
|
: OptionalBase<T>()
|
||||||
if (rhs.initialized())
|
|
||||||
{
|
{
|
||||||
|
if (rhs.initialized()) {
|
||||||
::new (static_cast<void*>(dataptr())) T(*rhs);
|
::new (static_cast<void*>(dataptr())) T(*rhs);
|
||||||
OptionalBase<T>::init_ = true;
|
OptionalBase<T>::init_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optional(optional&& rhs) noexcept(is_nothrow_move_constructible<T>::value) : OptionalBase<T>()
|
optional(optional&& rhs) noexcept(is_nothrow_move_constructible<T>::value)
|
||||||
{
|
: OptionalBase<T>()
|
||||||
if (rhs.initialized())
|
|
||||||
{
|
{
|
||||||
|
if (rhs.initialized()) {
|
||||||
::new (static_cast<void*>(dataptr())) T(std::move(*rhs));
|
::new (static_cast<void*>(dataptr())) T(std::move(*rhs));
|
||||||
OptionalBase<T>::init_ = true;
|
OptionalBase<T>::init_ = true;
|
||||||
}
|
}
|
||||||
@@ -473,15 +424,11 @@ public:
|
|||||||
|
|
||||||
template <class... Args>
|
template <class... Args>
|
||||||
explicit constexpr optional(in_place_t, Args&&... args)
|
explicit constexpr optional(in_place_t, Args&&... args)
|
||||||
: OptionalBase<T>(in_place_t{}, constexpr_forward<Args>(args)...)
|
: OptionalBase<T>(in_place_t{}, constexpr_forward<Args>(args)...) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
|
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
|
||||||
OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list<U> il, Args&&... args)
|
OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||||
: OptionalBase<T>(in_place_t{}, il, constexpr_forward<Args>(args)...)
|
: OptionalBase<T>(in_place_t{}, il, constexpr_forward<Args>(args)...) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// 20.5.4.2, Destructor
|
// 20.5.4.2, Destructor
|
||||||
~optional() = default;
|
~optional() = default;
|
||||||
@@ -495,38 +442,31 @@ public:
|
|||||||
|
|
||||||
optional& operator=(const optional& rhs)
|
optional& operator=(const optional& rhs)
|
||||||
{
|
{
|
||||||
if (initialized() == true && rhs.initialized() == false)
|
if (initialized() == true && rhs.initialized() == false) clear();
|
||||||
clear();
|
else if (initialized() == false && rhs.initialized() == true) initialize(*rhs);
|
||||||
else if (initialized() == false && rhs.initialized() == true)
|
else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs;
|
||||||
initialize(*rhs);
|
|
||||||
else if (initialized() == true && rhs.initialized() == true)
|
|
||||||
contained_val() = *rhs;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional& operator=(optional&& rhs) noexcept(is_nothrow_move_assignable<T>::value &&
|
optional& operator=(optional&& rhs)
|
||||||
is_nothrow_move_constructible<T>::value)
|
noexcept(is_nothrow_move_assignable<T>::value && is_nothrow_move_constructible<T>::value)
|
||||||
{
|
{
|
||||||
if (initialized() == true && rhs.initialized() == false)
|
if (initialized() == true && rhs.initialized() == false) clear();
|
||||||
clear();
|
else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs));
|
||||||
else if (initialized() == false && rhs.initialized() == true)
|
else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs);
|
||||||
initialize(std::move(*rhs));
|
|
||||||
else if (initialized() == true && rhs.initialized() == true)
|
|
||||||
contained_val() = std::move(*rhs);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class U>
|
template <class U>
|
||||||
auto operator=(U&& v) -> typename enable_if<is_same<typename decay<U>::type, T>::value, optional&>::type
|
auto operator=(U&& v)
|
||||||
|
-> typename enable_if
|
||||||
|
<
|
||||||
|
is_same<typename decay<U>::type, T>::value,
|
||||||
|
optional&
|
||||||
|
>::type
|
||||||
{
|
{
|
||||||
if (initialized())
|
if (initialized()) { contained_val() = std::forward<U>(v); }
|
||||||
{
|
else { initialize(std::forward<U>(v)); }
|
||||||
contained_val() = std::forward<U>(v);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
initialize(std::forward<U>(v));
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,24 +486,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 20.5.4.4, Swap
|
// 20.5.4.4, Swap
|
||||||
void swap(optional<T>& rhs) noexcept(is_nothrow_move_constructible<T>::value &&
|
void swap(optional<T>& rhs) noexcept(is_nothrow_move_constructible<T>::value && noexcept(swap(declval<T&>(), declval<T&>())))
|
||||||
noexcept(swap(declval<T&>(), declval<T&>())))
|
|
||||||
{
|
{
|
||||||
if (initialized() == true && rhs.initialized() == false)
|
if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); }
|
||||||
{
|
else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); }
|
||||||
rhs.initialize(std::move(**this));
|
else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); }
|
||||||
clear();
|
|
||||||
}
|
|
||||||
else if (initialized() == false && rhs.initialized() == true)
|
|
||||||
{
|
|
||||||
initialize(std::move(*rhs));
|
|
||||||
rhs.clear();
|
|
||||||
}
|
|
||||||
else if (initialized() == true && rhs.initialized() == true)
|
|
||||||
{
|
|
||||||
using std::swap;
|
|
||||||
swap(**this, *rhs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 20.5.4.5, Observers
|
// 20.5.4.5, Observers
|
||||||
@@ -571,70 +498,65 @@ public:
|
|||||||
explicit constexpr operator bool() const noexcept { return initialized(); }
|
explicit constexpr operator bool() const noexcept { return initialized(); }
|
||||||
bool is_initialized() const noexcept { return initialized(); }
|
bool is_initialized() const noexcept { return initialized(); }
|
||||||
|
|
||||||
constexpr T const* operator->() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); }
|
constexpr T const* operator ->() const {
|
||||||
|
return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr());
|
||||||
|
}
|
||||||
|
|
||||||
# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
|
# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
|
||||||
|
|
||||||
OPTIONAL_MUTABLE_CONSTEXPR T* operator->()
|
OPTIONAL_MUTABLE_CONSTEXPR T* operator ->() {
|
||||||
{
|
|
||||||
assert (initialized());
|
assert (initialized());
|
||||||
return dataptr();
|
return dataptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T const& operator*() const& { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); }
|
constexpr T const& operator *() const& {
|
||||||
|
return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val());
|
||||||
|
}
|
||||||
|
|
||||||
OPTIONAL_MUTABLE_CONSTEXPR T& operator*() &
|
OPTIONAL_MUTABLE_CONSTEXPR T& operator *() & {
|
||||||
{
|
|
||||||
assert (initialized());
|
assert (initialized());
|
||||||
return contained_val();
|
return contained_val();
|
||||||
}
|
}
|
||||||
|
|
||||||
OPTIONAL_MUTABLE_CONSTEXPR T&& operator*() &&
|
OPTIONAL_MUTABLE_CONSTEXPR T&& operator *() && {
|
||||||
{
|
|
||||||
assert (initialized());
|
assert (initialized());
|
||||||
return constexpr_move(contained_val());
|
return constexpr_move(contained_val());
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T const& value() const&
|
constexpr T const& value() const& {
|
||||||
{
|
|
||||||
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
||||||
}
|
}
|
||||||
|
|
||||||
OPTIONAL_MUTABLE_CONSTEXPR T& value() &
|
OPTIONAL_MUTABLE_CONSTEXPR T& value() & {
|
||||||
{
|
|
||||||
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
||||||
}
|
}
|
||||||
|
|
||||||
OPTIONAL_MUTABLE_CONSTEXPR T&& value() &&
|
OPTIONAL_MUTABLE_CONSTEXPR T&& value() && {
|
||||||
{
|
if (!initialized()) throw bad_optional_access("bad optional access");
|
||||||
if (!initialized())
|
|
||||||
throw bad_optional_access("bad optional access");
|
|
||||||
return std::move(contained_val());
|
return std::move(contained_val());
|
||||||
}
|
}
|
||||||
|
|
||||||
# else
|
# else
|
||||||
|
|
||||||
T* operator->()
|
T* operator ->() {
|
||||||
{
|
|
||||||
assert (initialized());
|
assert (initialized());
|
||||||
return dataptr();
|
return dataptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T const& operator*() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); }
|
constexpr T const& operator *() const {
|
||||||
|
return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val());
|
||||||
|
}
|
||||||
|
|
||||||
T& operator*()
|
T& operator *() {
|
||||||
{
|
|
||||||
assert (initialized());
|
assert (initialized());
|
||||||
return contained_val();
|
return contained_val();
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T const& value() const
|
constexpr T const& value() const {
|
||||||
{
|
|
||||||
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
||||||
}
|
}
|
||||||
|
|
||||||
T& value()
|
T& value() {
|
||||||
{
|
|
||||||
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,8 +575,7 @@ public:
|
|||||||
template <class V>
|
template <class V>
|
||||||
OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) &&
|
OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) &&
|
||||||
{
|
{
|
||||||
return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val())
|
return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val()) : detail_::convert<T>(constexpr_forward<V>(v));
|
||||||
: detail_::convert<T>(constexpr_forward<V>(v));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# else
|
# else
|
||||||
@@ -662,8 +583,7 @@ public:
|
|||||||
template <class V>
|
template <class V>
|
||||||
T value_or(V&& v) &&
|
T value_or(V&& v) &&
|
||||||
{
|
{
|
||||||
return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val())
|
return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val()) : detail_::convert<T>(constexpr_forward<V>(v));
|
||||||
: detail_::convert<T>(constexpr_forward<V>(v));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# endif
|
# endif
|
||||||
@@ -677,6 +597,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -688,6 +609,7 @@ class optional<T&>
|
|||||||
T* ref;
|
T* ref;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// 20.5.5.1, construction/destruction
|
// 20.5.5.1, construction/destruction
|
||||||
constexpr optional() noexcept : ref(nullptr) {}
|
constexpr optional() noexcept : ref(nullptr) {}
|
||||||
|
|
||||||
@@ -706,8 +628,7 @@ public:
|
|||||||
~optional() = default;
|
~optional() = default;
|
||||||
|
|
||||||
// 20.5.5.2, mutation
|
// 20.5.5.2, mutation
|
||||||
optional& operator=(nullopt_t) noexcept
|
optional& operator=(nullopt_t) noexcept {
|
||||||
{
|
|
||||||
ref = nullptr;
|
ref = nullptr;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -723,32 +644,54 @@ public:
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
auto operator=(U&& rhs) noexcept ->
|
auto operator=(U&& rhs) noexcept
|
||||||
typename enable_if<is_same<typename decay<U>::type, optional<T&>>::value, optional&>::type
|
-> typename enable_if
|
||||||
|
<
|
||||||
|
is_same<typename decay<U>::type, optional<T&>>::value,
|
||||||
|
optional&
|
||||||
|
>::type
|
||||||
{
|
{
|
||||||
ref = rhs.ref;
|
ref = rhs.ref;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
auto operator=(U&& rhs) noexcept ->
|
auto operator=(U&& rhs) noexcept
|
||||||
typename enable_if<!is_same<typename decay<U>::type, optional<T&>>::value, optional&>::type = delete;
|
-> typename enable_if
|
||||||
|
<
|
||||||
|
!is_same<typename decay<U>::type, optional<T&>>::value,
|
||||||
|
optional&
|
||||||
|
>::type
|
||||||
|
= delete;
|
||||||
|
|
||||||
void emplace(T& v) noexcept { ref = detail_::static_addressof(v); }
|
void emplace(T& v) noexcept {
|
||||||
|
ref = detail_::static_addressof(v);
|
||||||
|
}
|
||||||
|
|
||||||
void emplace(T&&) = delete;
|
void emplace(T&&) = delete;
|
||||||
|
|
||||||
|
|
||||||
void swap(optional<T&>& rhs) noexcept { std::swap(ref, rhs.ref); }
|
void swap(optional<T&>& rhs) noexcept
|
||||||
|
{
|
||||||
|
std::swap(ref, rhs.ref);
|
||||||
|
}
|
||||||
|
|
||||||
// 20.5.5.3, observers
|
// 20.5.5.3, observers
|
||||||
constexpr T* operator->() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); }
|
constexpr T* operator->() const {
|
||||||
|
return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr T& operator*() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); }
|
constexpr T& operator*() const {
|
||||||
|
return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr T& value() const { return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref); }
|
constexpr T& value() const {
|
||||||
|
return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref);
|
||||||
|
}
|
||||||
|
|
||||||
explicit constexpr operator bool() const noexcept { return ref != nullptr; }
|
explicit constexpr operator bool() const noexcept {
|
||||||
|
return ref != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
template <class V>
|
template <class V>
|
||||||
constexpr typename decay<T>::type value_or(V&& v) const
|
constexpr typename decay<T>::type value_or(V&& v) const
|
||||||
@@ -766,333 +709,280 @@ class optional<T&&>
|
|||||||
|
|
||||||
|
|
||||||
// 20.5.8, Relational operators
|
// 20.5.8, Relational operators
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const optional<T>& x, const optional<T>& y)
|
||||||
constexpr bool operator==(const optional<T>& x, const optional<T>& y)
|
|
||||||
{
|
{
|
||||||
return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y;
|
return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const optional<T>& x, const optional<T>& y)
|
||||||
constexpr bool operator!=(const optional<T>& x, const optional<T>& y)
|
|
||||||
{
|
{
|
||||||
return !(x == y);
|
return !(x == y);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const optional<T>& x, const optional<T>& y)
|
||||||
constexpr bool operator<(const optional<T>& x, const optional<T>& y)
|
|
||||||
{
|
{
|
||||||
return (!y) ? false : (!x) ? true : *x < *y;
|
return (!y) ? false : (!x) ? true : *x < *y;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const optional<T>& x, const optional<T>& y)
|
||||||
constexpr bool operator>(const optional<T>& x, const optional<T>& y)
|
|
||||||
{
|
{
|
||||||
return (y < x);
|
return (y < x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const optional<T>& x, const optional<T>& y)
|
||||||
constexpr bool operator<=(const optional<T>& x, const optional<T>& y)
|
|
||||||
{
|
{
|
||||||
return !(y < x);
|
return !(y < x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const optional<T>& x, const optional<T>& y)
|
||||||
constexpr bool operator>=(const optional<T>& x, const optional<T>& y)
|
|
||||||
{
|
{
|
||||||
return !(x < y);
|
return !(x < y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 20.5.9, Comparison with nullopt
|
// 20.5.9, Comparison with nullopt
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept
|
||||||
constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept
|
|
||||||
{
|
{
|
||||||
return (!x);
|
return (!x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept
|
||||||
constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept
|
|
||||||
{
|
{
|
||||||
return (!x);
|
return (!x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept
|
||||||
constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept
|
|
||||||
{
|
{
|
||||||
return bool(x);
|
return bool(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept
|
||||||
constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept
|
|
||||||
{
|
{
|
||||||
return bool(x);
|
return bool(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const optional<T>&, nullopt_t) noexcept
|
||||||
constexpr bool operator<(const optional<T>&, nullopt_t) noexcept
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept
|
||||||
constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept
|
|
||||||
{
|
{
|
||||||
return bool(x);
|
return bool(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept
|
||||||
constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept
|
|
||||||
{
|
{
|
||||||
return (!x);
|
return (!x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept
|
||||||
constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept
|
||||||
constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept
|
|
||||||
{
|
{
|
||||||
return bool(x);
|
return bool(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(nullopt_t, const optional<T>&) noexcept
|
||||||
constexpr bool operator>(nullopt_t, const optional<T>&) noexcept
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept
|
||||||
constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept
|
||||||
constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept
|
|
||||||
{
|
{
|
||||||
return (!x);
|
return (!x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 20.5.10, Comparison with T
|
// 20.5.10, Comparison with T
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const optional<T>& x, const T& v)
|
||||||
constexpr bool operator==(const optional<T>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x == v : false;
|
return bool(x) ? *x == v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const T& v, const optional<T>& x)
|
||||||
constexpr bool operator==(const T& v, const optional<T>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v == *x : false;
|
return bool(x) ? v == *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const optional<T>& x, const T& v)
|
||||||
constexpr bool operator!=(const optional<T>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x != v : true;
|
return bool(x) ? *x != v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const T& v, const optional<T>& x)
|
||||||
constexpr bool operator!=(const T& v, const optional<T>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v != *x : true;
|
return bool(x) ? v != *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const optional<T>& x, const T& v)
|
||||||
constexpr bool operator<(const optional<T>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x < v : true;
|
return bool(x) ? *x < v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const T& v, const optional<T>& x)
|
||||||
constexpr bool operator>(const T& v, const optional<T>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v > *x : true;
|
return bool(x) ? v > *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const optional<T>& x, const T& v)
|
||||||
constexpr bool operator>(const optional<T>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x > v : false;
|
return bool(x) ? *x > v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const T& v, const optional<T>& x)
|
||||||
constexpr bool operator<(const T& v, const optional<T>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v < *x : false;
|
return bool(x) ? v < *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const optional<T>& x, const T& v)
|
||||||
constexpr bool operator>=(const optional<T>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x >= v : false;
|
return bool(x) ? *x >= v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const T& v, const optional<T>& x)
|
||||||
constexpr bool operator<=(const T& v, const optional<T>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v <= *x : false;
|
return bool(x) ? v <= *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const optional<T>& x, const T& v)
|
||||||
constexpr bool operator<=(const optional<T>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x <= v : true;
|
return bool(x) ? *x <= v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const T& v, const optional<T>& x)
|
||||||
constexpr bool operator>=(const T& v, const optional<T>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v >= *x : true;
|
return bool(x) ? v >= *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Comparison of optional<T&> with T
|
// Comparison of optional<T&> with T
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const optional<T&>& x, const T& v)
|
||||||
constexpr bool operator==(const optional<T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x == v : false;
|
return bool(x) ? *x == v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const T& v, const optional<T&>& x)
|
||||||
constexpr bool operator==(const T& v, const optional<T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v == *x : false;
|
return bool(x) ? v == *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const optional<T&>& x, const T& v)
|
||||||
constexpr bool operator!=(const optional<T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x != v : true;
|
return bool(x) ? *x != v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const T& v, const optional<T&>& x)
|
||||||
constexpr bool operator!=(const T& v, const optional<T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v != *x : true;
|
return bool(x) ? v != *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const optional<T&>& x, const T& v)
|
||||||
constexpr bool operator<(const optional<T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x < v : true;
|
return bool(x) ? *x < v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const T& v, const optional<T&>& x)
|
||||||
constexpr bool operator>(const T& v, const optional<T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v > *x : true;
|
return bool(x) ? v > *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const optional<T&>& x, const T& v)
|
||||||
constexpr bool operator>(const optional<T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x > v : false;
|
return bool(x) ? *x > v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const T& v, const optional<T&>& x)
|
||||||
constexpr bool operator<(const T& v, const optional<T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v < *x : false;
|
return bool(x) ? v < *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const optional<T&>& x, const T& v)
|
||||||
constexpr bool operator>=(const optional<T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x >= v : false;
|
return bool(x) ? *x >= v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const T& v, const optional<T&>& x)
|
||||||
constexpr bool operator<=(const T& v, const optional<T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v <= *x : false;
|
return bool(x) ? v <= *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const optional<T&>& x, const T& v)
|
||||||
constexpr bool operator<=(const optional<T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x <= v : true;
|
return bool(x) ? *x <= v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const T& v, const optional<T&>& x)
|
||||||
constexpr bool operator>=(const T& v, const optional<T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v >= *x : true;
|
return bool(x) ? v >= *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comparison of optional<T const&> with T
|
// Comparison of optional<T const&> with T
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const optional<const T&>& x, const T& v)
|
||||||
constexpr bool operator==(const optional<const T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x == v : false;
|
return bool(x) ? *x == v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator==(const T& v, const optional<const T&>& x)
|
||||||
constexpr bool operator==(const T& v, const optional<const T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v == *x : false;
|
return bool(x) ? v == *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const optional<const T&>& x, const T& v)
|
||||||
constexpr bool operator!=(const optional<const T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x != v : true;
|
return bool(x) ? *x != v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator!=(const T& v, const optional<const T&>& x)
|
||||||
constexpr bool operator!=(const T& v, const optional<const T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v != *x : true;
|
return bool(x) ? v != *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const optional<const T&>& x, const T& v)
|
||||||
constexpr bool operator<(const optional<const T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x < v : true;
|
return bool(x) ? *x < v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const T& v, const optional<const T&>& x)
|
||||||
constexpr bool operator>(const T& v, const optional<const T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v > *x : true;
|
return bool(x) ? v > *x : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>(const optional<const T&>& x, const T& v)
|
||||||
constexpr bool operator>(const optional<const T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x > v : false;
|
return bool(x) ? *x > v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<(const T& v, const optional<const T&>& x)
|
||||||
constexpr bool operator<(const T& v, const optional<const T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v < *x : false;
|
return bool(x) ? v < *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const optional<const T&>& x, const T& v)
|
||||||
constexpr bool operator>=(const optional<const T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x >= v : false;
|
return bool(x) ? *x >= v : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const T& v, const optional<const T&>& x)
|
||||||
constexpr bool operator<=(const T& v, const optional<const T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v <= *x : false;
|
return bool(x) ? v <= *x : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator<=(const optional<const T&>& x, const T& v)
|
||||||
constexpr bool operator<=(const optional<const T&>& x, const T& v)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? *x <= v : true;
|
return bool(x) ? *x <= v : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T> constexpr bool operator>=(const T& v, const optional<const T&>& x)
|
||||||
constexpr bool operator>=(const T& v, const optional<const T&>& x)
|
|
||||||
{
|
{
|
||||||
return bool(x) ? v >= *x : true;
|
return bool(x) ? v >= *x : true;
|
||||||
}
|
}
|
||||||
@@ -1130,8 +1020,7 @@ struct hash<std::experimental::optional<T>>
|
|||||||
typedef typename hash<T>::result_type result_type;
|
typedef typename hash<T>::result_type result_type;
|
||||||
typedef std::experimental::optional<T> argument_type;
|
typedef std::experimental::optional<T> argument_type;
|
||||||
|
|
||||||
constexpr result_type operator()(argument_type const& arg) const
|
constexpr result_type operator()(argument_type const& arg) const {
|
||||||
{
|
|
||||||
return arg ? std::hash<T>{}(*arg) : result_type{};
|
return arg ? std::hash<T>{}(*arg) : result_type{};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1142,12 +1031,11 @@ struct hash<std::experimental::optional<T&>>
|
|||||||
typedef typename hash<T>::result_type result_type;
|
typedef typename hash<T>::result_type result_type;
|
||||||
typedef std::experimental::optional<T&> argument_type;
|
typedef std::experimental::optional<T&> argument_type;
|
||||||
|
|
||||||
constexpr result_type operator()(argument_type const& arg) const
|
constexpr result_type operator()(argument_type const& arg) const {
|
||||||
{
|
|
||||||
return arg ? std::hash<T>{}(*arg) : result_type{};
|
return arg ? std::hash<T>{}(*arg) : result_type{};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace std
|
}
|
||||||
|
|
||||||
# undef TR2_OPTIONAL_REQUIRES
|
# undef TR2_OPTIONAL_REQUIRES
|
||||||
# undef TR2_OPTIONAL_ASSERTED_EXPRESSION
|
# undef TR2_OPTIONAL_ASSERTED_EXPRESSION
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ int winVersion()
|
|||||||
static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
|
static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
|
||||||
{
|
{
|
||||||
// Open the file
|
// Open the file
|
||||||
HANDLE hFile =
|
HANDLE hFile = CreateFile( L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE,
|
||||||
CreateFile(L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||||
|
|
||||||
if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )
|
if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )
|
||||||
{
|
{
|
||||||
@@ -60,8 +60,8 @@ static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
|
|||||||
|
|
||||||
MINIDUMP_TYPE mdt = MiniDumpWithFullMemory;
|
MINIDUMP_TYPE mdt = MiniDumpWithFullMemory;
|
||||||
|
|
||||||
BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt,
|
BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(),
|
||||||
(ExceptionInfo != 0) ? &mdei : 0, 0, 0);
|
hFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, 0 );
|
||||||
|
|
||||||
// Close the file
|
// Close the file
|
||||||
CloseHandle( hFile );
|
CloseHandle( hFile );
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
|
|
||||||
#include "HL_Pointer.h"
|
#include "HL_Pointer.h"
|
||||||
|
|
||||||
UsageCounter::UsageCounter() {}
|
UsageCounter::UsageCounter()
|
||||||
|
{}
|
||||||
|
|
||||||
UsageCounter::~UsageCounter() {}
|
UsageCounter::~UsageCounter()
|
||||||
|
{}
|
||||||
|
|
||||||
int UsageCounter::obtain(int usageId)
|
int UsageCounter::obtain(int usageId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,399 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/// @file
|
|
||||||
/// @brief A thread-local fixed-block memory pool plus a std-conforming Allocator wrapper, used to
|
|
||||||
/// pool the small, short-lived, per-RTP-packet shared_ptr nodes produced by std::allocate_shared
|
|
||||||
/// (e.g. allocate_shared<jrtplib::RTPPacket> on the capture path and allocate_shared<RtpBuffer::
|
|
||||||
/// Packet> in the jitter buffer). Those objects are fixed-size and churn at the packet rate, so a
|
|
||||||
/// pool removes them from the general allocator's hot path.
|
|
||||||
///
|
|
||||||
/// Pooling is active by default. Define HL_RTP_POOL=0 at compile time to make hl::PoolAllocator a
|
|
||||||
/// transparent passthrough to the global allocator (i.e. allocate_shared behaves like make_shared)
|
|
||||||
/// for A/B benchmarking without touching the call sites.
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <mutex>
|
|
||||||
#include <new>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifndef HL_RTP_POOL
|
|
||||||
#define HL_RTP_POOL 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace hl
|
|
||||||
{
|
|
||||||
#if HL_RTP_POOL
|
|
||||||
/// @class FixedBlockPool
|
|
||||||
/// A process-wide, fixed-block pool with a lock-free thread-local fast path: uniform 256-byte
|
|
||||||
/// blocks carved from 64 KB chunks, an intrusive thread-local free list, and a per-block header
|
|
||||||
/// tag so deallocate() is O(1) and lock-free for any block (and can tell pooled blocks from the
|
|
||||||
/// global-allocator fallback used for oversized requests) regardless of the freeing thread.
|
|
||||||
///
|
|
||||||
/// CROSS-THREAD RECLAIM (2026-07-26) — fixes an unbounded capacity ratchet. A uniform block size
|
|
||||||
/// makes a block allocated on one thread safe to free on another, but "safe" is not "reclaimed".
|
|
||||||
/// With a purely thread-local free list the block joins the FREEING thread's list, so a
|
|
||||||
/// producer/consumer split strands it: vq-core allocates RTPPacket / jitter-buffer nodes on the
|
|
||||||
/// capture threads and frees them on the interval/reap side, so the capture threads' lists were
|
|
||||||
/// perpetually empty and refill() carved a fresh chunk every BlocksPerChunk allocations, forever.
|
|
||||||
/// Observed on vqmonitor (the only box with >1 capture thread): capacityBlocks() 256 -> 11.8M
|
|
||||||
/// = 2.7 GiB over 4 days at ~25 MiB/h, while activeBlocks() drained to 0 every idle interval.
|
|
||||||
///
|
|
||||||
/// The fix is a process-wide depot of free-block batches. A thread whose free list grows past
|
|
||||||
/// ThreadCacheHighWater hands a batch down to the depot; a thread that runs dry takes a batch
|
|
||||||
/// from the depot before carving a new chunk; a thread flushes its remainder to the depot on
|
|
||||||
/// exit. The depot is touched once per BlocksPerChunk operations and the hysteresis between
|
|
||||||
/// "empty" and the high-water keeps it off the per-packet path, so the fast path stays lock-free.
|
|
||||||
/// capacityBlocks() now plateaus at the true concurrent high-water instead of ratcheting.
|
|
||||||
class FixedBlockPool
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Usable bytes handed back to the caller from a pooled block. Comfortably covers the
|
|
||||||
/// shared_ptr nodes we pool (control block + RTPPacket / RtpBuffer::Packet, ~90-130 bytes).
|
|
||||||
static constexpr std::size_t PayloadSize = 240;
|
|
||||||
static constexpr std::size_t BlocksPerChunk = 256;
|
|
||||||
/// Free blocks a thread keeps to itself before handing a BlocksPerChunk batch to the depot.
|
|
||||||
/// Must exceed BlocksPerChunk so a thread that churns around the boundary does not bounce
|
|
||||||
/// on the depot mutex; 2x leaves a full batch in hand after every release.
|
|
||||||
static constexpr std::size_t ThreadCacheHighWater = 2 * BlocksPerChunk;
|
|
||||||
|
|
||||||
static void* allocate(std::size_t size)
|
|
||||||
{
|
|
||||||
if (size > PayloadSize)
|
|
||||||
{
|
|
||||||
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
|
||||||
tagOf(raw) = TagGlobal;
|
|
||||||
s_activeGlobal.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
return raw + HeaderSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadCache& tc = cache();
|
|
||||||
if (tc.head == nullptr)
|
|
||||||
{
|
|
||||||
// Reclaim from the depot before asking the OS for more memory. Only when the
|
|
||||||
// depot is dry as well has the pool genuinely outgrown its current capacity.
|
|
||||||
std::size_t taken = 0;
|
|
||||||
tc.head = depot().takeBatch(taken);
|
|
||||||
if (tc.head == nullptr)
|
|
||||||
{
|
|
||||||
tc.head = registry().refill();
|
|
||||||
taken = BlocksPerChunk;
|
|
||||||
}
|
|
||||||
tc.count = taken;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* block = static_cast<uint8_t*>(tc.head);
|
|
||||||
tc.head = nextOf(block);
|
|
||||||
--tc.count;
|
|
||||||
s_activeBlocks.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
return block + HeaderSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deallocate(void* ptr) noexcept
|
|
||||||
{
|
|
||||||
if (ptr == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint8_t* block = static_cast<uint8_t*>(ptr) - HeaderSize;
|
|
||||||
if (tagOf(block) == TagPool)
|
|
||||||
{
|
|
||||||
ThreadCache& tc = cache();
|
|
||||||
nextOf(block) = tc.head;
|
|
||||||
tc.head = block;
|
|
||||||
++tc.count;
|
|
||||||
s_activeBlocks.fetch_sub(1, std::memory_order_relaxed);
|
|
||||||
|
|
||||||
// The one line that closes the ratchet: surplus goes back to the depot, where
|
|
||||||
// the thread that actually allocates can reach it.
|
|
||||||
if (tc.count >= ThreadCacheHighWater)
|
|
||||||
releaseBatch(tc, BlocksPerChunk);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
::operator delete(static_cast<void*>(block));
|
|
||||||
s_activeGlobal.fetch_sub(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @name Diagnostics (relaxed gauges — not synchronization)
|
|
||||||
/// @{
|
|
||||||
/// Blocks currently handed out from the pool = pooled allocate() minus
|
|
||||||
/// pooled deallocate(). Unlike capacityBlocks() (the chunk high-water,
|
|
||||||
/// which only ever grows) this DROPS when objects are freed, so a rising
|
|
||||||
/// activeBlocks() is a genuine leak of referenced objects rather than a
|
|
||||||
/// traffic peak that merely carved extra chunks. Covers every pooled
|
|
||||||
/// shared_ptr node (allocate_shared<RTPPacket> + jitter-buffer packets).
|
|
||||||
static std::int64_t activeBlocks() noexcept { return s_activeBlocks.load(std::memory_order_relaxed); }
|
|
||||||
/// Live oversized allocations that overflowed to ::operator new.
|
|
||||||
static std::int64_t activeGlobalAllocations() noexcept { return s_activeGlobal.load(std::memory_order_relaxed); }
|
|
||||||
/// Total blocks ever carved (chunks x BlocksPerChunk) = high-water capacity.
|
|
||||||
/// With cross-thread reclaim this PLATEAUS once the pool has covered the
|
|
||||||
/// concurrent high-water; sustained linear growth means reclaim is not working.
|
|
||||||
static std::int64_t capacityBlocks() noexcept
|
|
||||||
{
|
|
||||||
return s_chunks.load(std::memory_order_relaxed) * static_cast<std::int64_t>(BlocksPerChunk);
|
|
||||||
}
|
|
||||||
/// Free blocks parked in the process-wide depot, i.e. reclaimed from a freeing
|
|
||||||
/// thread and available to any allocating thread. Healthy steady state is a
|
|
||||||
/// non-zero, bounded value: it is the surplus that used to be stranded.
|
|
||||||
static std::int64_t depotBlocks() noexcept { return s_depotBlocks.load(std::memory_order_relaxed); }
|
|
||||||
/// Approximate live pooled payload bytes (excludes per-block header).
|
|
||||||
static std::int64_t activeBytes() noexcept { return activeBlocks() * static_cast<std::int64_t>(PayloadSize); }
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline static std::atomic<std::int64_t> s_activeBlocks{0};
|
|
||||||
inline static std::atomic<std::int64_t> s_activeGlobal{0};
|
|
||||||
inline static std::atomic<std::int64_t> s_chunks{0};
|
|
||||||
inline static std::atomic<std::int64_t> s_depotBlocks{0};
|
|
||||||
|
|
||||||
static constexpr std::size_t HeaderSize =
|
|
||||||
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
|
|
||||||
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
|
|
||||||
|
|
||||||
static constexpr uint64_t TagPool = 0x504F4F4C52545008ULL; // "POOLRTP\b"
|
|
||||||
static constexpr uint64_t TagGlobal = 0x474C4F42524C0808ULL; // "GLOBRL\b\b"
|
|
||||||
|
|
||||||
/// A free block's payload is dead space, so the list links live there. Slot 0 chains
|
|
||||||
/// blocks within a list/batch; slots 1-2 are used only on a batch's head block, to chain
|
|
||||||
/// batches inside the depot and to carry the batch length (batches from a thread-exit
|
|
||||||
/// flush are shorter than BlocksPerChunk).
|
|
||||||
static_assert(PayloadSize >= 2 * sizeof(void*) + sizeof(std::size_t),
|
|
||||||
"pooled block payload must hold the free-list, depot and length slots");
|
|
||||||
|
|
||||||
static uint64_t& tagOf(void* block) noexcept { return *reinterpret_cast<uint64_t*>(block); }
|
|
||||||
|
|
||||||
static void*& nextOf(void* block) noexcept
|
|
||||||
{
|
|
||||||
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void*& batchNextOf(void* block) noexcept
|
|
||||||
{
|
|
||||||
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize + sizeof(void*));
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::size_t& batchLenOf(void* block) noexcept
|
|
||||||
{
|
|
||||||
return *reinterpret_cast<std::size_t*>(static_cast<uint8_t*>(block) + HeaderSize + 2 * sizeof(void*));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Per-thread free list. The destructor hands the remainder to the depot so a thread that
|
|
||||||
/// exits does not strand its blocks — the same ratchet as the cross-thread case, just
|
|
||||||
/// triggered once per thread instead of continuously.
|
|
||||||
struct ThreadCache
|
|
||||||
{
|
|
||||||
void* head = nullptr;
|
|
||||||
std::size_t count = 0;
|
|
||||||
|
|
||||||
~ThreadCache()
|
|
||||||
{
|
|
||||||
while (count > 0 && head != nullptr)
|
|
||||||
releaseBatch(*this, count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static ThreadCache& cache() noexcept
|
|
||||||
{
|
|
||||||
static thread_local ThreadCache tc;
|
|
||||||
return tc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Detach the first `blocks` entries of the thread list and park them in the depot.
|
|
||||||
static void releaseBatch(ThreadCache& tc, std::size_t blocks) noexcept
|
|
||||||
{
|
|
||||||
if (blocks == 0 || tc.head == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
void* batchHead = tc.head;
|
|
||||||
void* batchTail = batchHead;
|
|
||||||
std::size_t length = 1;
|
|
||||||
for (; length < blocks && nextOf(batchTail) != nullptr; ++length)
|
|
||||||
batchTail = nextOf(batchTail);
|
|
||||||
|
|
||||||
tc.head = nextOf(batchTail);
|
|
||||||
tc.count -= length;
|
|
||||||
nextOf(batchTail) = nullptr;
|
|
||||||
|
|
||||||
batchLenOf(batchHead) = length;
|
|
||||||
depot().giveBatch(batchHead, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process-wide store of reclaimed batches. Intrusive (the links live in the free blocks
|
|
||||||
/// themselves) so neither path can allocate or throw — deallocate() is noexcept. The mutex
|
|
||||||
/// is taken once per BlocksPerChunk operations, not per packet.
|
|
||||||
class Depot
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void* takeBatch(std::size_t& lengthOut) noexcept
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
|
||||||
void* batch = m_Head;
|
|
||||||
if (batch == nullptr)
|
|
||||||
{
|
|
||||||
lengthOut = 0;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
m_Head = batchNextOf(batch);
|
|
||||||
lengthOut = batchLenOf(batch);
|
|
||||||
batchNextOf(batch) = nullptr;
|
|
||||||
s_depotBlocks.fetch_sub(static_cast<std::int64_t>(lengthOut), std::memory_order_relaxed);
|
|
||||||
return batch;
|
|
||||||
}
|
|
||||||
|
|
||||||
void giveBatch(void* batch, std::size_t length) noexcept
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
|
||||||
batchNextOf(batch) = m_Head;
|
|
||||||
m_Head = batch;
|
|
||||||
s_depotBlocks.fetch_add(static_cast<std::int64_t>(length), std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex m_Mutex;
|
|
||||||
void* m_Head = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ChunkRegistry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void* refill()
|
|
||||||
{
|
|
||||||
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
|
||||||
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
|
|
||||||
s_chunks.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
|
||||||
m_Chunks.push_back(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* list = nullptr;
|
|
||||||
for (std::size_t i = 0; i < BlocksPerChunk; ++i)
|
|
||||||
{
|
|
||||||
uint8_t* block = chunk + i * BlockSize;
|
|
||||||
tagOf(block) = TagPool;
|
|
||||||
nextOf(block) = list;
|
|
||||||
list = block;
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex m_Mutex;
|
|
||||||
std::vector<uint8_t*> m_Chunks;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Both singletons are deliberately immortal (leaked at exit, reclaimed by the OS).
|
|
||||||
/// A thread_local ThreadCache is destroyed at thread exit — for the main thread that is
|
|
||||||
/// during static destruction — and its destructor touches the depot, so a depot with a
|
|
||||||
/// destructor could be used after being destroyed. The same applies to the chunk memory:
|
|
||||||
/// any pooled object outliving the registry would free into deleted chunks. Never
|
|
||||||
/// destroying them removes both hazards; the process is exiting either way.
|
|
||||||
static Depot& depot() noexcept
|
|
||||||
{
|
|
||||||
static Depot* instance = new Depot();
|
|
||||||
return *instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ChunkRegistry& registry()
|
|
||||||
{
|
|
||||||
static ChunkRegistry* instance = new ChunkRegistry();
|
|
||||||
return *instance;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif // HL_RTP_POOL
|
|
||||||
|
|
||||||
/// Mode-independent accessors for the pool diagnostics, so callers need not know
|
|
||||||
/// whether HL_RTP_POOL is compiled in. When pooling is disabled they return -1
|
|
||||||
/// ("not applicable") since allocate_shared then behaves like make_shared.
|
|
||||||
#if HL_RTP_POOL
|
|
||||||
inline std::int64_t poolActiveBlocks() noexcept
|
|
||||||
{
|
|
||||||
return FixedBlockPool::activeBlocks();
|
|
||||||
}
|
|
||||||
inline std::int64_t poolCapacityBlocks() noexcept
|
|
||||||
{
|
|
||||||
return FixedBlockPool::capacityBlocks();
|
|
||||||
}
|
|
||||||
inline std::int64_t poolDepotBlocks() noexcept
|
|
||||||
{
|
|
||||||
return FixedBlockPool::depotBlocks();
|
|
||||||
}
|
|
||||||
inline std::int64_t poolActiveGlobal() noexcept
|
|
||||||
{
|
|
||||||
return FixedBlockPool::activeGlobalAllocations();
|
|
||||||
}
|
|
||||||
inline std::int64_t poolActiveBytes() noexcept
|
|
||||||
{
|
|
||||||
return FixedBlockPool::activeBytes();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
inline std::int64_t poolActiveBlocks() noexcept
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
inline std::int64_t poolCapacityBlocks() noexcept
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
inline std::int64_t poolDepotBlocks() noexcept
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
inline std::int64_t poolActiveGlobal() noexcept
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
inline std::int64_t poolActiveBytes() noexcept
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @class PoolAllocator
|
|
||||||
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
|
|
||||||
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
|
|
||||||
/// that does not fit a pooled block) it delegates to the global allocator, matching make_shared.
|
|
||||||
template<class T>
|
|
||||||
struct PoolAllocator
|
|
||||||
{
|
|
||||||
using value_type = T;
|
|
||||||
|
|
||||||
PoolAllocator() noexcept = default;
|
|
||||||
template<class U>
|
|
||||||
PoolAllocator(const PoolAllocator<U>&) noexcept
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
T* allocate(std::size_t n)
|
|
||||||
{
|
|
||||||
#if HL_RTP_POOL
|
|
||||||
return static_cast<T*>(FixedBlockPool::allocate(n * sizeof(T)));
|
|
||||||
#else
|
|
||||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(T* p, std::size_t /*n*/) noexcept
|
|
||||||
{
|
|
||||||
#if HL_RTP_POOL
|
|
||||||
FixedBlockPool::deallocate(p);
|
|
||||||
#else
|
|
||||||
::operator delete(static_cast<void*>(p));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class U>
|
|
||||||
bool operator==(const PoolAllocator<U>&) const noexcept
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template<class U>
|
|
||||||
bool operator!=(const PoolAllocator<U>&) const noexcept
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace hl
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "HL_Process.h"
|
#include "HL_Process.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#ifdef TARGET_WIN
|
#ifdef TARGET_WIN
|
||||||
# define popen _popen
|
# define popen _popen
|
||||||
@@ -10,6 +9,7 @@
|
|||||||
|
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <iostream>
|
||||||
#include "helper/HL_String.h"
|
#include "helper/HL_String.h"
|
||||||
|
|
||||||
int OsProcess::execSystem(const std::string& cmd)
|
int OsProcess::execSystem(const std::string& cmd)
|
||||||
@@ -39,7 +39,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
|||||||
PROCESS_INFORMATION pi = { 0 };
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
char* cmdline = (char*)_alloca(cmd.size()+1);
|
char* cmdline = (char*)_alloca(cmd.size()+1);
|
||||||
strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str());
|
strcpy(cmdline, StringHelper::replace(cmd, "/", "\\").c_str());
|
||||||
|
|
||||||
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
||||||
if (! fSuccess)
|
if (! fSuccess)
|
||||||
@@ -68,7 +68,7 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
|||||||
if (!dwAvail) // no data available, return
|
if (!dwAvail) // no data available, return
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!::ReadFile(hPipeRead, buf, std::min(sizeof(buf) - 1, (size_t)dwAvail), &dwRead, NULL) || !dwRead)
|
if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
|
||||||
// error, the child process might ended
|
// error, the child process might ended
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -85,9 +85,10 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::thread>
|
std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline,
|
||||||
OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> callback,
|
std::function<void(const std::string& line)> callback,
|
||||||
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag)
|
std::function<void(const std::string& reason)> finished_callback,
|
||||||
|
bool& finish_flag)
|
||||||
{
|
{
|
||||||
// std::cout << cmdline << std::endl;
|
// std::cout << cmdline << std::endl;
|
||||||
|
|
||||||
@@ -102,8 +103,7 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
|
|||||||
if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) )
|
if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) )
|
||||||
return std::shared_ptr<std::thread>();
|
return std::shared_ptr<std::thread>();
|
||||||
|
|
||||||
STARTUPINFOA si;
|
STARTUPINFOA si; memset(&si, 0, sizeof si);
|
||||||
memset(&si, 0, sizeof si);
|
|
||||||
si.cb = sizeof(STARTUPINFOA);
|
si.cb = sizeof(STARTUPINFOA);
|
||||||
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||||
si.hStdOutput = hPipeWrite;
|
si.hStdOutput = hPipeWrite;
|
||||||
@@ -114,11 +114,11 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
|
|||||||
memset(&pi, 0, sizeof pi);
|
memset(&pi, 0, sizeof pi);
|
||||||
|
|
||||||
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
char* cmdbuffer = (char*)_alloca(cmdline.size()+1);
|
||||||
strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str());
|
strcpy(cmdbuffer, StringHelper::replace(cmdline, "/", "\\").c_str());
|
||||||
|
|
||||||
|
|
||||||
BOOL fSuccess =
|
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
||||||
CreateProcessA(nullptr, cmdbuffer, nullptr, nullptr, TRUE, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi);
|
CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi);
|
||||||
if (! fSuccess)
|
if (! fSuccess)
|
||||||
{
|
{
|
||||||
CloseHandle( hPipeWrite );
|
CloseHandle( hPipeWrite );
|
||||||
@@ -129,8 +129,7 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
|
|||||||
std::shared_ptr<std::thread> r = std::make_shared<std::thread>(
|
std::shared_ptr<std::thread> r = std::make_shared<std::thread>(
|
||||||
[&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]()
|
[&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]()
|
||||||
{
|
{
|
||||||
char buf[4096];
|
char buf[4096]; memset(buf, 0, sizeof buf);
|
||||||
memset(buf, 0, sizeof buf);
|
|
||||||
for (; !finish_flag ;)
|
for (; !finish_flag ;)
|
||||||
{
|
{
|
||||||
// Give some timeslice (50ms), so we won't waste 100% cpu.
|
// Give some timeslice (50ms), so we won't waste 100% cpu.
|
||||||
@@ -149,9 +148,7 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
int filled = strlen(buf);
|
int filled = strlen(buf);
|
||||||
if (!::ReadFile(hPipeRead, buf + filled, std::min(sizeof(buf) - 1 - filled, (size_t)dwAvail),
|
if (!::ReadFile(hPipeRead, buf + filled, min(sizeof(buf) - 1 - filled, dwAvail), &dwRead, nullptr) || !dwRead)
|
||||||
&dwRead, nullptr) ||
|
|
||||||
!dwRead)
|
|
||||||
// error, the child process might ended
|
// error, the child process might ended
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -163,14 +160,14 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
|
|||||||
{
|
{
|
||||||
std::string line(buf, cr - buf -1);
|
std::string line(buf, cr - buf -1);
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(strx::trim(line));
|
callback(StringHelper::trim(line));
|
||||||
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} //for
|
} //for
|
||||||
|
|
||||||
if (buf[0])
|
if (buf[0])
|
||||||
callback(strx::trim(std::string(buf)));
|
callback(StringHelper::trim(std::string(buf)));
|
||||||
|
|
||||||
char ctrlc = 3;
|
char ctrlc = 3;
|
||||||
//if (finish_flag)
|
//if (finish_flag)
|
||||||
@@ -232,12 +229,12 @@ int OsProcess::execSystem(const std::string& cmd)
|
|||||||
#include "helper/HL_String.h"
|
#include "helper/HL_String.h"
|
||||||
#include "helper/HL_Sync.h"
|
#include "helper/HL_Sync.h"
|
||||||
|
|
||||||
std::shared_ptr<std::thread>
|
std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline,
|
||||||
OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback,
|
std::function<void(const std::string& line)> line_callback,
|
||||||
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag)
|
std::function<void(const std::string& reason)> finished_callback,
|
||||||
|
bool& finish_flag)
|
||||||
{
|
{
|
||||||
std::shared_ptr<std::thread> t = std::make_shared<std::thread>(
|
std::shared_ptr<std::thread> t = std::make_shared<std::thread>([cmdline, line_callback, finished_callback, &finish_flag]()
|
||||||
[cmdline, line_callback, finished_callback, &finish_flag]()
|
|
||||||
{
|
{
|
||||||
ThreadHelper::setName("OsProcess::asyncExecCommand");
|
ThreadHelper::setName("OsProcess::asyncExecCommand");
|
||||||
std::string cp = cmdline;
|
std::string cp = cmdline;
|
||||||
@@ -278,18 +275,19 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
|
|||||||
buffer[r] = 0;
|
buffer[r] = 0;
|
||||||
lines += std::string(buffer);
|
lines += std::string(buffer);
|
||||||
}
|
}
|
||||||
} while (r == sizeof(buffer) - 1);
|
}
|
||||||
|
while (r == sizeof(buffer) - 1);
|
||||||
|
|
||||||
if (lines.find('\n') != std::string::npos && line_callback)
|
if (lines.find("\n") != std::string::npos && line_callback)
|
||||||
{
|
{
|
||||||
std::string::size_type p = 0;
|
std::string::size_type p = 0;
|
||||||
while (p < lines.size())
|
while (p < lines.size())
|
||||||
{
|
{
|
||||||
std::string::size_type d = lines.find('\n', p);
|
std::string::size_type d = lines.find("\n", p);
|
||||||
if (d != std::string::npos)
|
if (d != std::string::npos)
|
||||||
{
|
{
|
||||||
if (line_callback)
|
if (line_callback)
|
||||||
line_callback(strx::trim(lines.substr(p, d - p)));
|
line_callback(StringHelper::trim(lines.substr(p, d-p)));
|
||||||
p = d + 1;
|
p = d + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,8 +311,6 @@ OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
|
||||||
|
|
||||||
pid_t OsProcess::findPid(const std::string& cmdline)
|
pid_t OsProcess::findPid(const std::string& cmdline)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -336,6 +332,5 @@ void OsProcess::killByPid(pid_t pid)
|
|||||||
return;
|
return;
|
||||||
execSystem("kill -9 " + std::to_string(pid) + " &");
|
execSystem("kill -9 " + std::to_string(pid) + " &");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -11,13 +11,15 @@ class OsProcess
|
|||||||
public:
|
public:
|
||||||
static std::string execCommand(const std::string& cmdline);
|
static std::string execCommand(const std::string& cmdline);
|
||||||
static int execSystem(const std::string& cmdline);
|
static int execSystem(const std::string& cmdline);
|
||||||
static std::shared_ptr<std::thread>
|
static std::shared_ptr<std::thread> asyncExecCommand(const std::string& cmdline,
|
||||||
asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback,
|
std::function<void(const std::string& line)> line_callback,
|
||||||
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag);
|
std::function<void(const std::string& reason)> finished_callback,
|
||||||
|
bool& finish_flag);
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||||
static pid_t findPid(const std::string& cmdline);
|
static pid_t findPid(const std::string& cmdline);
|
||||||
static void killByPid(pid_t pid);
|
static void killByPid(pid_t pid);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+67
-368
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -8,29 +8,22 @@
|
|||||||
# include <Windows.h>
|
# include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX)
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "HL_Rtp.h"
|
#include "HL_Rtp.h"
|
||||||
#include "HL_Exception.h"
|
#include "HL_Exception.h"
|
||||||
#include "HL_Log.h"
|
#include "HL_String.h"
|
||||||
|
|
||||||
|
#if defined(USE_RTP_DUMP)
|
||||||
# include "jrtplib/src/rtprawpacket.h"
|
# include "jrtplib/src/rtprawpacket.h"
|
||||||
# include "jrtplib/src/rtpipv4address.h"
|
# include "jrtplib/src/rtpipv4address.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdexcept>
|
#if !defined(TARGET_WIN)
|
||||||
#include <fstream>
|
# include <alloca.h>
|
||||||
#include <cstring>
|
#endif
|
||||||
#include <cstdio>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "network"
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
static constexpr size_t MAX_RTP_PACKET_SIZE = 65535;
|
|
||||||
static const char RTPDUMP_SHEBANG[] = "#!rtpplay1.0";
|
|
||||||
|
|
||||||
// RTP fixed header (little-endian bit-field layout)
|
|
||||||
struct RtpHeader
|
struct RtpHeader
|
||||||
{
|
{
|
||||||
unsigned char cc:4; /* CSRC count */
|
unsigned char cc:4; /* CSRC count */
|
||||||
@@ -49,53 +42,29 @@ struct RtcpHeader
|
|||||||
unsigned char rc:5; /* reception report count */
|
unsigned char rc:5; /* reception report count */
|
||||||
unsigned char p:1; /* padding flag */
|
unsigned char p:1; /* padding flag */
|
||||||
unsigned char version:2; /* protocol version */
|
unsigned char version:2; /* protocol version */
|
||||||
unsigned char pt; /* payload type */
|
unsigned char pt:8; /* payload type */
|
||||||
uint16_t len; /* length */
|
uint16_t len; /* length */
|
||||||
uint32_t ssrc; /* synchronization source */
|
uint32_t ssrc; /* synchronization source */
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- IPv4 address helpers ---
|
|
||||||
|
|
||||||
static std::string ipToString(uint32_t ip)
|
|
||||||
{
|
|
||||||
// ip in host byte order → dotted-decimal
|
|
||||||
return std::to_string((ip >> 24) & 0xFF) + "." + std::to_string((ip >> 16) & 0xFF) + "." +
|
|
||||||
std::to_string((ip >> 8) & 0xFF) + "." + std::to_string(ip & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t stringToIp(const std::string& s)
|
|
||||||
{
|
|
||||||
unsigned a = 0, b = 0, c = 0, d = 0;
|
|
||||||
if (std::sscanf(s.c_str(), "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
|
|
||||||
return 0;
|
|
||||||
if (a > 255 || b > 255 || c > 255 || d > 255)
|
|
||||||
return 0;
|
|
||||||
return (a << 24) | (b << 16) | (c << 8) | d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- RtpHelper implementation ---
|
|
||||||
|
|
||||||
bool RtpHelper::isRtp(const void* buffer, size_t length)
|
bool RtpHelper::isRtp(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (length < 12)
|
if (length < 12)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
|
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
|
||||||
if (h->version != 2)
|
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) <= 127) || ((_type & 0x7F) < 35);
|
||||||
return false;
|
|
||||||
|
|
||||||
unsigned char pt = h->pt;
|
|
||||||
bool rtp = (pt >= 96 && pt <= 127) || (pt < 35);
|
|
||||||
return rtp;
|
return rtp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
|
bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
// A minimal RTCP packet (e.g. an empty receiver report) is 8 bytes
|
if (length < 12)
|
||||||
if (length < 8)
|
|
||||||
return false;
|
return false;
|
||||||
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
|
unsigned char b = ((const unsigned char*)buffer)[0];
|
||||||
return h->version == 2;
|
|
||||||
|
return (b & 0xC0 ) == 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
||||||
@@ -106,18 +75,9 @@ bool RtpHelper::isRtcp(const void* buffer, size_t length)
|
|||||||
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (isRtp(buffer, length))
|
if (isRtp(buffer, length))
|
||||||
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
|
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
|
||||||
else if (isRtpOrRtcp(buffer, length))
|
else
|
||||||
return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc);
|
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
|
|
||||||
{
|
|
||||||
if (isRtp(buffer, length))
|
|
||||||
reinterpret_cast<RtpHeader*>(buffer)->ssrc = htonl(ssrc);
|
|
||||||
else if (isRtpOrRtcp(buffer, length))
|
|
||||||
reinterpret_cast<RtcpHeader*>(buffer)->ssrc = htonl(ssrc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpHelper::findPtype(const void* buffer, size_t length)
|
int RtpHelper::findPtype(const void* buffer, size_t length)
|
||||||
@@ -138,202 +98,47 @@ int RtpHelper::findPacketNo(const void* buffer, size_t length)
|
|||||||
|
|
||||||
int RtpHelper::findPayloadLength(const void* buffer, size_t length)
|
int RtpHelper::findPayloadLength(const void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (!isRtp(buffer, length))
|
if (isRtp(buffer, length))
|
||||||
return -1;
|
|
||||||
|
|
||||||
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
|
|
||||||
const uint8_t* p = static_cast<const uint8_t*>(buffer);
|
|
||||||
|
|
||||||
// Fixed header (12 bytes) + CSRC list (4 * CC bytes)
|
|
||||||
size_t offset = 12 + 4u * h->cc;
|
|
||||||
if (offset > length)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Header extension
|
|
||||||
if (h->x)
|
|
||||||
{
|
{
|
||||||
if (offset + 4 > length)
|
return length - 12;
|
||||||
return -1;
|
}
|
||||||
uint16_t extWords = (static_cast<uint16_t>(p[offset + 2]) << 8) | p[offset + 3];
|
else
|
||||||
offset += 4 + 4u * extWords;
|
|
||||||
if (offset > length)
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t payloadLen = length - offset;
|
#if defined(USE_RTPDUMP)
|
||||||
|
RtpDump::RtpDump(const char *filename)
|
||||||
|
:mFilename(filename)
|
||||||
|
{}
|
||||||
|
|
||||||
// Padding
|
RtpDump::~RtpDump()
|
||||||
if (h->p && payloadLen > 0)
|
|
||||||
{
|
{
|
||||||
uint8_t padBytes = p[length - 1];
|
flush();
|
||||||
if (padBytes > payloadLen)
|
for (PacketList::iterator packetIter=mPacketList.begin(); packetIter!=mPacketList.end(); ++packetIter)
|
||||||
return -1;
|
|
||||||
payloadLen -= padBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<int>(payloadLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::microseconds RtpHelper::toMicroseconds(const jrtplib::RTPTime& t)
|
|
||||||
{
|
{
|
||||||
return std::chrono::microseconds(uint64_t(t.GetDouble() * 1000000));
|
//free(packetIter->mData);
|
||||||
|
delete packetIter->mPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RtpDump::RtpDump(const char* filename) : mFilename(filename ? filename : "") {}
|
|
||||||
|
|
||||||
RtpDump::~RtpDump() = default;
|
|
||||||
|
|
||||||
void RtpDump::setSource(uint32_t ip, uint16_t port)
|
|
||||||
{
|
|
||||||
mSourceIp = ip;
|
|
||||||
mSourcePort = port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpDump::load()
|
void RtpDump::load()
|
||||||
{
|
{
|
||||||
if (mFilename.empty())
|
FILE* f = fopen(mFilename.c_str(), "rb");
|
||||||
throw std::runtime_error("No filename specified");
|
if (!f)
|
||||||
|
throw Exception(ERR_WAVFILE_FAILED);
|
||||||
|
|
||||||
std::ifstream input(mFilename, std::ios::binary);
|
while (!feof(f))
|
||||||
if (!input.is_open())
|
|
||||||
throw std::runtime_error("Failed to open RTP dump file: " + mFilename);
|
|
||||||
|
|
||||||
mPacketList.clear();
|
|
||||||
|
|
||||||
// --- 1. Text header: "#!rtpplay1.0 <ip>/<port>\n" ---
|
|
||||||
std::string textLine;
|
|
||||||
std::getline(input, textLine);
|
|
||||||
if (textLine.compare(0, sizeof(RTPDUMP_SHEBANG) - 1, RTPDUMP_SHEBANG) != 0)
|
|
||||||
throw std::runtime_error("Invalid rtpdump header: expected " + std::string(RTPDUMP_SHEBANG));
|
|
||||||
|
|
||||||
// Parse source address from the text line
|
|
||||||
size_t spacePos = textLine.find(' ');
|
|
||||||
if (spacePos != std::string::npos)
|
|
||||||
{
|
{
|
||||||
std::string addrPart = textLine.substr(spacePos + 1);
|
RtpData data;
|
||||||
size_t slashPos = addrPart.find('/');
|
fread(&data.mLength, sizeof data.mLength, 1, f);
|
||||||
if (slashPos != std::string::npos)
|
data.mData = new char[data.mLength];
|
||||||
{
|
fread(data.mData, 1, data.mLength, f);
|
||||||
mSourceIp = stringToIp(addrPart.substr(0, slashPos));
|
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
|
||||||
try
|
jrtplib::RTPTime t(0);
|
||||||
{
|
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)data.mData, data.mLength, &addr, t, true);
|
||||||
mSourcePort = static_cast<uint16_t>(std::stoi(addrPart.substr(slashPos + 1)));
|
data.mPacket = new jrtplib::RTPPacket(*raw);
|
||||||
|
mPacketList.push_back(data);
|
||||||
}
|
}
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
mSourcePort = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 2. Binary file header (RD_hdr_t, 16 bytes) ---
|
|
||||||
uint32_t buf32;
|
|
||||||
uint16_t buf16;
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&buf32), 4);
|
|
||||||
mStartSec = ntohl(buf32);
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&buf32), 4);
|
|
||||||
mStartUsec = ntohl(buf32);
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&buf32), 4); // source IP (already NBO in file)
|
|
||||||
// The binary header stores IP in network byte order; convert to host
|
|
||||||
mSourceIp = ntohl(buf32);
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&buf16), 2);
|
|
||||||
mSourcePort = ntohs(buf16);
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&buf16), 2); // padding — discard
|
|
||||||
|
|
||||||
if (!input.good())
|
|
||||||
throw std::runtime_error("Failed to read rtpdump binary header");
|
|
||||||
|
|
||||||
// --- 3. Packet records ---
|
|
||||||
size_t packetCount = 0;
|
|
||||||
|
|
||||||
while (input.good() && input.peek() != EOF)
|
|
||||||
{
|
|
||||||
// Packet header: length(2) + plen(2) + offset(4) = 8 bytes
|
|
||||||
uint16_t recLength, plen;
|
|
||||||
uint32_t offsetMs;
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&recLength), 2);
|
|
||||||
if (input.gcount() != 2)
|
|
||||||
break;
|
|
||||||
recLength = ntohs(recLength);
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&plen), 2);
|
|
||||||
if (input.gcount() != 2)
|
|
||||||
break;
|
|
||||||
plen = ntohs(plen);
|
|
||||||
|
|
||||||
input.read(reinterpret_cast<char*>(&offsetMs), 4);
|
|
||||||
if (input.gcount() != 4)
|
|
||||||
break;
|
|
||||||
offsetMs = ntohl(offsetMs);
|
|
||||||
|
|
||||||
// All-zeros record signals end of file in some implementations
|
|
||||||
if (recLength == 0 && plen == 0 && offsetMs == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (plen == 0 || plen > MAX_RTP_PACKET_SIZE)
|
|
||||||
throw std::runtime_error("Invalid packet payload length: " + std::to_string(plen));
|
|
||||||
|
|
||||||
if (recLength < plen + 8)
|
|
||||||
throw std::runtime_error("Record length (" + std::to_string(recLength) +
|
|
||||||
") smaller than payload + header (" + std::to_string(plen + 8) + ")");
|
|
||||||
|
|
||||||
// Read body
|
|
||||||
std::vector<uint8_t> body(plen);
|
|
||||||
input.read(reinterpret_cast<char*>(body.data()), plen);
|
|
||||||
if (static_cast<size_t>(input.gcount()) != plen)
|
|
||||||
throw std::runtime_error("Incomplete packet data in rtpdump file");
|
|
||||||
|
|
||||||
// Skip any padding between plen and recLength-8
|
|
||||||
size_t pad = static_cast<size_t>(recLength) - 8 - plen;
|
|
||||||
if (pad > 0)
|
|
||||||
input.seekg(static_cast<std::streamoff>(pad), std::ios::cur);
|
|
||||||
|
|
||||||
RtpData entry;
|
|
||||||
entry.mRawData = std::move(body);
|
|
||||||
entry.mOffsetMs = offsetMs;
|
|
||||||
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
|
|
||||||
|
|
||||||
mPacketList.push_back(std::move(entry));
|
|
||||||
packetCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICELogInfo(<< "Loaded " << packetCount << " packets from " << mFilename);
|
|
||||||
mLoaded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RtpDump::count() const
|
size_t RtpDump::count() const
|
||||||
@@ -343,145 +148,39 @@ size_t RtpDump::count() const
|
|||||||
|
|
||||||
jrtplib::RTPPacket& RtpDump::packetAt(size_t index)
|
jrtplib::RTPPacket& RtpDump::packetAt(size_t index)
|
||||||
{
|
{
|
||||||
if (index >= mPacketList.size())
|
|
||||||
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
|
|
||||||
|
|
||||||
if (!mPacketList[index].mPacket)
|
|
||||||
throw std::runtime_error("No parsed RTP data at index " + std::to_string(index));
|
|
||||||
|
|
||||||
return *mPacketList[index].mPacket;
|
return *mPacketList[index].mPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<uint8_t>& RtpDump::rawDataAt(size_t index) const
|
|
||||||
{
|
|
||||||
if (index >= mPacketList.size())
|
|
||||||
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
|
|
||||||
|
|
||||||
return mPacketList[index].mRawData;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t RtpDump::offsetAt(size_t index) const
|
|
||||||
{
|
|
||||||
if (index >= mPacketList.size())
|
|
||||||
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
|
|
||||||
|
|
||||||
return mPacketList[index].mOffsetMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RtpDump::add(const void* buffer, size_t len)
|
void RtpDump::add(const void* buffer, size_t len)
|
||||||
{
|
{
|
||||||
if (!buffer || len == 0)
|
RtpData data;
|
||||||
return;
|
data.mData = malloc(len);
|
||||||
|
memcpy(data.mData, buffer, len);
|
||||||
|
data.mLength = len;
|
||||||
|
|
||||||
uint32_t offsetMs = 0;
|
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
|
||||||
auto now = std::chrono::steady_clock::now();
|
jrtplib::RTPTime t(0);
|
||||||
|
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)const_cast<void*>(data.mData), data.mLength, &addr, t, true);
|
||||||
if (!mRecording)
|
data.mPacket = new jrtplib::RTPPacket(*raw);
|
||||||
{
|
//delete raw;
|
||||||
mRecording = true;
|
mPacketList.push_back(data);
|
||||||
mRecordStart = now;
|
|
||||||
|
|
||||||
// Capture wall-clock start time
|
|
||||||
auto wallNow = std::chrono::system_clock::now();
|
|
||||||
auto epoch = wallNow.time_since_epoch();
|
|
||||||
auto sec = std::chrono::duration_cast<std::chrono::seconds>(epoch);
|
|
||||||
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(epoch - sec);
|
|
||||||
mStartSec = static_cast<uint32_t>(sec.count());
|
|
||||||
mStartUsec = static_cast<uint32_t>(usec.count());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - mRecordStart);
|
|
||||||
offsetMs = static_cast<uint32_t>(elapsed.count());
|
|
||||||
}
|
|
||||||
|
|
||||||
add(buffer, len, offsetMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RtpDump::add(const void* buffer, size_t len, uint32_t offsetMs)
|
|
||||||
{
|
|
||||||
if (!buffer || len == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// The record length field is 16-bit and covers payload + 8 byte header
|
|
||||||
if (len > MAX_RTP_PACKET_SIZE - 8)
|
|
||||||
throw std::runtime_error("Packet too large: " + std::to_string(len));
|
|
||||||
|
|
||||||
RtpData entry;
|
|
||||||
entry.mRawData.assign(static_cast<const uint8_t*>(buffer), static_cast<const uint8_t*>(buffer) + len);
|
|
||||||
entry.mOffsetMs = offsetMs;
|
|
||||||
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
|
|
||||||
|
|
||||||
mPacketList.push_back(std::move(entry));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpDump::flush()
|
void RtpDump::flush()
|
||||||
{
|
{
|
||||||
if (mFilename.empty())
|
FILE* f = fopen(mFilename.c_str(), "wb");
|
||||||
throw std::runtime_error("No filename specified");
|
if (!f)
|
||||||
|
throw Exception(ERR_WAVFILE_FAILED);
|
||||||
|
|
||||||
std::ofstream output(mFilename, std::ios::binary);
|
PacketList::iterator packetIter = mPacketList.begin();
|
||||||
if (!output.is_open())
|
for (;packetIter != mPacketList.end(); ++packetIter)
|
||||||
throw std::runtime_error("Failed to open file for writing: " + mFilename);
|
|
||||||
|
|
||||||
// --- 1. Text header ---
|
|
||||||
std::string textLine =
|
|
||||||
std::string(RTPDUMP_SHEBANG) + " " + ipToString(mSourceIp) + "/" + std::to_string(mSourcePort) + "\n";
|
|
||||||
output.write(textLine.data(), static_cast<std::streamsize>(textLine.size()));
|
|
||||||
|
|
||||||
// --- 2. Binary file header (16 bytes) ---
|
|
||||||
uint32_t buf32;
|
|
||||||
uint16_t buf16;
|
|
||||||
|
|
||||||
buf32 = htonl(mStartSec);
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf32), 4);
|
|
||||||
|
|
||||||
buf32 = htonl(mStartUsec);
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf32), 4);
|
|
||||||
|
|
||||||
buf32 = htonl(mSourceIp);
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf32), 4);
|
|
||||||
|
|
||||||
buf16 = htons(mSourcePort);
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf16), 2);
|
|
||||||
|
|
||||||
buf16 = 0; // padding
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf16), 2);
|
|
||||||
|
|
||||||
// --- 3. Packet records ---
|
|
||||||
size_t written = 0;
|
|
||||||
|
|
||||||
for (const auto& pkt : mPacketList)
|
|
||||||
{
|
{
|
||||||
if (pkt.mRawData.empty())
|
RtpData& data = *packetIter;
|
||||||
continue;
|
// Disabled for debugging only
|
||||||
|
//fwrite(&data.mLength, sizeof data.mLength, 1, f);
|
||||||
uint16_t plen = static_cast<uint16_t>(pkt.mRawData.size());
|
fwrite(data.mData, data.mLength, 1, f);
|
||||||
uint16_t recLength = static_cast<uint16_t>(plen + 8);
|
|
||||||
|
|
||||||
buf16 = htons(recLength);
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf16), 2);
|
|
||||||
|
|
||||||
buf16 = htons(plen);
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf16), 2);
|
|
||||||
|
|
||||||
buf32 = htonl(pkt.mOffsetMs);
|
|
||||||
output.write(reinterpret_cast<const char*>(&buf32), 4);
|
|
||||||
|
|
||||||
output.write(reinterpret_cast<const char*>(pkt.mRawData.data()), plen);
|
|
||||||
|
|
||||||
written++;
|
|
||||||
}
|
}
|
||||||
|
fclose(f);
|
||||||
if (!output.good())
|
|
||||||
throw std::runtime_error("Failed to write rtpdump file: " + mFilename);
|
|
||||||
|
|
||||||
ICELogInfo(<< "Wrote " << written << " packets to " << mFilename);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void RtpDump::clear()
|
|
||||||
{
|
|
||||||
mPacketList.clear();
|
|
||||||
mLoaded = false;
|
|
||||||
mRecording = false;
|
|
||||||
}
|
|
||||||
|
|||||||
+18
-90
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com)
|
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@@ -6,14 +6,15 @@
|
|||||||
#ifndef __HL_RTP_H
|
#ifndef __HL_RTP_H
|
||||||
#define __HL_RTP_H
|
#define __HL_RTP_H
|
||||||
|
|
||||||
|
#if defined(USE_RTPDUMP)
|
||||||
# include "jrtplib/src/rtppacket.h"
|
# include "jrtplib/src/rtppacket.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "HL_Uuid.h"
|
||||||
|
#include "HL_InternetAddress.h"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
// Class to carry rtp/rtcp socket pair
|
// Class to carry rtp/rtcp socket pair
|
||||||
template<class T>
|
template<class T>
|
||||||
@@ -22,11 +23,14 @@ struct RtpPair
|
|||||||
T mRtp;
|
T mRtp;
|
||||||
T mRtcp;
|
T mRtcp;
|
||||||
|
|
||||||
RtpPair() {}
|
RtpPair()
|
||||||
|
{}
|
||||||
|
|
||||||
RtpPair(const T& rtp, const T& rtcp) : mRtp(rtp), mRtcp(rtcp) {}
|
RtpPair(const T& rtp, const T& rtcp)
|
||||||
|
:mRtp(rtp), mRtcp(rtcp)
|
||||||
|
{}
|
||||||
|
|
||||||
bool multiplexed() const { return mRtp == mRtcp; }
|
bool multiplexed() { return mRtp == mRtcp; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class RtpHelper
|
class RtpHelper
|
||||||
@@ -38,110 +42,34 @@ public:
|
|||||||
static bool isRtpOrRtcp(const void* buffer, size_t length);
|
static bool isRtpOrRtcp(const void* buffer, size_t length);
|
||||||
static bool isRtcp(const void* buffer, size_t length);
|
static bool isRtcp(const void* buffer, size_t length);
|
||||||
static unsigned findSsrc(const void* buffer, size_t length);
|
static unsigned findSsrc(const void* buffer, size_t length);
|
||||||
static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
|
|
||||||
static int findPayloadLength(const void* buffer, size_t length);
|
static int findPayloadLength(const void* buffer, size_t length);
|
||||||
|
|
||||||
static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
#if defined(USE_RTPDUMP)
|
||||||
* @brief Standard rtpdump file format (rtptools / Wireshark compatible)
|
|
||||||
*
|
|
||||||
* Conforms to the rtpdump format defined by rtptools:
|
|
||||||
* https://formats.kaitai.io/rtpdump/
|
|
||||||
*
|
|
||||||
* File layout:
|
|
||||||
* 1. Text header line:
|
|
||||||
* "#!rtpplay1.0 <source_ip>/<source_port>\n"
|
|
||||||
*
|
|
||||||
* 2. Binary file header (RD_hdr_t, 16 bytes, all big-endian):
|
|
||||||
* uint32_t start_sec - recording start time, seconds since epoch
|
|
||||||
* uint32_t start_usec - recording start time, microseconds
|
|
||||||
* uint32_t source_ip - source IP address (network byte order)
|
|
||||||
* uint16_t source_port - source port
|
|
||||||
* uint16_t padding - always 0
|
|
||||||
*
|
|
||||||
* 3. Packet records (repeated until EOF):
|
|
||||||
* Per-packet header (RD_packet_t, 8 bytes, all big-endian):
|
|
||||||
* uint16_t length - total record length (this 8-byte header + plen)
|
|
||||||
* uint16_t plen - RTP/RTCP payload length in bytes
|
|
||||||
* uint32_t offset - milliseconds since recording start
|
|
||||||
* Followed by plen bytes of RTP/RTCP packet data.
|
|
||||||
*
|
|
||||||
* Maximum single packet payload: 65535 bytes (enforced for safety).
|
|
||||||
*/
|
|
||||||
class RtpDump
|
class RtpDump
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct RtpData
|
struct RtpData
|
||||||
{
|
{
|
||||||
std::shared_ptr<jrtplib::RTPPacket> mPacket;
|
jrtplib::RTPPacket* mPacket;
|
||||||
std::vector<uint8_t> mRawData;
|
void* mData;
|
||||||
uint32_t mOffsetMs = 0;
|
size_t mLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<RtpData> PacketList;
|
typedef std::vector<RtpData> PacketList;
|
||||||
PacketList mPacketList;
|
PacketList mPacketList;
|
||||||
std::string mFilename;
|
std::string mFilename;
|
||||||
bool mLoaded = false;
|
|
||||||
|
|
||||||
// File header fields
|
|
||||||
uint32_t mSourceIp = 0;
|
|
||||||
uint16_t mSourcePort = 0;
|
|
||||||
uint32_t mStartSec = 0;
|
|
||||||
uint32_t mStartUsec = 0;
|
|
||||||
|
|
||||||
// Auto-compute packet offsets during recording
|
|
||||||
bool mRecording = false;
|
|
||||||
std::chrono::steady_clock::time_point mRecordStart;
|
|
||||||
|
|
||||||
std::shared_ptr<jrtplib::RTPPacket> parseRtpData(const uint8_t* data, size_t len);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RtpDump(const char* filename);
|
RtpDump(const char* filename);
|
||||||
~RtpDump();
|
~RtpDump();
|
||||||
|
|
||||||
/** Set source address for the file header (host byte order). */
|
|
||||||
void setSource(uint32_t ip, uint16_t port);
|
|
||||||
uint32_t sourceIp() const { return mSourceIp; }
|
|
||||||
uint16_t sourcePort() const { return mSourcePort; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Load packets from an rtpdump file
|
|
||||||
* @throws std::runtime_error on file/format error
|
|
||||||
*/
|
|
||||||
void load();
|
void load();
|
||||||
bool isLoaded() const { return mLoaded; }
|
|
||||||
|
|
||||||
size_t count() const;
|
size_t count() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get parsed RTP packet at index
|
|
||||||
* @throws std::out_of_range if index is invalid
|
|
||||||
* @throws std::runtime_error if packet could not be parsed as RTP
|
|
||||||
*/
|
|
||||||
jrtplib::RTPPacket& packetAt(size_t index);
|
jrtplib::RTPPacket& packetAt(size_t index);
|
||||||
|
|
||||||
/** @brief Get raw packet bytes at index */
|
|
||||||
const std::vector<uint8_t>& rawDataAt(size_t index) const;
|
|
||||||
|
|
||||||
/** @brief Get packet time offset in milliseconds */
|
|
||||||
uint32_t offsetAt(size_t index) const;
|
|
||||||
|
|
||||||
/** @brief Add a packet; time offset is auto-computed from first add() call */
|
|
||||||
void add(const void* data, size_t len);
|
void add(const void* data, size_t len);
|
||||||
|
|
||||||
/** @brief Add a packet with an explicit millisecond offset */
|
|
||||||
void add(const void* data, size_t len, uint32_t offsetMs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Write all packets to file in rtpdump format
|
|
||||||
* @throws std::runtime_error on file error
|
|
||||||
*/
|
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
void clear();
|
|
||||||
const std::string& filename() const { return mFilename; }
|
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ class SafeSingleton
|
|||||||
protected:
|
protected:
|
||||||
static std::atomic<T*> SharedInstance;
|
static std::atomic<T*> SharedInstance;
|
||||||
static std::mutex mMutex;
|
static std::mutex mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static T& instance()
|
static T& instance()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "../engine_config.h"
|
#include "../config.h"
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <chrono>
|
|
||||||
#if !defined(TARGET_WIN)
|
#if !defined(TARGET_WIN)
|
||||||
# include <unistd.h> // Responsible for close() call on Linux
|
# include <unistd.h> // Responsible for close() call on Linux
|
||||||
#endif
|
#endif
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
#include "HL_Sync.h"
|
#include "HL_Sync.h"
|
||||||
#include "HL_Exception.h"
|
#include "HL_Exception.h"
|
||||||
|
|
||||||
#define LOG_SUBSYSTEM "network"
|
#define LOG_SUBSYSTEM "[SocketHeap]"
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#define WSAGetLastError(X) errno
|
#define WSAGetLastError(X) errno
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
#define WSAEADDRINUSE EADDRINUSE
|
#define WSAEADDRINUSE EADDRINUSE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
// ----------------------------- SocketSink -------------------------
|
// ----------------------------- SocketSink -------------------------
|
||||||
SocketSink::~SocketSink() {}
|
SocketSink::~SocketSink()
|
||||||
|
{}
|
||||||
|
|
||||||
// ----------------------------- SocketHeap -------------------------
|
// ----------------------------- SocketHeap -------------------------
|
||||||
|
|
||||||
@@ -96,20 +96,7 @@ RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink* sin
|
|||||||
rtcp = allocSocket(family, sink, rtp->localport() + 1);
|
rtcp = allocSocket(family, sink, rtp->localport() + 1);
|
||||||
}
|
}
|
||||||
catch(...)
|
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)
|
if (!rtp || !rtcp)
|
||||||
@@ -120,8 +107,9 @@ RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink* sin
|
|||||||
freeSocket(rtcp);
|
freeSocket(rtcp);
|
||||||
throw Exception(ERR_NET_FAILED);
|
throw Exception(ERR_NET_FAILED);
|
||||||
}
|
}
|
||||||
ICELogInfo(<< "Allocated socket pair " << (family == AF_INET ? "AF_INET" : "AF_INET6") << " " << rtp->socket()
|
ICELogInfo(<< "Allocated socket pair " << (family == AF_INET ? "AF_INET" : "AF_INET6") << " "
|
||||||
<< ":" << rtcp->socket() << " at ports " << rtp->localport() << ":" << rtcp->localport());
|
<< rtp->socket() << ":" << rtcp->socket()
|
||||||
|
<< " at ports " << rtp->localport() << ":"<< rtcp->localport());
|
||||||
|
|
||||||
return RtpPair<PDatagramSocket>(rtp, rtcp);
|
return RtpPair<PDatagramSocket>(rtp, rtcp);
|
||||||
}
|
}
|
||||||
@@ -139,7 +127,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||||||
if (sock == INVALID_SOCKET)
|
if (sock == INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
// Return null socket
|
// Return null socket
|
||||||
auto result = std::make_shared<DatagramSocket>();
|
PDatagramSocket result(new DatagramSocket());
|
||||||
result->mLocalPort = port;
|
result->mLocalPort = port;
|
||||||
result->mFamily = family;
|
result->mFamily = family;
|
||||||
return result;
|
return result;
|
||||||
@@ -150,9 +138,6 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||||||
sockaddr_in6 addr6;
|
sockaddr_in6 addr6;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int testport;
|
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
|
do
|
||||||
{
|
{
|
||||||
testport = port ? port : rand() % ((mFinish - mStart) / 2) * 2 + mStart;
|
testport = port ? port : rand() % ((mFinish - mStart) / 2) * 2 + mStart;
|
||||||
@@ -178,14 +163,14 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (result == WSAEADDRINUSE && --attemptsLeft > 0);
|
} while (result == WSAEADDRINUSE);
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
throw Exception(ERR_NET_FAILED, WSAGetLastError());
|
||||||
}
|
}
|
||||||
auto resultObject = std::make_shared<DatagramSocket>();
|
PDatagramSocket resultObject(new DatagramSocket());
|
||||||
resultObject->mLocalPort = testport;
|
resultObject->mLocalPort = testport;
|
||||||
resultObject->mHandle = sock;
|
resultObject->mHandle = sock;
|
||||||
if (!resultObject->setBlocking(false))
|
if (!resultObject->setBlocking(false))
|
||||||
@@ -258,8 +243,7 @@ void SocketHeap::thread()
|
|||||||
for (auto& socketIter: mSocketMap)
|
for (auto& socketIter: mSocketMap)
|
||||||
agreggator.addSocket(socketIter.second.mSocket);
|
agreggator.addSocket(socketIter.second.mSocket);
|
||||||
|
|
||||||
/* for (SocketMap::iterator socketIter = mSocketMap.begin(); socketIter != mSocketMap.end();
|
/* for (SocketMap::iterator socketIter = mSocketMap.begin(); socketIter != mSocketMap.end(); ++socketIter)
|
||||||
++socketIter)
|
|
||||||
{
|
{
|
||||||
// Add handle to set
|
// Add handle to set
|
||||||
agreggator.addSocket(socketIter->second.mSocket);
|
agreggator.addSocket(socketIter->second.mSocket);
|
||||||
@@ -269,7 +253,7 @@ void SocketHeap::thread()
|
|||||||
// If set is not empty
|
// If set is not empty
|
||||||
if (agreggator.count() > 0)
|
if (agreggator.count() > 0)
|
||||||
{
|
{
|
||||||
if (agreggator.waitForData(10ms))
|
if (agreggator.waitForData(10))
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "There is data on UDP sockets");
|
ICELogMedia(<< "There is data on UDP sockets");
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user