- start checks with Kimi + many fixes

This commit is contained in:
2026-01-31 21:29:23 +03:00
parent 260413fad1
commit 7bd3e981d5
17 changed files with 653 additions and 289 deletions

366
AGENTS.md Normal file
View 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/`

View File

@@ -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));

View File

@@ -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

View File

@@ -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;
} }

View File

@@ -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();
} }

View File

@@ -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();

View File

@@ -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();

View File

@@ -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
{
return mCode;
}
int subcode() const int code() const
{ {
return mSubcode; return mCode;
} }
const char* what() const noexcept int subcode() const
{ {
return mMessage; return mSubcode;
} }
const char* what() const noexcept
{
return mMessage;
}
protected: protected:
int mCode, mSubcode; int mCode = 0, mSubcode = 0;
char mMessage[256]; char mMessage[256] = {0};
}; };
#endif #endif

View File

@@ -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;
} }

View File

@@ -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 unsigned recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity);
virtual void closeSocket();
virtual bool isValid() const;
virtual int family() const;
virtual bool setBlocking(bool blocking);
virtual SOCKET socket() const;
virtual void open(int family); virtual void sendDatagram(InternetAddress& dest, const void* packetData, unsigned packetSize);
virtual unsigned recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity);
virtual void closeSocket();
virtual bool isValid() const;
virtual int family() const;
virtual bool setBlocking(bool blocking);
virtual SOCKET socket() const;
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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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,42 +46,42 @@ 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;
}; };
}; };
class BufferQueue class BufferQueue
{ {
public: public:
@@ -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();

View File

@@ -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
} }

View File

@@ -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:

View File

@@ -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