- start checks with Kimi + many fixes
This commit is contained in:
366
AGENTS.md
Normal file
366
AGENTS.md
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
# 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/`
|
||||||
@@ -472,7 +472,10 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &ans
|
|||||||
//int x = 0;
|
//int x = 0;
|
||||||
//int y = 1/x;
|
//int y = 1/x;
|
||||||
|
|
||||||
int timeout = request["timeout"].asInt();
|
int timeout = 0;
|
||||||
|
if (request.isMember("timeout"))
|
||||||
|
timeout = request["timeout"].asInt();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> eventLock(mEventListMutex);
|
std::unique_lock<std::mutex> eventLock(mEventListMutex);
|
||||||
if (mEventList.empty())
|
if (mEventList.empty())
|
||||||
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout));
|
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout));
|
||||||
|
|||||||
@@ -16,46 +16,46 @@
|
|||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
myMicrophone = 1,
|
myMicrophone = 1,
|
||||||
mySpeaker = 2
|
mySpeaker = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Format
|
struct Format
|
||||||
{
|
{
|
||||||
int mRate;
|
int mRate;
|
||||||
int mChannels;
|
int mChannels;
|
||||||
|
|
||||||
Format()
|
Format()
|
||||||
:mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS)
|
:mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Format(int rate, int channels)
|
Format(int rate, int channels)
|
||||||
:mRate(rate), mChannels(channels)
|
:mRate(rate), mChannels(channels)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
size_t samplesFromSize(size_t length) const
|
size_t samplesFromSize(size_t length) const
|
||||||
{
|
{
|
||||||
return length / 2 / mChannels;
|
return length / 2 / mChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns milliseconds
|
// Returns milliseconds
|
||||||
float timeFromSize(size_t length) const
|
float timeFromSize(size_t length) const
|
||||||
{
|
{
|
||||||
return float(samplesFromSize(length) / (mRate / 1000.0));
|
return float(samplesFromSize(length) / (mRate / 1000.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
float sizeFromTime(size_t milliseconds) const
|
float sizeFromTime(size_t milliseconds) const
|
||||||
{
|
{
|
||||||
return float((milliseconds * mRate) / 500.0 * mChannels);
|
return float((milliseconds * mRate) / 500.0 * mChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString()
|
std::string toString()
|
||||||
{
|
{
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
sprintf(buffer, "%dHz %dch", mRate, mChannels);
|
std::snprintf(buffer, sizeof(buffer), "%dHz %dch", mRate, mChannels);
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator == (const Format& rhs) const
|
bool operator == (const Format& rhs) const
|
||||||
@@ -78,19 +78,19 @@ namespace Audio
|
|||||||
return mChannels;
|
return mChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataConnection
|
class DataConnection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void onMicData(const Format& format, const void* buffer, int length) = 0;
|
virtual void onMicData(const Format& format, const void* buffer, int length) = 0;
|
||||||
virtual void onSpkData(const Format& format, void* buffer, int length) = 0;
|
virtual void onSpkData(const Format& format, void* buffer, int length) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Device
|
class Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Device();
|
Device();
|
||||||
virtual ~Device();
|
virtual ~Device();
|
||||||
|
|
||||||
@@ -100,34 +100,34 @@ namespace Audio
|
|||||||
virtual bool open() = 0;
|
virtual bool open() = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
virtual Format getFormat() = 0;
|
virtual Format getFormat() = 0;
|
||||||
protected:
|
protected:
|
||||||
DataConnection* mConnection;
|
DataConnection* mConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class InputDevice: public Device
|
class InputDevice: public Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InputDevice();
|
InputDevice();
|
||||||
virtual ~InputDevice();
|
virtual ~InputDevice();
|
||||||
|
|
||||||
static InputDevice* make(int devId);
|
static InputDevice* make(int devId);
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<InputDevice> PInputDevice;
|
typedef std::shared_ptr<InputDevice> PInputDevice;
|
||||||
|
|
||||||
class OutputDevice: public Device
|
class OutputDevice: public Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OutputDevice();
|
OutputDevice();
|
||||||
virtual ~OutputDevice();
|
virtual ~OutputDevice();
|
||||||
|
|
||||||
static OutputDevice* make(int devId);
|
static OutputDevice* make(int devId);
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<OutputDevice> POutputDevice;
|
typedef std::shared_ptr<OutputDevice> POutputDevice;
|
||||||
|
|
||||||
class Enumerator
|
class Enumerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Enumerator();
|
Enumerator();
|
||||||
virtual ~Enumerator();
|
virtual ~Enumerator();
|
||||||
int nameToIndex(const std::tstring& name);
|
int nameToIndex(const std::tstring& name);
|
||||||
@@ -141,16 +141,16 @@ namespace Audio
|
|||||||
virtual int indexOfDefaultDevice() = 0;
|
virtual int indexOfDefaultDevice() = 0;
|
||||||
|
|
||||||
static Enumerator* make(bool useNull = false);
|
static Enumerator* make(bool useNull = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
class OsEngine
|
class OsEngine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void open() = 0;
|
virtual void open() = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
|
||||||
static OsEngine* instance();
|
static OsEngine* instance();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -591,7 +591,7 @@ UserAgent::SipAddress UserAgent::parseSipAddress(const std::string& sip)
|
|||||||
if (nameaddr.uri().port())
|
if (nameaddr.uri().port())
|
||||||
{
|
{
|
||||||
char porttext[32];
|
char porttext[32];
|
||||||
sprintf(porttext, ":%u", (unsigned)nameaddr.uri().port());
|
std::snprintf(porttext, sizeof(porttext), ":%u", (unsigned)nameaddr.uri().port());
|
||||||
result.mDomain += porttext;
|
result.mDomain += porttext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ WatcherQueue::WatcherQueue(UserAgent& ua)
|
|||||||
WatcherQueue::~WatcherQueue()
|
WatcherQueue::~WatcherQueue()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
int WatcherQueue::add(std::string peer, std::string package, void* tag)
|
int WatcherQueue::add(const std::string& peer, const std::string& package, void* tag)
|
||||||
{
|
{
|
||||||
ice::Lock l(mGuard);
|
ice::Lock l(mGuard);
|
||||||
|
|
||||||
@@ -16,8 +16,7 @@ int WatcherQueue::add(std::string peer, std::string package, void* tag)
|
|||||||
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 &&
|
if (item.mTarget == peer && item.mPackage == package && item.mState != Item::State_Deleting)
|
||||||
item.mState != Item::State_Deleting)
|
|
||||||
return item.mId;
|
return item.mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,10 +43,9 @@ 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 (unsigned i=0; i<mItemList.size(); i++)
|
for (auto& item: mItemList)
|
||||||
{
|
{
|
||||||
Item& item = mItemList[i];
|
if (item.mId == id && id)
|
||||||
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;
|
||||||
@@ -62,10 +60,9 @@ 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 (unsigned i=0; i<mItemList.size(); i++)
|
for (auto& item: mItemList)
|
||||||
{
|
{
|
||||||
Item& item = mItemList[i];
|
if (item.mId == id && id)
|
||||||
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;
|
||||||
@@ -142,9 +139,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,15 @@ public:
|
|||||||
State_Deleting
|
State_Deleting
|
||||||
};
|
};
|
||||||
|
|
||||||
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
||||||
ResipSession* mSession;
|
ResipSession* mSession = nullptr;
|
||||||
State mState;
|
State mState = State::State_None;
|
||||||
std::string mTarget; // Target's address
|
std::string mTarget; // Target's address
|
||||||
std::string mPackage; // Event package
|
std::string mPackage; // Event package
|
||||||
void* mTag; // User tag
|
void* mTag = nullptr; // User tag
|
||||||
int mId;
|
int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only)
|
||||||
|
|
||||||
Item()
|
Item()
|
||||||
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool scheduled()
|
bool scheduled()
|
||||||
@@ -47,7 +46,7 @@ public:
|
|||||||
WatcherQueue(UserAgent& agent);
|
WatcherQueue(UserAgent& agent);
|
||||||
~WatcherQueue();
|
~WatcherQueue();
|
||||||
|
|
||||||
int add(std::string peer, std::string package, void* tag);
|
int add(const std::string& peer, const 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();
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ ResipSessionAppDialog::~ResipSessionAppDialog()
|
|||||||
std::atomic_int ResipSession::InstanceCounter;
|
std::atomic_int ResipSession::InstanceCounter;
|
||||||
|
|
||||||
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
||||||
: resip::AppDialogSet(dum), mUserAgent(NULL), mType(Type_None), mSessionId(0), mSession(0)
|
: resip::AppDialogSet(dum), mUserAgent(nullptr), mType(Type_None), mSessionId(0), mSession(0)
|
||||||
{
|
{
|
||||||
ResipSession::InstanceCounter++;
|
ResipSession::InstanceCounter++;
|
||||||
mTag = NULL;
|
mTag = nullptr;
|
||||||
mTerminated = false;
|
mTerminated = false;
|
||||||
mOnWatchingStartSent = false;
|
mOnWatchingStartSent = false;
|
||||||
mSessionId = Session::generateId();
|
mSessionId = Session::generateId();
|
||||||
|
|||||||
@@ -10,76 +10,76 @@
|
|||||||
#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
|
||||||
ERR_CANNOT_FIND_SESSION = 2, // Cannot find session
|
ERR_CANNOT_FIND_SESSION = 2, // Cannot find session
|
||||||
ERR_NO_CREDENTIALS = 3, // No credentials to configure instance
|
ERR_NO_CREDENTIALS = 3, // No credentials to configure instance
|
||||||
ERR_BAD_VARIANT_TYPE = 4, // Bad variant type conversion
|
ERR_BAD_VARIANT_TYPE = 4, // Bad variant type conversion
|
||||||
ERR_RINSTANCE = 5,
|
ERR_RINSTANCE = 5,
|
||||||
ERR_SRTP = 6, // libsrtp error
|
ERR_SRTP = 6, // libsrtp error
|
||||||
ERR_WEBRTC = 7, // webrtc error
|
ERR_WEBRTC = 7, // webrtc error
|
||||||
ERR_NOMEM = 8, // no more memory
|
ERR_NOMEM = 8, // no more memory
|
||||||
ERR_WMME_FAILED = 9, // WMME error
|
ERR_WMME_FAILED = 9, // WMME error
|
||||||
ERR_QPC = 10, // QueryPerformanceCounter failed
|
ERR_QPC = 10, // QueryPerformanceCounter failed
|
||||||
ERR_BAD_PARAM = 11, // Bad parameter
|
ERR_BAD_PARAM = 11, // Bad parameter
|
||||||
ERR_NET_FAILED = 12, // Call to OS network subsystem failed
|
ERR_NET_FAILED = 12, // Call to OS network subsystem failed
|
||||||
ERR_NOT_IMPLEMENTED = 13, // Not implemented in this build
|
ERR_NOT_IMPLEMENTED = 13, // Not implemented in this build
|
||||||
ERR_MIXER_OVERFLOW = 14, // No more available channels in audio mixer
|
ERR_MIXER_OVERFLOW = 14, // No more available channels in audio mixer
|
||||||
ERR_WAVFILE_FAILED = 15, // Error with .wav file
|
ERR_WAVFILE_FAILED = 15, // Error with .wav file
|
||||||
ERR_DSOUND = 16, // DSound error
|
ERR_DSOUND = 16, // DSound error
|
||||||
ERR_COREAUDIO = 17, // CoreAudio error
|
ERR_COREAUDIO = 17, // CoreAudio error
|
||||||
ERR_CREATEWINDOW = 18, // CreateWindow failed
|
ERR_CREATEWINDOW = 18, // CreateWindow failed
|
||||||
ERR_REGISTERNOTIFICATION = 19, // RegisterDeviceNotification failed
|
ERR_REGISTERNOTIFICATION = 19, // RegisterDeviceNotification failed
|
||||||
ERR_PCAP = 20, // Smth bad with libpcap
|
ERR_PCAP = 20, // Smth bad with libpcap
|
||||||
ERR_CACHE_FAILED = 21, // Failed to open cache directory
|
ERR_CACHE_FAILED = 21, // Failed to open cache directory
|
||||||
ERR_FILENOTOPEN = 22, // Cannot open the file
|
ERR_FILENOTOPEN = 22, // Cannot open the file
|
||||||
ERR_OPENSLES = 23 // OpenSL ES failed. Subcode has actual error code.
|
ERR_OPENSLES = 23 // OpenSL ES failed. Subcode has actual error code.
|
||||||
};
|
};
|
||||||
|
|
||||||
class Exception: public std::exception
|
class Exception: public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Exception(int code, int subcode = 0)
|
Exception(int code, int subcode = 0)
|
||||||
:mCode(code), mSubcode(subcode)
|
:mCode(code), mSubcode(subcode)
|
||||||
{
|
{
|
||||||
sprintf(mMessage, "%d-%d", code, subcode);
|
std::snprintf(mMessage, sizeof(mMessage), "%d-%d", code, subcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Exception(int code, const char* message)
|
Exception(int code, const char* message)
|
||||||
{
|
{
|
||||||
if (message)
|
if (message)
|
||||||
strncpy(mMessage, message, (sizeof mMessage) - 1 );
|
strncpy(mMessage, message, (sizeof mMessage) - 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
Exception(const Exception& src)
|
Exception(const Exception& src)
|
||||||
:mCode(src.mCode), mSubcode(src.mSubcode)
|
:mCode(src.mCode), mSubcode(src.mSubcode)
|
||||||
{
|
{
|
||||||
memcpy(mMessage, src.mMessage, sizeof mMessage);
|
memcpy(mMessage, src.mMessage, sizeof mMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Exception()
|
~Exception()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
int code() const
|
int code() const
|
||||||
{
|
{
|
||||||
return mCode;
|
return mCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
int subcode() const
|
int subcode() const
|
||||||
{
|
{
|
||||||
return mSubcode;
|
return mSubcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* what() const noexcept
|
const char* what() const noexcept
|
||||||
{
|
{
|
||||||
return mMessage;
|
return mMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int mCode, mSubcode;
|
int mCode = 0, mSubcode = 0;
|
||||||
char mMessage[256];
|
char mMessage[256] = {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -20,82 +20,89 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
DatagramSocket::DatagramSocket()
|
DatagramSocket::DatagramSocket()
|
||||||
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
|
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DatagramSocket::~DatagramSocket()
|
DatagramSocket::~DatagramSocket()
|
||||||
{
|
{
|
||||||
internalClose();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::open(int family)
|
void DatagramSocket::open(int family)
|
||||||
{
|
{
|
||||||
if (mHandle != INVALID_SOCKET || mFamily != family)
|
if (mHandle != INVALID_SOCKET || mFamily != family)
|
||||||
closeSocket();
|
closeSocket();
|
||||||
|
|
||||||
assert(family == AF_INET || family == AF_INET6);
|
assert(family == AF_INET || family == AF_INET6);
|
||||||
mFamily = family;
|
mFamily = 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_in6 addr6;
|
|
||||||
memset(&addr4, 0, sizeof(addr4)); memset(&addr6, 0, sizeof(addr6));
|
|
||||||
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
|
|
||||||
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
|
|
||||||
if (!retcode)
|
|
||||||
{
|
{
|
||||||
mLocalPort = ntohs(mFamily == AF_INET ? addr4.sin_port : addr6.sin6_port);
|
sockaddr_in addr4; sockaddr_in6 addr6;
|
||||||
|
memset(&addr4, 0, sizeof(addr4)); memset(&addr6, 0, sizeof(addr6));
|
||||||
|
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
|
||||||
|
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
|
||||||
|
if (!retcode)
|
||||||
|
{
|
||||||
|
mLocalPort = ntohs(mFamily == AF_INET ? addr4.sin_port : addr6.sin6_port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int DatagramSocket::localport()
|
int DatagramSocket::localport()
|
||||||
{
|
{
|
||||||
return mLocalPort;
|
return mLocalPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::sendDatagram(InternetAddress &dest, const void *packetData, unsigned int packetSize)
|
void DatagramSocket::sendDatagram(InternetAddress &dest, const void *packetData, unsigned int packetSize)
|
||||||
{
|
{
|
||||||
if (mHandle == INVALID_SOCKET)
|
if (mHandle == INVALID_SOCKET)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*int sent = */::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
/*int sent = */::sendto(mHandle, (const char*)packetData, packetSize, 0, dest.genericsockaddr(), dest.sockaddrLen());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
|
||||||
{
|
{
|
||||||
if (mHandle == INVALID_SOCKET)
|
if (mHandle == INVALID_SOCKET)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sockaddr_in sourceaddr;
|
sockaddr* addr = nullptr;
|
||||||
#ifdef WIN32
|
socklen_t addrLen = 0;
|
||||||
int addrlen = sizeof(sourceaddr);
|
sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}};
|
||||||
#else
|
sockaddr_in6 addr_6 = {AF_INET6, 0, 0, {0}, 0};
|
||||||
socklen_t addrlen = sizeof(sourceaddr);
|
switch (mFamily)
|
||||||
#endif
|
{
|
||||||
int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, (sockaddr*)&sourceaddr, &addrlen);
|
case AF_INET: addr = (sockaddr*)&addr_4; addrLen = sizeof(addr_4); break;
|
||||||
if (received > 0)
|
case AF_INET6: addr = (sockaddr*)&addr_6; addrLen = sizeof(addr_6); break;
|
||||||
{
|
default:
|
||||||
src = InternetAddress((sockaddr&)sourceaddr, addrlen);
|
assert(0);
|
||||||
return received;
|
}
|
||||||
}
|
if (!addr || !addrLen)
|
||||||
else
|
return 0;
|
||||||
|
|
||||||
|
int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, addr, &addrLen);
|
||||||
|
if (received > 0)
|
||||||
|
{
|
||||||
|
src = InternetAddress((sockaddr&)*addr, addrLen);
|
||||||
|
return received;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::internalClose()
|
void DatagramSocket::internalClose()
|
||||||
{
|
{
|
||||||
if (mHandle != INVALID_SOCKET)
|
if (mHandle != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
::closesocket(mHandle);
|
::closesocket(mHandle);
|
||||||
#else
|
#else
|
||||||
close(mHandle);
|
close(mHandle);
|
||||||
#endif
|
#endif
|
||||||
mHandle = INVALID_SOCKET;
|
mHandle = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramSocket::closeSocket()
|
void DatagramSocket::closeSocket()
|
||||||
@@ -105,43 +112,43 @@ void DatagramSocket::closeSocket()
|
|||||||
|
|
||||||
bool DatagramSocket::isValid() const
|
bool DatagramSocket::isValid() const
|
||||||
{
|
{
|
||||||
return mHandle != INVALID_SOCKET;
|
return mHandle != INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DatagramSocket::family() const
|
int DatagramSocket::family() const
|
||||||
{
|
{
|
||||||
return mFamily;
|
return mFamily;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatagramSocket::setBlocking(bool blocking)
|
bool DatagramSocket::setBlocking(bool blocking)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
unsigned long mode = blocking ? 0 : 1;
|
unsigned long mode = blocking ? 0 : 1;
|
||||||
return (ioctlsocket(mHandle, FIONBIO, &mode) == 0) ? true : false;
|
return (ioctlsocket(mHandle, FIONBIO, &mode) == 0) ? true : false;
|
||||||
#endif
|
#endif
|
||||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||||
int flags = fcntl(mHandle, F_GETFL, 0);
|
int flags = fcntl(mHandle, F_GETFL, 0);
|
||||||
if (flags < 0)
|
if (flags < 0)
|
||||||
return false;
|
return false;
|
||||||
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
|
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
|
||||||
return (fcntl(mHandle, F_SETFL, flags) == 0) ? true : false;
|
return (fcntl(mHandle, F_SETFL, flags) == 0) ? true : false;
|
||||||
#endif
|
#endif
|
||||||
#if defined(TARGET_ANDROID)
|
#if defined(TARGET_ANDROID)
|
||||||
unsigned long mode = blocking ? 0 : 1;
|
unsigned long mode = blocking ? 0 : 1;
|
||||||
return (ioctl(mHandle, FIONBIO, &mode) == 0) ? true : false;
|
return (ioctl(mHandle, FIONBIO, &mode) == 0) ? true : false;
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKET DatagramSocket::socket() const
|
SOCKET DatagramSocket::socket() const
|
||||||
{
|
{
|
||||||
return mHandle;
|
return mHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
DatagramAgreggator::DatagramAgreggator()
|
DatagramAgreggator::DatagramAgreggator()
|
||||||
{
|
{
|
||||||
FD_ZERO(&mReadSet);
|
FD_ZERO(&mReadSet);
|
||||||
mMaxHandle = 0;
|
mMaxHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DatagramAgreggator::~DatagramAgreggator()
|
DatagramAgreggator::~DatagramAgreggator()
|
||||||
@@ -150,38 +157,38 @@ DatagramAgreggator::~DatagramAgreggator()
|
|||||||
|
|
||||||
void DatagramAgreggator::addSocket(PDatagramSocket socket)
|
void DatagramAgreggator::addSocket(PDatagramSocket socket)
|
||||||
{
|
{
|
||||||
if (socket->mHandle == INVALID_SOCKET)
|
if (socket->mHandle == INVALID_SOCKET)
|
||||||
return;
|
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;
|
||||||
|
|
||||||
mSocketVector.push_back(socket);
|
mSocketVector.push_back(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned DatagramAgreggator::count()
|
unsigned DatagramAgreggator::count()
|
||||||
{
|
{
|
||||||
return mSocketVector.size();
|
return mSocketVector.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
|
||||||
{
|
{
|
||||||
return mSocketVector[index];
|
return mSocketVector[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatagramAgreggator::waitForData(unsigned milliseconds)
|
bool DatagramAgreggator::waitForData(std::chrono::milliseconds timeout)
|
||||||
{
|
{
|
||||||
timeval tv;
|
timeval tv;
|
||||||
tv.tv_sec = milliseconds / 1000;
|
tv.tv_sec = timeout.count() / 1000;
|
||||||
tv.tv_usec = (milliseconds % 1000) * 1000;
|
tv.tv_usec = (timeout.count() % 1000) * 1000;
|
||||||
|
|
||||||
int rescode = ::select(mMaxHandle, &mReadSet, NULL, NULL, &tv);
|
int rescode = ::select(mMaxHandle+1, &mReadSet, nullptr, nullptr, &tv);
|
||||||
return rescode > 0;
|
return rescode > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
/* Copyright(C) 2007-2026 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,60 +9,61 @@
|
|||||||
#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();
|
||||||
|
|
||||||
virtual int localport();
|
virtual int localport();
|
||||||
|
|
||||||
virtual void sendDatagram(InternetAddress& dest, const void* packetData, unsigned packetSize);
|
virtual void sendDatagram(InternetAddress& dest, const void* packetData, unsigned packetSize);
|
||||||
virtual unsigned recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity);
|
virtual unsigned recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity);
|
||||||
virtual void closeSocket();
|
virtual void closeSocket();
|
||||||
virtual bool isValid() const;
|
virtual bool isValid() const;
|
||||||
virtual int family() const;
|
virtual int family() const;
|
||||||
virtual bool setBlocking(bool blocking);
|
virtual bool setBlocking(bool blocking);
|
||||||
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();
|
void internalClose();
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
||||||
|
|
||||||
class DatagramAgreggator
|
class DatagramAgreggator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DatagramAgreggator();
|
DatagramAgreggator();
|
||||||
~DatagramAgreggator();
|
~DatagramAgreggator();
|
||||||
|
|
||||||
void addSocket(PDatagramSocket socket);
|
void addSocket(PDatagramSocket socket);
|
||||||
unsigned count();
|
unsigned count();
|
||||||
bool hasDataAtIndex(unsigned index);
|
bool hasDataAtIndex(unsigned index);
|
||||||
PDatagramSocket socketAt(unsigned index);
|
PDatagramSocket socketAt(unsigned index);
|
||||||
|
|
||||||
bool waitForData(unsigned milliseconds);
|
bool waitForData(std::chrono::milliseconds timeout);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::vector<PDatagramSocket> SocketList;
|
typedef std::vector<PDatagramSocket> SocketList;
|
||||||
SocketList mSocketVector;
|
SocketList mSocketVector;
|
||||||
fd_set mReadSet;
|
fd_set mReadSet;
|
||||||
SOCKET mMaxHandle;
|
SOCKET mMaxHandle;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#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
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
#define WSAEADDRINUSE EADDRINUSE
|
#define WSAEADDRINUSE EADDRINUSE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
// ----------------------------- SocketSink -------------------------
|
// ----------------------------- SocketSink -------------------------
|
||||||
SocketSink::~SocketSink()
|
SocketSink::~SocketSink()
|
||||||
@@ -253,7 +254,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(10))
|
if (agreggator.waitForData(10ms))
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "There is data on UDP sockets");
|
ICELogMedia(<< "There is data on UDP sockets");
|
||||||
Lock l(mGuard);
|
Lock l(mGuard);
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
|||||||
std::string strx::toHex(unsigned int value)
|
std::string strx::toHex(unsigned int value)
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "%x", value);
|
std::snprintf(buffer, sizeof(buffer), "%x", value);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ std::pair<std::string, std::string> strx::parseAssignment(const std::string& s,
|
|||||||
std::string strx::intToString(int value)
|
std::string strx::intToString(int value)
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
sprintf(buffer, "%d", value);
|
std::snprintf(buffer, sizeof(buffer), "%d", value);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +304,10 @@ std::string strx::millisecondsToString(uint64_t t)
|
|||||||
int strx::fromHex2Int(const std::string &s)
|
int strx::fromHex2Int(const std::string &s)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
sscanf(s.c_str(), "%x", &result);
|
int retcode = sscanf(s.c_str(), "%x", &result);
|
||||||
|
if (retcode == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +341,7 @@ std::string strx::replace(const std::string& s, char f, char r)
|
|||||||
{
|
{
|
||||||
std::string result(s);
|
std::string result(s);
|
||||||
for (std::string::size_type i = 0; i < result.size(); i++)
|
for (std::string::size_type i = 0; i < result.size(); i++)
|
||||||
if (result[i] == 'f')
|
if (result[i] == f)
|
||||||
result[i] = r;
|
result[i] = r;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -32,24 +32,12 @@ void SyncHelper::delay(unsigned int microseconds)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
long SyncHelper::increment(long *value)
|
|
||||||
{
|
|
||||||
assert(value);
|
|
||||||
#ifdef TARGET_WIN
|
|
||||||
return ::InterlockedIncrement((LONG*)value);
|
|
||||||
#elif TARGET_OSX
|
|
||||||
return OSAtomicIncrement32((int32_t*)value);
|
|
||||||
#elif TARGET_LINUX
|
|
||||||
return -1;
|
|
||||||
#else
|
|
||||||
return -1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------- ThreadHelper -------------------
|
// ------------------- ThreadHelper -------------------
|
||||||
void ThreadHelper::setName(const std::string &name)
|
void ThreadHelper::setName(const std::string &name)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_LINUX)
|
#if defined(TARGET_LINUX)
|
||||||
|
// The name will be truncated to 8 or 16 characters
|
||||||
int retcode = pthread_setname_np(pthread_self(), name.c_str());
|
int retcode = pthread_setname_np(pthread_self(), name.c_str());
|
||||||
if (retcode != 0)
|
if (retcode != 0)
|
||||||
{
|
{
|
||||||
@@ -128,7 +116,7 @@ uint64_t chronox::toTimestamp(const timeval& ts)
|
|||||||
int64_t chronox::getDelta(const timespec& a, const timespec& b)
|
int64_t chronox::getDelta(const timespec& a, const timespec& b)
|
||||||
{
|
{
|
||||||
uint64_t ms_a = a.tv_sec * 1000 + a.tv_nsec / 10000000;
|
uint64_t ms_a = a.tv_sec * 1000 + a.tv_nsec / 10000000;
|
||||||
uint64_t ms_b = b.tv_sec * 1000 + a.tv_nsec / 10000000;
|
uint64_t ms_b = b.tv_sec * 1000 + b.tv_nsec / 10000000;
|
||||||
return ms_a - ms_b;
|
return ms_a - ms_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,10 +196,10 @@ void Semaphore::wait()
|
|||||||
m_count--;
|
m_count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Semaphore::waitFor(int milliseconds) {
|
bool Semaphore::waitFor(std::chrono::milliseconds timeout) {
|
||||||
std::unique_lock<std::mutex> lock(m_mtx);
|
std::unique_lock<std::mutex> lock(m_mtx);
|
||||||
|
|
||||||
if (!m_cv.wait_for(lock, std::chrono::milliseconds(milliseconds), [this]() { return m_count > 0; }))
|
if (!m_cv.wait_for(lock, timeout, [this]() { return m_count > 0; }))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_count--;
|
m_count--;
|
||||||
@@ -317,11 +305,8 @@ void TimerQueue::run()
|
|||||||
auto end = calcWaitTime();
|
auto end = calcWaitTime();
|
||||||
if (end.first)
|
if (end.first)
|
||||||
{
|
{
|
||||||
// Timers found, so wait until it expires (or something else
|
// Timers found, so wait until it expires (or something else changes)
|
||||||
// changes)
|
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end.second - std::chrono::steady_clock::now());
|
||||||
int milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>
|
|
||||||
(end.second - std::chrono::steady_clock::now()).count();
|
|
||||||
//std::cout << "Waiting m_checkWork for " << milliseconds * 1000 << "ms." << std::endl;
|
|
||||||
m_checkWork.waitFor(milliseconds);
|
m_checkWork.waitFor(milliseconds);
|
||||||
} else {
|
} else {
|
||||||
// No timers exist, so wait forever until something changes
|
// No timers exist, so wait forever until something changes
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ typedef std::unique_lock<std::recursive_mutex> Lock;
|
|||||||
class SyncHelper
|
class SyncHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void delay(unsigned microseconds);
|
static void delay(unsigned microseconds);
|
||||||
static long increment(long* value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Semaphore
|
class Semaphore
|
||||||
@@ -36,7 +35,7 @@ public:
|
|||||||
|
|
||||||
void notify();
|
void notify();
|
||||||
void wait();
|
void wait();
|
||||||
bool waitFor(int milliseconds);
|
bool waitFor(std::chrono::milliseconds timeout);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex m_mtx;
|
std::mutex m_mtx;
|
||||||
@@ -47,39 +46,39 @@ private:
|
|||||||
class ThreadHelper
|
class ThreadHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void setName(const std::string& name);
|
static void setName(const std::string& name);
|
||||||
static uint64_t getCurrentId();
|
static uint64_t getCurrentId();
|
||||||
};
|
};
|
||||||
|
|
||||||
class chronox
|
class chronox
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Returns current timestamp in milliseconds
|
// Returns current timestamp in milliseconds
|
||||||
static uint64_t getTimestamp();
|
static uint64_t getTimestamp();
|
||||||
|
|
||||||
// Returns uptime (of calling process) in milliseconds
|
// Returns uptime (of calling process) in milliseconds
|
||||||
static uint64_t getUptime();
|
static uint64_t getUptime();
|
||||||
|
|
||||||
// Finds time delta between 'later' and 'earlier' time points.
|
// Finds time delta between 'later' and 'earlier' time points.
|
||||||
// Handles cases when clock is wrapped.
|
// Handles cases when clock is wrapped.
|
||||||
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
static uint32_t getDelta(uint32_t later, uint32_t earlier);
|
||||||
|
|
||||||
// Converts number of milliseconds starting from Epoch begin to timespec.
|
// Converts number of milliseconds starting from Epoch begin to timespec.
|
||||||
static timespec toTimespec(uint64_t milliseconds);
|
static timespec toTimespec(uint64_t milliseconds);
|
||||||
static uint64_t toTimestamp(const timeval& ts);
|
static uint64_t toTimestamp(const timeval& ts);
|
||||||
|
|
||||||
// Returns difference between timestamps in milliseconds
|
// Returns difference between timestamps in milliseconds
|
||||||
static int64_t getDelta(const timespec& a, const timespec& b);
|
static int64_t getDelta(const timespec& a, const timespec& b);
|
||||||
static int64_t getDelta(const timeval& a, const timeval& b);
|
static int64_t getDelta(const timeval& a, const timeval& b);
|
||||||
|
|
||||||
class ExecutionTime
|
class ExecutionTime
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ExecutionTime();
|
ExecutionTime();
|
||||||
uint64_t getSpentTime() const;
|
uint64_t getSpentTime() const;
|
||||||
protected:
|
protected:
|
||||||
uint64_t mStart;
|
uint64_t mStart;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -160,7 +159,7 @@ private:
|
|||||||
std::mutex m_mtx;
|
std::mutex m_mtx;
|
||||||
// Inheriting from priority_queue, so we can access the internal container
|
// Inheriting from priority_queue, so we can access the internal container
|
||||||
class Queue : public std::priority_queue<WorkItem, std::vector<WorkItem>,
|
class Queue : public std::priority_queue<WorkItem, std::vector<WorkItem>,
|
||||||
std::greater<WorkItem>>
|
std::greater<WorkItem>>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::vector<WorkItem>& getContainer();
|
std::vector<WorkItem>& getContainer();
|
||||||
|
|||||||
@@ -59,8 +59,11 @@ void thread_pool::run_worker()
|
|||||||
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
||||||
|
|
||||||
this->condition.wait(lock, [this]{return !this->tasks.empty() || this->stop;});
|
this->condition.wait(lock, [this]{return !this->tasks.empty() || this->stop;});
|
||||||
t = this->tasks.front();
|
if (!tasks.empty())
|
||||||
this->tasks.pop();
|
{
|
||||||
|
t = tasks.front();
|
||||||
|
tasks.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t(); // function<void()> type
|
t(); // function<void()> type
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,18 +299,18 @@ std::string Variant::asStdString() const
|
|||||||
return mString;
|
return mString;
|
||||||
|
|
||||||
case VTYPE_INT:
|
case VTYPE_INT:
|
||||||
sprintf(buffer, "%d", mInt);
|
std::snprintf(buffer, sizeof(buffer), "%d", mInt);
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
case VTYPE_INT64:
|
case VTYPE_INT64:
|
||||||
sprintf(buffer, "%lli", static_cast<long long int>(mInt64));
|
std::snprintf(buffer, sizeof(buffer), "%lli", static_cast<long long int>(mInt64));
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
return mBool ? "true" : "false";
|
return mBool ? "true" : "false";
|
||||||
|
|
||||||
case VTYPE_FLOAT:
|
case VTYPE_FLOAT:
|
||||||
sprintf(buffer, "%f", mFloat);
|
std::snprintf(buffer, sizeof(buffer), "%f", mFloat);
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -313,10 +313,10 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Declare the data input pointer
|
// Declare the data input pointer
|
||||||
const short *dataIn = (const short *)input;
|
auto *dataIn = (const short *)input;
|
||||||
|
|
||||||
// Declare the data output pointer
|
// Declare the data output pointer
|
||||||
unsigned char *dataOut = (unsigned char *)output;
|
auto *dataOut = (unsigned char *)output;
|
||||||
|
|
||||||
// Find how much RTP frames will be generated
|
// Find how much RTP frames will be generated
|
||||||
unsigned int frames = inputBytes / pcmLength();
|
unsigned int frames = inputBytes / pcmLength();
|
||||||
@@ -328,7 +328,7 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
|
|||||||
dataIn += pcmLength() / 2;
|
dataIn += pcmLength() / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return frames * rtpLength();
|
return dataOut - (unsigned char*)output;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define L_FRAME 160
|
#define L_FRAME 160
|
||||||
|
|||||||
Reference in New Issue
Block a user