Compare commits

...

4 Commits

Author SHA1 Message Date
Dmytro Bogovych 03c58c4572 - G.722: decode as 16 kHz audio, keep the 8 kHz RTP clock separate
G722Codec::info() reported an 8 kHz sample rate for its 16 kHz output (640
bytes of PCM per 20 ms frame, read as 40 ms at 8 kHz). AudioReceiver therefore
fetched packets at half the rate they arrived, and the high-water trim
discarded about 44% of every G.722 stream: 109 of 250 packets per 5 s, with the
buffer at 360 ms instead of 100 ms.

The 8 kHz figure is right for one thing: RFC 3551 keeps G.722's RTP clock at
8000. Codec::Info already had mTimestampUnit for that. G.722 now reports
mSamplerate 16000 and mTimestampUnit 1/8000, and Codec::rtpClockRate() gives
the clock rate. The uses of samplerate() that mean RTP time now use it:
- the jitter statistics in AudioReceiver::add;
- the timestamp-gap-to-milliseconds conversion in AudioReceiver;
- AudioStream's transmit timestamp unit and increment (numerically unchanged
  for every codec, including G.722).
PCM uses stay on samplerate(), so AudioStream now also feeds the G.722
encoder 16 kHz audio instead of 8 kHz. The factory still advertises G722/8000
in SDP, as it must; only its backwards comment is fixed.

AudioReceiver::infoFor() also applied a "G729A silence" rule to payload type
9, which is G.722; G.729 is 18. It now applies to PT 18. Other fixed-frame
payloads are timed proportionally, as AudioReceiver::add already does, so a
10 ms G.722 packet (half a 160-byte frame) counts as 10 ms, not 20.

Measured in vq-core with 16 replayed calls (NISQA speech; G.722 from ffmpeg
and pjmedia, G.711 controls of the same speech; 3 runs each, all identical):
- G.722 packet drops: 44% -> 0; buffer 100 ms like G.711; no underruns;
- Sevana MOS of every G.722 call now equals its G.711 control, normal and loud
  (with the previous code three calls read 4.45 against 4.25);
- DeadAir-01 0.68-0.89 -> 0.01-0.27 (G.711: 0.00-0.14); SilentCall within 0.01
  of G.711 for the ffmpeg-encoded calls (was 0.15-0.17 higher);
- AmpClipping on loud calls 0.004/0.001, as G.711; the 0.02-0.03 seen after
  the codec swap alone came from the dropped packets;
- G.711 results, packet counts, loss, jitter and audio durations unchanged.
The transmit path (AudioStream) builds but was not exercised; vq-core only
receives.

Needs the vq_net change that computes jitter with rtpClockRate().

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 18:03:58 +02:00
Dmytro Bogovych c8d9bad845 - replace the LGPL G.722 codec with Asterisk's public-domain copy
The previous src/libs/g722 was spandsp's G.722 offered only under GPL-2 or
LGPL-2.1. That blocks shipping rtphone statically linked in a closed binary
(the freeware VQ for Asterisk engine) without a relink offer. Asterisk's
codecs/g722 is the same code: Steve Underwood placed his contributions in the
public domain there, and the CMU 1993 code under it is "completely
unrestricted" for any use. src/libs/g722/README.md records the provenance and
both statements.

It also fixes the decoded level. Asterisk's copy halves the encoder input and
doubles the decoder output, so streams from Asterisk and ffmpeg decode at
their original level. The old copy decoded them 6 dB too quiet (ITU-convention
streams from pjmedia 12 dB), and its encoder clipped input above -6 dBFS.

Local changes to Asterisk's files:
- the decoder saturates its output instead of casting. Asterisk's cast wraps:
  259 samples in 11 s of speech peaking at -0.2 dBFS came out with the
  opposite sign;
- g722.h includes <stdint.h> and maps __inline__ for MSVC (the removed
  g722_inttypes.h used to).
Dropped: g722_bitstream.*, g722codec.c and three helper headers, none used.
The API is unchanged (g722_encode_init / g722_decode, same options).

Measured in vq-core (16 simultaneous replayed calls, NISQA speech):
- G.711 control calls score identically;
- normal-level G.722: SilentCall -0.09..-0.14, DeadAir-01 -0.06..-0.09, MOS
  unchanged;
- loud G.722 with a clipped source now shows AmpClipping 0.02-0.03 and MOS
  3.89/4.28 instead of 4.45: the codec overshoots on the clipped peaks at the
  correct level, which the old -6 dB hid;
- results are identical across runs (the old decoder varied by up to 0.30 MOS).

G.722 still loses about 44% of its packets in AudioReceiver because
G722Codec::info() reports an 8 kHz sample rate for 16 kHz audio. That is fixed
separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 17:54:18 +02:00
dmytro.bogovych 405483a00d - add clang-format settings 2026-09-03 10:30:22 +03:00
dmytro.bogovych e166bae1ea - clang format + naming improved 2026-09-03 10:03:42 +03:00
217 changed files with 21705 additions and 22869 deletions
+69
View File
@@ -0,0 +1,69 @@
# Formatting rules for rtphone's own C++ sources (src/engine, src/libs/ice, test).
# Third-party code under src/libs/ is excluded via .clang-format-ignore.
---
Language: Cpp
BasedOnStyle: LLVM
Standard: c++20
# Indentation: 4 columns, spaces only.
IndentWidth: 4
TabWidth: 4
UseTab: Never
AccessModifierOffset: -4
NamespaceIndentation: None
IndentCaseLabels: false
ColumnLimit: 120
# Allman braces, as used throughout the codebase.
BreakBeforeBraces: Allman
PointerAlignment: Left
ReferenceAlignment: Left
DerivePointerAlignment: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortBlocksOnASingleLine: Empty
# Include order matters here (platform headers, windows.h, vendored libs),
# so never reorder them.
SortIncludes: Never
IndentPPDirectives: None
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
FixNamespaceComments: true
SpaceAfterTemplateKeyword: false
AlwaysBreakTemplateDeclarations: Yes
AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments
# Objective-C++ (src/libs/ice/ICEIosSupport.mm) — same rules as C++.
---
Language: ObjC
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: Never
AccessModifierOffset: -4
NamespaceIndentation: None
IndentCaseLabels: false
ColumnLimit: 120
BreakBeforeBraces: Allman
PointerAlignment: Left
ReferenceAlignment: Left
DerivePointerAlignment: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortBlocksOnASingleLine: Empty
SortIncludes: Never
IndentPPDirectives: None
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
FixNamespaceComments: true
AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
+2
View File
@@ -0,0 +1,2 @@
src/libs/**
!src/libs/ice/ICE*
+6 -10
View File
@@ -9,14 +9,13 @@
#include "HL_String.h" #include "HL_String.h"
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
# include "../engine/audio/Audio_Android.h" #include "../engine/audio/Audio_Android.h"
#endif #endif
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "audio"
AudioManager::AudioManager() AudioManager::AudioManager() : mTerminal(nullptr), mAudioMonitoring(nullptr)
:mTerminal(nullptr), mAudioMonitoring(nullptr)
{ {
mPlayer.setDelegate(this); mPlayer.setDelegate(this);
} }
@@ -92,7 +91,7 @@ void AudioManager::start(int usageId)
if (!mAudioOutput) if (!mAudioOutput)
{ {
Audio::Enumerator *enumerator = Audio::Enumerator::make(usageId == atNull); Audio::Enumerator* enumerator = Audio::Enumerator::make(usageId == atNull);
enumerator->open(Audio::mySpeaker); enumerator->open(Audio::mySpeaker);
int outputIndex = enumerator->indexOfDefaultDevice(); int outputIndex = enumerator->indexOfDefaultDevice();
@@ -102,8 +101,7 @@ void AudioManager::start(int usageId)
if (outputIndex >= enumerator->count()) if (outputIndex >= enumerator->count())
outputIndex = 0; outputIndex = 0;
mAudioOutput = Audio::POutputDevice( mAudioOutput = Audio::POutputDevice(Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
} }
else else
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice()); mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
@@ -139,7 +137,7 @@ void AudioManager::stop(int usageId)
{ {
LOCK_MANAGER; LOCK_MANAGER;
ICELogInfo( << "Stop main audio with usage id " << usageId); ICELogInfo(<< "Stop main audio with usage id " << usageId);
if (mTerminal) if (mTerminal)
{ {
if (mTerminal->audio()) if (mTerminal->audio())
@@ -196,9 +194,7 @@ void AudioManager::stopPlayFile(int usageId)
mPlayer.release(usageId); mPlayer.release(usageId);
} }
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item) void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item) {}
{
}
void AudioManager::process() void AudioManager::process()
{ {
+16 -17
View File
@@ -11,7 +11,6 @@
#include "../engine/media/MT_Box.h" #include "../engine/media/MT_Box.h"
enum enum
{ {
AudioPrefix_Ring = 1, AudioPrefix_Ring = 1,
@@ -31,7 +30,7 @@ enum
#define AudioSessionCoeff 64 #define AudioSessionCoeff 64
class AudioManager: public Audio::Player::EndOfAudioDelegate class AudioManager : public Audio::Player::EndOfAudioDelegate
{ {
public: public:
AudioManager(); AudioManager();
@@ -40,24 +39,24 @@ public:
// static AudioManager& instance(); // static AudioManager& instance();
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application // Enforces to close audio devices. Used to shutdown AudioManager on exit from application
void close(); void close();
// Terminal and settings must be available for AudioManager // Terminal and settings must be available for AudioManager
void setTerminal(MT::Terminal* terminal); void setTerminal(MT::Terminal* terminal);
MT::Terminal* terminal(); MT::Terminal* terminal();
void setAudioMonitoring(Audio::DataConnection* monitoring); void setAudioMonitoring(Audio::DataConnection* monitoring);
Audio::DataConnection* audioMonitoring(); Audio::DataConnection* audioMonitoring();
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually // Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
void start(int usageId); void start(int usageId);
void stop(int usageId); void stop(int usageId);
// Inject a custom input device. Must be called before start(): when set, // Inject a custom input device. Must be called before start(): when set,
// start() skips construction of the default platform microphone. Pass an // start() skips construction of the default platform microphone. Pass an
// empty pointer to clear the override. // empty pointer to clear the override.
void setAudioInput(Audio::PInputDevice input); void setAudioInput(Audio::PInputDevice input);
void setAudioOutput(Audio::POutputDevice output); void setAudioOutput(Audio::POutputDevice output);
enum AudioTarget enum AudioTarget
{ {
@@ -82,14 +81,14 @@ public:
void process(); void process();
protected: protected:
Audio::PInputDevice mAudioInput; Audio::PInputDevice mAudioInput;
Audio::POutputDevice mAudioOutput; Audio::POutputDevice mAudioOutput;
Audio::Player mPlayer; Audio::Player mPlayer;
MT::Terminal* mTerminal; MT::Terminal* mTerminal;
Audio::DataConnection* mAudioMonitoring; Audio::DataConnection* mAudioMonitoring;
std::map<int, int> UsageMap; std::map<int, int> UsageMap;
UsageCounter mUsage; UsageCounter mUsage;
std::mutex mGuard; std::mutex mGuard;
}; };
#endif #endif
+141 -152
View File
@@ -21,8 +21,7 @@ const std::string Status_NoAudioManager = "no audio manager";
#define LOG_SUBSYSTEM "agent" #define LOG_SUBSYSTEM "agent"
AgentImpl::AgentImpl() AgentImpl::AgentImpl() : mShutdown(false), mEventListChangeCondVar()
:mShutdown(false), mEventListChangeCondVar()
{ {
#if defined(TARGET_ANDROID) || defined(TARGET_WIN) #if defined(TARGET_ANDROID) || defined(TARGET_WIN)
ice::GLogger.useDebugWindow(true); ice::GLogger.useDebugWindow(true);
@@ -84,76 +83,59 @@ std::string AgentImpl::command(const std::string& command)
} }
if (cmd == "config") if (cmd == "config")
processConfig(d, answer); processConfig(d, answer);
else if (cmd == "start")
processStart(d, answer);
else if (cmd == "stop")
processStop(d, answer);
else if (cmd == "account_create")
processCreateAccount(d, answer);
else if (cmd == "account_start")
processStartAccount(d, answer);
else if (cmd == "account_setuserinfo")
processSetUserInfoToAccount(d, answer);
else if (cmd == "session_create")
{
// For Bugsnag test
// int* v = nullptr;
// *v = 0;
processCreateSession(d, answer);
}
else if (cmd == "session_start")
processStartSession(d, answer);
else if (cmd == "session_stop")
processStopSession(d, answer);
else if (cmd == "session_accept")
processAcceptSession(d, answer);
else if (cmd == "session_destroy")
processDestroySession(d, answer);
else if (cmd == "session_use_stream")
processUseStreamForSession(d, answer);
else if (cmd == "wait_for_event")
processWaitForEvent(d, answer);
else if (cmd == "session_get_media_stats")
processGetMediaStats(d, answer);
else if (cmd == "agent_network_changed")
processNetworkChanged(d, answer);
else if (cmd == "agent_add_root_cert")
processAddRootCert(d, answer);
else if (cmd == "detach_log")
{
GLogger.closeFile();
answer["status"] = Status_Ok;
}
else if (cmd == "attach_log")
{
GLogger.openFile();
answer["status"] = Status_Ok;
}
else if (cmd == "log_message")
processLogMessage(d, answer);
else else
if (cmd == "start") {
processStart(d, answer); answer["status"] = Status_NoCommand;
else }
if (cmd == "stop")
processStop(d, answer);
else
if (cmd == "account_create")
processCreateAccount(d, answer);
else
if (cmd == "account_start")
processStartAccount(d, answer);
else
if (cmd == "account_setuserinfo")
processSetUserInfoToAccount(d, answer);
else
if (cmd == "session_create") {
// For Bugsnag test
// int* v = nullptr;
// *v = 0;
processCreateSession(d, answer);
}
else
if (cmd == "session_start")
processStartSession(d, answer);
else
if (cmd == "session_stop")
processStopSession(d, answer);
else
if (cmd == "session_accept")
processAcceptSession(d, answer);
else
if (cmd == "session_destroy")
processDestroySession(d, answer);
else
if (cmd == "session_use_stream")
processUseStreamForSession(d, answer);
else
if (cmd == "wait_for_event")
processWaitForEvent(d, answer);
else
if (cmd == "session_get_media_stats")
processGetMediaStats(d, answer);
else
if (cmd == "agent_network_changed")
processNetworkChanged(d, answer);
else
if (cmd == "agent_add_root_cert")
processAddRootCert(d, answer);
else
if (cmd == "detach_log")
{
GLogger.closeFile();
answer["status"] = Status_Ok;
}
else
if (cmd == "attach_log")
{
GLogger.openFile();
answer["status"] = Status_Ok;
}
else
if (cmd == "log_message")
processLogMessage(d, answer);
else
{
answer["status"] = Status_NoCommand;
}
} }
catch(std::exception& e) catch (std::exception& e)
{ {
answer["status"] = e.what(); answer["status"] = e.what();
} }
@@ -172,17 +154,20 @@ std::string AgentImpl::read()
return ""; return "";
} }
void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer) void AgentImpl::processConfig(JsonCpp::Value& d, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
std::string transport = d["transport"].asString(); std::string transport = d["transport"].asString();
config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls)); config()[CONFIG_TRANSPORT] =
(transport == "any")
? TransportType_Any
: (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
config()[CONFIG_IPV4] = d["ipv4"].asBool(); config()[CONFIG_IPV4] = d["ipv4"].asBool();
config()[CONFIG_IPV6] = d["ipv6"].asBool(); config()[CONFIG_IPV6] = d["ipv6"].asBool();
// Log file // Log file
std::string logfile = d["logfile"].asString(); std::string logfile = d["logfile"].asString();
ice::Logger& logger = ice::GLogger; ice::Logger& logger = ice::GLogger;
logger.useFile(logfile.empty() ? nullptr : logfile.c_str()); logger.useFile(logfile.empty() ? nullptr : logfile.c_str());
@@ -199,7 +184,7 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer) void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
if (mThread) if (mThread)
@@ -220,9 +205,9 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
mTerminal = std::make_shared<MT::Terminal>(settings); mTerminal = std::make_shared<MT::Terminal>(settings);
// Enable/disable codecs // Enable/disable codecs
PVariantMap priorityConfig = std::make_shared<VariantMap>(); PVariantMap priorityConfig = std::make_shared<VariantMap>();
MT::CodecList& cl = mTerminal->codeclist(); MT::CodecList& cl = mTerminal->codeclist();
for (int i=0; i<cl.count(); i++) for (int i = 0; i < cl.count(); i++)
priorityConfig->at(i) = i; priorityConfig->at(i) = i;
config()[CONFIG_CODEC_PRIORITY] = priorityConfig; config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
@@ -250,10 +235,10 @@ void AgentImpl::processStop(JsonCpp::Value& /*request*/, JsonCpp::Value& answer)
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processCreateAccount(JsonCpp::Value &d, JsonCpp::Value& answer) void AgentImpl::processCreateAccount(JsonCpp::Value& d, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
PVariantMap c = std::make_shared<VariantMap>(); PVariantMap c = std::make_shared<VariantMap>();
(*c)[CONFIG_USERNAME] = d["username"].asString(); (*c)[CONFIG_USERNAME] = d["username"].asString();
(*c)[CONFIG_PASSWORD] = d["password"].asString(); (*c)[CONFIG_PASSWORD] = d["password"].asString();
@@ -276,7 +261,7 @@ void AgentImpl::processStartAccount(JsonCpp::Value& request, JsonCpp::Value& ans
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map // Locate account in map
auto accountIter = mAccountMap.find(request["account_id"].asInt()); auto accountIter = mAccountMap.find(request["account_id"].asInt());
if (accountIter != mAccountMap.end()) if (accountIter != mAccountMap.end())
{ {
accountIter->second->start(); accountIter->second->start();
@@ -286,17 +271,17 @@ void AgentImpl::processStartAccount(JsonCpp::Value& request, JsonCpp::Value& ans
answer["status"] = Status_AccountNotFound; answer["status"] = Status_AccountNotFound;
} }
void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Value &answer) void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map // Locate account in map
auto accountIter = mAccountMap.find(request["account_id"].asInt()); auto accountIter = mAccountMap.find(request["account_id"].asInt());
if (accountIter != mAccountMap.end()) if (accountIter != mAccountMap.end())
{ {
Account::UserInfo info; Account::UserInfo info;
JsonCpp::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys) for (const std::string& k : keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
accountIter->second->setUserInfo(info); accountIter->second->setUserInfo(info);
@@ -306,10 +291,10 @@ void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Va
answer["status"] = Status_AccountNotFound; answer["status"] = Status_AccountNotFound;
} }
void AgentImpl::processCreateSession(JsonCpp::Value &request, JsonCpp::Value &answer) void AgentImpl::processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto accountIter = mAccountMap.find(request["account_id"].asInt()); auto accountIter = mAccountMap.find(request["account_id"].asInt());
if (accountIter != mAccountMap.end()) if (accountIter != mAccountMap.end())
{ {
PSession session = createSession(accountIter->second); PSession session = createSession(accountIter->second);
@@ -345,9 +330,10 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
{ {
// Ensure audio provider is here // Ensure audio provider is here
PSession session = sessionIter->second; PSession session = sessionIter->second;
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal); PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing)); audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) |
static_cast<int>(StreamState::Playing));
/*#if defined(USE_AQUA_LIBRARY) /*#if defined(USE_AQUA_LIBRARY)
std::string path_faults = request["path_faults"].asString(); std::string path_faults = request["path_faults"].asString();
@@ -372,7 +358,8 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
{ "specp", "32"} { "specp", "32"}
}; };
// std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on -spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json"; // std::string config = "-avlp on -smtnrm on -decor off -mprio off -npnt auto -voip off -enorm off -g711 on
-spfrcor off -grad off -tmc on -miter 1 -trim a 10 -output json";
// if (temp_path.size()) // if (temp_path.size())
// config += " -fau " + temp_path; // config += " -fau " + temp_path;
@@ -392,10 +379,10 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
// TODO: support SRTP via StreamState::Srtp option in audio provider state // TODO: support SRTP via StreamState::Srtp option in audio provider state
// Get user headers // Get user headers
Session::UserHeaders info; Session::UserHeaders info;
JsonCpp::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys) for (const std::string& k : keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
session->setUserHeaders(info); session->setUserHeaders(info);
@@ -413,7 +400,7 @@ void AgentImpl::processStopSession(JsonCpp::Value& request, JsonCpp::Value& answ
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto sessionIter = mSessionMap.find(request["session_id"].asInt()); auto sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
{ {
PSession session = sessionIter->second; PSession session = sessionIter->second;
@@ -427,7 +414,7 @@ void AgentImpl::processStopSession(JsonCpp::Value& request, JsonCpp::Value& answ
void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto sessionIter = mSessionMap.find(request["session_id"].asInt()); auto sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
{ {
if (!mAudioManager) if (!mAudioManager)
@@ -445,13 +432,13 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull); mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
// Accept session on SIP level // Accept session on SIP level
PSession session = sessionIter->second; PSession session = sessionIter->second;
// Get user headers // Get user headers
Session::UserHeaders info; Session::UserHeaders info;
JsonCpp::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys) for (const std::string& k : keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
session->setUserHeaders(info); session->setUserHeaders(info);
@@ -469,17 +456,17 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
int sessionId = request["session_id"].asInt(); int sessionId = request["session_id"].asInt();
auto sessionIter = mSessionMap.find(sessionId); auto sessionIter = mSessionMap.find(sessionId);
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
mSessionMap.erase(sessionIter); mSessionMap.erase(sessionIter);
//#if defined(USE_AQUA_LIBRARY) // #if defined(USE_AQUA_LIBRARY)
// closeAqua(sessionId); // closeAqua(sessionId);
//#endif // #endif
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &answer) void AgentImpl::processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
// Deliberately does NOT take mAgentMutex: events are produced by the worker // Deliberately does NOT take mAgentMutex: events are produced by the worker
// thread inside process(), which needs mAgentMutex. Holding it here would // thread inside process(), which needs mAgentMutex. Holding it here would
@@ -506,14 +493,13 @@ void AgentImpl::processWaitForEvent(JsonCpp::Value &request, JsonCpp::Value &ans
void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
int sessionId = request["session_id"].asInt(); int sessionId = request["session_id"].asInt();
SessionMap::iterator sessionIter = mSessionMap.find(sessionId); SessionMap::iterator sessionIter = mSessionMap.find(sessionId);
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
{ {
PSession session = sessionIter->second; PSession session = sessionIter->second;
VariantMap result; VariantMap result;
session->getSessionInfo(Session::InfoOptions::Detailed, session->getSessionInfo(Session::InfoOptions::Detailed, result);
result);
if (result.exists(SessionInfo_AudioCodec)) if (result.exists(SessionInfo_AudioCodec))
answer["codec"] = result[SessionInfo_AudioCodec].asStdString(); answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
@@ -560,17 +546,19 @@ void AgentImpl::processNetworkChanged(JsonCpp::Value& /*request*/, JsonCpp::Valu
const std::string BeginCertificate = "-----BEGIN CERTIFICATE-----"; const std::string BeginCertificate = "-----BEGIN CERTIFICATE-----";
const std::string EndCertificate = "-----END CERTIFICATE-----"; const std::string EndCertificate = "-----END CERTIFICATE-----";
void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
std::string pem = request["cert"].asString(); std::string pem = request["cert"].asString();
std::string::size_type pb = 0, pe = 0; std::string::size_type pb = 0, pe = 0;
while (pb != std::string::npos && pe != std::string::npos) { while (pb != std::string::npos && pe != std::string::npos)
{
pb = pem.find(BeginCertificate, pb); pb = pem.find(BeginCertificate, pb);
pe = pem.find(EndCertificate, pe); pe = pem.find(EndCertificate, pe);
if (pb != std::string::npos && pe != std::string::npos && pe > pb) { if (pb != std::string::npos && pe != std::string::npos && pe > pb)
{
std::string cert = pem.substr(pb, pe - pb + EndCertificate.size()); std::string cert = pem.substr(pb, pe - pb + EndCertificate.size());
addRootCert(ByteBuffer(cert.c_str(), cert.size())); addRootCert(ByteBuffer(cert.c_str(), cert.size()));
@@ -581,9 +569,9 @@ void AgentImpl::processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answ
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
void AgentImpl::processLogMessage(JsonCpp::Value &request, JsonCpp::Value &answer) void AgentImpl::processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
int level = request["level"].asInt(); int level = request["level"].asInt();
std::string message = request["message"].asString(); std::string message = request["message"].asString();
ICELog(static_cast<ice::LogLevel>(level), "App", << message); ICELog(static_cast<ice::LogLevel>(level), "App", << message);
@@ -600,7 +588,8 @@ void AgentImpl::stopAgentAndThread()
stop(); stop();
} }
catch (...) catch (...)
{} {
}
// Stop worker thread // Stop worker thread
if (mThread) if (mThread)
@@ -634,19 +623,19 @@ void AgentImpl::stopAgentAndThread()
void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer)
{ {
std::unique_lock<std::recursive_mutex> l(mAgentMutex); std::unique_lock<std::recursive_mutex> l(mAgentMutex);
SessionMap::iterator sessionIter = mSessionMap.find(request["session_id"].asInt()); SessionMap::iterator sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
{ {
// Extract ptr to session // Extract ptr to session
PSession session = sessionIter->second; PSession session = sessionIter->second;
// Parse command // Parse command
std::string actionText = request["media_action"].asString(), std::string actionText = request["media_action"].asString(),
directionText = request["media_direction"].asString(); directionText = request["media_direction"].asString();
MT::Stream::MediaDirection direction = directionText == "incoming" ? MT::Stream::MediaDirection::Incoming MT::Stream::MediaDirection direction =
: MT::Stream::MediaDirection::Outgoing; directionText == "incoming" ? MT::Stream::MediaDirection::Incoming : MT::Stream::MediaDirection::Outgoing;
std::string path = request["path"].asString(); std::string path = request["path"].asString();
// Try to open file // Try to open file
AudioProvider* prov = session->findProviderForActiveAudio(); AudioProvider* prov = session->findProviderForActiveAudio();
@@ -672,42 +661,41 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
} }
} }
} }
else else if (actionText == "write")
if (actionText == "write") {
if (path.empty())
{ {
if (path.empty()) // Turn off recording from the stream
{ prov->writeFile(Audio::PWavFileWriter(), direction);
// Turn off recording from the stream answer["status"] = Status_Ok;
prov->writeFile(Audio::PWavFileWriter(), direction);
answer["status"] = Status_Ok;
}
else
{
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
answer["status"] = Status_FailedToOpenFile;
else
{
prov->writeFile(writer, direction);
answer["status"] = Status_Ok;
}
}
} }
else else
if (actionText == "mirror") {
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
answer["status"] = Status_FailedToOpenFile;
else
{ {
prov->setupMirror(request["enable"].asBool()); prov->writeFile(writer, direction);
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
else }
answer["status"] = Status_NoCommand; }
else if (actionText == "mirror")
{
prov->setupMirror(request["enable"].asBool());
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_NoCommand;
} }
else else
answer["status"] = Status_NoMediaAction; answer["status"] = Status_NoMediaAction;
} }
} }
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context,
void* userTag)
{ {
/*switch (direction) /*switch (direction)
{ {
@@ -718,7 +706,9 @@ void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection
// Called on new incoming session; providers shoukld // Called on new incoming session; providers shoukld
#define EVENT_WITH_NAME(X) JsonCpp::Value v; v["event_name"] = X; #define EVENT_WITH_NAME(X) \
JsonCpp::Value v; \
v["event_name"] = X;
PDataProvider AgentImpl::onProviderNeeded(const std::string& name) PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
{ {
@@ -856,8 +846,7 @@ void AgentImpl::onCheckFinished(PSession s, const char* description)
} }
// Called when log message must be recorded // Called when log message must be recorded
void AgentImpl::onLog(const char* /*msg*/) void AgentImpl::onLog(const char* /*msg*/) {}
{}
// Called when problem with SIP connection(s) detected // Called when problem with SIP connection(s) detected
void AgentImpl::onSipConnectionFailed() void AgentImpl::onSipConnectionFailed()
+43 -42
View File
@@ -16,72 +16,72 @@
#include <atomic> #include <atomic>
class AgentImpl: public UserAgent, public MT::Stream::MediaObserver class AgentImpl : public UserAgent, public MT::Stream::MediaObserver
{ {
protected: protected:
std::recursive_mutex mAgentMutex; std::recursive_mutex mAgentMutex;
std::mutex mEventListMutex; std::mutex mEventListMutex;
std::condition_variable mEventListChangeCondVar; std::condition_variable mEventListChangeCondVar;
std::vector<JsonCpp::Value> mEventList; std::vector<JsonCpp::Value> mEventList;
bool mUseNativeAudio = false; bool mUseNativeAudio = false;
typedef std::map<int, PAccount> AccountMap; typedef std::map<int, PAccount> AccountMap;
AccountMap mAccountMap; AccountMap mAccountMap;
typedef std::map<int, PSession> SessionMap; typedef std::map<int, PSession> SessionMap;
SessionMap mSessionMap; SessionMap mSessionMap;
std::shared_ptr<std::thread> mThread; std::shared_ptr<std::thread> mThread;
std::atomic<bool> mShutdown; std::atomic<bool> mShutdown;
std::shared_ptr<MT::Terminal> mTerminal; std::shared_ptr<MT::Terminal> mTerminal;
std::shared_ptr<AudioManager> mAudioManager; std::shared_ptr<AudioManager> mAudioManager;
Audio::DataConnection* mAudioMonitoring = nullptr; Audio::DataConnection* mAudioMonitoring = nullptr;
void run(); void run();
void addEvent(const JsonCpp::Value& v); void addEvent(const JsonCpp::Value& v);
void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer); void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStart(JsonCpp::Value& request, JsonCpp::Value& answer); void processStart(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStop(JsonCpp::Value& request, JsonCpp::Value& answer); void processStop(JsonCpp::Value& request, JsonCpp::Value& answer);
void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer); void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer); void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer); void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer); void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer); void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer); void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer); void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer); void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer); void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer);
void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer); void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer);
void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer); void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer);
void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer); void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer);
void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer); void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer);
void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer); void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer);
void stopAgentAndThread(); void stopAgentAndThread();
public: public:
AgentImpl(); AgentImpl();
~AgentImpl(); ~AgentImpl();
std::string command(const std::string& command); std::string command(const std::string& command);
bool waitForData(int milliseconds); bool waitForData(int milliseconds);
std::string read(); std::string read();
// Get access to internal audio manager. Value can be nullptr. // Get access to internal audio manager. Value can be nullptr.
const std::shared_ptr<AudioManager>& audioManager() const; const std::shared_ptr<AudioManager>& audioManager() const;
void setAudioMonitoring(Audio::DataConnection* monitoring); void setAudioMonitoring(Audio::DataConnection* monitoring);
Audio::DataConnection* monitoring() const; Audio::DataConnection* monitoring() const;
// UserAgent overrides // UserAgent overrides
// Called on new incoming session; providers shoukld // Called on new incoming session; providers shoukld
PDataProvider onProviderNeeded(const std::string& name) override; PDataProvider onProviderNeeded(const std::string& name) override;
// Called on new session offer // Called on new session offer
void onNewSession(PSession s) override; void onNewSession(PSession s) override;
// Called when session is terminated // Called when session is terminated
void onSessionTerminated(PSession s, int responsecode, int reason) override; void onSessionTerminated(PSession s, int responsecode, int reason) override;
// Called when session is established ok i.e. after all ICE signalling is finished // Called when session is established ok i.e. after all ICE signalling is finished
// Conntype is type of establish event - EV_SIP or EV_ICE // Conntype is type of establish event - EV_SIP or EV_ICE
@@ -123,7 +123,8 @@ public:
void onSipConnectionFailed() override; void onSipConnectionFailed() override;
// Called on incoming & outgoing audio for voice sessions // Called on incoming & outgoing audio for voice sessions
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override; void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context,
void* userTag) override;
}; };
#endif #endif
+5 -16
View File
@@ -1,27 +1,16 @@
#include "Agent_Interface.h" #include "Agent_Interface.h"
#include "Agent_Impl.h" #include "Agent_Impl.h"
Agent::Agent() Agent::Agent() : mContext(new AgentImpl());
:mContext(new AgentImpl());
{ {
} }
Agent::~Agent() Agent::~Agent() {}
{
} void Agent::write(const std::string& command) {}
void Agent::write(const std::string& command)
{
}
bool Agent::waitForData(int milliseconds) bool Agent::waitForData(int milliseconds)
{ {
return false; return false;
} }
std::string Agent::read() std::string Agent::read() {}
{
}
+6 -6
View File
@@ -6,14 +6,14 @@
class Agent class Agent
{ {
protected: protected:
void* mContext; void* mContext;
public: public:
Agent(); Agent();
~Agent(); ~Agent();
void write(const std::string& command); void write(const std::string& command);
bool waitForData(int milliseconds); bool waitForData(int milliseconds);
std::string read(); std::string read();
}; };
#endif #endif
+372 -378
View File
@@ -14,586 +14,580 @@ using namespace Audio;
// -------------------- AndroidEnumerator ----------------------------- // -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator() AndroidEnumerator::AndroidEnumerator() {}
{}
AndroidEnumerator::~AndroidEnumerator() AndroidEnumerator::~AndroidEnumerator() {}
{}
int AndroidEnumerator::indexOfDefaultDevice() int AndroidEnumerator::indexOfDefaultDevice()
{ {
return 0; return 0;
} }
int AndroidEnumerator::count() int AndroidEnumerator::count()
{ {
return 1; return 1;
} }
int AndroidEnumerator::idAt(int index) int AndroidEnumerator::idAt(int index)
{ {
return 0; return 0;
} }
std::string AndroidEnumerator::nameAt(int index) std::string AndroidEnumerator::nameAt(int index)
{ {
return "Audio"; return "Audio";
} }
void AndroidEnumerator::open(int direction) void AndroidEnumerator::open(int direction) {}
{}
void AndroidEnumerator::close() void AndroidEnumerator::close() {}
{}
// ----------------------- // -----------------------
OpenSLEngine::OpenSLEngine() OpenSLEngine::OpenSLEngine() {}
{}
OpenSLEngine::~OpenSLEngine() OpenSLEngine::~OpenSLEngine() {}
{}
void OpenSLEngine::open() void OpenSLEngine::open()
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
if (++mUsageCounter == 1) if (++mUsageCounter == 1)
internalOpen(); internalOpen();
} }
void OpenSLEngine::close() void OpenSLEngine::close()
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
if (mUsageCounter == 0) if (mUsageCounter == 0)
return; return;
if (--mUsageCounter == 0) if (--mUsageCounter == 0)
internalClose(); internalClose();
} }
#define CHECK_OPENSLES_ERROR if (resultCode != SL_RESULT_SUCCESS) throw Exception(ERR_OPENSLES, (int)resultCode) #define CHECK_OPENSLES_ERROR \
if (resultCode != SL_RESULT_SUCCESS) \
throw Exception(ERR_OPENSLES, (int)resultCode)
void OpenSLEngine::internalOpen() void OpenSLEngine::internalOpen()
{ {
SLresult resultCode; SLresult resultCode;
// Instantiate OpenSL ES engine object // Instantiate OpenSL ES engine object
resultCode = slCreateEngine(&mEngineObject, 0, nullptr, 0, nullptr, nullptr); resultCode = slCreateEngine(&mEngineObject, 0, nullptr, 0, nullptr, nullptr);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Bring it online (realize) // Bring it online (realize)
resultCode = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE); resultCode = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Get interface finally // Get interface finally
resultCode = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface); resultCode = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
ICELogInfo(<< "OpenSL engine object created."); ICELogInfo(<< "OpenSL engine object created.");
} }
void OpenSLEngine::internalClose() void OpenSLEngine::internalClose()
{ {
if (mEngineObject != nullptr) if (mEngineObject != nullptr)
{ {
ICELogInfo(<< "Destroy OpenSL engine object."); ICELogInfo(<< "Destroy OpenSL engine object.");
(*mEngineObject)->Destroy(mEngineObject); (*mEngineObject)->Destroy(mEngineObject);
mEngineObject = nullptr; mEngineObject = nullptr;
mEngineInterface = nullptr; mEngineInterface = nullptr;
} }
} }
SLEngineItf OpenSLEngine::getNativeEngine() const SLEngineItf OpenSLEngine::getNativeEngine() const
{ {
return mEngineInterface; return mEngineInterface;
} }
static OpenSLEngine OpenSLEngineInstance; static OpenSLEngine OpenSLEngineInstance;
OpenSLEngine& OpenSLEngine::instance() OpenSLEngine& OpenSLEngine::instance()
{ {
return OpenSLEngineInstance; return OpenSLEngineInstance;
} }
// --------------- Input implementation ---------------- // --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId) AndroidInputDevice::AndroidInputDevice(int devId) {}
{}
AndroidInputDevice::~AndroidInputDevice() AndroidInputDevice::~AndroidInputDevice() {}
{}
static int RateToProbe[12][2] = { static int RateToProbe[12][2] = {
{ SL_SAMPLINGRATE_16, 16000 }, {SL_SAMPLINGRATE_16, 16000}, {SL_SAMPLINGRATE_8, 8000}, {SL_SAMPLINGRATE_32, 32000},
{ SL_SAMPLINGRATE_8, 8000 }, {SL_SAMPLINGRATE_44_1, 44100}, {SL_SAMPLINGRATE_11_025, 10025}, {SL_SAMPLINGRATE_22_05, 22050},
{ SL_SAMPLINGRATE_32, 32000 }, {SL_SAMPLINGRATE_24, 24000}, {SL_SAMPLINGRATE_48, 48000}, {SL_SAMPLINGRATE_64, 64000},
{ SL_SAMPLINGRATE_44_1, 44100 }, {SL_SAMPLINGRATE_88_2, 88200}, {SL_SAMPLINGRATE_96, 96000}, {SL_SAMPLINGRATE_192, 192000}};
{ SL_SAMPLINGRATE_11_025, 10025 },
{ SL_SAMPLINGRATE_22_05, 22050 },
{ SL_SAMPLINGRATE_24, 24000 },
{ SL_SAMPLINGRATE_48, 48000 },
{ SL_SAMPLINGRATE_64, 64000 },
{ SL_SAMPLINGRATE_88_2, 88200 },
{ SL_SAMPLINGRATE_96, 96000 },
{ SL_SAMPLINGRATE_192, 192000} };
bool AndroidInputDevice::open() bool AndroidInputDevice::open()
{ {
if (active()) if (active())
return true; return true;
OpenSLEngine::instance().open(); OpenSLEngine::instance().open();
// Probe few sampling rates // Probe few sampling rates
bool opened = false; bool opened = false;
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++) for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
{
try
{ {
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1]); try
mDeviceRate = RateToProbe[rateIndex][1]; {
ICELogInfo(<< "Input Opened with rate " << mDeviceRate << " and rate index " << rateIndex); internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1]);
opened = mDeviceRate != 0; mDeviceRate = RateToProbe[rateIndex][1];
if (!opened) ICELogInfo(<< "Input Opened with rate " << mDeviceRate << " and rate index " << rateIndex);
internalClose(); opened = mDeviceRate != 0;
if (!opened)
internalClose();
}
catch (...)
{
opened = false;
internalClose();
}
} }
catch(...) mActive = opened;
{
opened = false;
internalClose();
}
}
mActive = opened;
return opened; return opened;
} }
void AndroidInputDevice::close() void AndroidInputDevice::close()
{ {
// There is no check for active() value because close() can be called to cleanup after bad open() call. // There is no check for active() value because close() can be called to cleanup after bad open() call.
internalClose(); internalClose();
OpenSLEngine::instance().close(); OpenSLEngine::instance().close();
mActive = false; mActive = false;
} }
Format AndroidInputDevice::getFormat() Format AndroidInputDevice::getFormat()
{ {
return Format(mDeviceRate, 1); return Format(mDeviceRate, 1);
} }
bool AndroidInputDevice::active() const bool AndroidInputDevice::active() const
{ {
return mActive; return mActive;
} }
bool AndroidInputDevice::fakeMode() bool AndroidInputDevice::fakeMode()
{ {
return false; return false;
} }
void AndroidInputDevice::setFakeMode(bool fakemode) void AndroidInputDevice::setFakeMode(bool fakemode) {}
{}
int AndroidInputDevice::readBuffer(void* buffer) int AndroidInputDevice::readBuffer(void* buffer)
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
while (mSdkRateCache.filled() < AUDIO_MIC_BUFFER_SIZE) while (mSdkRateCache.filled() < AUDIO_MIC_BUFFER_SIZE)
{ {
mDataCondVar.wait(l); mDataCondVar.wait(l);
} }
return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE); return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE);
} }
#define CHECK_SL_INTERFACE(INTF, ERR) {if (!INTF) throw Exception(ERR_OPENSLES, ERR); if (!(*INTF)) throw Exception(ERR_OPENSLES, ERR);} #define CHECK_SL_INTERFACE(INTF, ERR) \
{ \
if (!INTF) \
throw Exception(ERR_OPENSLES, ERR); \
if (!(*INTF)) \
throw Exception(ERR_OPENSLES, ERR); \
}
void AndroidInputDevice::internalOpen(int rateCode, int rate) void AndroidInputDevice::internalOpen(int rateCode, int rate)
{ {
SLresult resultCode = 0; SLresult resultCode = 0;
SLuint32 nrOfChannels = 1; SLuint32 nrOfChannels = 1;
// Prepare audio source // Prepare audio source
SLDataLocator_IODevice devDescription = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL}; SLDataLocator_IODevice devDescription = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SLDataSource audioSource = { &devDescription, NULL }; SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSource = {&devDescription, NULL};
// Source flags // Source flags
SLuint32 speakersFlags = nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER; SLuint32 speakersFlags =
nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Buffer queue // Buffer queue
SLDataLocator_AndroidSimpleBufferQueue queueDescription = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 }; SLDataLocator_AndroidSimpleBufferQueue queueDescription = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
// Audio format // Audio format
SLDataFormat_PCM formatDescription = { SL_DATAFORMAT_PCM, nrOfChannels, (SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16, SLDataFormat_PCM formatDescription = {SL_DATAFORMAT_PCM, nrOfChannels,
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags, SL_BYTEORDER_LITTLEENDIAN }; (SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags,
SL_BYTEORDER_LITTLEENDIAN};
SLDataSink audioSink = { &queueDescription, &formatDescription }; SLDataSink audioSink = {&queueDescription, &formatDescription};
// Create recorder // Create recorder
// Do not forget about RECORD_AUDIO permission // Do not forget about RECORD_AUDIO permission
const SLInterfaceID interfacesList[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; const SLInterfaceID interfacesList[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
const SLboolean interfacesRequirements[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; const SLboolean interfacesRequirements[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
// Get access to OpenSL engine // Get access to OpenSL engine
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine(); SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
CHECK_SL_INTERFACE(engine_interface, -1); CHECK_SL_INTERFACE(engine_interface, -1);
resultCode = (*engine_interface)->CreateAudioRecorder( resultCode = (*engine_interface)
OpenSLEngine::instance().getNativeEngine(), ->CreateAudioRecorder(OpenSLEngine::instance().getNativeEngine(), &mRecorderObject, &audioSource,
&mRecorderObject, &audioSource, &audioSink, 2, interfacesList, interfacesRequirements); &audioSink, 2, interfacesList, interfacesRequirements);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderObject, -2); CHECK_SL_INTERFACE(mRecorderObject, -2);
// Obtain stream type // Obtain stream type
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDCONFIGURATION, &mAndroidCfg); resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDCONFIGURATION, &mAndroidCfg);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Now audio recorder goes to real world // Now audio recorder goes to real world
resultCode = (*mRecorderObject)->Realize(mRecorderObject, SL_BOOLEAN_FALSE); resultCode = (*mRecorderObject)->Realize(mRecorderObject, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Get recorder interface // Get recorder interface
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_RECORD, &mRecorderInterface); resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_RECORD, &mRecorderInterface);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderInterface, -3); CHECK_SL_INTERFACE(mRecorderInterface, -3);
// Now buffer queue interface... // Now buffer queue interface...
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface); resultCode =
CHECK_OPENSLES_ERROR; (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4); CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
// Resampler is needed to provide SDK's rate // Resampler is needed to provide SDK's rate
mResampler = std::make_shared<Resampler>(); mResampler = std::make_shared<Resampler>();
mResampler->start(nrOfChannels, rate, AUDIO_SAMPLERATE); mResampler->start(nrOfChannels, rate, AUDIO_SAMPLERATE);
// Allocate recorder buffer size // Allocate recorder buffer size
mBufferSize = (AUDIO_MIC_BUFFER_LENGTH / 10) * (rate / 100) * 2; mBufferSize = (AUDIO_MIC_BUFFER_LENGTH / 10) * (rate / 100) * 2;
mRecorderBuffer.setCapacity(mBufferSize * AUDIO_MIC_BUFFER_COUNT); mRecorderBuffer.setCapacity(mBufferSize * AUDIO_MIC_BUFFER_COUNT);
mRecorderBufferIndex = 0; mRecorderBufferIndex = 0;
// Setup data consuming callback // Setup data consuming callback
resultCode = (*mRecorderBufferInterface)->RegisterCallback(mRecorderBufferInterface, DeviceCallback, (void*)this); resultCode = (*mRecorderBufferInterface)->RegisterCallback(mRecorderBufferInterface, DeviceCallback, (void*)this);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Setup buffers // Setup buffers
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
(*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize); (*mRecorderBufferInterface)
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
// Start finally // Start finally
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING); resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
} }
void AndroidInputDevice::internalClose() void AndroidInputDevice::internalClose()
{ {
if (!mRecorderObject) if (!mRecorderObject)
return; return;
if (*mRecorderObject) if (*mRecorderObject)
{
if (active())
{ {
// Stop recording if (active())
(*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_STOPPED); {
// Stop recording
(*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_STOPPED);
// Wait until recording will not stop really // Wait until recording will not stop really
SLuint32 state = SL_RECORDSTATE_STOPPED; SLuint32 state = SL_RECORDSTATE_STOPPED;
do do
{ {
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state); (*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
SyncHelper::delay(1); SyncHelper::delay(1);
} } while (state == SL_RECORDSTATE_RECORDING);
while (state == SL_RECORDSTATE_RECORDING); }
(*mRecorderObject)->Destroy(mRecorderObject);
} }
(*mRecorderObject)->Destroy(mRecorderObject);
}
mRecorderObject = nullptr; mRecorderObject = nullptr;
mRecorderInterface = nullptr; mRecorderInterface = nullptr;
mRecorderBufferInterface = nullptr; mRecorderBufferInterface = nullptr;
mAndroidCfg = nullptr; mAndroidCfg = nullptr;
} }
void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq) void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
// Send data to AudioPair // Send data to AudioPair
if (mConnection)
mConnection->onMicData(getFormat(), mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
/*
// Send audio to cache with native sample rate
mDeviceRateCache.add(mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
// Check if there is enough data (10 ms) to send
int tenMsSize = (int)Format(mDeviceRate, 1).sizeFromTime(10);
while (mDeviceRateCache.filled() >= tenMsSize)
{
char* resampled = (char*)alloca(Format().sizeFromTime(10));
int processed = 0;
int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled, Format().sizeFromTime(10));
if (outlen > 0)
mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10));
mDeviceRateCache.erase(tenMsSize);
}
// Tell about data
while (mSdkRateCache.filled() >= AUDIO_MIC_BUFFER_SIZE)
{
if (mConnection) if (mConnection)
mConnection->onMicData(Format(), mSdkRateCache.data(), AUDIO_MIC_BUFFER_SIZE); mConnection->onMicData(getFormat(), mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
mSdkRateCache.erase(AUDIO_MIC_BUFFER_SIZE); /*
} // Send audio to cache with native sample rate
*/ mDeviceRateCache.add(mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
// Re-enqueue used buffer
(*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize); // Check if there is enough data (10 ms) to send
mRecorderBufferIndex++; int tenMsSize = (int)Format(mDeviceRate, 1).sizeFromTime(10);
mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT; while (mDeviceRateCache.filled() >= tenMsSize)
{
char* resampled = (char*)alloca(Format().sizeFromTime(10));
int processed = 0;
int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled,
Format().sizeFromTime(10)); if (outlen > 0) mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10));
mDeviceRateCache.erase(tenMsSize);
}
// Tell about data
while (mSdkRateCache.filled() >= AUDIO_MIC_BUFFER_SIZE)
{
if (mConnection)
mConnection->onMicData(Format(), mSdkRateCache.data(), AUDIO_MIC_BUFFER_SIZE);
mSdkRateCache.erase(AUDIO_MIC_BUFFER_SIZE);
}
*/
// Re-enqueue used buffer
(*mRecorderBufferInterface)
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
mRecorderBufferIndex++;
mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT;
} }
void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void *context) void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{ {
try try
{ {
if (context) if (context)
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq); reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
} }
catch(...) catch (...)
{} {
}
} }
// ------------ AndroidOutputDevice ----------------- // ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId) AndroidOutputDevice::AndroidOutputDevice(int devId)
{ {
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this)); ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
} }
AndroidOutputDevice::~AndroidOutputDevice() AndroidOutputDevice::~AndroidOutputDevice()
{ {
ICELogDebug(<< "Deleting AndroidOutputDevice."); ICELogDebug(<< "Deleting AndroidOutputDevice.");
close(); close();
} }
bool AndroidOutputDevice::open() bool AndroidOutputDevice::open()
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
bool opened = false; bool opened = false;
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++) for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
{
try
{ {
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1], true); try
opened = true; {
mDeviceRate = RateToProbe[rateIndex][1]; internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1], true);
ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex); opened = true;
mDeviceRate = RateToProbe[rateIndex][1];
ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex);
}
catch (...)
{
opened = false;
}
} }
catch(...) if (opened)
{ ICELogInfo(<< "Speaker opened on rate " << mDeviceRate);
opened = false;
}
}
if (opened)
ICELogInfo(<< "Speaker opened on rate " << mDeviceRate);
return opened; return opened;
} }
void AndroidOutputDevice::close() void AndroidOutputDevice::close()
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
internalClose(); internalClose();
} }
Format AndroidOutputDevice::getFormat() Format AndroidOutputDevice::getFormat()
{ {
return Format(mDeviceRate, 1); return Format(mDeviceRate, 1);
} }
bool AndroidOutputDevice::fakeMode() bool AndroidOutputDevice::fakeMode()
{ {
return false; return false;
} }
void AndroidOutputDevice::setFakeMode(bool fakemode) void AndroidOutputDevice::setFakeMode(bool fakemode) {}
{
}
void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice) void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
{ {
mInShutdown = false; mInShutdown = false;
SLresult resultCode; SLresult resultCode;
SLuint32 channels = 1; SLuint32 channels = 1;
// Configure audio source // Configure audio source
SLDataLocator_AndroidSimpleBufferQueue queue_desc = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 }; SLDataLocator_AndroidSimpleBufferQueue queue_desc = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
const SLInterfaceID interfacesList[] = { SL_IID_VOLUME }; const SLInterfaceID interfacesList[] = {SL_IID_VOLUME};
const SLboolean interfaceRequirements[] = { SL_BOOLEAN_FALSE }; const SLboolean interfaceRequirements[] = {SL_BOOLEAN_FALSE};
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateOutputMix( resultCode = (*OpenSLEngine::instance().getNativeEngine())
OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList, ->CreateOutputMix(OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
interfaceRequirements); interfaceRequirements);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Bring mixer online // Bring mixer online
resultCode = (*mMixer)->Realize(mMixer, SL_BOOLEAN_FALSE); resultCode = (*mMixer)->Realize(mMixer, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Prepare mixer configuration // Prepare mixer configuration
SLuint32 speakers = SLuint32 speakers = channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Describe audio format // Describe audio format
SLDataFormat_PCM pcm_format = {SL_DATAFORMAT_PCM, channels, (SLuint32) rateId, SLDataFormat_PCM pcm_format = {
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, SL_DATAFORMAT_PCM, channels, (SLuint32)rateId, SL_PCMSAMPLEFORMAT_FIXED_16,
speakers, SL_BYTEORDER_LITTLEENDIAN}; SL_PCMSAMPLEFORMAT_FIXED_16, speakers, SL_BYTEORDER_LITTLEENDIAN};
// Describe audio source - buffers + audio format // Describe audio source - buffers + audio format
SLDataSource audio_source = { &queue_desc, &pcm_format }; SLDataSource audio_source = {&queue_desc, &pcm_format};
// Describe audio sink // Describe audio sink
SLDataLocator_OutputMix mixer_desc = { SL_DATALOCATOR_OUTPUTMIX, mMixer }; SLDataLocator_OutputMix mixer_desc = {SL_DATALOCATOR_OUTPUTMIX, mMixer};
SLDataSink audio_sink = { &mixer_desc, NULL }; SLDataSink audio_sink = {&mixer_desc, NULL};
// Create player instance // Create player instance
const SLInterfaceID playerInterfaces[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, const SLInterfaceID playerInterfaces[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME,
SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
SL_IID_ANDROIDCONFIGURATION }; const SLboolean playerInterfacesReqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
const SLboolean playerInterfacesReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateAudioPlayer( resultCode = (*OpenSLEngine::instance().getNativeEngine())
OpenSLEngine::instance().getNativeEngine(), &mPlayer, ->CreateAudioPlayer(OpenSLEngine::instance().getNativeEngine(), &mPlayer, &audio_source,
&audio_source, &audio_sink, 3, playerInterfaces, playerInterfacesReqs); &audio_sink, 3, playerInterfaces, playerInterfacesReqs);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Get android config interface // Get android config interface
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDCONFIGURATION, &mAndroidConfig); resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDCONFIGURATION, &mAndroidConfig);
if (resultCode == SL_RESULT_SUCCESS) if (resultCode == SL_RESULT_SUCCESS)
{ {
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA; SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
resultCode = (*mAndroidConfig)->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE, resultCode = (*mAndroidConfig)
&streamType, sizeof(SLint32)); ->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
if (resultCode != SL_RESULT_SUCCESS) if (resultCode != SL_RESULT_SUCCESS)
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode); ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
} }
else else
ICELogCritical(<< "Failed to obtain android cfg audio interface with error " << (unsigned)resultCode); ICELogCritical(<< "Failed to obtain android cfg audio interface with error " << (unsigned)resultCode);
// Bring player online // Bring player online
resultCode = (*mPlayer)->Realize(mPlayer, SL_BOOLEAN_FALSE); resultCode = (*mPlayer)->Realize(mPlayer, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Obtain player control // Obtain player control
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_PLAY, &mPlayerControl); resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_PLAY, &mPlayerControl);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Get the buffer queue interface // Get the buffer queue interface
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mBufferQueue);
&mBufferQueue); CHECK_OPENSLES_ERROR;
CHECK_OPENSLES_ERROR;
// Setup callback // Setup callback
resultCode = (*mBufferQueue)->RegisterCallback(mBufferQueue, DeviceCallback, this); resultCode = (*mBufferQueue)->RegisterCallback(mBufferQueue, DeviceCallback, this);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
// Enqueue buffers // Enqueue buffers
mBufferSize = (int)Format(rate, channels).sizeFromTime(AUDIO_SPK_BUFFER_LENGTH); mBufferSize = (int)Format(rate, channels).sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
mPlayBuffer.setCapacity(AUDIO_SPK_BUFFER_COUNT * mBufferSize); mPlayBuffer.setCapacity(AUDIO_SPK_BUFFER_COUNT * mBufferSize);
mBufferIndex = 0; mBufferIndex = 0;
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize, (*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize, (SLuint32)mBufferSize);
(SLuint32)mBufferSize);
// Set the player's state to playing // Set the player's state to playing
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING); resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
CHECK_OPENSLES_ERROR; CHECK_OPENSLES_ERROR;
ICELogInfo(<< "Android audio output is opened and playing."); ICELogInfo(<< "Android audio output is opened and playing.");
} }
void AndroidOutputDevice::internalClose() void AndroidOutputDevice::internalClose()
{ {
if (mPlayer) if (mPlayer)
{
if (*mPlayer)
{ {
mInShutdown = true; if (*mPlayer)
ICELogInfo(<< "Stop player"); {
if (mPlayerControl) { mInShutdown = true;
if (*mPlayerControl) { ICELogInfo(<< "Stop player");
SLuint32 state = SL_PLAYSTATE_PLAYING; if (mPlayerControl)
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED); {
if (*mPlayerControl)
{
SLuint32 state = SL_PLAYSTATE_PLAYING;
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
while (state != SL_PLAYSTATE_STOPPED) { while (state != SL_PLAYSTATE_STOPPED)
(*mPlayerControl)->GetPlayState(mPlayerControl, &state); {
SyncHelper::delay(1); (*mPlayerControl)->GetPlayState(mPlayerControl, &state);
} SyncHelper::delay(1);
}
}
}
// Clear buffer queue
ICELogInfo(<< "Clear player buffer queue");
(*mBufferQueue)->Clear(mBufferQueue);
ICELogInfo(<< "Destroy player object");
// Destroy player object
(*mPlayer)->Destroy(mPlayer);
ICELogInfo(<< "Android audio output closed.");
mPlayer = nullptr;
mPlayerControl = nullptr;
mBufferQueue = nullptr;
mEffect = nullptr;
mAndroidConfig = nullptr;
} }
}
// Clear buffer queue
ICELogInfo(<< "Clear player buffer queue");
(*mBufferQueue)->Clear(mBufferQueue);
ICELogInfo(<< "Destroy player object");
// Destroy player object
(*mPlayer)->Destroy(mPlayer);
ICELogInfo(<< "Android audio output closed.");
mPlayer = nullptr;
mPlayerControl = nullptr;
mBufferQueue = nullptr;
mEffect = nullptr;
mAndroidConfig = nullptr;
} }
}
if (mMixer) if (mMixer)
{ {
if (*mMixer) if (*mMixer)
(*mMixer)->Destroy(mMixer); (*mMixer)->Destroy(mMixer);
mMixer = nullptr; mMixer = nullptr;
} }
} }
void AndroidOutputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq) void AndroidOutputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{ {
if (mInShutdown) if (mInShutdown)
return; return;
/*{ /*{
char silence[mBufferSize]; memset(silence, 0, mBufferSize); char silence[mBufferSize]; memset(silence, 0, mBufferSize);
(*mBufferQueue)->Enqueue(mBufferQueue, silence, mBufferSize); (*mBufferQueue)->Enqueue(mBufferQueue, silence, mBufferSize);
return; return;
}*/ }*/
// Ask producer about data // Ask producer about data
char* buffer = mPlayBuffer.mutableData() + mBufferIndex * mBufferSize; char* buffer = mPlayBuffer.mutableData() + mBufferIndex * mBufferSize;
if (mConnection) if (mConnection)
{ {
Format f = getFormat(); Format f = getFormat();
if (f.mRate != 0) if (f.mRate != 0)
mConnection->onSpkData(f, buffer, mBufferSize); mConnection->onSpkData(f, buffer, mBufferSize);
} }
(*mBufferQueue)->Enqueue(mBufferQueue, buffer, (SLuint32)mBufferSize); (*mBufferQueue)->Enqueue(mBufferQueue, buffer, (SLuint32)mBufferSize);
mBufferIndex++; mBufferIndex++;
mBufferIndex %= AUDIO_SPK_BUFFER_COUNT; mBufferIndex %= AUDIO_SPK_BUFFER_COUNT;
} }
void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context) void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{ {
if (!context) if (!context)
return; return;
try try
{ {
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq); reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
} }
catch(...) catch (...)
{} {
}
} }
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
+92 -93
View File
@@ -25,121 +25,120 @@
namespace Audio namespace Audio
{ {
class AndroidEnumerator: public Enumerator class AndroidEnumerator : public Enumerator
{ {
public: public:
AndroidEnumerator(); AndroidEnumerator();
~AndroidEnumerator(); ~AndroidEnumerator();
void open(int direction); void open(int direction);
void close(); void close();
int count(); int count();
std::string nameAt(int index); std::string nameAt(int index);
int idAt(int index); int idAt(int index);
int indexOfDefaultDevice(); int indexOfDefaultDevice();
protected: protected:
}; };
class AndroidInputDevice: public InputDevice class AndroidInputDevice : public InputDevice
{ {
public: public:
AndroidInputDevice(int devId); AndroidInputDevice(int devId);
~AndroidInputDevice(); ~AndroidInputDevice();
bool open(); bool open();
void close(); void close();
Format getFormat(); Format getFormat();
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
int readBuffer(void* buffer); int readBuffer(void* buffer);
bool active() const; bool active() const;
protected: protected:
bool mActive = false; bool mActive = false;
SLObjectItf mRecorderObject = nullptr; SLObjectItf mRecorderObject = nullptr;
SLRecordItf mRecorderInterface = nullptr; SLRecordItf mRecorderInterface = nullptr;
SLAndroidSimpleBufferQueueItf mRecorderBufferInterface = nullptr; SLAndroidSimpleBufferQueueItf mRecorderBufferInterface = nullptr;
SLAndroidConfigurationItf mAndroidCfg = nullptr; SLAndroidConfigurationItf mAndroidCfg = nullptr;
PResampler mResampler; PResampler mResampler;
DataWindow mDeviceRateCache, mSdkRateCache; DataWindow mDeviceRateCache, mSdkRateCache;
int mDeviceRate; // Actual rate of opened recorder int mDeviceRate; // Actual rate of opened recorder
int mBufferSize; // Size of buffer used for recording (at native sample rate) int mBufferSize; // Size of buffer used for recording (at native sample rate)
DataWindow mRecorderBuffer; DataWindow mRecorderBuffer;
std::condition_variable mDataCondVar; std::condition_variable mDataCondVar;
int mRecorderBufferIndex; int mRecorderBufferIndex;
std::mutex mMutex; std::mutex mMutex;
void internalOpen(int rateCode, int rate); void internalOpen(int rateCode, int rate);
void internalClose(); void internalClose();
void handleCallback(SLAndroidSimpleBufferQueueItf bq); void handleCallback(SLAndroidSimpleBufferQueueItf bq);
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context); static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
}; };
class AndroidOutputDevice: public OutputDevice class AndroidOutputDevice : public OutputDevice
{ {
public: public:
AndroidOutputDevice(int devId); AndroidOutputDevice(int devId);
~AndroidOutputDevice(); ~AndroidOutputDevice();
bool open(); bool open();
void close(); void close();
Format getFormat(); Format getFormat();
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
protected: protected:
std::mutex mMutex; std::mutex mMutex;
int mDeviceRate = 0; int mDeviceRate = 0;
SLObjectItf mMixer = nullptr; SLObjectItf mMixer = nullptr;
SLObjectItf mPlayer = nullptr; SLObjectItf mPlayer = nullptr;
SLPlayItf mPlayerControl = nullptr; SLPlayItf mPlayerControl = nullptr;
SLAndroidSimpleBufferQueueItf mBufferQueue = nullptr; SLAndroidSimpleBufferQueueItf mBufferQueue = nullptr;
SLAndroidConfigurationItf mAndroidConfig = nullptr; SLAndroidConfigurationItf mAndroidConfig = nullptr;
SLEffectSendItf mEffect = nullptr; SLEffectSendItf mEffect = nullptr;
DataWindow mPlayBuffer; DataWindow mPlayBuffer;
int mBufferIndex = 0, mBufferSize = 0; int mBufferIndex = 0, mBufferSize = 0;
bool mInShutdown = false; bool mInShutdown = false;
void internalOpen(int rateId, int rate, bool voice); void internalOpen(int rateId, int rate, bool voice);
void internalClose(); void internalClose();
void handleCallback(SLAndroidSimpleBufferQueueItf bq); void handleCallback(SLAndroidSimpleBufferQueueItf bq);
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context); static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
};
}; class OpenSLEngine : public OsEngine
{
public:
OpenSLEngine();
~OpenSLEngine();
class OpenSLEngine: public OsEngine // open() / close() methods are based on usage counting.
{ // It means every close() call must be matched by corresponding open() call.
public: // True audio engine close will happen only on last close() call.
OpenSLEngine(); void open() override;
~OpenSLEngine(); void close() override;
// open() / close() methods are based on usage counting. SLEngineItf getNativeEngine() const;
// It means every close() call must be matched by corresponding open() call.
// True audio engine close will happen only on last close() call.
void open() override;
void close() override;
SLEngineItf getNativeEngine() const; static OpenSLEngine& instance();
static OpenSLEngine& instance(); protected:
std::mutex mMutex;
int mUsageCounter = 0;
SLObjectItf mEngineObject = nullptr;
SLEngineItf mEngineInterface = nullptr;
protected: void internalOpen();
std::mutex mMutex; void internalClose();
int mUsageCounter = 0; };
SLObjectItf mEngineObject = nullptr; } // namespace Audio
SLEngineItf mEngineInterface = nullptr;
void internalOpen();
void internalClose();
};
}
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
+169 -174
View File
@@ -18,284 +18,279 @@ using namespace Audio;
// -------------------- AndroidEnumerator ----------------------------- // -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator() AndroidEnumerator::AndroidEnumerator() {}
{}
AndroidEnumerator::~AndroidEnumerator() AndroidEnumerator::~AndroidEnumerator() {}
{}
int AndroidEnumerator::indexOfDefaultDevice() int AndroidEnumerator::indexOfDefaultDevice()
{ {
return 0; return 0;
} }
int AndroidEnumerator::count() int AndroidEnumerator::count()
{ {
return 1; return 1;
} }
int AndroidEnumerator::idAt(int index) int AndroidEnumerator::idAt(int index)
{ {
return 0; return 0;
} }
std::string AndroidEnumerator::nameAt(int index) std::string AndroidEnumerator::nameAt(int index)
{ {
return "Audio"; return "Audio";
} }
void AndroidEnumerator::open(int direction) void AndroidEnumerator::open(int direction) {}
{}
void AndroidEnumerator::close() void AndroidEnumerator::close() {}
{}
// --------------- Input implementation ---------------- // --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId) AndroidInputDevice::AndroidInputDevice(int devId) {}
{}
AndroidInputDevice::~AndroidInputDevice() AndroidInputDevice::~AndroidInputDevice()
{ {
close(); close();
} }
bool AndroidInputDevice::open() bool AndroidInputDevice::open()
{ {
if (active()) if (active())
return true; return true;
oboe::AudioStreamBuilder builder; oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Input); builder.setDirection(oboe::Direction::Input);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency); builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.setSharingMode(oboe::SharingMode::Exclusive); builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setFormat(oboe::AudioFormat::I16); builder.setFormat(oboe::AudioFormat::I16);
builder.setChannelCount(oboe::ChannelCount::Mono); builder.setChannelCount(oboe::ChannelCount::Mono);
builder.setCallback(this); builder.setCallback(this);
oboe::Result rescode = builder.openStream(&mRecordingStream); oboe::Result rescode = builder.openStream(&mRecordingStream);
if (rescode != oboe::Result::OK) if (rescode != oboe::Result::OK)
return false; return false;
mDeviceRate = mRecordingStream->getSampleRate(); mDeviceRate = mRecordingStream->getSampleRate();
ICELogInfo(<< "Input Opened with rate " << mDeviceRate); ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
mActive = true; mActive = true;
rescode = mRecordingStream->requestStart(); rescode = mRecordingStream->requestStart();
if (rescode != oboe::Result::OK) if (rescode != oboe::Result::OK)
{ {
close(); close();
mActive = false; mActive = false;
} }
return mActive; return mActive;
} }
void AndroidInputDevice::close() void AndroidInputDevice::close()
{ {
// There is no check for active() value because close() can be called to cleanup after bad open() call. // There is no check for active() value because close() can be called to cleanup after bad open() call.
if (mRecordingStream != nullptr) if (mRecordingStream != nullptr)
{ {
mRecordingStream->close(); mRecordingStream->close();
delete mRecordingStream; mRecordingStream = nullptr; delete mRecordingStream;
} mRecordingStream = nullptr;
mActive = false; }
mActive = false;
} }
oboe::DataCallbackResult oboe::DataCallbackResult AndroidInputDevice::onAudioReady(oboe::AudioStream* audioStream, void* audioData,
AndroidInputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) int32_t numFrames)
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
// Send data to AudioPair // Send data to AudioPair
if (mConnection) if (mConnection)
mConnection->onMicData(getFormat(), audioData, numFrames); mConnection->onMicData(getFormat(), audioData, numFrames);
return oboe::DataCallbackResult::Continue; return oboe::DataCallbackResult::Continue;
} }
Format AndroidInputDevice::getFormat() Format AndroidInputDevice::getFormat()
{ {
return Format(mDeviceRate, 1); return Format(mDeviceRate, 1);
} }
bool AndroidInputDevice::active() const bool AndroidInputDevice::active() const
{ {
return mActive; return mActive;
} }
bool AndroidInputDevice::fakeMode() bool AndroidInputDevice::fakeMode()
{ {
return false; return false;
} }
void AndroidInputDevice::setFakeMode(bool fakemode) void AndroidInputDevice::setFakeMode(bool fakemode) {}
{}
int AndroidInputDevice::readBuffer(void* buffer) int AndroidInputDevice::readBuffer(void* buffer)
{ {
throw std::runtime_error("AndroidInputDevice::readBuffer() is not implemented."); throw std::runtime_error("AndroidInputDevice::readBuffer() is not implemented.");
} }
// ------------ AndroidOutputDevice ----------------- // ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId) AndroidOutputDevice::AndroidOutputDevice(int devId)
{ {
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this)); ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
} }
AndroidOutputDevice::~AndroidOutputDevice() AndroidOutputDevice::~AndroidOutputDevice()
{ {
ICELogDebug(<< "Deleting AndroidOutputDevice."); ICELogDebug(<< "Deleting AndroidOutputDevice.");
// Mark shutdown before closing so a disconnect callback racing with teardown // Mark shutdown before closing so a disconnect callback racing with teardown
// does not resurrect the stream via onErrorAfterClose()'s restart. // does not resurrect the stream via onErrorAfterClose()'s restart.
mInShutdown = true; mInShutdown = true;
close(); close();
} }
bool AndroidOutputDevice::open() bool AndroidOutputDevice::open()
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
if (mActive) if (mActive)
return true; return true;
mInShutdown = false; mInShutdown = false;
mRequestedFrames = 0; mRequestedFrames = 0;
mStartTime = 0.0; mStartTime = 0.0;
mEndTime = 0.0; mEndTime = 0.0;
oboe::AudioStreamBuilder builder; oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Output); builder.setDirection(oboe::Direction::Output);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency); builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.setSharingMode(oboe::SharingMode::Exclusive); builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setFormat(oboe::AudioFormat::I16); builder.setFormat(oboe::AudioFormat::I16);
builder.setChannelCount(oboe::ChannelCount::Mono); builder.setChannelCount(oboe::ChannelCount::Mono);
// Route through the platform voice-call path: correct device selection/volume // Route through the platform voice-call path: correct device selection/volume
// and platform voice tuning for a softphone. // and platform voice tuning for a softphone.
builder.setUsage(oboe::Usage::VoiceCommunication); builder.setUsage(oboe::Usage::VoiceCommunication);
builder.setContentType(oboe::ContentType::Speech); builder.setContentType(oboe::ContentType::Speech);
// builder.setDataCallback(this); // builder.setDataCallback(this);
builder.setCallback(this); builder.setCallback(this);
//builder.setErrorCallback(this) // builder.setErrorCallback(this)
oboe::Result rescode = builder.openStream(&mPlayingStream); oboe::Result rescode = builder.openStream(&mPlayingStream);
if (rescode != oboe::Result::OK) if (rescode != oboe::Result::OK)
return false; return false;
mDeviceRate = mPlayingStream->getSampleRate(); mDeviceRate = mPlayingStream->getSampleRate();
ICELogInfo(<< "Output opened with rate " << mDeviceRate); ICELogInfo(<< "Output opened with rate " << mDeviceRate);
mActive = true; mActive = true;
rescode = mPlayingStream->requestStart(); rescode = mPlayingStream->requestStart();
if (rescode != oboe::Result::OK) if (rescode != oboe::Result::OK)
{ {
close(); close();
mActive = false; mActive = false;
return mActive; return mActive;
} }
// Latch the burst size and start from a two-burst buffer. onAudioReady() grows // Latch the burst size and start from a two-burst buffer. onAudioReady() grows
// this on XRuns (up to a cap) so we keep low latency when the device can sustain // this on XRuns (up to a cap) so we keep low latency when the device can sustain
// it and trade a little latency for glitch-free playback when it can't. // it and trade a little latency for glitch-free playback when it can't.
mBurstFrames = mPlayingStream->getFramesPerBurst(); mBurstFrames = mPlayingStream->getFramesPerBurst();
mXRunLast = 0; mXRunLast = 0;
mHeartbeatLast = 0.0f; mHeartbeatLast = 0.0f;
if (mBurstFrames > 0) if (mBurstFrames > 0)
mPlayingStream->setBufferSizeInFrames(mBurstFrames * 2); mPlayingStream->setBufferSizeInFrames(mBurstFrames * 2);
return mActive; return mActive;
} }
void AndroidOutputDevice::close() void AndroidOutputDevice::close()
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
if (!mActive) if (!mActive)
return; return;
if (mPlayingStream != nullptr) if (mPlayingStream != nullptr)
{ {
mPlayingStream->close(); mPlayingStream->close();
delete mPlayingStream; mPlayingStream = nullptr; delete mPlayingStream;
} mPlayingStream = nullptr;
mEndTime = now_ms(); }
mActive = false; mEndTime = now_ms();
mActive = false;
ICELogInfo(<< "For time " << mEndTime - mStartTime << " ms was requested " ICELogInfo(<< "For time " << mEndTime - mStartTime << " ms was requested "
<< float(mRequestedFrames) / getFormat().mRate * 1000 << " ms"); << float(mRequestedFrames) / getFormat().mRate * 1000 << " ms");
} }
Format AndroidOutputDevice::getFormat() Format AndroidOutputDevice::getFormat()
{ {
return {mDeviceRate, 1}; return {mDeviceRate, 1};
} }
bool AndroidOutputDevice::fakeMode() bool AndroidOutputDevice::fakeMode()
{ {
return false; return false;
} }
void AndroidOutputDevice::setFakeMode(bool /*fakemode*/) void AndroidOutputDevice::setFakeMode(bool /*fakemode*/) {}
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream* audioStream, void* audioData,
int32_t numFrames)
{ {
} if (mInShutdown)
return oboe::DataCallbackResult::Stop;
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) if (mStartTime == 0.0)
{ mStartTime = now_ms();
if (mInShutdown)
return oboe::DataCallbackResult::Stop;
if (mStartTime == 0.0) // Ask producer about data
mStartTime = now_ms(); memset(audioData, 0, numFrames * 2);
if (mConnection)
// Ask producer about data
memset(audioData, 0, numFrames * 2);
if (mConnection)
{
Format f = getFormat();
if (f.mRate != 0)
mConnection->onSpkData(f, audioData, numFrames * 2);
}
mRequestedFrames += numFrames;
// Adaptive buffer sizing: on new XRuns (device-side underruns, i.e. we missed a
// callback deadline), grow the buffer one burst at a time up to a cap. Both calls
// are documented as safe from within the data callback.
auto xrun = audioStream->getXRunCount();
if (xrun && xrun.value() > mXRunLast)
{
mXRunLast = xrun.value();
if (mBurstFrames > 0)
{ {
int32_t cap = mBurstFrames * 8; Format f = getFormat();
int32_t cur = audioStream->getBufferSizeInFrames(); if (f.mRate != 0)
int32_t next = std::min(cur + mBurstFrames, cap); mConnection->onSpkData(f, audioData, numFrames * 2);
if (next > cur)
audioStream->setBufferSizeInFrames(next);
} }
} mRequestedFrames += numFrames;
// Device-side heartbeat (Step 0): surfaces XRuns/buffer growth so device glitches // Adaptive buffer sizing: on new XRuns (device-side underruns, i.e. we missed a
// can be told apart from network/jitter impairments. // callback deadline), grow the buffer one burst at a time up to a cap. Both calls
float t = now_ms(); // are documented as safe from within the data callback.
if (mHeartbeatLast == 0.0f) auto xrun = audioStream->getXRunCount();
mHeartbeatLast = t; if (xrun && xrun.value() > mXRunLast)
else if (t - mHeartbeatLast >= 5000.0f) {
{ mXRunLast = xrun.value();
ICELogInfo(<< "[spk-heartbeat] xruns=" << mXRunLast if (mBurstFrames > 0)
<< " bufFrames=" << audioStream->getBufferSizeInFrames() {
<< " burst=" << mBurstFrames int32_t cap = mBurstFrames * 8;
<< " rate=" << mDeviceRate); int32_t cur = audioStream->getBufferSizeInFrames();
mHeartbeatLast = t; int32_t next = std::min(cur + mBurstFrames, cap);
} if (next > cur)
audioStream->setBufferSizeInFrames(next);
}
}
return oboe::DataCallbackResult::Continue; // Device-side heartbeat (Step 0): surfaces XRuns/buffer growth so device glitches
// can be told apart from network/jitter impairments.
float t = now_ms();
if (mHeartbeatLast == 0.0f)
mHeartbeatLast = t;
else if (t - mHeartbeatLast >= 5000.0f)
{
ICELogInfo(<< "[spk-heartbeat] xruns=" << mXRunLast << " bufFrames=" << audioStream->getBufferSizeInFrames()
<< " burst=" << mBurstFrames << " rate=" << mDeviceRate);
mHeartbeatLast = t;
}
return oboe::DataCallbackResult::Continue;
} }
// Disconnect recovery: on a route change (headset/BT plug/unplug) AAudio tears the // Disconnect recovery: on a route change (headset/BT plug/unplug) AAudio tears the
// stream down and calls this on its own thread after the stream is closed. Rebuild // stream down and calls this on its own thread after the stream is closed. Rebuild
// on the new default route so audio doesn't silently die mid-call. // on the new default route so audio doesn't silently die mid-call.
// See https://github.com/google/oboe/blob/master/docs/notes/disconnect.md // See https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result) { void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream* stream, oboe::Result result)
if (result == oboe::Result::ErrorDisconnected && !mInShutdown) { {
ICELogInfo(<< "Output stream disconnected; restarting on the new route"); if (result == oboe::Result::ErrorDisconnected && !mInShutdown)
// close() and open() each take mMutex internally; this callback holds none. {
close(); ICELogInfo(<< "Output stream disconnected; restarting on the new route");
open(); // close() and open() each take mMutex internally; this callback holds none.
} close();
open();
}
} }
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
+66 -67
View File
@@ -25,89 +25,88 @@
namespace Audio namespace Audio
{ {
class AndroidEnumerator: public Enumerator class AndroidEnumerator : public Enumerator
{ {
public: public:
AndroidEnumerator(); AndroidEnumerator();
~AndroidEnumerator(); ~AndroidEnumerator();
void open(int direction); void open(int direction);
void close(); void close();
int count(); int count();
std::string nameAt(int index); std::string nameAt(int index);
int idAt(int index); int idAt(int index);
int indexOfDefaultDevice(); int indexOfDefaultDevice();
protected: protected:
}; };
class AndroidInputDevice: public InputDevice, public oboe::AudioStreamCallback class AndroidInputDevice : public InputDevice, public oboe::AudioStreamCallback
{ {
public: public:
AndroidInputDevice(int devId); AndroidInputDevice(int devId);
~AndroidInputDevice(); ~AndroidInputDevice();
bool open(); bool open();
void close(); void close();
Format getFormat(); Format getFormat();
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
int readBuffer(void* buffer); int readBuffer(void* buffer);
bool active() const; bool active() const;
oboe::DataCallbackResult oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames);
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
protected: protected:
bool mActive = false; bool mActive = false;
oboe::AudioStream* mRecordingStream = nullptr; oboe::AudioStream* mRecordingStream = nullptr;
PResampler mResampler; PResampler mResampler;
DataWindow mDeviceRateCache, mSdkRateCache; DataWindow mDeviceRateCache, mSdkRateCache;
int mDeviceRate; // Actual rate of opened recorder int mDeviceRate; // Actual rate of opened recorder
int mBufferSize; // Size of buffer used for recording (at native sample rate) int mBufferSize; // Size of buffer used for recording (at native sample rate)
DataWindow mRecorderBuffer; DataWindow mRecorderBuffer;
std::condition_variable mDataCondVar; std::condition_variable mDataCondVar;
int mRecorderBufferIndex; int mRecorderBufferIndex;
std::mutex mMutex; std::mutex mMutex;
}; };
class AndroidOutputDevice: public OutputDevice, public oboe::AudioStreamCallback class AndroidOutputDevice : public OutputDevice, public oboe::AudioStreamCallback
{ {
public: public:
AndroidOutputDevice(int devId); AndroidOutputDevice(int devId);
~AndroidOutputDevice(); ~AndroidOutputDevice();
bool open(); bool open();
void close(); void close();
Format getFormat(); Format getFormat();
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames); oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames);
void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result); void onErrorAfterClose(oboe::AudioStream* stream, oboe::Result result);
protected: protected:
std::mutex mMutex; std::mutex mMutex;
int mDeviceRate = 0; int mDeviceRate = 0;
oboe::AudioStream* mPlayingStream = nullptr; oboe::AudioStream* mPlayingStream = nullptr;
DataWindow mPlayBuffer; DataWindow mPlayBuffer;
int mBufferIndex = 0, mBufferSize = 0; int mBufferIndex = 0, mBufferSize = 0;
bool mInShutdown = false; bool mInShutdown = false;
bool mActive = false; bool mActive = false;
// Adaptive buffer sizing + device-side diagnostics (Step 0 / #5). // Adaptive buffer sizing + device-side diagnostics (Step 0 / #5).
int32_t mBurstFrames = 0; // frames per burst, latched at open() int32_t mBurstFrames = 0; // frames per burst, latched at open()
int32_t mXRunLast = 0; // last observed cumulative XRun count int32_t mXRunLast = 0; // last observed cumulative XRun count
float mHeartbeatLast = 0.0f; // now_ms() of last device heartbeat log float mHeartbeatLast = 0.0f; // now_ms() of last device heartbeat log
// Statistics // Statistics
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0; float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
}; };
} } // namespace Audio
#endif // TARGET_ANDROID #endif // TARGET_ANDROID
File diff suppressed because it is too large Load Diff
+96 -103
View File
@@ -25,69 +25,71 @@
namespace Audio namespace Audio
{ {
class AudioException: public Exception class AudioException : public Exception
{ {
public: public:
AudioException(int code, OSStatus subcode) AudioException(int code, OSStatus subcode) : Exception(code, int(subcode)) {}
:Exception(code, int(subcode))
{}
}; };
//#ifndef AudioDeviceID // #ifndef AudioDeviceID
//# define AudioDeviceID unsigned // # define AudioDeviceID unsigned
//#endif // #endif
class MacEnumerator: public Enumerator class MacEnumerator : public Enumerator
{ {
public: public:
MacEnumerator(); MacEnumerator();
~MacEnumerator(); ~MacEnumerator();
void open(int direction); void open(int direction);
void close(); void close();
int count(); int count();
std::tstring nameAt(int index); std::tstring nameAt(int index);
int idAt(int index); int idAt(int index);
int indexOfDefaultDevice(); int indexOfDefaultDevice();
protected: protected:
struct DeviceInfo struct DeviceInfo
{ {
AudioDeviceID mId; AudioDeviceID mId;
std::string mName; std::string mName;
bool mCanChangeOutputVolume; bool mCanChangeOutputVolume;
bool mCanChangeInputVolume; bool mCanChangeInputVolume;
int mInputCount, mOutputCount; int mInputCount, mOutputCount;
int mDefaultRate; int mDefaultRate;
DeviceInfo(): mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0), mDefaultRate(16000) {} DeviceInfo()
}; : mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0),
std::vector<DeviceInfo> mDeviceList; mDefaultRate(16000)
unsigned mDefaultInput, mDefaultOutput; {
int mDirection; }
void getInfo(DeviceInfo& di); };
std::vector<DeviceInfo> mDeviceList;
unsigned mDefaultInput, mDefaultOutput;
int mDirection;
void getInfo(DeviceInfo& di);
}; };
class CoreAudioUnit class CoreAudioUnit
{ {
public: public:
CoreAudioUnit(); CoreAudioUnit();
~CoreAudioUnit(); ~CoreAudioUnit();
void open(bool voice); void open(bool voice);
void close(); void close();
AudioStreamBasicDescription getFormat(int scope, int bus); AudioStreamBasicDescription getFormat(int scope, int bus);
void setFormat(AudioStreamBasicDescription& format, int scope, int bus); void setFormat(AudioStreamBasicDescription& format, int scope, int bus);
bool getEnabled(int scope, int bus); bool getEnabled(int scope, int bus);
void setEnabled(bool enabled, int scope, int bus); void setEnabled(bool enabled, int scope, int bus);
void makeCurrent(AudioDeviceID deviceId, int scope, int bus); void makeCurrent(AudioDeviceID deviceId, int scope, int bus);
void setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus); void setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus);
void setBufferFrameSizeInMilliseconds(int ms); void setBufferFrameSizeInMilliseconds(int ms);
int getBufferFrameSize(); int getBufferFrameSize();
void initialize(); void initialize();
AudioUnit getHandle(); AudioUnit getHandle();
protected: protected:
AudioUnit mUnit; AudioUnit mUnit;
}; };
class MacDevice class MacDevice
@@ -96,101 +98,92 @@ public:
MacDevice(int devId); MacDevice(int devId);
~MacDevice(); ~MacDevice();
bool open(); bool open();
void close(); void close();
void setRender(bool render); void setRender(bool render);
void setCapture(bool capture); void setCapture(bool capture);
int getId(); int getId();
Format getFormat(); Format getFormat();
DataConnection* connection(); DataConnection* connection();
void setConnection(DataConnection* c); void setConnection(DataConnection* c);
void provideAudioToSpeaker(int channels, void* buffer, int length); void provideAudioToSpeaker(int channels, void* buffer, int length);
void obtainAudioFromMic(int channels, const void* buffer, int length); void obtainAudioFromMic(int channels, const void* buffer, int length);
protected: protected:
AudioDeviceID mDeviceId; AudioDeviceID mDeviceId;
bool mCapture, mRender; bool mCapture, mRender;
bool mActive; bool mActive;
int mUsageCount; int mUsageCount;
Mutex mGuard; Mutex mGuard;
CoreAudioUnit mAudioUnit; CoreAudioUnit mAudioUnit;
AudioComponent mComponent; AudioComponent mComponent;
AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat, mStreamFormat; AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat,
mStreamFormat;
AudioBufferList* mInputBufferList; AudioBufferList* mInputBufferList;
DataConnection* mConnection; DataConnection* mConnection;
SpeexResampler mCaptureResampler, mRenderResampler; SpeexResampler mCaptureResampler, mRenderResampler;
ByteBuffer mTail; ByteBuffer mTail;
DataWindow mInputBuffer, mOutputBuffer; DataWindow mInputBuffer, mOutputBuffer;
bool createUnit(bool voice); bool createUnit(bool voice);
void destroyUnit(); void destroyUnit();
void startStream(); void startStream();
void stopStream(); void stopStream();
void setupStreamFormat(); void setupStreamFormat();
bool createResampleUnit(AudioStreamBasicDescription format); bool createResampleUnit(AudioStreamBasicDescription format);
static OSStatus outputCallback( void *inRefCon, static OSStatus outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
const AudioTimeStamp *inTimeStamp, AudioBufferList* ioData);
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData );
static OSStatus inputCallback(void *inRefCon, static OSStatus inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
const AudioTimeStamp *inTimeStamp, AudioBufferList* ioData);
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
#ifdef TARGET_IOS #ifdef TARGET_IOS
static void propListener(void *inClientData, static void propListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inData);
AudioSessionPropertyID inID, static void interruptionListener(void* inClientData, UInt32 inInterruption);
UInt32 inDataSize,
const void * inData);
static void interruptionListener(void *inClientData, UInt32 inInterruption);
#endif #endif
}; };
typedef std::shared_ptr<MacDevice> PMacDevice; typedef std::shared_ptr<MacDevice> PMacDevice;
class MacInputDevice: public InputDevice class MacInputDevice : public InputDevice
{ {
public: public:
MacInputDevice(int devId); MacInputDevice(int devId);
~MacInputDevice(); ~MacInputDevice();
bool open(); bool open();
void close(); void close();
Format getFormat(); Format getFormat();
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
int readBuffer(void* buffer); int readBuffer(void* buffer);
protected: protected:
PMacDevice mDevice; PMacDevice mDevice;
}; };
class MacOutputDevice: public OutputDevice class MacOutputDevice : public OutputDevice
{ {
public: public:
MacOutputDevice(int devId); MacOutputDevice(int devId);
~MacOutputDevice(); ~MacOutputDevice();
bool open(); bool open();
void close(); void close();
Format getFormat(); Format getFormat();
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
protected: protected:
PMacDevice mDevice; PMacDevice mDevice;
}; };
} } // namespace Audio
#endif // TARGET_OSX #endif // TARGET_OSX
+9 -10
View File
@@ -8,8 +8,7 @@
using namespace Audio; using namespace Audio;
DataWindow::DataWindow() DataWindow::DataWindow() {}
{}
DataWindow::~DataWindow() DataWindow::~DataWindow()
{ {
@@ -28,8 +27,8 @@ void DataWindow::setCapacity(size_t capacity)
if (capacity <= mCapacity) if (capacity <= mCapacity)
return; return;
size_t tail = capacity - mCapacity; size_t tail = capacity - mCapacity;
char* buffer = mData; char* buffer = mData;
mData = (char*)realloc(mData, capacity); mData = (char*)realloc(mData, capacity);
if (!mData) if (!mData)
{ {
@@ -145,7 +144,7 @@ size_t DataWindow::read(void* buffer, size_t length)
if (buffer) if (buffer)
memcpy(buffer, mData, length); memcpy(buffer, mData, length);
if (length < mFilled) if (length < mFilled)
memmove(mData, mData+length, mFilled - length); memmove(mData, mData + length, mFilled - length);
mFilled -= length; mFilled -= length;
} }
return length; return length;
@@ -181,7 +180,7 @@ void DataWindow::zero(size_t length)
size_t DataWindow::moveTo(DataWindow& dst, size_t size) size_t DataWindow::moveTo(DataWindow& dst, size_t size)
{ {
Lock l(mMutex); Lock l(mMutex);
size_t avail = std::min(size, (size_t)filled()); size_t avail = std::min(size, (size_t)filled());
if (avail != 0) if (avail != 0)
@@ -195,18 +194,18 @@ size_t DataWindow::moveTo(DataWindow& dst, size_t size)
std::chrono::milliseconds DataWindow::getTimeLength(const Audio::Format& fmt) const std::chrono::milliseconds DataWindow::getTimeLength(const Audio::Format& fmt) const
{ {
Lock l(mMutex); Lock l(mMutex);
return std::chrono::milliseconds(mFilled / sizeof(short) / fmt.channels() / (fmt.rate()/ 1000)); return std::chrono::milliseconds(mFilled / sizeof(short) / fmt.channels() / (fmt.rate() / 1000));
} }
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src) void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
{ {
Lock lockDst(dst.mMutex), lockSrc(src.mMutex); Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
dst.setCapacity(src.filled()*2); dst.setCapacity(src.filled() * 2);
short* input = (short*)src.mutableData(); short* input = (short*)src.mutableData();
short* output = (short*)dst.mutableData(); short* output = (short*)dst.mutableData();
for (int i=0; i<src.filled()/2; i++) for (int i = 0; i < src.filled() / 2; i++)
output[i*2] = output[i*2+1] = input[i]; output[i * 2] = output[i * 2 + 1] = input[i];
dst.mFilled = src.filled() * 2; dst.mFilled = src.filled() * 2;
} }
+21 -21
View File
@@ -18,34 +18,34 @@ public:
DataWindow(); DataWindow();
~DataWindow(); ~DataWindow();
void setCapacity(size_t capacity); void setCapacity(size_t capacity);
size_t capacity() const; size_t capacity() const;
void addZero(size_t length); void addZero(size_t length);
void add(const void* data, size_t length); void add(const void* data, size_t length);
void add(short sample); void add(short sample);
size_t read(void* buffer, size_t length); size_t read(void* buffer, size_t length);
void erase(size_t length); void erase(size_t length);
const char* data() const; const char* data() const;
char* mutableData(); char* mutableData();
size_t filled() const; size_t filled() const;
void setFilled(size_t filled); void setFilled(size_t filled);
void clear(); void clear();
short shortAt(size_t index) const; short shortAt(size_t index) const;
void setShortAt(short value, size_t index); void setShortAt(short value, size_t index);
void zero(size_t length); void zero(size_t length);
size_t moveTo(DataWindow& dst, size_t size /* in bytes*/ ); size_t moveTo(DataWindow& dst, size_t size /* in bytes*/);
std::chrono::milliseconds getTimeLength(const Format& fmt) const; std::chrono::milliseconds getTimeLength(const Format& fmt) const;
static void makeStereoFromMono(DataWindow& dst, DataWindow& src); static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
protected: protected:
mutable Mutex mMutex; mutable Mutex mMutex;
char* mData = nullptr; char* mData = nullptr;
size_t mFilled = 0; size_t mFilled = 0;
size_t mCapacity = 0; size_t mCapacity = 0;
}; };
} } // namespace Audio
#endif #endif
+162 -166
View File
@@ -13,278 +13,274 @@ using namespace Audio;
// --- DevicePair --- // --- DevicePair ---
DevicePair::DevicePair() DevicePair::DevicePair()
:mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS), : mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false),
mMonitoring(nullptr) mAecFilter(AUDIO_MIC_BUFFER_LENGTH * 10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS),
mMonitoring(nullptr)
{ {
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1)); mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1)); mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
mInputResampingData.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1)); mInputResampingData.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
mOutput10msBuffer.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH)); mOutput10msBuffer.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH));
mOutputNativeData.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH * AUDIO_SPK_BUFFER_COUNT * 24)); mOutputNativeData.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH * AUDIO_SPK_BUFFER_COUNT * 24));
} }
DevicePair::~DevicePair() DevicePair::~DevicePair()
{ {
if (mInput) if (mInput)
{ {
if (mInput->connection() == this) if (mInput->connection() == this)
mInput->setConnection(nullptr); mInput->setConnection(nullptr);
mInput.reset(); mInput.reset();
} }
if (mOutput) if (mOutput)
{ {
if (mOutput->connection() == this) if (mOutput->connection() == this)
mOutput->setConnection(nullptr); mOutput->setConnection(nullptr);
mOutput.reset(); mOutput.reset();
} }
} }
DevicePair& DevicePair::setAec(bool aec) DevicePair& DevicePair::setAec(bool aec)
{ {
mAec = aec; mAec = aec;
return *this; return *this;
} }
bool DevicePair::aec() bool DevicePair::aec()
{ {
return mAec; return mAec;
} }
DevicePair& DevicePair::setAgc(bool agc) DevicePair& DevicePair::setAgc(bool agc)
{ {
mAgc = agc; mAgc = agc;
return *this; return *this;
} }
bool DevicePair::agc() bool DevicePair::agc()
{ {
return mAgc; return mAgc;
} }
VariantMap* DevicePair::config() VariantMap* DevicePair::config()
{ {
return mConfig; return mConfig;
} }
DevicePair& DevicePair::setConfig(VariantMap* config) DevicePair& DevicePair::setConfig(VariantMap* config)
{ {
mConfig = config; mConfig = config;
return *this; return *this;
} }
PInputDevice DevicePair::input() PInputDevice DevicePair::input()
{ {
return mInput; return mInput;
} }
DevicePair& DevicePair::setInput(PInputDevice input) DevicePair& DevicePair::setInput(PInputDevice input)
{ {
if (mInput == input) if (mInput == input)
return *this;
mInput = input;
mInput->setConnection(this);
if (mDelegate)
mDelegate->deviceChanged(this);
return *this; return *this;
mInput = input;
mInput->setConnection(this);
if (mDelegate)
mDelegate->deviceChanged(this);
return *this;
} }
POutputDevice DevicePair::output() POutputDevice DevicePair::output()
{ {
return mOutput; return mOutput;
} }
DevicePair& DevicePair::setOutput(POutputDevice output) DevicePair& DevicePair::setOutput(POutputDevice output)
{ {
if (output == mOutput) if (output == mOutput)
return *this;
mOutput = output;
mOutput->setConnection(this);
if (mDelegate)
mDelegate->deviceChanged(this);
return *this; return *this;
mOutput = output;
mOutput->setConnection(this);
if (mDelegate)
mDelegate->deviceChanged(this);
return *this;
} }
bool DevicePair::start() bool DevicePair::start()
{ {
bool result = false; bool result = false;
if (mInput) if (mInput)
result = mInput->open(); result = mInput->open();
if (mOutput && result) if (mOutput && result)
result &= mOutput->open(); result &= mOutput->open();
return result; return result;
} }
void DevicePair::stop() void DevicePair::stop()
{ {
if (mInput) if (mInput)
mInput->close(); mInput->close();
if (mOutput) if (mOutput)
mOutput->close(); mOutput->close();
} }
DevicePair& DevicePair::setDelegate(Delegate* dc) DevicePair& DevicePair::setDelegate(Delegate* dc)
{ {
mDelegate = dc; mDelegate = dc;
return *this; return *this;
} }
DevicePair::Delegate* DevicePair::delegate() DevicePair::Delegate* DevicePair::delegate()
{ {
return mDelegate; return mDelegate;
} }
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring) DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
{ {
mMonitoring = monitoring; mMonitoring = monitoring;
return *this; return *this;
} }
DataConnection* DevicePair::monitoring() DataConnection* DevicePair::monitoring()
{ {
return mMonitoring; return mMonitoring;
} }
Player& DevicePair::player() Player& DevicePair::player()
{ {
return mPlayer; return mPlayer;
} }
void DevicePair::onMicData(const Format& f, const void* buffer, int length) void DevicePair::onMicData(const Format& f, const void* buffer, int length)
{ {
#ifdef DUMP_NATIVEINPUT #ifdef DUMP_NATIVEINPUT
if (!mNativeInputDump) if (!mNativeInputDump)
{ {
mNativeInputDump = std::make_shared<WavFileWriter>(); mNativeInputDump = std::make_shared<WavFileWriter>();
mNativeInputDump->open("nativeinput.wav", f.mRate, f.mChannels); mNativeInputDump->open("nativeinput.wav", f.mRate, f.mChannels);
} }
if (mNativeInputDump) if (mNativeInputDump)
mNativeInputDump->write(buffer, length); mNativeInputDump->write(buffer, length);
#endif #endif
// send the data to internal queue - it can hold data which were not processed by resampler in last call // send the data to internal queue - it can hold data which were not processed by resampler in last call
mInputResampingData.add(buffer, length); mInputResampingData.add(buffer, length);
// split processing by blocks // split processing by blocks
int blocks = mInputResampingData.filled() / (int)f.sizeFromTime(AUDIO_MIC_BUFFER_LENGTH); int blocks = mInputResampingData.filled() / (int)f.sizeFromTime(AUDIO_MIC_BUFFER_LENGTH);
for (int blockIndex = 0; blockIndex < blocks; blockIndex++) for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
{ {
size_t wasProcessed = 0;
size_t wasProcessed = 0; size_t wasProduced = mMicResampler.resample(f.mRate, // Source rate
mInputResampingData.data(), // Source data
(int)f.sizeFromTime(AUDIO_MIC_BUFFER_LENGTH), // Source size
wasProcessed,
AUDIO_SAMPLERATE, // Dest rate
mInputBuffer.mutableData() + mInputBuffer.filled(),
mInputBuffer.capacity() - mInputBuffer.filled());
size_t wasProduced = mMicResampler.resample(f.mRate, // Source rate mInputBuffer.setFilled(mInputBuffer.filled() + wasProduced);
mInputResampingData.data(), // Source data mInputResampingData.erase((int)f.sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
(int)f.sizeFromTime(AUDIO_MIC_BUFFER_LENGTH), // Source size processMicData(Format(), mInputBuffer.mutableData(), (int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
wasProcessed,
AUDIO_SAMPLERATE, // Dest rate
mInputBuffer.mutableData() + mInputBuffer.filled(),
mInputBuffer.capacity() - mInputBuffer.filled());
mInputBuffer.setFilled(mInputBuffer.filled() + wasProduced);
mInputResampingData.erase((int)f.sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
processMicData(Format(), mInputBuffer.mutableData(), (int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
mInputBuffer.erase((int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
}
mInputBuffer.erase((int)Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
}
} }
void DevicePair::onSpkData(const Format& f, void* buffer, int length) void DevicePair::onSpkData(const Format& f, void* buffer, int length)
{ {
//ICELogMedia(<< "Audio::DevicePair::onSpkData() begin"); // ICELogMedia(<< "Audio::DevicePair::onSpkData() begin");
#ifdef DUMP_NATIVEOUTPUT #ifdef DUMP_NATIVEOUTPUT
if (!mNativeOutputDump) if (!mNativeOutputDump)
{
mNativeOutputDump = std::make_shared<WavFileWriter>();
mNativeOutputDump->open("nativeoutput.wav", f.mRate, f.mChannels);
}
#endif
#ifdef CONSOLE_LOGGING
printf("Speaker requests %d\n", length);
#endif
Format nativeFormat = mOutput->getFormat();
// See how much bytes are needed yet - mOutputNativeData can contain some data already
int required = length - mOutputNativeData.filled();
if (required > 0)
{
// Find how much blocks must be received from RTP/decoder side
int nativeBufferSize = (int)nativeFormat.sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
int blocks = required / nativeBufferSize;
if (required % nativeBufferSize)
blocks++;
// Now request data from terminal or whetever delegate is
for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
{ {
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity()); mNativeOutputDump = std::make_shared<WavFileWriter>();
mNativeOutputDump->open("nativeoutput.wav", f.mRate, f.mChannels);
// Ask audio data on main AUDIO_SAMPLERATE frequency
if (mDelegate)
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
// Replace received data with custom file or data playing
mPlayer.onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
// Save it to process with AEC
if (mAec)
mAecSpkBuffer.add(mOutput10msBuffer.data(), mOutput10msBuffer.capacity());
// Resample these 10 milliseconds it to native format
size_t wasProcessed = 0;
size_t wasProduced = mSpkResampler.resample(Format().mRate,
mOutput10msBuffer.data(),
mOutput10msBuffer.capacity(),
wasProcessed, f.mRate,
mOutputNativeData.mutableData() + mOutputNativeData.filled(),
mOutputNativeData.capacity() - mOutputNativeData.filled());
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
#ifdef CONSOLE_LOGGING
printf("Resampled %d to %d\n", wasProcessed, wasProduced);
#endif
} }
} #endif
#ifdef CONSOLE_LOGGING
// assert(mOutputNativeData.filled() >= length); printf("Speaker requests %d\n", length);
#ifdef DUMP_NATIVEOUTPUT
if (mNativeOutputDump)
mNativeOutputDump->write(mOutputNativeData.data(), length);
#endif #endif
mOutputNativeData.read(buffer, length); Format nativeFormat = mOutput->getFormat();
// See how much bytes are needed yet - mOutputNativeData can contain some data already
int required = length - mOutputNativeData.filled();
if (required > 0)
{
// Find how much blocks must be received from RTP/decoder side
int nativeBufferSize = (int)nativeFormat.sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
int blocks = required / nativeBufferSize;
if (required % nativeBufferSize)
blocks++;
// Send data to monitoring if needed // Now request data from terminal or whetever delegate is
if (mMonitoring) for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
mMonitoring->onSpkData(f, buffer, length); {
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short)) // Ask audio data on main AUDIO_SAMPLERATE frequency
if (mDelegate)
mDelegate->onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
// AEC filter wants frames. // Replace received data with custom file or data playing
if (mAec) mPlayer.onSpkData(Format(), mOutput10msBuffer.mutableData(), mOutput10msBuffer.capacity());
{
int nrOfFrames = mAecSpkBuffer.filled() / AEC_FRAME_SIZE; // Save it to process with AEC
for (int frameIndex=0; frameIndex < nrOfFrames; frameIndex++) if (mAec)
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex); mAecSpkBuffer.add(mOutput10msBuffer.data(), mOutput10msBuffer.capacity());
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
} // Resample these 10 milliseconds it to native format
size_t wasProcessed = 0;
size_t wasProduced = mSpkResampler.resample(Format().mRate, mOutput10msBuffer.data(),
mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
mOutputNativeData.mutableData() + mOutputNativeData.filled(),
mOutputNativeData.capacity() - mOutputNativeData.filled());
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
#ifdef CONSOLE_LOGGING
printf("Resampled %d to %d\n", wasProcessed, wasProduced);
#endif
}
}
// assert(mOutputNativeData.filled() >= length);
#ifdef DUMP_NATIVEOUTPUT
if (mNativeOutputDump)
mNativeOutputDump->write(mOutputNativeData.data(), length);
#endif
mOutputNativeData.read(buffer, length);
// Send data to monitoring if needed
if (mMonitoring)
mMonitoring->onSpkData(f, buffer, length);
#define AEC_FRAME_SIZE (AUDIO_CHANNELS * (AUDIO_SAMPLERATE / 1000) * AEC_FRAME_TIME * sizeof(short))
// AEC filter wants frames.
if (mAec)
{
int nrOfFrames = mAecSpkBuffer.filled() / AEC_FRAME_SIZE;
for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++)
mAecFilter.toSpeaker(mAecSpkBuffer.mutableData() + AEC_FRAME_SIZE * frameIndex);
mAecSpkBuffer.erase(nrOfFrames * AEC_FRAME_SIZE);
}
} }
void DevicePair::processMicData(const Format& f, void* buffer, int length) void DevicePair::processMicData(const Format& f, void* buffer, int length)
{ {
if (mAgc) if (mAgc)
mAgcFilter.process(buffer, length); mAgcFilter.process(buffer, length);
if (mAec) if (mAec)
mAecFilter.fromMic(buffer); mAecFilter.fromMic(buffer);
if (mDelegate) if (mDelegate)
mDelegate->onMicData(f, buffer, length); mDelegate->onMicData(f, buffer, length);
} }
+36 -36
View File
@@ -11,59 +11,59 @@
#include "Audio_Resampler.h" #include "Audio_Resampler.h"
#include "Audio_DataWindow.h" #include "Audio_DataWindow.h"
//#define DUMP_NATIVEOUTPUT // #define DUMP_NATIVEOUTPUT
//#define DUMP_NATIVEINPUT // #define DUMP_NATIVEINPUT
namespace Audio namespace Audio
{ {
class DevicePair: protected DataConnection class DevicePair : protected DataConnection
{ {
public: public:
class Delegate: public DataConnection class Delegate : public DataConnection
{ {
public: public:
virtual void deviceChanged(DevicePair* dpair) = 0; virtual void deviceChanged(DevicePair* dpair) = 0;
}; };
DevicePair(); DevicePair();
virtual ~DevicePair(); virtual ~DevicePair();
DevicePair& setAec(bool aec); DevicePair& setAec(bool aec);
bool aec(); bool aec();
DevicePair& setAgc(bool agc); DevicePair& setAgc(bool agc);
bool agc(); bool agc();
VariantMap* config(); VariantMap* config();
DevicePair& setConfig(VariantMap* config); DevicePair& setConfig(VariantMap* config);
PInputDevice input(); PInputDevice input();
DevicePair& setInput(PInputDevice input); DevicePair& setInput(PInputDevice input);
POutputDevice output(); POutputDevice output();
DevicePair& setOutput(POutputDevice output); DevicePair& setOutput(POutputDevice output);
bool start(); bool start();
void stop(); void stop();
DevicePair& setDelegate(Delegate* dc); DevicePair& setDelegate(Delegate* dc);
Delegate* delegate(); Delegate* delegate();
DevicePair& setMonitoring(DataConnection* monitoring); DevicePair& setMonitoring(DataConnection* monitoring);
DataConnection* monitoring(); DataConnection* monitoring();
Player& player(); Player& player();
protected: protected:
VariantMap* mConfig; VariantMap* mConfig;
PInputDevice mInput; PInputDevice mInput;
POutputDevice mOutput; POutputDevice mOutput;
Delegate* mDelegate; Delegate* mDelegate;
bool mAec; bool mAec;
bool mAgc; bool mAgc;
AgcFilter mAgcFilter; AgcFilter mAgcFilter;
AecFilter mAecFilter; AecFilter mAecFilter;
Player mPlayer; Player mPlayer;
UniversalResampler mMicResampler, mSpkResampler; UniversalResampler mMicResampler, mSpkResampler;
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer; DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
DataConnection* mMonitoring; DataConnection* mMonitoring;
@@ -77,9 +77,9 @@ namespace Audio
void onMicData(const Format& f, const void* buffer, int length); void onMicData(const Format& f, const void* buffer, int length);
void onSpkData(const Format& f, void* buffer, int length); void onSpkData(const Format& f, void* buffer, int length);
void processMicData(const Format& f, void* buffer, int length); void processMicData(const Format& f, void* buffer, int length);
}; };
typedef std::shared_ptr<DevicePair> PDevicePair; typedef std::shared_ptr<DevicePair> PDevicePair;
} } // namespace Audio
#endif #endif
+142 -159
View File
@@ -19,7 +19,7 @@
#define DRVM_MAPPER_CONSOLEVOICECOM_GET (0x2000 + 23) #define DRVM_MAPPER_CONSOLEVOICECOM_GET (0x2000 + 23)
#define DRVM_MAPPER_PREFERRED_GET (0x2000 + 21) #define DRVM_MAPPER_PREFERRED_GET (0x2000 + 21)
#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) #define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) #define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)
#define LOG_SUBSYSTEM "audio" #define LOG_SUBSYSTEM "audio"
@@ -39,32 +39,29 @@ public:
{ {
HINSTANCE mInstance; HINSTANCE mInstance;
HRESULT (WINAPI *DirectSoundCreate8)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN); HRESULT(WINAPI* DirectSoundCreate8)(LPGUID, LPDIRECTSOUND8*, LPUNKNOWN);
HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); HRESULT(WINAPI* DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID); HRESULT(WINAPI* DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
HRESULT (WINAPI *DirectSoundCaptureCreate8)(LPGUID, LPDIRECTSOUNDCAPTURE8* , LPUNKNOWN); HRESULT(WINAPI* DirectSoundCaptureCreate8)(LPGUID, LPDIRECTSOUNDCAPTURE8*, LPUNKNOWN);
HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); HRESULT(WINAPI* DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID); HRESULT(WINAPI* DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
HRESULT (WINAPI *GetDeviceID)(LPCGUID src, LPGUID dst); HRESULT(WINAPI* GetDeviceID)(LPCGUID src, LPGUID dst);
} mRoutines; } mRoutines;
protected: protected:
LPDIRECTSOUND mDirectSound; LPDIRECTSOUND mDirectSound;
Mutex mGuard; Mutex mGuard;
unsigned int mRefCount; unsigned int mRefCount;
}; };
DSoundInit gDSoundInit; DSoundInit gDSoundInit;
DSoundInit::DSoundInit() DSoundInit::DSoundInit() : mRefCount(0) {}
:mRefCount(0)
{
}
DSoundInit::~DSoundInit() DSoundInit::~DSoundInit()
{ {
//Unload(); // Unload();
} }
void DSoundInit::load() void DSoundInit::load()
@@ -77,16 +74,21 @@ void DSoundInit::load()
hr = ::CoInitialize(NULL); hr = ::CoInitialize(NULL);
//load the DirectSound DLL // load the DirectSound DLL
mRoutines.mInstance = ::LoadLibraryW(L"dsound.dll"); mRoutines.mInstance = ::LoadLibraryW(L"dsound.dll");
if (!mRoutines.mInstance) if (!mRoutines.mInstance)
throw std::logic_error("Cannot load dsound.dll"); throw std::logic_error("Cannot load dsound.dll");
mRoutines.DirectSoundCaptureCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureCreate8"); mRoutines.DirectSoundCaptureCreate8 =
mRoutines.DirectSoundCaptureEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureEnumerateW"); (HRESULT(WINAPI*)(LPGUID, LPDIRECTSOUNDCAPTURE8*, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance,
mRoutines.DirectSoundCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCreate8"); "DirectSoundCaptureCreate8");
mRoutines.DirectSoundEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW"); mRoutines.DirectSoundCaptureEnumerateW = (HRESULT(WINAPI*)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(
mRoutines.GetDeviceID = (HRESULT (WINAPI*) (LPCGUID, LPGUID)) GetProcAddress(mRoutines.mInstance, "GetDeviceID"); mRoutines.mInstance, "DirectSoundCaptureEnumerateW");
mRoutines.DirectSoundCreate8 = (HRESULT(WINAPI*)(LPGUID, LPDIRECTSOUND8*, LPUNKNOWN))::GetProcAddress(
mRoutines.mInstance, "DirectSoundCreate8");
mRoutines.DirectSoundEnumerateW =
(HRESULT(WINAPI*)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW");
mRoutines.GetDeviceID = (HRESULT(WINAPI*)(LPCGUID, LPGUID))GetProcAddress(mRoutines.mInstance, "GetDeviceID");
} }
} }
@@ -106,10 +108,7 @@ void DSoundInit::unload()
} }
// --------------- VistaEnumerator --------------------- // --------------- VistaEnumerator ---------------------
VistaEnumerator::VistaEnumerator() VistaEnumerator::VistaEnumerator() : mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture) {}
:mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture)
{
}
VistaEnumerator::~VistaEnumerator() VistaEnumerator::~VistaEnumerator()
{ {
@@ -119,14 +118,12 @@ VistaEnumerator::~VistaEnumerator()
void VistaEnumerator::open(int direction) void VistaEnumerator::open(int direction)
{ {
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
mDirection = (direction == myMicrophone) ? eCapture : eRender; mDirection = (direction == myMicrophone) ? eCapture : eRender;
HRESULT hr = CoCreateInstance( HRESULT hr =
CLSID_MMDeviceEnumerator, NULL, CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mEnumerator);
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&mEnumerator);
if (!mEnumerator) if (!mEnumerator)
return; return;
@@ -152,7 +149,7 @@ void VistaEnumerator::close()
if (mDefaultDevice) if (mDefaultDevice)
{ {
//mDefaultDevice->Release(); // mDefaultDevice->Release();
mDefaultDevice = NULL; mDefaultDevice = NULL;
} }
@@ -162,7 +159,7 @@ void VistaEnumerator::close()
mEnumerator = NULL; mEnumerator = NULL;
} }
} }
catch(...) catch (...)
{ {
} }
} }
@@ -175,9 +172,9 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
if (index == -1) if (index == -1)
return mDefaultDevice; return mDefaultDevice;
size_t idSize = 0; size_t idSize = 0;
MMRESULT mmres = 0; MMRESULT mmres = 0;
WCHAR* id = NULL; WCHAR* id = NULL;
if (mDirection == eCapture) if (mDirection == eCapture)
{ {
mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEIDSIZE, (DWORD_PTR)&idSize, NULL); mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEIDSIZE, (DWORD_PTR)&idSize, NULL);
@@ -185,7 +182,7 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
if (mmres != MMSYSERR_NOERROR) if (mmres != MMSYSERR_NOERROR)
return NULL; return NULL;
id = (WCHAR*)_alloca(idSize*sizeof(WCHAR)); id = (WCHAR*)_alloca(idSize * sizeof(WCHAR));
mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize); mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize);
} }
else else
@@ -195,7 +192,7 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
if (mmres != MMSYSERR_NOERROR) if (mmres != MMSYSERR_NOERROR)
return NULL; return NULL;
id = (WCHAR*)_alloca(idSize*sizeof(WCHAR)); id = (WCHAR*)_alloca(idSize * sizeof(WCHAR));
mmres = waveOutMessage((HWAVEOUT)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize); mmres = waveOutMessage((HWAVEOUT)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize);
} }
@@ -213,7 +210,7 @@ void VistaEnumerator::enumerate()
mNameList.clear(); mNameList.clear();
int res = (int)count(); int res = (int)count();
for (int i=0; i<res; i++) for (int i = 0; i < res; i++)
{ {
IMMDevice* dev = mapIndexToInterface(i); IMMDevice* dev = mapIndexToInterface(i);
if (dev) if (dev)
@@ -258,26 +255,23 @@ int VistaEnumerator::indexOfDefaultDevice()
if (mDirection == mySpeaker) if (mDirection == mySpeaker)
{ {
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR) if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
else else
{ {
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR) if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
return devID; return devID;
} }
// -------------- XpEnumerator --------------- // -------------- XpEnumerator ---------------
XpEnumerator::XpEnumerator() XpEnumerator::XpEnumerator() : mDirection(-1) {}
:mDirection(-1)
{
}
XpEnumerator::~XpEnumerator() XpEnumerator::~XpEnumerator() {}
{
}
void XpEnumerator::open(int direction) void XpEnumerator::open(int direction)
{ {
@@ -285,7 +279,7 @@ void XpEnumerator::open(int direction)
if (direction == myMicrophone) if (direction == myMicrophone)
{ {
int count = waveInGetNumDevs(); int count = waveInGetNumDevs();
for (int i=0; i<count; i++) for (int i = 0; i < count; i++)
{ {
WAVEINCAPSW caps; WAVEINCAPSW caps;
if (waveInGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR) if (waveInGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR)
@@ -297,7 +291,7 @@ void XpEnumerator::open(int direction)
else else
{ {
int count = waveOutGetNumDevs(); int count = waveOutGetNumDevs();
for (int i=0; i<count; i++) for (int i = 0; i < count; i++)
{ {
WAVEOUTCAPSW caps; WAVEOUTCAPSW caps;
if (waveOutGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR) if (waveOutGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR)
@@ -308,11 +302,9 @@ void XpEnumerator::open(int direction)
} }
} }
void XpEnumerator::close() void XpEnumerator::close() {}
{
}
int XpEnumerator::count() int XpEnumerator::count()
{ {
return mNameList.size(); return mNameList.size();
} }
@@ -333,12 +325,14 @@ int XpEnumerator::indexOfDefaultDevice()
if (mDirection == mySpeaker) if (mDirection == mySpeaker)
{ {
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR) if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
else else
{ {
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR) if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID,
(DWORD_PTR)&status) != MMSYSERR_NOERROR)
waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status);
} }
return devID; return devID;
@@ -346,11 +340,12 @@ int XpEnumerator::indexOfDefaultDevice()
// -------- DSoundInputDevice --------------- // -------- DSoundInputDevice ---------------
DSoundInputDevice::DSoundInputDevice(GUID deviceId) DSoundInputDevice::DSoundInputDevice(GUID deviceId)
:mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE), mEnableDenoiser(true), : mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE),
mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT) mEnableDenoiser(true), mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT)
#ifdef AUDIO_DUMPINPUT #ifdef AUDIO_DUMPINPUT
,mDump(AUDIO_SAMPLERATE) ,
#endif mDump(AUDIO_SAMPLERATE)
#endif
{ {
gDSoundInit.load(); gDSoundInit.load();
@@ -359,7 +354,7 @@ DSoundInputDevice::DSoundInputDevice(GUID deviceId)
mDevice = NULL; mDevice = NULL;
mBuffer = NULL; mBuffer = NULL;
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
for (unsigned i=0; i<AUDIO_MIC_BUFFER_COUNT; i++) for (unsigned i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
{ {
mEventArray[i].dwOffset = (i + 1) * AUDIO_MIC_BUFFER_SIZE - 1; mEventArray[i].dwOffset = (i + 1) * AUDIO_MIC_BUFFER_SIZE - 1;
mEventSignals[i] = mEventArray[i].hEventNotify = ::CreateEvent(NULL, TRUE, FALSE, NULL); mEventSignals[i] = mEventArray[i].hEventNotify = ::CreateEvent(NULL, TRUE, FALSE, NULL);
@@ -371,7 +366,7 @@ DSoundInputDevice::~DSoundInputDevice()
{ {
close(); close();
::CloseHandle(mShutdownSignal); ::CloseHandle(mShutdownSignal);
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
::CloseHandle(mEventArray[i].hEventNotify); ::CloseHandle(mEventArray[i].hEventNotify);
gDSoundInit.unload(); gDSoundInit.unload();
@@ -388,14 +383,13 @@ bool DSoundInputDevice::isSimulate() const
} }
void DSoundInputDevice::openDevice() void DSoundInputDevice::openDevice()
{ {
ICELogInfo(<< "Open DirectSound audio input.") ICELogInfo(<< "Open DirectSound audio input.")::CoInitialize(NULL);
::CoInitialize(NULL);
Lock l(mGuard); Lock l(mGuard);
// Ensure if GUID is not null // Ensure if GUID is not null
if (IsEqualGUID(mGUID, GUID_NULL)) if (IsEqualGUID(mGUID, GUID_NULL))
{ {
setSimulate( true ); setSimulate(true);
return; return;
} }
@@ -404,24 +398,28 @@ void DSoundInputDevice::openDevice()
mDump.open(L"audioinput.wav"); mDump.open(L"audioinput.wav");
#endif #endif
mNextBuffer = 0; mDevice = NULL; IUnknown* unk = NULL; mBuffer = NULL; mNextBuffer = 0;
mDevice = NULL;
IUnknown* unk = NULL;
mBuffer = NULL;
DSoundHelper::checkComResult(gDSoundInit.mRoutines.DirectSoundCaptureCreate8(&mGUID, &mDevice, NULL)); DSoundHelper::checkComResult(gDSoundInit.mRoutines.DirectSoundCaptureCreate8(&mGUID, &mDevice, NULL));
WAVEFORMATEX wfx; WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(wfx)); memset(&wfx, 0, sizeof(wfx));
//wfx.cbSize = sizeof(wfx); // wfx.cbSize = sizeof(wfx);
wfx.nChannels = AUDIO_CHANNELS; wfx.nChannels = AUDIO_CHANNELS;
wfx.nSamplesPerSec = AUDIO_SAMPLERATE; wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
wfx.wBitsPerSample = 16; wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;; wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
;
wfx.nAvgBytesPerSec = AUDIO_SAMPLERATE * 2 * AUDIO_CHANNELS; wfx.nAvgBytesPerSec = AUDIO_SAMPLERATE * 2 * AUDIO_CHANNELS;
wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.wFormatTag = WAVE_FORMAT_PCM;
DSCBUFFERDESC dsbd; DSCBUFFERDESC dsbd;
ZeroMemory(&dsbd, sizeof(dsbd)); ZeroMemory(&dsbd, sizeof(dsbd));
dsbd.dwSize = sizeof(DSCBUFFERDESC); dsbd.dwSize = sizeof(DSCBUFFERDESC);
dsbd.dwFlags = 0;//DSBCAPS_CTRLPOSITIONNOTIFY; dsbd.dwFlags = 0; // DSBCAPS_CTRLPOSITIONNOTIFY;
dsbd.dwBufferBytes = AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE; dsbd.dwBufferBytes = AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_SIZE;
dsbd.lpwfxFormat = &wfx; dsbd.lpwfxFormat = &wfx;
dsbd.dwFXCount = 0; dsbd.dwFXCount = 0;
@@ -434,7 +432,7 @@ void DSoundInputDevice::openDevice()
DSoundHelper::checkComResult(mNotifications->SetNotificationPositions(AUDIO_MIC_BUFFER_COUNT, mEventArray)); DSoundHelper::checkComResult(mNotifications->SetNotificationPositions(AUDIO_MIC_BUFFER_COUNT, mEventArray));
DSoundHelper::checkComResult(mBuffer->Start(DSCBSTART_LOOPING)); DSoundHelper::checkComResult(mBuffer->Start(DSCBSTART_LOOPING));
dscb->Release(); dscb->Release();
setSimulate( false ); setSimulate(false);
} }
bool DSoundInputDevice::open() bool DSoundInputDevice::open()
@@ -452,7 +450,7 @@ bool DSoundInputDevice::open()
void DSoundInputDevice::closeDevice() void DSoundInputDevice::closeDevice()
{ {
ICELogInfo(<<"Close DirectSound audio input"); ICELogInfo(<< "Close DirectSound audio input");
Lock l(mGuard); Lock l(mGuard);
#ifdef AUDIO_DUMPINPUT #ifdef AUDIO_DUMPINPUT
@@ -505,7 +503,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
// Ensure device exists // Ensure device exists
if (!mDevice) if (!mDevice)
{ {
setSimulate( true ); setSimulate(true);
return false; return false;
} }
@@ -524,9 +522,10 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
try try
{ {
if (::WaitForSingleObject(mEventArray[mNextBuffer].hEventNotify, AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_LENGTH * 4) != WAIT_OBJECT_0) if (::WaitForSingleObject(mEventArray[mNextBuffer].hEventNotify,
AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_LENGTH * 4) != WAIT_OBJECT_0)
{ {
setSimulate( true ); setSimulate(true);
return false; return false;
} }
@@ -534,7 +533,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
if (::WaitForMultipleObjects(AUDIO_MIC_BUFFER_COUNT, mEventSignals, TRUE, 0) != WAIT_TIMEOUT) if (::WaitForMultipleObjects(AUDIO_MIC_BUFFER_COUNT, mEventSignals, TRUE, 0) != WAIT_TIMEOUT)
{ {
// Possible overflow. Consider current buffer resulting. Reset ALL events. // Possible overflow. Consider current buffer resulting. Reset ALL events.
for (int i = 0; i<AUDIO_MIC_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
ResetEvent(mEventArray[i].hEventNotify); ResetEvent(mEventArray[i].hEventNotify);
} }
else else
@@ -543,11 +542,12 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
// Find the buffer start offset // Find the buffer start offset
mReadOffset = mNextBuffer * AUDIO_MIC_BUFFER_SIZE; mReadOffset = mNextBuffer * AUDIO_MIC_BUFFER_SIZE;
//increase the buffer's index // increase the buffer's index
if (++mNextBuffer == AUDIO_MIC_BUFFER_COUNT) if (++mNextBuffer == AUDIO_MIC_BUFFER_COUNT)
mNextBuffer = 0; mNextBuffer = 0;
LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0; LPVOID ptr1 = NULL, ptr2 = NULL;
DWORD len1 = 0, len2 = 0;
DSoundHelper::checkComResult(mBuffer->Lock(mReadOffset, AUDIO_MIC_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0)); DSoundHelper::checkComResult(mBuffer->Lock(mReadOffset, AUDIO_MIC_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
// Copy&Enqueue captured data to mQueue // Copy&Enqueue captured data to mQueue
@@ -574,9 +574,9 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
return true; return true;
} }
catch(...) catch (...)
{ {
setSimulate( true ); setSimulate(true);
} }
return false; return false;
} }
@@ -585,9 +585,8 @@ void DSoundInputDevice::setSimulate(bool s)
{ {
if (!mSimulate && s) if (!mSimulate && s)
mNullAudio.start(); mNullAudio.start();
else else if (mSimulate && !s)
if (mSimulate && !s) mNullAudio.stop();
mNullAudio.stop();
mSimulate = s; mSimulate = s;
} }
@@ -599,7 +598,7 @@ Format DSoundInputDevice::getFormat()
int DSoundInputDevice::readBuffer(void* buffer) int DSoundInputDevice::readBuffer(void* buffer)
{ {
//Lock lock(mGuard); // Lock lock(mGuard);
if (mRefCount <= 0 || isSimulate()) if (mRefCount <= 0 || isSimulate())
return 0; return 0;
@@ -642,10 +641,8 @@ void DSoundInputDevice::threadProc(void* arg)
DSoundOutputDevice::DSoundOutputDevice(GUID deviceId) DSoundOutputDevice::DSoundOutputDevice(GUID deviceId)
:mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL), : mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL), mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0),
mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0), mThreadHandle(0), mSimulate(false), mGUID(deviceId), mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT)
mThreadHandle(0), mSimulate(false), mGUID(deviceId),
mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT)
{ {
gDSoundInit.load(); gDSoundInit.load();
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -700,7 +697,7 @@ void DSoundOutputDevice::openDevice()
ICELogInfo(<< "Open DirectSound audio output"); ICELogInfo(<< "Open DirectSound audio output");
if (IsEqualGUID(mGUID, GUID_NULL)) if (IsEqualGUID(mGUID, GUID_NULL))
{ {
setSimulate( true ); setSimulate(true);
return; return;
} }
@@ -729,10 +726,10 @@ void DSoundOutputDevice::openDevice()
dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0; dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = NULL;//&wfx; dsbd.lpwfxFormat = NULL; //&wfx;
dsbd.guid3DAlgorithm = DS3DALG_DEFAULT; dsbd.guid3DAlgorithm = DS3DALG_DEFAULT;
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mPrimaryBuffer, NULL )); DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mPrimaryBuffer, NULL));
DSBCAPS caps; DSBCAPS caps;
caps.dwSize = sizeof(caps); caps.dwSize = sizeof(caps);
caps.dwFlags = 0; caps.dwFlags = 0;
@@ -751,14 +748,16 @@ void DSoundOutputDevice::openDevice()
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL)); DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL));
// Fill the buffer with silence // Fill the buffer with silence
LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0; LPVOID ptr1 = NULL, ptr2 = NULL;
DSoundHelper::checkComResult(mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0)); DWORD len1 = 0, len2 = 0;
DSoundHelper::checkComResult(
mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0));
if (len1 && ptr1) if (len1 && ptr1)
memset(ptr1, 0, len1); memset(ptr1, 0, len1);
if (len2 && ptr2) if (len2 && ptr2)
memset(ptr2, 0, len2); memset(ptr2, 0, len2);
DSoundHelper::checkComResult(mBuffer->Unlock(ptr1, len1, ptr2, len2)); DSoundHelper::checkComResult(mBuffer->Unlock(ptr1, len1, ptr2, len2));
DSoundHelper::checkComResult(mBuffer->Play(0,0,DSBPLAY_LOOPING)); DSoundHelper::checkComResult(mBuffer->Play(0, 0, DSBPLAY_LOOPING));
mBuffer->GetCurrentPosition(NULL, &mWriteCursor); mBuffer->GetCurrentPosition(NULL, &mWriteCursor);
} }
@@ -804,8 +803,9 @@ bool DSoundOutputDevice::getMediaFrame()
if (mConnection) if (mConnection)
mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame); mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame);
} }
catch(...) catch (...)
{} {
}
return true; return true;
} }
@@ -833,7 +833,8 @@ bool DSoundOutputDevice::process()
offset %= mBufferSize; offset %= mBufferSize;
// See what we can write // See what we can write
LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0; LPVOID ptr1 = NULL, ptr2 = NULL;
DWORD len1 = 0, len2 = 0;
DSoundHelper::checkComResult(mBuffer->Lock(offset, AUDIO_SPK_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0)); DSoundHelper::checkComResult(mBuffer->Lock(offset, AUDIO_SPK_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0));
assert(ptr2 == NULL); assert(ptr2 == NULL);
@@ -858,10 +859,10 @@ void DSoundOutputDevice::threadProc(void* arg)
DSoundOutputDevice* impl = (DSoundOutputDevice*)arg; DSoundOutputDevice* impl = (DSoundOutputDevice*)arg;
impl->openDevice(); impl->openDevice();
DWORD waitResult = 0; DWORD waitResult = 0;
HANDLE waitArray[2] = {impl->mBufferSignal, impl->mShutdownSignal}; HANDLE waitArray[2] = {impl->mBufferSignal, impl->mShutdownSignal};
unsigned exitCount = 0; unsigned exitCount = 0;
bool exitSignal = false; bool exitSignal = false;
while (true) while (true)
{ {
// Poll for shutdown signal // Poll for shutdown signal
@@ -884,12 +885,12 @@ void DSoundOutputDevice::threadProc(void* arg)
impl->restoreBuffer(); impl->restoreBuffer();
impl->process(); impl->process();
} }
catch(const Exception& e) catch (const Exception& e)
{ {
ICELogError(<< "DirectSound output failed with code = " << e.code() << ", subcode = " << e.subcode()); ICELogError(<< "DirectSound output failed with code = " << e.code() << ", subcode = " << e.subcode());
impl->setSimulate(true); impl->setSimulate(true);
} }
catch(...) catch (...)
{ {
ICELogError(<< "DirectSound output failed due to unexpected exception."); ICELogError(<< "DirectSound output failed due to unexpected exception.");
impl->setSimulate(true); impl->setSimulate(true);
@@ -925,62 +926,61 @@ bool DSoundOutputDevice::closing()
return false; return false;
} }
typedef WINUSERAPI HRESULT (WINAPI *LPFNDLLGETCLASSOBJECT) (const CLSID &, const IID &, void **); typedef WINUSERAPI HRESULT(WINAPI* LPFNDLLGETCLASSOBJECT)(const CLSID&, const IID&, void**);
HRESULT DirectSoundPrivateCreate (OUT LPKSPROPERTYSET * ppKsPropertySet) HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET* ppKsPropertySet)
{ {
HMODULE hLibDsound = NULL; HMODULE hLibDsound = NULL;
LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL; LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL;
LPCLASSFACTORY pClassFactory = NULL; LPCLASSFACTORY pClassFactory = NULL;
LPKSPROPERTYSET pKsPropertySet = NULL; LPKSPROPERTYSET pKsPropertySet = NULL;
HRESULT hr = DS_OK; HRESULT hr = DS_OK;
// Load dsound.dll // Load dsound.dll
hLibDsound = LoadLibrary(TEXT("dsound.dll")); hLibDsound = LoadLibrary(TEXT("dsound.dll"));
if(!hLibDsound) if (!hLibDsound)
{ {
hr = DSERR_GENERIC; hr = DSERR_GENERIC;
} }
// Find DllGetClassObject // Find DllGetClassObject
if(SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
pfnDllGetClassObject = pfnDllGetClassObject = (LPFNDLLGETCLASSOBJECT)GetProcAddress(hLibDsound, "DllGetClassObject");
(LPFNDLLGETCLASSOBJECT)GetProcAddress ( hLibDsound, "DllGetClassObject" );
if(!pfnDllGetClassObject) if (!pfnDllGetClassObject)
{ {
hr = DSERR_GENERIC; hr = DSERR_GENERIC;
} }
} }
// Create a class factory object // Create a class factory object
if(SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = pfnDllGetClassObject (CLSID_DirectSoundPrivate, IID_IClassFactory, (LPVOID *)&pClassFactory ); hr = pfnDllGetClassObject(CLSID_DirectSoundPrivate, IID_IClassFactory, (LPVOID*)&pClassFactory);
} }
// Create the DirectSoundPrivate object and query for an IKsPropertySet // Create the DirectSoundPrivate object and query for an IKsPropertySet
// interface // interface
if(SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = pClassFactory->CreateInstance ( NULL, IID_IKsPropertySet, (LPVOID *)&pKsPropertySet ); hr = pClassFactory->CreateInstance(NULL, IID_IKsPropertySet, (LPVOID*)&pKsPropertySet);
} }
// Release the class factory // Release the class factory
if(pClassFactory) if (pClassFactory)
{ {
pClassFactory->Release(); pClassFactory->Release();
} }
// Handle final success or failure // Handle final success or failure
if(SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
*ppKsPropertySet = pKsPropertySet; *ppKsPropertySet = pKsPropertySet;
} }
else if(pKsPropertySet) else if (pKsPropertySet)
{ {
pKsPropertySet->Release(); pKsPropertySet->Release();
} }
@@ -988,56 +988,45 @@ HRESULT DirectSoundPrivateCreate (OUT LPKSPROPERTYSET * ppKsPropertySet)
FreeLibrary(hLibDsound); FreeLibrary(hLibDsound);
return hr; return hr;
} }
BOOL GetInfoFromDSoundGUID( GUID i_sGUID, int &dwWaveID) BOOL GetInfoFromDSoundGUID(GUID i_sGUID, int& dwWaveID)
{ {
LPKSPROPERTYSET pKsPropertySet = NULL; LPKSPROPERTYSET pKsPropertySet = NULL;
HRESULT hr; HRESULT hr;
BOOL retval = FALSE; BOOL retval = FALSE;
PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL; PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL;
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription; DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription;
memset(&sDirectSoundDeviceDescription,0,sizeof(sDirectSoundDeviceDescription)); memset(&sDirectSoundDeviceDescription, 0, sizeof(sDirectSoundDeviceDescription));
hr = DirectSoundPrivateCreate( &pKsPropertySet ); hr = DirectSoundPrivateCreate(&pKsPropertySet);
if(SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
ULONG ulBytesReturned = 0; ULONG ulBytesReturned = 0;
sDirectSoundDeviceDescription.DeviceId = i_sGUID; sDirectSoundDeviceDescription.DeviceId = i_sGUID;
// On the first call the final size is unknown so pass the size of the struct in order to receive // On the first call the final size is unknown so pass the size of the struct in order to receive
// "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings. // "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings.
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, &sDirectSoundDeviceDescription, sizeof(sDirectSoundDeviceDescription),
NULL, &ulBytesReturned);
0,
&sDirectSoundDeviceDescription,
sizeof(sDirectSoundDeviceDescription),
&ulBytesReturned
);
if (ulBytesReturned) if (ulBytesReturned)
{ {
// On the first call it notifies us of the required amount of memory in order to receive the strings. // On the first call it notifies us of the required amount of memory in order to receive the strings.
// Allocate the required memory, the strings will be pointed to the memory space directly after the struct. // Allocate the required memory, the strings will be pointed to the memory space directly after the struct.
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA)new BYTE[ulBytesReturned]; psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA) new BYTE[ulBytesReturned];
*psDirectSoundDeviceDescription = sDirectSoundDeviceDescription; *psDirectSoundDeviceDescription = sDirectSoundDeviceDescription;
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, psDirectSoundDeviceDescription, ulBytesReturned, &ulBytesReturned);
NULL,
0,
psDirectSoundDeviceDescription,
ulBytesReturned,
&ulBytesReturned
);
dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId; dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId;
/*Description = psDirectSoundDeviceDescription->Description; /*Description = psDirectSoundDeviceDescription->Description;
Module = psDirectSoundDeviceDescription->Module; Module = psDirectSoundDeviceDescription->Module;
Interface = psDirectSoundDeviceDescription->Interface;*/ Interface = psDirectSoundDeviceDescription->Interface;*/
delete [] psDirectSoundDeviceDescription; delete[] psDirectSoundDeviceDescription;
retval = TRUE; retval = TRUE;
} }
@@ -1045,24 +1034,18 @@ BOOL GetInfoFromDSoundGUID( GUID i_sGUID, int &dwWaveID)
} }
return retval; return retval;
} }
struct EnumResult struct EnumResult
{ {
int mDeviceId; int mDeviceId;
GUID mGuid; GUID mGuid;
}; };
BOOL CALLBACK DSEnumCallback( BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
LPGUID lpGuid,
LPCTSTR lpcstrDescription,
LPCTSTR lpcstrModule,
LPVOID lpContext
)
{ {
if (lpGuid) if (lpGuid)
{ {
int devId = -1; int devId = -1;
GetInfoFromDSoundGUID(*lpGuid, devId); GetInfoFromDSoundGUID(*lpGuid, devId);
EnumResult* er = (EnumResult*)lpContext; EnumResult* er = (EnumResult*)lpContext;
+79 -79
View File
@@ -22,7 +22,7 @@
#include <EndpointVolume.h> #include <EndpointVolume.h>
#include <MMDeviceAPI.h> #include <MMDeviceAPI.h>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# include <Functiondiscoverykeys_devpkey.h> #include <Functiondiscoverykeys_devpkey.h>
#endif #endif
#include <vector> #include <vector>
#include <string> #include <string>
@@ -31,48 +31,48 @@
namespace Audio namespace Audio
{ {
class VistaEnumerator: public Enumerator class VistaEnumerator : public Enumerator
{ {
public: public:
VistaEnumerator(); VistaEnumerator();
~VistaEnumerator(); ~VistaEnumerator();
void open(int direction); void open(int direction);
void close(); void close();
int count(); int count();
std::tstring nameAt(int index); std::tstring nameAt(int index);
int idAt(int index); int idAt(int index);
int indexOfDefaultDevice(); int indexOfDefaultDevice();
protected: protected:
IMMDeviceCollection* mCollection; IMMDeviceCollection* mCollection;
IMMDevice* mDefaultDevice; IMMDevice* mDefaultDevice;
IMMDeviceEnumerator* mEnumerator; IMMDeviceEnumerator* mEnumerator;
EDataFlow mDirection; EDataFlow mDirection;
std::vector<std::wstring> mNameList; std::vector<std::wstring> mNameList;
void enumerate(); void enumerate();
IMMDevice* mapIndexToInterface(int index); IMMDevice* mapIndexToInterface(int index);
}; };
class XpEnumerator: public Enumerator class XpEnumerator : public Enumerator
{ {
public: public:
XpEnumerator(); XpEnumerator();
~XpEnumerator(); ~XpEnumerator();
void open(int direction); void open(int direction);
void close(); void close();
int count(); int count();
std::tstring nameAt(int index); std::tstring nameAt(int index);
int idAt(int index); int idAt(int index);
int indexOfDefaultDevice(); int indexOfDefaultDevice();
protected: protected:
std::vector<std::wstring> mNameList; std::vector<std::wstring> mNameList;
int mDirection; int mDirection;
}; };
class DSoundHelper class DSoundHelper
@@ -83,32 +83,32 @@ public:
}; };
#if !defined(_MSC_VER) #if !defined(_MSC_VER)
typedef struct IDirectSoundNotify8 *LPDIRECTSOUNDNOTIFY8; typedef struct IDirectSoundNotify8* LPDIRECTSOUNDNOTIFY8;
#endif #endif
class DSoundInputDevice: public InputDevice class DSoundInputDevice : public InputDevice
{ {
public: public:
DSoundInputDevice(GUID deviceId); DSoundInputDevice(GUID deviceId);
~DSoundInputDevice(); ~DSoundInputDevice();
void enableDenoiser(bool enable); void enableDenoiser(bool enable);
bool open(); bool open();
void close(); void close();
bool isSimulate() const; bool isSimulate() const;
void setSimulate(bool s); void setSimulate(bool s);
int readBuffer(void* buffer); int readBuffer(void* buffer);
Format getFormat(); Format getFormat();
protected: protected:
Mutex mGuard; /// Mutex to protect this instance. Mutex mGuard; /// Mutex to protect this instance.
LPDIRECTSOUNDCAPTURE8 mDevice; LPDIRECTSOUNDCAPTURE8 mDevice;
LPDIRECTSOUNDCAPTUREBUFFER8 mBuffer; LPDIRECTSOUNDCAPTUREBUFFER8 mBuffer;
LPDIRECTSOUNDNOTIFY8 mNotifications; LPDIRECTSOUNDNOTIFY8 mNotifications;
DSBPOSITIONNOTIFY mEventArray[AUDIO_MIC_BUFFER_COUNT]; DSBPOSITIONNOTIFY mEventArray[AUDIO_MIC_BUFFER_COUNT];
HANDLE mEventSignals[AUDIO_MIC_BUFFER_COUNT]; // Helper array to make WaitForMultipleObjects in loop HANDLE mEventSignals[AUDIO_MIC_BUFFER_COUNT]; // Helper array to make WaitForMultipleObjects in loop
int mBufferIndex; int mBufferIndex;
int mNextBuffer; int mNextBuffer;
@@ -116,7 +116,7 @@ protected:
HANDLE mThreadHandle; HANDLE mThreadHandle;
HANDLE mShutdownSignal; HANDLE mShutdownSignal;
volatile bool mSimulate; /// Marks if simulate mode is active. volatile bool mSimulate; /// Marks if simulate mode is active.
int mRefCount; int mRefCount;
ByteBuffer mQueue; ByteBuffer mQueue;
unsigned mReadOffset; unsigned mReadOffset;
@@ -129,59 +129,59 @@ protected:
WavFileWriter mDump; WavFileWriter mDump;
#endif #endif
bool tryReadBuffer(void* buffer); bool tryReadBuffer(void* buffer);
void openDevice(); void openDevice();
void closeDevice(); void closeDevice();
static void threadProc(void* arg); static void threadProc(void* arg);
}; };
class DSoundOutputDevice: public OutputDevice class DSoundOutputDevice : public OutputDevice
{ {
public: public:
DSoundOutputDevice(GUID deviceId); DSoundOutputDevice(GUID deviceId);
~DSoundOutputDevice(); ~DSoundOutputDevice();
bool open();
void close();
unsigned playedTime() const; bool open();
bool isSimulate() const; void close();
void setSimulate(bool s);
bool closing(); unsigned playedTime() const;
Format getFormat(); bool isSimulate() const;
void setSimulate(bool s);
bool closing();
Format getFormat();
protected: protected:
Mutex mGuard; /// Mutex to protect this instance Mutex mGuard; /// Mutex to protect this instance
int mDeviceID; int mDeviceID;
LPDIRECTSOUND8 mDevice; LPDIRECTSOUND8 mDevice;
LPDIRECTSOUNDBUFFER mPrimaryBuffer; LPDIRECTSOUNDBUFFER mPrimaryBuffer;
LPDIRECTSOUNDBUFFER mBuffer; LPDIRECTSOUNDBUFFER mBuffer;
GUID mGUID; GUID mGUID;
unsigned mWriteOffset; unsigned mWriteOffset;
unsigned mPlayedSamples; unsigned mPlayedSamples;
unsigned mSentBytes; unsigned mSentBytes;
DWORD mPlayCursor; // Measured in bytes DWORD mPlayCursor; // Measured in bytes
unsigned mBufferSize; unsigned mBufferSize;
unsigned mTotalPlayed; // Measured in bytes unsigned mTotalPlayed; // Measured in bytes
unsigned mTail; // Measured in bytes unsigned mTail; // Measured in bytes
HANDLE mShutdownSignal; HANDLE mShutdownSignal;
HANDLE mBufferSignal; HANDLE mBufferSignal;
HANDLE mThreadHandle; HANDLE mThreadHandle;
bool mSimulate; bool mSimulate;
StubTimer mNullAudio; StubTimer mNullAudio;
DWORD mWriteCursor; DWORD mWriteCursor;
char mMediaFrame[AUDIO_SPK_BUFFER_SIZE]; char mMediaFrame[AUDIO_SPK_BUFFER_SIZE];
unsigned mRefCount; unsigned mRefCount;
void openDevice(); void openDevice();
void closeDevice(); void closeDevice();
void restoreBuffer(); void restoreBuffer();
bool process(); bool process();
bool getMediaFrame(); bool getMediaFrame();
static void threadProc(void* arg); static void threadProc(void* arg);
}; };
} } // namespace Audio
#endif #endif
+71 -72
View File
@@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef TARGET_WIN #ifdef TARGET_WIN
# include <WinSock2.h> #include <WinSock2.h>
#endif #endif
#include <assert.h> #include <assert.h>
@@ -17,93 +17,92 @@ using namespace Audio;
TimeSource::TimeSource(int quantTime, int nrOfQuants) TimeSource::TimeSource(int quantTime, int nrOfQuants)
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
mCounter.QuadPart = 0; mCounter.QuadPart = 0;
#endif #endif
#if defined(TARGET_OSX) || defined(TARGET_IOS) #if defined(TARGET_OSX) || defined(TARGET_IOS)
mach_timebase_info(&mTimebase); mach_timebase_info(&mTimebase);
mRatio = ((double)mTimebase.numer / (double)mTimebase.denom) / 1000000; mRatio = ((double)mTimebase.numer / (double)mTimebase.denom) / 1000000;
#endif #endif
mQuantTime = quantTime; mQuantTime = quantTime;
mDepthTime = quantTime * nrOfQuants; mDepthTime = quantTime * nrOfQuants;
mTailTime = 0; mTailTime = 0;
} }
void TimeSource::start() void TimeSource::start()
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
if (!QueryPerformanceFrequency(&mFreq)) if (!QueryPerformanceFrequency(&mFreq))
throw Exception(ERR_QPC, GetLastError()); throw Exception(ERR_QPC, GetLastError());
if (!QueryPerformanceCounter(&mCounter)) if (!QueryPerformanceCounter(&mCounter))
throw Exception(ERR_QPC, GetLastError()); throw Exception(ERR_QPC, GetLastError());
#endif #endif
} }
void TimeSource::stop() void TimeSource::stop() {}
{
}
unsigned TimeSource::time() unsigned TimeSource::time()
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
LARGE_INTEGER c; LARGE_INTEGER c;
if (!QueryPerformanceCounter(&c)) if (!QueryPerformanceCounter(&c))
throw Exception(ERR_QPC, GetLastError()); throw Exception(ERR_QPC, GetLastError());
//find the f
double f = (double)mFreq.QuadPart / 1000.0;
//find the difference
unsigned __int64 diff = c.QuadPart - mCounter.QuadPart;
mCounter.QuadPart = c.QuadPart; // find the f
double f = (double)mFreq.QuadPart / 1000.0;
diff = (unsigned __int64)((double)diff / f + 0.5); //get ms
diff += mTailTime;
if (diff > mDepthTime) // find the difference
{ unsigned __int64 diff = c.QuadPart - mCounter.QuadPart;
mTailTime = 0;
return mDepthTime; mCounter.QuadPart = c.QuadPart;
}
else diff = (unsigned __int64)((double)diff / f + 0.5); // get ms
{ diff += mTailTime;
mTailTime = (unsigned )(diff % (unsigned __int64)mQuantTime);
unsigned int t = (unsigned )(diff / (unsigned __int64)mQuantTime); if (diff > mDepthTime)
return t * mQuantTime; {
} mTailTime = 0;
return mDepthTime;
}
else
{
mTailTime = (unsigned)(diff % (unsigned __int64)mQuantTime);
unsigned int t = (unsigned)(diff / (unsigned __int64)mQuantTime);
return t * mQuantTime;
}
#endif #endif
#if defined(TARGET_OSX) || defined(TARGET_IOS) #if defined(TARGET_OSX) || defined(TARGET_IOS)
uint64_t t = mach_absolute_time(); uint64_t t = mach_absolute_time();
uint64_t c = uint64_t((double)t * mRatio + 0.5); uint64_t c = uint64_t((double)t * mRatio + 0.5);
uint64_t diff = c - this->mTime + mTailTime; uint64_t diff = c - this->mTime + mTailTime;
mTime = c; mTime = c;
if (diff > mDepthTime) if (diff > mDepthTime)
{ {
mTailTime = 0; mTailTime = 0;
return mDepthTime; return mDepthTime;
} }
else else
{ {
mTailTime = diff % mQuantTime; mTailTime = diff % mQuantTime;
uint64_t t = diff / mQuantTime; uint64_t t = diff / mQuantTime;
return t * mQuantTime; return t * mQuantTime;
} }
#endif #endif
#if defined(TARGET_LINUX) #if defined(TARGET_LINUX)
assert(0); assert(0);
#endif #endif
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
assert(0); assert(0);
#endif #endif
return 0; return 0;
} }
// --- StubTimer --- // --- StubTimer ---
StubTimer::StubTimer(int bufferTime, int bufferCount) StubTimer::StubTimer(int bufferTime, int bufferCount)
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false), mCurrentTime(0) : mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false),
mCurrentTime(0)
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -113,38 +112,38 @@ StubTimer::StubTimer(int bufferTime, int bufferCount)
StubTimer::~StubTimer() StubTimer::~StubTimer()
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
::CloseHandle(mStubSignal); ::CloseHandle(mStubSignal);
#endif #endif
} }
void StubTimer::start() void StubTimer::start()
{ {
mTimeSource.start(); mTimeSource.start();
mCurrentTime = mTimeSource.time(); mCurrentTime = mTimeSource.time();
mActive = true; mActive = true;
} }
void StubTimer::stop() void StubTimer::stop()
{ {
mTimeSource.stop(); mTimeSource.stop();
mActive = false; mActive = false;
} }
void StubTimer::waitForBuffer() void StubTimer::waitForBuffer()
{ {
if (!mActive) if (!mActive)
start(); start();
unsigned t = mTimeSource.time(); unsigned t = mTimeSource.time();
while (!t) while (!t)
{ {
#ifdef TARGET_WIN #ifdef TARGET_WIN
::WaitForSingleObject(mStubSignal, mBufferTime); ::WaitForSingleObject(mStubSignal, mBufferTime);
#endif #endif
#if defined(TARGET_OSX) || defined(TARGET_IOS) #if defined(TARGET_OSX) || defined(TARGET_IOS)
usleep(100); usleep(100);
#endif #endif
t = mTimeSource.time(); t = mTimeSource.time();
} }
} }
+31 -31
View File
@@ -10,15 +10,15 @@
#include <EndpointVolume.h> #include <EndpointVolume.h>
#include <MMDeviceAPI.h> #include <MMDeviceAPI.h>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# include <Functiondiscoverykeys_devpkey.h> #include <Functiondiscoverykeys_devpkey.h>
#endif #endif
#endif #endif
#if defined(TARGET_OSX) || defined(TARGET_IOS) #if defined(TARGET_OSX) || defined(TARGET_IOS)
# include <AudioUnit/AudioUnit.h> #include <AudioUnit/AudioUnit.h>
# include <AudioToolbox/AudioConverter.h> #include <AudioToolbox/AudioConverter.h>
# include <AudioToolbox/AudioServices.h> #include <AudioToolbox/AudioServices.h>
# include <mach/mach_time.h> #include <mach/mach_time.h>
#endif #endif
#include <vector> #include <vector>
@@ -27,52 +27,52 @@
namespace Audio namespace Audio
{ {
class TimeSource class TimeSource
{ {
protected: protected:
#ifdef TARGET_WIN #ifdef TARGET_WIN
LARGE_INTEGER mCounter; /// Current value from QPC. LARGE_INTEGER mCounter; /// Current value from QPC.
LARGE_INTEGER mFreq; /// Current frequency from QPC. LARGE_INTEGER mFreq; /// Current frequency from QPC.
#endif #endif
#if defined(TARGET_OSX) || defined(TARGET_IOS) #if defined(TARGET_OSX) || defined(TARGET_IOS)
uint64_t mTime; uint64_t mTime;
struct mach_timebase_info mTimebase; struct mach_timebase_info mTimebase;
double mRatio; double mRatio;
#endif #endif
unsigned mQuantTime; /// Used time quants length in milliseconds. unsigned mQuantTime; /// Used time quants length in milliseconds.
unsigned mDepthTime; /// Number of available time quants. unsigned mDepthTime; /// Number of available time quants.
unsigned mTailTime; /// Not-accounted milliseconds. unsigned mTailTime; /// Not-accounted milliseconds.
public: public:
TimeSource(int quantTime, int nrOfQuants); TimeSource(int quantTime, int nrOfQuants);
~TimeSource() = default; ~TimeSource() = default;
void start(); void start();
void stop(); void stop();
unsigned time(); unsigned time();
}; };
class StubTimer class StubTimer
{ {
public: public:
StubTimer(int bufferTime, int bufferCount); StubTimer(int bufferTime, int bufferCount);
~StubTimer(); ~StubTimer();
void start(); void start();
void stop(); void stop();
void waitForBuffer(); void waitForBuffer();
protected: protected:
unsigned mBufferTime; unsigned mBufferTime;
unsigned mBufferCount; unsigned mBufferCount;
unsigned mCurrentTime; unsigned mCurrentTime;
TimeSource mTimeSource; TimeSource mTimeSource;
#ifdef TARGET_WIN #ifdef TARGET_WIN
HANDLE mStubSignal; HANDLE mStubSignal;
#endif #endif
bool mActive; bool mActive;
}; };
} } // namespace Audio
#endif #endif
+20 -38
View File
@@ -7,16 +7,16 @@
#include "../helper/HL_OsVersion.h" #include "../helper/HL_OsVersion.h"
#if !defined(USE_NULL_AUDIO) #if !defined(USE_NULL_AUDIO)
# ifdef TARGET_WIN #ifdef TARGET_WIN
# include "Audio_Wmme.h" #include "Audio_Wmme.h"
# include "Audio_DirectSound.h" #include "Audio_DirectSound.h"
# endif #endif
# ifdef TARGET_OSX #ifdef TARGET_OSX
# include "Audio_CoreAudio.h" #include "Audio_CoreAudio.h"
# endif #endif
# ifdef TARGET_ANDROID #ifdef TARGET_ANDROID
# include "Audio_Android.h" #include "Audio_Android.h"
# endif #endif
#endif #endif
#include "Audio_Helper.h" #include "Audio_Helper.h"
@@ -24,14 +24,9 @@
using namespace Audio; using namespace Audio;
Device::Device() Device::Device() : mConnection(nullptr) {}
:mConnection(nullptr)
{
}
Device::~Device() Device::~Device() {}
{
}
void Device::setConnection(DataConnection* connection) void Device::setConnection(DataConnection* connection)
@@ -44,13 +39,9 @@ DataConnection* Device::connection()
return mConnection; return mConnection;
} }
InputDevice::InputDevice() InputDevice::InputDevice() {}
{
}
InputDevice::~InputDevice() InputDevice::~InputDevice() {}
{
}
InputDevice* InputDevice::make(int devId) InputDevice* InputDevice::make(int devId)
{ {
@@ -71,13 +62,9 @@ InputDevice* InputDevice::make(int devId)
return nullptr; return nullptr;
} }
OutputDevice::OutputDevice() OutputDevice::OutputDevice() {}
{
}
OutputDevice::~OutputDevice() OutputDevice::~OutputDevice() {}
{
}
OutputDevice* OutputDevice::make(int devId) OutputDevice* OutputDevice::make(int devId)
{ {
@@ -85,7 +72,7 @@ OutputDevice* OutputDevice::make(int devId)
return new NullOutputDevice(); return new NullOutputDevice();
#else #else
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
//return new WmmeOutputDevice(index); // return new WmmeOutputDevice(index);
return new DSoundOutputDevice(DSoundHelper::deviceId2Guid(devId, false)); return new DSoundOutputDevice(DSoundHelper::deviceId2Guid(devId, false));
#endif #endif
#ifdef TARGET_OSX #ifdef TARGET_OSX
@@ -100,13 +87,9 @@ OutputDevice* OutputDevice::make(int devId)
// --- Enumerator --- // --- Enumerator ---
Enumerator::Enumerator() Enumerator::Enumerator() {}
{
}
Enumerator::~Enumerator() Enumerator::~Enumerator() {}
{
}
int Enumerator::nameToIndex(const std::tstring& name) int Enumerator::nameToIndex(const std::tstring& name)
{ {
@@ -119,7 +102,6 @@ int Enumerator::nameToIndex(const std::tstring& name)
Enumerator* Enumerator::make(bool useNull) Enumerator* Enumerator::make(bool useNull)
{ {
if (useNull) if (useNull)
return new NullEnumerator(); return new NullEnumerator();
#ifndef USE_NULL_AUDIO #ifndef USE_NULL_AUDIO
@@ -147,7 +129,7 @@ OsEngine* OsEngine::instance()
#ifdef TARGET_ANDROID #ifdef TARGET_ANDROID
return nullptr; // As we use Oboe library for now return nullptr; // As we use Oboe library for now
//return &OpenSLEngine::instance(); // return &OpenSLEngine::instance();
#endif #endif
return nullptr; return nullptr;
+30 -58
View File
@@ -27,34 +27,18 @@ 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);
}
size_t sizeFromTime(std::chrono::milliseconds ms) const size_t sizeFromTime(std::chrono::milliseconds ms) const { return sizeFromTime(ms.count()); }
{
return sizeFromTime(ms.count());
}
std::string toString() std::string toString()
{ {
@@ -63,26 +47,13 @@ struct Format
return std::string(buffer); return std::string(buffer);
} }
bool operator == (const Format& rhs) const bool operator==(const Format& rhs) const { return mRate == rhs.mRate && mChannels == rhs.mChannels; }
{
return mRate == rhs.mRate && mChannels == rhs.mChannels;
}
bool operator != (const Format& rhs) const bool operator!=(const Format& rhs) const { return mRate != rhs.mRate || mChannels != rhs.mChannels; }
{
return mRate != rhs.mRate || mChannels != rhs.mChannels;
}
int rate() const int rate() const { return mRate; }
{
return mRate;
}
int channels() const
{
return mChannels;
}
int channels() const { return mChannels; }
}; };
class DataConnection class DataConnection
@@ -99,28 +70,29 @@ public:
Device(); Device();
virtual ~Device(); virtual ~Device();
void setConnection(DataConnection* connection); void setConnection(DataConnection* connection);
DataConnection* connection(); DataConnection* connection();
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();
@@ -135,27 +107,27 @@ class Enumerator
public: public:
Enumerator(); Enumerator();
virtual ~Enumerator(); virtual ~Enumerator();
int nameToIndex(const std::tstring& name); int nameToIndex(const std::tstring& name);
virtual void open(int direction) = 0; virtual void open(int direction) = 0;
virtual void close() = 0; virtual void close() = 0;
virtual int count() = 0; virtual int count() = 0;
virtual std::tstring nameAt(int index) = 0; virtual std::tstring nameAt(int index) = 0;
virtual int idAt(int index) = 0; virtual int idAt(int index) = 0;
virtual int indexOfDefaultDevice() = 0; virtual int indexOfDefaultDevice() = 0;
static Enumerator* make(bool useNull = false); static Enumerator* make(bool useNull = false);
}; };
class OsEngine class OsEngine
{ {
public: public:
virtual void open() = 0; virtual void open() = 0;
virtual void close() = 0; virtual void close() = 0;
static OsEngine* instance(); static OsEngine* instance();
}; };
}; }; // namespace Audio
#endif #endif
+225 -232
View File
@@ -17,341 +17,334 @@ using namespace Audio;
Mixer::Stream::Stream() Mixer::Stream::Stream()
{ {
mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE); mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE);
mResampler16.start(AUDIO_CHANNELS, 16000, AUDIO_SAMPLERATE); mResampler16.start(AUDIO_CHANNELS, 16000, AUDIO_SAMPLERATE);
mResampler32.start(AUDIO_CHANNELS, 32000, AUDIO_SAMPLERATE); mResampler32.start(AUDIO_CHANNELS, 32000, AUDIO_SAMPLERATE);
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE); mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
mActive = false; mActive = false;
mContext = nullptr; mContext = nullptr;
mSSRC = 0; mSSRC = 0;
mFadeOutCounter = 0; mFadeOutCounter = 0;
mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT); mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT);
} }
Mixer::Stream::~Stream() Mixer::Stream::~Stream() {}
{
}
void Mixer::Stream::setSsrc(unsigned ssrc) void Mixer::Stream::setSsrc(unsigned ssrc)
{ {
mSSRC = ssrc; mSSRC = ssrc;
} }
unsigned Mixer::Stream::ssrc() unsigned Mixer::Stream::ssrc()
{ {
return mSSRC; return mSSRC;
} }
void Mixer::Stream::setContext(void* context) void Mixer::Stream::setContext(void* context)
{ {
mContext = context; mContext = context;
} }
void* Mixer::Stream::context() void* Mixer::Stream::context()
{ {
return mContext; return mContext;
} }
DataWindow& Mixer::Stream::data() DataWindow& Mixer::Stream::data()
{ {
return mData; return mData;
} }
bool Mixer::Stream::active() bool Mixer::Stream::active()
{ {
return mActive; return mActive;
} }
void Mixer::Stream::setActive(bool active) void Mixer::Stream::setActive(bool active)
{ {
mActive = active; mActive = active;
} }
void Mixer::Stream::addPcm(int rate, const void* input, int length) void Mixer::Stream::addPcm(int rate, const void* input, int length)
{ {
// Resample to internal sample rate // Resample to internal sample rate
size_t outputSize = size_t(0.5 + length * ((float)AUDIO_SAMPLERATE / rate)); size_t outputSize = size_t(0.5 + length * ((float)AUDIO_SAMPLERATE / rate));
if (mTempBuffer.size() < outputSize) if (mTempBuffer.size() < outputSize)
mTempBuffer.resize(outputSize); mTempBuffer.resize(outputSize);
Resampler* resampler = (rate == 8000) ? &mResampler8 : ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
size_t inputProcessed = 0;
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize);
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length
// Queue data Resampler* resampler = (rate == 8000)
mData.add(mTempBuffer.data(), outputSize); ? &mResampler8
: ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
size_t inputProcessed = 0;
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize);
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length
// Queue data
mData.add(mTempBuffer.data(), outputSize);
} }
Mixer::Mixer() Mixer::Mixer()
{ {
mActiveCounter = 0; mActiveCounter = 0;
mOutput.setCapacity(32768); mOutput.setCapacity(32768);
} }
Mixer::~Mixer() Mixer::~Mixer() {}
{
}
void Mixer::unregisterChannel(void* channel) void Mixer::unregisterChannel(void* channel)
{ {
Lock l(mMutex); Lock l(mMutex);
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++) for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
{
Stream& c = mChannelList[i];
if (c.active() && c.context() == channel)
{ {
c.setActive(false); // stream is not active anymore Stream& c = mChannelList[i];
c.data().clear(); // clear data if (c.active() && c.context() == channel)
mActiveCounter--; {
c.setActive(false); // stream is not active anymore
c.data().clear(); // clear data
mActiveCounter--;
}
} }
}
} }
void Mixer::clear(void* context, unsigned ssrc) void Mixer::clear(void* context, unsigned ssrc)
{ {
Lock l(mMutex); Lock l(mMutex);
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++) for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
{
Stream& c = mChannelList[i];
if (c.active() && c.context() == context && c.ssrc() == ssrc)
{ {
c.setActive(false); Stream& c = mChannelList[i];
c.data().clear(); if (c.active() && c.context() == context && c.ssrc() == ssrc)
mActiveCounter--; {
c.setActive(false);
c.data().clear();
mActiveCounter--;
}
} }
}
} }
Mixer::Stream* Mixer::allocateChannel(void* context, unsigned ssrc) Mixer::Stream* Mixer::allocateChannel(void* context, unsigned ssrc)
{ {
// Allocate new channel // Allocate new channel
Lock l(mMutex); Lock l(mMutex);
Stream* channel; Stream* channel;
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT;i++) for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
{
channel = &mChannelList[i];
if (!channel->active())
{ {
channel->setSsrc(ssrc); channel = &mChannelList[i];
channel->setContext(context); if (!channel->active())
channel->data().clear(); {
mActiveCounter++; channel->setSsrc(ssrc);
channel->setActive(true); channel->setContext(context);
return channel; channel->data().clear();
mActiveCounter++;
channel->setActive(true);
return channel;
}
} }
} return NULL;
return NULL;
} }
void Mixer::addPcm(void* context, unsigned ssrc, void Mixer::addPcm(void* context, unsigned ssrc, const void* inputData, int inputLength, int inputRate, bool fadeOut)
const void* inputData, int inputLength,
int inputRate, bool fadeOut)
{ {
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000); assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
Lock l(mMutex); Lock l(mMutex);
int i; int i;
// Locate a channel // Locate a channel
Stream* channel = NULL; Stream* channel = NULL;
for (i=0; i<AUDIO_MIX_CHANNEL_COUNT && !channel; i++) for (i = 0; i < AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
{ {
Stream& c = mChannelList[i]; Stream& c = mChannelList[i];
if (c.active() && c.context() == context && c.ssrc() == ssrc) if (c.active() && c.context() == context && c.ssrc() == ssrc)
channel = &c; channel = &c;
} }
if (!channel)
{
channel = allocateChannel(context, ssrc);
if (!channel) if (!channel)
throw Exception(ERR_MIXER_OVERFLOW); {
} channel = allocateChannel(context, ssrc);
if (!channel)
channel->addPcm(inputRate, inputData, inputLength); throw Exception(ERR_MIXER_OVERFLOW);
}
channel->addPcm(inputRate, inputData, inputLength);
} }
void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate, bool fadeOut) void Mixer::addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate, bool fadeOut)
{ {
assert(rate == 8000 || rate == 16000 || rate == 32000 || rate == 48000); assert(rate == 8000 || rate == 16000 || rate == 32000 || rate == 48000);
Lock l(mMutex); Lock l(mMutex);
int i; int i;
// Locate a channel // Locate a channel
Stream* channel = NULL; Stream* channel = NULL;
for (i=0; i<AUDIO_MIX_CHANNEL_COUNT && !channel; i++) for (i = 0; i < AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
{ {
Stream& c = mChannelList[i]; Stream& c = mChannelList[i];
if (c.active() && c.context() == context && c.ssrc() == ssrc) if (c.active() && c.context() == context && c.ssrc() == ssrc)
channel = &c; channel = &c;
} }
if (!channel)
{
channel = allocateChannel(context, ssrc);
if (!channel) if (!channel)
throw Exception(ERR_MIXER_OVERFLOW); {
} channel = allocateChannel(context, ssrc);
if (!channel)
channel->addPcm(rate, w.data(), w.filled()); throw Exception(ERR_MIXER_OVERFLOW);
//ICELogSpecial(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes"); }
channel->addPcm(rate, w.data(), w.filled());
// ICELogSpecial(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes");
} }
void Mixer::mix() void Mixer::mix()
{ {
Lock l(mMutex); Lock l(mMutex);
// Current sample // Current sample
int sample = 0; int sample = 0;
// Counter of processed active channels // Counter of processed active channels
int processed = 0; int processed = 0;
// Samples & sources counters // Samples & sources counters
unsigned sampleCounter = 0, sourceCounter; unsigned sampleCounter = 0, sourceCounter;
short outputBuffer[512]; short outputBuffer[512];
unsigned outputCounter = 0; unsigned outputCounter = 0;
// Build active channel map // Build active channel map
Stream* channelList[AUDIO_MIX_CHANNEL_COUNT]; Stream* channelList[AUDIO_MIX_CHANNEL_COUNT];
int activeCounter = 0; int activeCounter = 0;
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++) for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
if (mChannelList[i].active()) if (mChannelList[i].active())
channelList[activeCounter++] = &mChannelList[i]; channelList[activeCounter++] = &mChannelList[i];
// No active channels - nothing to mix - exit
if (!activeCounter)
{
// ICELogDebug(<< "No active channel");
return;
}
// Optimized versions for 1& 2 active channels
if (activeCounter == 1)
{
// Copy much samples as we have
Stream& audio = *channelList[0];
// Copy the decoded data // No active channels - nothing to mix - exit
mOutput.add(audio.data().data(), audio.data().filled()); if (!activeCounter)
// Erase copied audio samples
audio.data().erase(audio.data().filled());
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
}
else
if (activeCounter == 2)
{
Stream& audio1 = *channelList[0];
Stream& audio2 = *channelList[1];
int filled1 = audio1.data().filled() / 2, filled2 = audio2.data().filled() / 2;
int available = filled1 > filled2 ? filled1 : filled2;
// Find how much samples can be mixed
int filled = mOutput.filled() / 2;
int maxsize = mOutput.capacity() / 2;
if (maxsize - filled < available)
available = maxsize - filled;
short sample = 0;
for (int i=0; i<available; i++)
{
short sample1 = filled1 > i ? audio1.data().shortAt(i) : 0;
short sample2 = filled2 > i ? audio2.data().shortAt(i) : 0;
sample = (abs(sample1) > abs(sample2)) ? sample1 : sample2;
mOutput.add(sample);
}
audio1.data().erase(available*2);
audio2.data().erase(available*2);
}
else
{
do
{ {
sample = 0; // ICELogDebug(<< "No active channel");
sourceCounter = 0; return;
processed = 0; }
for (int i=0; i<activeCounter; i++)
{
Stream& audio = *channelList[i];
processed++;
if (audio.data().filled() > (int)sampleCounter * 2) // Optimized versions for 1& 2 active channels
if (activeCounter == 1)
{
// Copy much samples as we have
Stream& audio = *channelList[0];
// Copy the decoded data
mOutput.add(audio.data().data(), audio.data().filled());
// Erase copied audio samples
audio.data().erase(audio.data().filled());
// ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
}
else if (activeCounter == 2)
{
Stream& audio1 = *channelList[0];
Stream& audio2 = *channelList[1];
int filled1 = audio1.data().filled() / 2, filled2 = audio2.data().filled() / 2;
int available = filled1 > filled2 ? filled1 : filled2;
// Find how much samples can be mixed
int filled = mOutput.filled() / 2;
int maxsize = mOutput.capacity() / 2;
if (maxsize - filled < available)
available = maxsize - filled;
short sample = 0;
for (int i = 0; i < available; i++)
{ {
short currentSample = audio.data().shortAt(sampleCounter); short sample1 = filled1 > i ? audio1.data().shortAt(i) : 0;
if (abs(currentSample) > abs(sample)) short sample2 = filled2 > i ? audio2.data().shortAt(i) : 0;
sample = currentSample; sample = (abs(sample1) > abs(sample2)) ? sample1 : sample2;
sourceCounter++;
mOutput.add(sample);
} }
} audio1.data().erase(available * 2);
audio2.data().erase(available * 2);
if (sourceCounter)
{
outputBuffer[outputCounter++] = (short)sample;
sampleCounter++;
}
// Check if time to flash output buffer
if ((!sourceCounter || outputCounter == 512) && outputCounter)
{
mOutput.add(outputBuffer, outputCounter * 2);
outputCounter = 0;
}
} }
while (sourceCounter); else
processed = 0;
for (int i=0; i<activeCounter; i++)
{ {
Stream& audio = *channelList[i]; do
audio.data().erase(sampleCounter*2); {
sample = 0;
sourceCounter = 0;
processed = 0;
for (int i = 0; i < activeCounter; i++)
{
Stream& audio = *channelList[i];
processed++;
if (audio.data().filled() > (int)sampleCounter * 2)
{
short currentSample = audio.data().shortAt(sampleCounter);
if (abs(currentSample) > abs(sample))
sample = currentSample;
sourceCounter++;
}
}
if (sourceCounter)
{
outputBuffer[outputCounter++] = (short)sample;
sampleCounter++;
}
// Check if time to flash output buffer
if ((!sourceCounter || outputCounter == 512) && outputCounter)
{
mOutput.add(outputBuffer, outputCounter * 2);
outputCounter = 0;
}
} while (sourceCounter);
processed = 0;
for (int i = 0; i < activeCounter; i++)
{
Stream& audio = *channelList[i];
audio.data().erase(sampleCounter * 2);
}
} }
}
} }
int Mixer::getPcm(void* outputData, int outputLength) int Mixer::getPcm(void* outputData, int outputLength)
{ {
Lock l(mMutex); Lock l(mMutex);
if (mOutput.filled() < outputLength) if (mOutput.filled() < outputLength)
mix(); mix();
//ICELogSpecial(<<"Mixer has " << mOutput.filled() << " available bytes"); // ICELogSpecial(<<"Mixer has " << mOutput.filled() << " available bytes");
memset(outputData, 0, outputLength); memset(outputData, 0, outputLength);
return mOutput.read(outputData, outputLength); return mOutput.read(outputData, outputLength);
} }
int Mixer::mixAndGetPcm(Audio::DataWindow& output) int Mixer::mixAndGetPcm(Audio::DataWindow& output)
{ {
Lock l(mMutex); Lock l(mMutex);
// Mix // Mix
mix(); mix();
size_t avail = mOutput.filled(); size_t avail = mOutput.filled();
if (!avail) if (!avail)
{ {
output.setFilled(0); output.setFilled(0);
return 0; return 0;
} }
// Make sure output has enough space (setCapacity only ever grows the window) // Make sure output has enough space (setCapacity only ever grows the window)
if (output.capacity() < avail) if (output.capacity() < avail)
output.setCapacity(avail); output.setCapacity(avail);
// Read mixed data to output and publish the real byte count // Read mixed data to output and publish the real byte count
size_t got = mOutput.read(output.mutableData(), avail); size_t got = mOutput.read(output.mutableData(), avail);
output.setFilled(got); output.setFilled(got);
return static_cast<int>(got); return static_cast<int>(got);
} }
int Mixer::available() int Mixer::available()
{ {
return mOutput.filled(); return mOutput.filled();
} }
+33 -36
View File
@@ -14,59 +14,56 @@
#include <map> #include <map>
#include <atomic> #include <atomic>
namespace Audio namespace Audio
{ {
class Mixer class Mixer
{ {
protected: protected:
class Stream class Stream
{ {
protected: protected:
DataWindow mData; DataWindow mData;
Resampler mResampler8, Resampler mResampler8, mResampler16, mResampler32, mResampler48;
mResampler16, bool mActive;
mResampler32, void* mContext;
mResampler48; unsigned mSSRC;
bool mActive; unsigned mFadeOutCounter;
void* mContext; ByteBuffer mTempBuffer;
unsigned mSSRC;
unsigned mFadeOutCounter;
ByteBuffer mTempBuffer;
public: public:
Stream(); Stream();
~Stream(); ~Stream();
void setSsrc(unsigned ssrc); void setSsrc(unsigned ssrc);
unsigned ssrc(); unsigned ssrc();
void setContext(void* context); void setContext(void* context);
void* context(); void* context();
DataWindow& data(); DataWindow& data();
bool active(); bool active();
void setActive(bool active); void setActive(bool active);
void addPcm(int rate, const void* input, int length); void addPcm(int rate, const void* input, int length);
}; };
Stream mChannelList[AUDIO_MIX_CHANNEL_COUNT];
Mutex mMutex;
DataWindow mOutput;
std::atomic_int mActiveCounter;
void mix(); Stream mChannelList[AUDIO_MIX_CHANNEL_COUNT];
Stream* allocateChannel(void* context, unsigned ssrc); Mutex mMutex;
DataWindow mOutput;
std::atomic_int mActiveCounter;
public: void mix();
Stream* allocateChannel(void* context, unsigned ssrc);
public:
Mixer(); Mixer();
~Mixer(); ~Mixer();
void unregisterChannel(void* context); void unregisterChannel(void* context);
void clear(void* context, unsigned ssrc); void clear(void* context, unsigned ssrc);
void addPcm(void* context, unsigned ssrc, const void* inputData, int inputLength, int inputRate, bool fadeOut); void addPcm(void* context, unsigned ssrc, const void* inputData, int inputLength, int inputRate, bool fadeOut);
void addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate, bool fadeOut); void addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate, bool fadeOut);
int getPcm(void* outputData, int outputLength); int getPcm(void* outputData, int outputLength);
int mixAndGetPcm(Audio::DataWindow& output); int mixAndGetPcm(Audio::DataWindow& output);
int available(); int available();
}; };
} //end of namespace } // namespace Audio
#endif #endif
+21 -28
View File
@@ -7,8 +7,8 @@
using namespace Audio; using namespace Audio;
using namespace std::chrono_literals; using namespace std::chrono_literals;
NullTimer::NullTimer(std::chrono::milliseconds interval, Delegate *delegate, const char* name) NullTimer::NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name)
:mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name) : mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
{ {
start(); start();
} }
@@ -49,15 +49,13 @@ void NullTimer::run()
// Sleep for mInterval - mTail milliseconds // Sleep for mInterval - mTail milliseconds
std::this_thread::sleep_for(mInterval - mTail); std::this_thread::sleep_for(mInterval - mTail);
mTail = mTail + std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp); mTail =
mTail + std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp);
} }
} }
// --------------------- NullInputDevice ------------------------- // --------------------- NullInputDevice -------------------------
NullInputDevice::NullInputDevice() NullInputDevice::NullInputDevice() : mBuffer(nullptr) {}
:mBuffer(nullptr)
{
}
NullInputDevice::~NullInputDevice() NullInputDevice::~NullInputDevice()
{ {
@@ -69,7 +67,8 @@ bool NullInputDevice::open()
ICELogInfo(<< "Starting NullInputDevice for " << AUDIO_MIC_BUFFER_LENGTH << "ms buffers"); ICELogInfo(<< "Starting NullInputDevice for " << AUDIO_MIC_BUFFER_LENGTH << "ms buffers");
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE); mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE); memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
mTimeCounter = 0; mDataCounter = 0; mTimeCounter = 0;
mDataCounter = 0;
// Creation of timer starts it also. So first onTimerSignal can come even before open() returns. // Creation of timer starts it also. So first onTimerSignal can come even before open() returns.
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_MIC_BUFFER_LENGTH), this, "null_mic"); mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_MIC_BUFFER_LENGTH), this, "null_mic");
@@ -85,7 +84,7 @@ void NullInputDevice::internalClose()
free(mBuffer); free(mBuffer);
mBuffer = nullptr; mBuffer = nullptr;
} }
ICELogInfo( << "Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes."); ICELogInfo(<< "Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
} }
void NullInputDevice::close() void NullInputDevice::close()
@@ -95,7 +94,7 @@ void NullInputDevice::close()
Format NullInputDevice::getFormat() Format NullInputDevice::getFormat()
{ {
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE); assert(Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
return {}; // Return library-define default format return {}; // Return library-define default format
} }
@@ -109,10 +108,7 @@ void NullInputDevice::onTimerSignal(NullTimer& timer)
} }
// --------------------- NullOutputDevice -------------------------- // --------------------- NullOutputDevice --------------------------
NullOutputDevice::NullOutputDevice() NullOutputDevice::NullOutputDevice() : mBuffer(nullptr) {}
:mBuffer(nullptr)
{
}
NullOutputDevice::~NullOutputDevice() NullOutputDevice::~NullOutputDevice()
{ {
@@ -122,7 +118,8 @@ NullOutputDevice::~NullOutputDevice()
bool NullOutputDevice::open() bool NullOutputDevice::open()
{ {
mTimeCounter = 0; mDataCounter = 0; mTimeCounter = 0;
mDataCounter = 0;
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE); mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
// Creation of timer starts it also. So first onSpkData() can come before open() returns even. // Creation of timer starts it also. So first onSpkData() can come before open() returns even.
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_SPK_BUFFER_LENGTH), this, "null_spk"); mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_SPK_BUFFER_LENGTH), this, "null_spk");
@@ -132,7 +129,8 @@ bool NullOutputDevice::open()
void NullOutputDevice::internalClose() void NullOutputDevice::internalClose()
{ {
mTimer.reset(); mTimer.reset();
free(mBuffer); mBuffer = nullptr; free(mBuffer);
mBuffer = nullptr;
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes."); ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
} }
@@ -143,11 +141,11 @@ void NullOutputDevice::close()
Format NullOutputDevice::getFormat() Format NullOutputDevice::getFormat()
{ {
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE); assert(Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
return Format(); return Format();
} }
void NullOutputDevice::onTimerSignal(NullTimer &timer) void NullOutputDevice::onTimerSignal(NullTimer& timer)
{ {
mTimeCounter += AUDIO_SPK_BUFFER_LENGTH; mTimeCounter += AUDIO_SPK_BUFFER_LENGTH;
mDataCounter += AUDIO_SPK_BUFFER_SIZE; mDataCounter += AUDIO_SPK_BUFFER_SIZE;
@@ -156,19 +154,15 @@ void NullOutputDevice::onTimerSignal(NullTimer &timer)
} }
// ---------------------- NullEnumerator -------------------------- // ---------------------- NullEnumerator --------------------------
NullEnumerator::NullEnumerator() NullEnumerator::NullEnumerator() {}
{}
NullEnumerator::~NullEnumerator() NullEnumerator::~NullEnumerator() {}
{}
void NullEnumerator::open(int direction) void NullEnumerator::open(int direction) {}
{}
void NullEnumerator::close() void NullEnumerator::close() {}
{}
int NullEnumerator::count() int NullEnumerator::count()
{ {
return 1; return 1;
} }
@@ -191,4 +185,3 @@ int NullEnumerator::indexOfDefaultDevice()
{ {
return 0; return 0;
} }
+32 -30
View File
@@ -17,75 +17,77 @@ public:
}; };
protected: protected:
std::thread mWorkerThread; std::thread mWorkerThread;
std::atomic_bool mShutdown = {false}; std::atomic_bool mShutdown = {false};
Delegate* mDelegate = nullptr; Delegate* mDelegate = nullptr;
std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds
std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink
std::string mThreadName; std::string mThreadName;
void start();
void stop();
void run();
void start();
void stop();
void run();
public: public:
/* Interval is in milliseconds. */ /* Interval is in milliseconds. */
NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name = nullptr); NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name = nullptr);
~NullTimer(); ~NullTimer();
}; };
class NullInputDevice: public InputDevice, public NullTimer::Delegate class NullInputDevice : public InputDevice, public NullTimer::Delegate
{ {
protected: protected:
void* mBuffer = nullptr; void* mBuffer = nullptr;
std::shared_ptr<NullTimer> mTimer; std::shared_ptr<NullTimer> mTimer;
int64_t mTimeCounter = 0, mDataCounter = 0; int64_t mTimeCounter = 0, mDataCounter = 0;
void internalClose(); void internalClose();
public: public:
NullInputDevice(); NullInputDevice();
virtual ~NullInputDevice(); virtual ~NullInputDevice();
bool open() override; bool open() override;
void close() override; void close() override;
Format getFormat() override; Format getFormat() override;
void onTimerSignal(NullTimer& timer) override; void onTimerSignal(NullTimer& timer) override;
}; };
class NullOutputDevice: public OutputDevice, public NullTimer::Delegate class NullOutputDevice : public OutputDevice, public NullTimer::Delegate
{ {
protected: protected:
std::shared_ptr<NullTimer> mTimer; std::shared_ptr<NullTimer> mTimer;
void* mBuffer = nullptr; void* mBuffer = nullptr;
int64_t mDataCounter = 0, mTimeCounter = 0; int64_t mDataCounter = 0, mTimeCounter = 0;
void internalClose();
void internalClose();
public: public:
NullOutputDevice(); NullOutputDevice();
virtual ~NullOutputDevice(); virtual ~NullOutputDevice();
bool open() override; bool open() override;
void close() override; void close() override;
Format getFormat() override; Format getFormat() override;
void onTimerSignal(NullTimer& timer) override; void onTimerSignal(NullTimer& timer) override;
}; };
class NullEnumerator: public Enumerator class NullEnumerator : public Enumerator
{ {
public: public:
NullEnumerator(); NullEnumerator();
~NullEnumerator(); ~NullEnumerator();
void open(int direction) override; void open(int direction) override;
void close() override; void close() override;
int count() override; int count() override;
std::tstring nameAt(int index) override; std::tstring nameAt(int index) override;
int idAt(int index) override; int idAt(int index) override;
int indexOfDefaultDevice() override; int indexOfDefaultDevice() override;
}; };
} } // namespace Audio
#endif #endif
+87 -92
View File
@@ -11,161 +11,156 @@
using namespace Audio; using namespace Audio;
// -------------- Player ----------- // -------------- Player -----------
Player::Player() Player::Player() : mDelegate(nullptr), mPlayedTime(0) {}
:mDelegate(nullptr), mPlayedTime(0)
{
}
Player::~Player() Player::~Player() {}
{
}
void Player::setDelegate(EndOfAudioDelegate* d) void Player::setDelegate(EndOfAudioDelegate* d)
{ {
mDelegate = d; mDelegate = d;
} }
Player::EndOfAudioDelegate* Player::getDelegate() const Player::EndOfAudioDelegate* Player::getDelegate() const
{ {
return mDelegate; return mDelegate;
} }
void Player::setOutput(POutputDevice output) void Player::setOutput(POutputDevice output)
{ {
mOutput = output; mOutput = output;
if (mOutput) if (mOutput)
mOutput->setConnection(this); mOutput->setConnection(this);
} }
POutputDevice Player::getOutput() const POutputDevice Player::getOutput() const
{ {
return mOutput; return mOutput;
} }
void Player::onMicData(const Format& f, const void* buffer, int length) void Player::onMicData(const Format& f, const void* buffer, int length)
{ {
// Do nothing here - this data sink is not used in player // Do nothing here - this data sink is not used in player
} }
#define BYTES_PER_MILLISECOND (AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS) #define BYTES_PER_MILLISECOND (AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
void Player::onSpkData(const Format& f, void* buffer, int length) void Player::onSpkData(const Format& f, void* buffer, int length)
{ {
Lock l(mGuard); Lock l(mGuard);
// Fill buffer by zero if player owns dedicated device // Fill buffer by zero if player owns dedicated device
if (mOutput) if (mOutput)
memset(buffer, 0, length); memset(buffer, 0, length);
// See if there is item in playlist // See if there is item in playlist
int produced = 0; int produced = 0;
while (mPlaylist.size() && produced < length) while (mPlaylist.size() && produced < length)
{
PlaylistItem& item = mPlaylist.front();
// Check for timelength
if (item.mTimelength > 0 && item.mTimelength < mPlayedTime)
{ {
onFilePlayed(); PlaylistItem& item = mPlaylist.front();
continue; // Check for timelength
} if (item.mTimelength > 0 && item.mTimelength < mPlayedTime)
{
onFilePlayed();
continue;
}
int wasread = item.mFile->read((char*)buffer+produced, length-produced); int wasread = item.mFile->read((char*)buffer + produced, length - produced);
mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND;
produced += wasread;
if (wasread < length-produced)
{
if (item.mLoop)
{
item.mFile->rewind();
wasread = item.mFile->read((char*)buffer+produced, (length - produced));
mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND; mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND;
produced += wasread; produced += wasread;
} if (wasread < length - produced)
else {
onFilePlayed(); if (item.mLoop)
{
item.mFile->rewind();
wasread = item.mFile->read((char*)buffer + produced, (length - produced));
mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND;
produced += wasread;
}
else
onFilePlayed();
}
} }
}
} }
void Player::onFilePlayed() void Player::onFilePlayed()
{ {
// Save usage id to release later from main loop // Save usage id to release later from main loop
mFinishedUsages.push_back(mPlaylist.front().mUsageId); mFinishedUsages.push_back(mPlaylist.front().mUsageId);
// Send event // Send event
if (mDelegate) if (mDelegate)
mDelegate->onFilePlayed(mPlaylist.front()); mDelegate->onFilePlayed(mPlaylist.front());
// Remove played item & reset played time // Remove played item & reset played time
mPlaylist.pop_front(); mPlaylist.pop_front();
mPlayedTime = 0; mPlayedTime = 0;
} }
void Player::obtain(int usage) void Player::obtain(int usage)
{ {
Lock l(mGuard); Lock l(mGuard);
auto usageIter = mUsage.find(usage); auto usageIter = mUsage.find(usage);
if (usageIter == mUsage.end()) if (usageIter == mUsage.end())
mUsage[usage] = 1; mUsage[usage] = 1;
else else
usageIter->second = usageIter->second + 1; usageIter->second = usageIter->second + 1;
if (mUsage.size() == 1 && mOutput) if (mUsage.size() == 1 && mOutput)
mOutput->open(); mOutput->open();
} }
void Player::release(int usage) void Player::release(int usage)
{ {
Lock l(mGuard); Lock l(mGuard);
UsageMap::iterator usageIter = mUsage.find(usage); UsageMap::iterator usageIter = mUsage.find(usage);
if (usageIter == mUsage.end()) if (usageIter == mUsage.end())
return; return;
usageIter->second = usageIter->second - 1; usageIter->second = usageIter->second - 1;
if (!usageIter->second) if (!usageIter->second)
mUsage.erase(usageIter); mUsage.erase(usageIter);
for (unsigned i=0; i<mPlaylist.size(); i++) for (unsigned i = 0; i < mPlaylist.size(); i++)
if (mPlaylist[i].mUsageId == usage) if (mPlaylist[i].mUsageId == usage)
mPlaylist.erase(mPlaylist.begin() + i); mPlaylist.erase(mPlaylist.begin() + i);
if (mUsage.empty() && mOutput) if (mUsage.empty() && mOutput)
mOutput->close(); mOutput->close();
} }
int Player::releasePlayed() int Player::releasePlayed()
{ {
Lock l(mGuard); Lock l(mGuard);
int result = mFinishedUsages.size(); int result = mFinishedUsages.size();
while (!mFinishedUsages.empty()) while (!mFinishedUsages.empty())
{ {
release(mFinishedUsages.front()); release(mFinishedUsages.front());
mFinishedUsages.erase(mFinishedUsages.begin()); mFinishedUsages.erase(mFinishedUsages.begin());
} }
return result; return result;
} }
void Player::add(int usageId, PWavFileReader file, bool loop, int timelength) void Player::add(int usageId, PWavFileReader file, bool loop, int timelength)
{ {
Lock l(mGuard); Lock l(mGuard);
PlaylistItem item; PlaylistItem item;
item.mFile = file; item.mFile = file;
item.mLoop = loop; item.mLoop = loop;
item.mTimelength = timelength; item.mTimelength = timelength;
item.mUsageId = usageId; item.mUsageId = usageId;
mPlaylist.push_back(item); mPlaylist.push_back(item);
obtain(usageId); obtain(usageId);
} }
void Player::clear() void Player::clear()
{ {
Lock l(mGuard); Lock l(mGuard);
while (mPlaylist.size()) while (mPlaylist.size())
onFilePlayed(); onFilePlayed();
} }
void Player::retrieveUsageIds(std::vector<int>& ids) void Player::retrieveUsageIds(std::vector<int>& ids)
{ {
ids.assign(mFinishedUsages.begin(), mFinishedUsages.end()); ids.assign(mFinishedUsages.begin(), mFinishedUsages.end());
mFinishedUsages.clear(); mFinishedUsages.clear();
} }
+33 -32
View File
@@ -16,56 +16,57 @@
namespace Audio namespace Audio
{ {
class Player: public DataConnection class Player : public DataConnection
{ {
friend class DevicePair; friend class DevicePair;
public:
public:
struct PlaylistItem struct PlaylistItem
{ {
PWavFileReader mFile; PWavFileReader mFile;
bool mLoop; bool mLoop;
int mTimelength; int mTimelength;
int mUsageId; int mUsageId;
}; };
typedef std::deque<PlaylistItem> Playlist; typedef std::deque<PlaylistItem> Playlist;
class EndOfAudioDelegate class EndOfAudioDelegate
{ {
public: public:
virtual void onFilePlayed(PlaylistItem& item) = 0; virtual void onFilePlayed(PlaylistItem& item) = 0;
}; };
protected: protected:
typedef std::map<int, int> UsageMap; typedef std::map<int, int> UsageMap;
Audio::POutputDevice mOutput; Audio::POutputDevice mOutput;
UsageMap mUsage; // References map UsageMap mUsage; // References map
std::vector<int> mFinishedUsages; // Finished plays std::vector<int> mFinishedUsages; // Finished plays
Mutex mGuard; Mutex mGuard;
Playlist mPlaylist; Playlist mPlaylist;
float mPlayedTime; float mPlayedTime;
EndOfAudioDelegate* mDelegate; EndOfAudioDelegate* mDelegate;
void onMicData(const Format& f, const void* buffer, int length); void onMicData(const Format& f, const void* buffer, int length);
void onSpkData(const Format& f, void* buffer, int length); void onSpkData(const Format& f, void* buffer, int length);
void onFilePlayed(); void onFilePlayed();
void obtain(int usageId); void obtain(int usageId);
public: public:
Player(); Player();
~Player(); ~Player();
void setDelegate(EndOfAudioDelegate* d); void setDelegate(EndOfAudioDelegate* d);
EndOfAudioDelegate* getDelegate() const; EndOfAudioDelegate* getDelegate() const;
void setOutput(POutputDevice output); void setOutput(POutputDevice output);
POutputDevice getOutput() const; POutputDevice getOutput() const;
void add(int usageId, PWavFileReader file, bool loop, int timelength); void add(int usageId, PWavFileReader file, bool loop, int timelength);
void release(int usageId); void release(int usageId);
void clear(); void clear();
int releasePlayed(); int releasePlayed();
void retrieveUsageIds(std::vector<int>& ids); void retrieveUsageIds(std::vector<int>& ids);
}; };
} } // namespace Audio
#endif #endif
+122 -126
View File
@@ -10,7 +10,7 @@
#include "speex/speex_preprocess.h" #include "speex/speex_preprocess.h"
#ifdef WIN32 #ifdef WIN32
# include <malloc.h> #include <malloc.h>
#endif #endif
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@@ -18,221 +18,217 @@
using namespace Audio; using namespace Audio;
#ifndef SHRT_MAX #ifndef SHRT_MAX
# define SHRT_MAX 32767 /* maximum (signed) short value */ #define SHRT_MAX 32767 /* maximum (signed) short value */
#endif #endif
AgcFilter::AgcFilter(int channels) AgcFilter::AgcFilter(int channels)
{ {
static const float DefaultLevel = 0.8f; static const float DefaultLevel = 0.8f;
for (int i=0; i<channels; i++) for (int i = 0; i < channels; i++)
{ {
Channel c; Channel c;
float level = DefaultLevel; float level = DefaultLevel;
c.mSampleMax = 1; c.mSampleMax = 1;
c.mCounter = 0; c.mCounter = 0;
c.mIgain = 65536; c.mIgain = 65536;
if (level > 1.0f) if (level > 1.0f)
level = 1.0f; level = 1.0f;
else else if (level < 0.5f)
if (level < 0.5f) level = 0.5f;
level = 0.5f;
c.mIpeak = (int)(SHRT_MAX * level * 65536); c.mIpeak = (int)(SHRT_MAX * level * 65536);
c.mSilenceCounter = 0; c.mSilenceCounter = 0;
mChannelList.push_back(c); mChannelList.push_back(c);
} }
} }
AgcFilter::~AgcFilter() AgcFilter::~AgcFilter() {}
{
}
void AgcFilter::process(void *pcm, int length) void AgcFilter::process(void* pcm, int length)
{ {
for (size_t i=0; i<mChannelList.size(); i++) for (size_t i = 0; i < mChannelList.size(); i++)
processChannel((short*)pcm, length / (sizeof(short) * mChannelList.size()), i); processChannel((short*)pcm, length / (sizeof(short) * mChannelList.size()), i);
} }
void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex) void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex)
{ {
int i; int i;
for(i=0; i<nrOfSamples; i++) for (i = 0; i < nrOfSamples; i++)
{
long gain_new;
int sample;
int sampleIndex = mChannelList.size() * i + channelIndex;
Channel& channel = mChannelList[channelIndex];
/* get the abs of buffer[i] */
sample = pcm[sampleIndex];
sample = (sample < 0 ? -(sample):sample);
if(sample > (int)channel.mSampleMax)
{ {
/* update the max */ long gain_new;
channel.mSampleMax = (unsigned int)sample; int sample;
} int sampleIndex = mChannelList.size() * i + channelIndex;
channel.mCounter ++; Channel& channel = mChannelList[channelIndex];
/* Will we get an overflow with the current gain factor? */ /* get the abs of buffer[i] */
if (((sample * channel.mIgain) >> 16) > channel.mIpeak) sample = pcm[sampleIndex];
{ sample = (sample < 0 ? -(sample) : sample);
/* Yes: Calculate new gain. */
channel.mIgain = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16;
channel.mSilenceCounter = 0;
pcm[sampleIndex] = (short) ((pcm[sampleIndex] * channel.mIgain) >> 16);
continue;
}
/* Calculate new gain factor 10x per second */ if (sample > (int)channel.mSampleMax)
if (channel.mCounter >= AUDIO_SAMPLERATE / 10) {
{ /* update the max */
if (channel.mSampleMax > AUDIO_SAMPLERATE / 10) /* speaking? */ channel.mSampleMax = (unsigned int)sample;
{ }
gain_new = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16; channel.mCounter++;
if (channel.mSilenceCounter > 40) /* pause -> speaking */
channel.mIgain += (gain_new - channel.mIgain) >> 2;
else
channel.mIgain += (gain_new - channel.mIgain) / 20;
channel.mSilenceCounter = 0; /* Will we get an overflow with the current gain factor? */
} if (((sample * channel.mIgain) >> 16) > channel.mIpeak)
else /* silence */ {
{ /* Yes: Calculate new gain. */
channel.mSilenceCounter++; channel.mIgain = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16;
/* silence > 2 seconds: reduce gain */ channel.mSilenceCounter = 0;
if ((channel.mIgain > 65536) && (channel.mSilenceCounter >= 20)) pcm[sampleIndex] = (short)((pcm[sampleIndex] * channel.mIgain) >> 16);
channel.mIgain = (channel.mIgain * 62259) >> 16; continue;
} }
channel.mCounter = 0; /* Calculate new gain factor 10x per second */
channel.mSampleMax = 1; if (channel.mCounter >= AUDIO_SAMPLERATE / 10)
{
if (channel.mSampleMax > AUDIO_SAMPLERATE / 10) /* speaking? */
{
gain_new = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16;
if (channel.mSilenceCounter > 40) /* pause -> speaking */
channel.mIgain += (gain_new - channel.mIgain) >> 2;
else
channel.mIgain += (gain_new - channel.mIgain) / 20;
channel.mSilenceCounter = 0;
}
else /* silence */
{
channel.mSilenceCounter++;
/* silence > 2 seconds: reduce gain */
if ((channel.mIgain > 65536) && (channel.mSilenceCounter >= 20))
channel.mIgain = (channel.mIgain * 62259) >> 16;
}
channel.mCounter = 0;
channel.mSampleMax = 1;
}
pcm[sampleIndex] = (short)((pcm[sampleIndex] * channel.mIgain) >> 16);
} }
pcm[sampleIndex] = (short) ((pcm[sampleIndex] * channel.mIgain) >> 16);
}
} }
// --- AecFilter --- // --- AecFilter ---
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
# include "speex/speex_echo.h" #include "speex/speex_echo.h"
#include "Audio_Interface.h" #include "Audio_Interface.h"
#if !defined(TARGET_WIN) #if !defined(TARGET_WIN)
# include <alloca.h> #include <alloca.h>
#endif #endif
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
# include "aec/echo_cancellation.h" #include "aec/echo_cancellation.h"
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
static void CheckWRACode(unsigned errorcode) static void CheckWRACode(unsigned errorcode)
{ {
if (errorcode) if (errorcode)
throw Exception(ERR_WEBRTC, errorcode); throw Exception(ERR_WEBRTC, errorcode);
} }
#endif #endif
AecFilter::AecFilter(int tailTime, int frameTime, int rate) AecFilter::AecFilter(int tailTime, int frameTime, int rate) : mCtx(nullptr), mFrameTime(frameTime), mRate(rate)
:mCtx(nullptr), mFrameTime(frameTime), mRate(rate)
{ {
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
if (AUDIO_CHANNELS == 2) if (AUDIO_CHANNELS == 2)
mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS, AUDIO_CHANNELS ); mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS,
else AUDIO_CHANNELS);
mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000)); else
int tmp = rate; mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000));
speex_echo_ctl((SpeexEchoState*)mCtx, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp); int tmp = rate;
speex_echo_ctl((SpeexEchoState*)mCtx, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp);
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
CheckWRACode(WebRtcAec_Create(&mCtx)); CheckWRACode(WebRtcAec_Create(&mCtx));
CheckWRACode(WebRtcAec_Init(mCtx, rate, rate)); CheckWRACode(WebRtcAec_Init(mCtx, rate, rate));
#endif #endif
} }
AecFilter::~AecFilter() AecFilter::~AecFilter()
{ {
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
if (mCtx) if (mCtx)
{ {
//speex_echo_state_destroy((SpeexEchoState*)mCtx); // speex_echo_state_destroy((SpeexEchoState*)mCtx);
mCtx = nullptr; mCtx = nullptr;
} }
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
CheckWRACode(WebRtcAec_Free(mCtx)); CheckWRACode(WebRtcAec_Free(mCtx));
mCtx = NULL; mCtx = NULL;
#endif #endif
} }
void AecFilter::fromMic(void *data) void AecFilter::fromMic(void* data)
{ {
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
short* output = (short*)alloca(Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH)); short* output = (short*)alloca(Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
speex_echo_capture((SpeexEchoState*)mCtx, (short*)data, (short*)output); speex_echo_capture((SpeexEchoState*)mCtx, (short*)data, (short*)output);
memmove(data, output, AUDIO_MIC_BUFFER_SIZE); memmove(data, output, AUDIO_MIC_BUFFER_SIZE);
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
short* inputframe = (short*)ALLOCA(framesize); short* inputframe = (short*)ALLOCA(framesize);
memcpy(inputframe, (char*)data+framesize*i, framesize); memcpy(inputframe, (char*)data + framesize * i, framesize);
CheckWRACode(WebRtcAec_Process(mCtx, (short*)inputframe, NULL, (short*)data+framesize/2*i, NULL, mFrameTime * mRate / 1000, 0,0)); CheckWRACode(WebRtcAec_Process(mCtx, (short*)inputframe, NULL, (short*)data + framesize / 2 * i, NULL,
mFrameTime * mRate / 1000, 0, 0));
#endif #endif
} }
void AecFilter::toSpeaker(void *data) void AecFilter::toSpeaker(void* data)
{ {
#ifdef USE_SPEEX_AEC #ifdef USE_SPEEX_AEC
speex_echo_playback((SpeexEchoState*)mCtx, (short*)data); speex_echo_playback((SpeexEchoState*)mCtx, (short*)data);
#endif #endif
#ifdef USE_WEBRTC_AEC #ifdef USE_WEBRTC_AEC
CheckWRACode(WebRtcAec_BufferFarend(mCtx, (short*)data, length / 2 / AUDIO_CHANNELS)); CheckWRACode(WebRtcAec_BufferFarend(mCtx, (short*)data, length / 2 / AUDIO_CHANNELS));
#endif #endif
} }
int AecFilter::frametime() int AecFilter::frametime()
{ {
return mFrameTime; return mFrameTime;
} }
DenoiseFilter::DenoiseFilter(int rate) DenoiseFilter::DenoiseFilter(int rate) : mRate(rate)
:mRate(rate)
{ {
mCtx = speex_preprocess_state_init(mRate/100, mRate); mCtx = speex_preprocess_state_init(mRate / 100, mRate);
} }
DenoiseFilter::~DenoiseFilter() DenoiseFilter::~DenoiseFilter()
{ {
if (mCtx) if (mCtx)
speex_preprocess_state_destroy((SpeexPreprocessState*)mCtx); speex_preprocess_state_destroy((SpeexPreprocessState*)mCtx);
} }
void DenoiseFilter::fromMic(void* data, int timelength) void DenoiseFilter::fromMic(void* data, int timelength)
{ {
assert(timelength % 10 == 0); assert(timelength % 10 == 0);
// Process by 10-ms blocks // Process by 10-ms blocks
spx_int16_t* in = (spx_int16_t*)data; spx_int16_t* in = (spx_int16_t*)data;
for (int blockIndex=0; blockIndex<timelength/10; blockIndex++) for (int blockIndex = 0; blockIndex < timelength / 10; blockIndex++)
{ {
spx_int16_t* block = in + blockIndex * (mRate / 100) * AUDIO_CHANNELS; spx_int16_t* block = in + blockIndex * (mRate / 100) * AUDIO_CHANNELS;
speex_preprocess_run((SpeexPreprocessState*)mCtx, block); speex_preprocess_run((SpeexPreprocessState*)mCtx, block);
} }
} }
int DenoiseFilter::rate() int DenoiseFilter::rate()
{ {
return mRate; return mRate;
} }
+35 -34
View File
@@ -11,59 +11,60 @@
namespace Audio namespace Audio
{ {
class AgcFilter class AgcFilter
{ {
protected: protected:
struct Channel struct Channel
{ {
unsigned int mSampleMax; unsigned int mSampleMax;
int mCounter; int mCounter;
long mIgain; long mIgain;
int mIpeak; int mIpeak;
int mSilenceCounter; int mSilenceCounter;
}; };
std::vector<Channel> mChannelList; std::vector<Channel> mChannelList;
void processChannel(short* pcm, int nrOfSamples, int channelIndex); void processChannel(short* pcm, int nrOfSamples, int channelIndex);
public:
public:
AgcFilter(int channels); AgcFilter(int channels);
~AgcFilter(); ~AgcFilter();
void process(void* pcm, int length);
};
class AecFilter void process(void* pcm, int length);
{ };
public:
class AecFilter
{
public:
AecFilter(int tailTime, int frameTime, int rate); AecFilter(int tailTime, int frameTime, int rate);
~AecFilter(); ~AecFilter();
// These methods accept input block with timelength "frameTime" used in constructor. // These methods accept input block with timelength "frameTime" used in constructor.
void toSpeaker(void* data); void toSpeaker(void* data);
void fromMic(void* data); void fromMic(void* data);
int frametime(); int frametime();
protected:
void* mCtx; /// The echo canceller context's pointer.
Mutex mGuard; /// Mutex to protect this instance.
int mFrameTime; /// Duration of single audio frame (in milliseconds)
int mRate;
};
class DenoiseFilter protected:
{ void* mCtx; /// The echo canceller context's pointer.
public: Mutex mGuard; /// Mutex to protect this instance.
int mFrameTime; /// Duration of single audio frame (in milliseconds)
int mRate;
};
class DenoiseFilter
{
public:
DenoiseFilter(int rate); DenoiseFilter(int rate);
~DenoiseFilter(); ~DenoiseFilter();
void fromMic(void* data, int timelength); void fromMic(void* data, int timelength);
int rate(); int rate();
protected: protected:
Mutex mGuard; /// Mutex to protect this instance. Mutex mGuard; /// Mutex to protect this instance.
void* mCtx; /// The denoiser context pointer. void* mCtx; /// The denoiser context pointer.
int mRate; /// Duration of single audio frame (in milliseconds) int mRate; /// Duration of single audio frame (in milliseconds)
}; };
} } // namespace Audio
#endif #endif
+34 -45
View File
@@ -17,8 +17,7 @@ namespace Audio
{ {
SpeexResampler::SpeexResampler() SpeexResampler::SpeexResampler() {}
{}
void SpeexResampler::start(int channels, int sourceRate, int destRate) void SpeexResampler::start(int channels, int sourceRate, int destRate)
{ {
@@ -35,8 +34,8 @@ void SpeexResampler::start(int channels, int sourceRate, int destRate)
if (sourceRate != destRate) if (sourceRate != destRate)
{ {
// Defer context creation until first request // Defer context creation until first request
//mContext = speex_resampler_init(channels, sourceRate, destRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode); // mContext = speex_resampler_init(channels, sourceRate, destRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
//assert(mContext != NULL); // assert(mContext != NULL);
} }
} }
@@ -59,8 +58,8 @@ SpeexResampler::~SpeexResampler()
stop(); stop();
} }
size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed, size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed, void* dest,
void* dest, size_t destCapacity) size_t destCapacity)
{ {
assert(mSourceRate != 0 && mDestRate != 0); assert(mSourceRate != 0 && mDestRate != 0);
@@ -77,8 +76,7 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
if (!mContext) if (!mContext)
{ {
mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate, mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
AUDIO_RESAMPLER_QUALITY, &mErrorCode);
if (!mContext) if (!mContext)
return 0; return 0;
} }
@@ -103,11 +101,9 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
unsigned inLen = sourceLength / (sizeof(short) * mChannels); unsigned inLen = sourceLength / (sizeof(short) * mChannels);
outLen /= sizeof(short) * mChannels; outLen /= sizeof(short) * mChannels;
assert(mContext != NULL); assert(mContext != NULL);
spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen), spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen), out_len = static_cast<spx_uint32_t>(outLen);
out_len = static_cast<spx_uint32_t>(outLen);
int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState *)mContext, int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState*)mContext, (spx_int16_t*)src, &in_len,
(spx_int16_t*)src, &in_len,
(spx_int16_t*)dest, &out_len); (spx_int16_t*)dest, &out_len);
assert(speexCode == RESAMPLER_ERR_SUCCESS); assert(speexCode == RESAMPLER_ERR_SUCCESS);
@@ -147,27 +143,27 @@ size_t SpeexResampler::getSize() const
} }
// -------------------------- ChannelConverter -------------------- // -------------------------- ChannelConverter --------------------
int ChannelConverter::stereoToMono(const void *source, int sourceLength, void *dest, int destLength) int ChannelConverter::stereoToMono(const void* source, int sourceLength, void* dest, int destLength)
{ {
assert(destLength == sourceLength / 2); assert(destLength == sourceLength / 2);
const short* input = (const short*)source; const short* input = (const short*)source;
short* output = (short*)dest; short* output = (short*)dest;
for (int sampleIndex = 0; sampleIndex < destLength/2; sampleIndex++) for (int sampleIndex = 0; sampleIndex < destLength / 2; sampleIndex++)
{ {
output[sampleIndex] = (input[sampleIndex*2] + input[sampleIndex*2+1]) >> 1; output[sampleIndex] = (input[sampleIndex * 2] + input[sampleIndex * 2 + 1]) >> 1;
} }
return sourceLength / 2; return sourceLength / 2;
} }
int ChannelConverter::monoToStereo(const void *source, int sourceLength, void *dest, int destLength) int ChannelConverter::monoToStereo(const void* source, int sourceLength, void* dest, int destLength)
{ {
assert (destLength == sourceLength * 2); assert(destLength == sourceLength * 2);
const short* input = (const short*)source; const short* input = (const short*)source;
short* output = (short*)dest; short* output = (short*)dest;
// Convert starting from the end of buffer to allow inplace conversion // Convert starting from the end of buffer to allow inplace conversion
for (int sampleIndex = sourceLength/2 - 1; sampleIndex >= 0; sampleIndex--) for (int sampleIndex = sourceLength / 2 - 1; sampleIndex >= 0; sampleIndex--)
{ {
output[2*sampleIndex] = output[2*sampleIndex+1] = input[sampleIndex]; output[2 * sampleIndex] = output[2 * sampleIndex + 1] = input[sampleIndex];
} }
return sourceLength * 2; return sourceLength * 2;
} }
@@ -184,13 +180,14 @@ Resampler48kTo16k::~Resampler48kTo16k()
WebRtcSpl_ResetResample48khzTo16khz(&mContext); WebRtcSpl_ResetResample48khzTo16khz(&mContext);
} }
int Resampler48kTo16k::process(const void *source, int sourceLen, void *dest, int destLen) int Resampler48kTo16k::process(const void* source, int sourceLen, void* dest, int destLen)
{ {
const short* input = (const short*)source; int inputLen = sourceLen / 2; const short* input = (const short*)source;
short* output = (short*)dest; //int outputCapacity = destLen / 2; int inputLen = sourceLen / 2;
short* output = (short*)dest; // int outputCapacity = destLen / 2;
assert(inputLen % 480 == 0); assert(inputLen % 480 == 0);
int frames = inputLen / 480; int frames = inputLen / 480;
for (int i=0; i<frames; i++) for (int i = 0; i < frames; i++)
WebRtcSpl_Resample48khzTo16khz(input + i * 480, output + i * 160, &mContext, mTemp); WebRtcSpl_Resample48khzTo16khz(input + i * 480, output + i * 160, &mContext, mTemp);
return sourceLen / 3; return sourceLen / 3;
@@ -207,13 +204,14 @@ Resampler16kto48k::~Resampler16kto48k()
WebRtcSpl_ResetResample16khzTo48khz(&mContext); WebRtcSpl_ResetResample16khzTo48khz(&mContext);
} }
int Resampler16kto48k::process(const void *source, int sourceLen, void *dest, int destLen) int Resampler16kto48k::process(const void* source, int sourceLen, void* dest, int destLen)
{ {
const WebRtc_Word16* input = (const WebRtc_Word16*)source; int inputLen = sourceLen / 2; const WebRtc_Word16* input = (const WebRtc_Word16*)source;
WebRtc_Word16* output = (WebRtc_Word16*)dest; //int outputCapacity = destLen / 2; int inputLen = sourceLen / 2;
WebRtc_Word16* output = (WebRtc_Word16*)dest; // int outputCapacity = destLen / 2;
assert(inputLen % 160 == 0); assert(inputLen % 160 == 0);
int frames = inputLen / 160; int frames = inputLen / 160;
for (int i=0; i<frames; i++) for (int i = 0; i < frames; i++)
WebRtcSpl_Resample16khzTo48khz(input + i * 160, output + i * 480, &mContext, mTemp); WebRtcSpl_Resample16khzTo48khz(input + i * 160, output + i * 480, &mContext, mTemp);
return sourceLen * 3; return sourceLen * 3;
@@ -222,18 +220,12 @@ int Resampler16kto48k::process(const void *source, int sourceLen, void *dest, in
#endif #endif
// ---------------- UniversalResampler ------------------- // ---------------- UniversalResampler -------------------
UniversalResampler::UniversalResampler() UniversalResampler::UniversalResampler() {}
{
} UniversalResampler::~UniversalResampler() {}
UniversalResampler::~UniversalResampler() size_t UniversalResampler::resample(int sourceRate, const void* sourceBuffer, size_t sourceLength,
{ size_t& sourceProcessed, int destRate, void* destBuffer, size_t destCapacity)
}
size_t UniversalResampler::resample(int sourceRate, const void *sourceBuffer, size_t sourceLength,
size_t& sourceProcessed, int destRate, void *destBuffer, size_t destCapacity)
{ {
assert(destBuffer && sourceBuffer); assert(destBuffer && sourceBuffer);
size_t result; size_t result;
@@ -252,10 +244,7 @@ size_t UniversalResampler::resample(int sourceRate, const void *sourceBuffer, si
return result; return result;
} }
void UniversalResampler::preload() void UniversalResampler::preload() {}
{
}
size_t UniversalResampler::getDestLength(int sourceRate, int destRate, size_t sourceLength) size_t UniversalResampler::getDestLength(int sourceRate, int destRate, size_t sourceLength)
{ {
@@ -277,7 +266,7 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
{ {
assert(sourceRate != destRate); assert(sourceRate != destRate);
ResamplerMap::iterator resamplerIter = mResamplerMap.find(RatePair(sourceRate, destRate)); ResamplerMap::iterator resamplerIter = mResamplerMap.find(RatePair(sourceRate, destRate));
PResampler r; PResampler r;
if (resamplerIter == mResamplerMap.end()) if (resamplerIter == mResamplerMap.end())
{ {
r = std::make_shared<Resampler>(); r = std::make_shared<Resampler>();
@@ -289,4 +278,4 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
return r; return r;
} }
} // end of namespace } // namespace Audio
+74 -75
View File
@@ -7,100 +7,99 @@
#define __AUDIO_RESAMPLER_H #define __AUDIO_RESAMPLER_H
#ifdef USE_WEBRTC_RESAMPLER #ifdef USE_WEBRTC_RESAMPLER
# include "signal_processing_library/signal_processing_library.h" #include "signal_processing_library/signal_processing_library.h"
#endif #endif
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <map> #include <map>
namespace Audio namespace Audio
{ {
class SpeexResampler class SpeexResampler
{ {
public: public:
SpeexResampler(); SpeexResampler();
~SpeexResampler(); ~SpeexResampler();
void start(int channels, int sourceRate, int destRate); void start(int channels, int sourceRate, int destRate);
void stop(); void stop();
bool isOpened() const; bool isOpened() const;
size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed, size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed, void* dest,
void* dest, size_t destCapacity); size_t destCapacity);
int sourceRate() const; int sourceRate() const;
int destRate() const; int destRate() const;
size_t getDestLength(size_t sourceLen) const; size_t getDestLength(size_t sourceLen) const;
size_t getSourceLength(size_t destLen) const; size_t getSourceLength(size_t destLen) const;
// Returns instance + speex encoder size in bytes // Returns instance + speex encoder size in bytes
size_t getSize() const; size_t getSize() const;
protected: protected:
void* mContext = nullptr; void* mContext = nullptr;
int mErrorCode = 0; int mErrorCode = 0;
int mSourceRate = 0, int mSourceRate = 0, mDestRate = 0, mChannels = 0;
mDestRate = 0, short mLastSample = 0;
mChannels = 0; };
short mLastSample = 0;
};
typedef SpeexResampler Resampler; typedef SpeexResampler Resampler;
typedef std::shared_ptr<Resampler> PResampler; typedef std::shared_ptr<Resampler> PResampler;
class ChannelConverter class ChannelConverter
{ {
public: public:
static int stereoToMono(const void* source, int sourceLength, void* dest, int destLength); static int stereoToMono(const void* source, int sourceLength, void* dest, int destLength);
static int monoToStereo(const void* source, int sourceLength, void* dest, int destLength); static int monoToStereo(const void* source, int sourceLength, void* dest, int destLength);
}; };
// Operates with AUDIO_CHANNELS number of channels // Operates with AUDIO_CHANNELS number of channels
class UniversalResampler class UniversalResampler
{ {
public: public:
UniversalResampler(); UniversalResampler();
~UniversalResampler(); ~UniversalResampler();
size_t resample(int sourceRate, const void* sourceBuffer, size_t sourceLength, size_t& sourceProcessed, size_t resample(int sourceRate, const void* sourceBuffer, size_t sourceLength, size_t& sourceProcessed,
int destRate, void* destBuffer, size_t destCapacity); int destRate, void* destBuffer, size_t destCapacity);
size_t getDestLength(int sourceRate, int destRate, size_t sourceLength); size_t getDestLength(int sourceRate, int destRate, size_t sourceLength);
size_t getSourceLength(int sourceRate, int destRate, size_t destLength); size_t getSourceLength(int sourceRate, int destRate, size_t destLength);
protected: protected:
typedef std::pair<int, int> RatePair; typedef std::pair<int, int> RatePair;
typedef std::map<RatePair, PResampler> ResamplerMap; typedef std::map<RatePair, PResampler> ResamplerMap;
ResamplerMap mResamplerMap; ResamplerMap mResamplerMap;
PResampler findResampler(int sourceRate, int destRate); PResampler findResampler(int sourceRate, int destRate);
void preload(); void preload();
}; };
#ifdef USE_WEBRTC_RESAMPLER #ifdef USE_WEBRTC_RESAMPLER
// n*10 milliseconds buffers required! // n*10 milliseconds buffers required!
class Resampler48kTo16k class Resampler48kTo16k
{ {
public: public:
Resampler48kTo16k(); Resampler48kTo16k();
~Resampler48kTo16k(); ~Resampler48kTo16k();
int process(const void* source, int sourceLen, void* dest, int destLen); int process(const void* source, int sourceLen, void* dest, int destLen);
protected:
WebRtc_Word32 mTemp[496];
WebRtcSpl_State48khzTo16khz mContext;
};
class Resampler16kto48k protected:
{ WebRtc_Word32 mTemp[496];
public: WebRtcSpl_State48khzTo16khz mContext;
Resampler16kto48k(); };
~Resampler16kto48k();
int process(const void* source, int sourceLen, void* dest, int destLen);
protected: class Resampler16kto48k
WebRtc_Word32 mTemp[336]; {
WebRtcSpl_State16khzTo48khz mContext; public:
}; Resampler16kto48k();
#endif ~Resampler16kto48k();
} // end of namespace int process(const void* source, int sourceLen, void* dest, int destLen);
protected:
WebRtc_Word32 mTemp[336];
WebRtcSpl_State16khzTo48khz mContext;
};
#endif
} // namespace Audio
#endif #endif
+26 -32
View File
@@ -13,13 +13,14 @@
#include <assert.h> #include <assert.h>
#ifndef WORD #ifndef WORD
# define WORD unsigned short #define WORD unsigned short
#endif #endif
#ifndef DWORD #ifndef DWORD
# define DWORD unsigned int #define DWORD unsigned int
#endif #endif
typedef struct { typedef struct
{
WORD wFormatTag; WORD wFormatTag;
WORD nChannels; WORD nChannels;
DWORD nSamplesPerSec; DWORD nSamplesPerSec;
@@ -27,8 +28,7 @@ typedef struct {
WORD nBlockAlign; WORD nBlockAlign;
WORD wBitsPerSample; WORD wBitsPerSample;
WORD cbSize; WORD cbSize;
} } WaveFormatEx;
WaveFormatEx;
#define WAVE_FORMAT_PCM 1 #define WAVE_FORMAT_PCM 1
@@ -39,17 +39,14 @@ WaveFormatEx;
using namespace Audio; using namespace Audio;
// ---------------------- WavFileReader ------------------------- // ---------------------- WavFileReader -------------------------
WavFileReader::WavFileReader() WavFileReader::WavFileReader() : mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
:mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
{ {
mDataOffset = 0; mDataOffset = 0;
} }
WavFileReader::~WavFileReader() WavFileReader::~WavFileReader() {}
{
}
#define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED); #define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED);
std::string WavFileReader::readChunk() std::string WavFileReader::readChunk()
{ {
@@ -57,7 +54,7 @@ std::string WavFileReader::readChunk()
readBuffer(name, 4); readBuffer(name, 4);
std::string result = name; std::string result = name;
uint32_t size = 0; uint32_t size = 0;
readBuffer(&size, 4); readBuffer(&size, 4);
if (result == "data") if (result == "data")
@@ -69,7 +66,7 @@ std::string WavFileReader::readChunk()
return result; return result;
} }
void WavFileReader::readBuffer(void* buffer, size_t sz) void WavFileReader::readBuffer(void* buffer, size_t sz)
{ {
auto p = mInput->tellg(); auto p = mInput->tellg();
mInput->read(reinterpret_cast<char*>(buffer), sz); mInput->read(reinterpret_cast<char*>(buffer), sz);
@@ -122,12 +119,12 @@ bool WavFileReader::open(const std::filesystem::path& p)
uint32_t fmtSize = 0; uint32_t fmtSize = 0;
readBuffer(&fmtSize, sizeof(fmtSize)); readBuffer(&fmtSize, sizeof(fmtSize));
auto fmtStart = mInput->tellg(); auto fmtStart = mInput->tellg();
uint16_t formattag = 0; uint16_t formattag = 0;
readBuffer(&formattag, sizeof(formattag)); readBuffer(&formattag, sizeof(formattag));
if (formattag != 1/*WAVE_FORMAT_PCM*/) if (formattag != 1 /*WAVE_FORMAT_PCM*/)
THROW_READERROR; THROW_READERROR;
mChannels = 0; mChannels = 0;
@@ -168,7 +165,7 @@ bool WavFileReader::open(const std::filesystem::path& p)
mDataOffset = mInput->tellg(); mDataOffset = mInput->tellg();
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE); mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
} }
catch(...) catch (...)
{ {
mInput.reset(); mInput.reset();
mLastError = static_cast<unsigned>(-1); mLastError = static_cast<unsigned>(-1);
@@ -211,8 +208,8 @@ size_t WavFileReader::read(short* buffer, size_t samples)
// Get number of samples that must be read from source file // Get number of samples that must be read from source file
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8; size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
bool useHeap = requiredBytes > sizeof mTempBuffer; bool useHeap = requiredBytes > sizeof mTempBuffer;
void* temp; void* temp;
if (useHeap) if (useHeap)
temp = malloc(requiredBytes); temp = malloc(requiredBytes);
else else
@@ -223,19 +220,18 @@ size_t WavFileReader::read(short* buffer, size_t samples)
// Find required size of input buffer // Find required size of input buffer
if (mDataLength) if (mDataLength)
{ {
auto filePosition = mInput->tellg(); auto filePosition = mInput->tellg();
// Check how much data we can read // Check how much data we can read
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset; std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset;
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0; size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes; requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
} }
size_t readBytes = tryReadBuffer(temp, requiredBytes); size_t readBytes = tryReadBuffer(temp, requiredBytes);
size_t processedBytes = 0; size_t processedBytes = 0;
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes, size_t result = mResampler.processBuffer(temp, readBytes, processedBytes, buffer, samples * 2 * AUDIO_CHANNELS);
buffer, samples * 2 * AUDIO_CHANNELS);
if (useHeap) if (useHeap)
free(temp); free(temp);
@@ -256,11 +252,11 @@ size_t WavFileReader::readRaw(short* buffer, size_t samples)
// Find required size of input buffer // Find required size of input buffer
if (mDataLength) if (mDataLength)
{ {
auto filePosition = mInput->tellg(); auto filePosition = mInput->tellg();
// Check how much data we can read // Check how much data we can read
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset; std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset;
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0; size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes; requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
} }
@@ -302,11 +298,9 @@ unsigned WavFileReader::lastError() const
// ------------------------- WavFileWriter ------------------------- // ------------------------- WavFileWriter -------------------------
#define LOG_SUBSYTEM "WavFileWriter" #define LOG_SUBSYTEM "WavFileWriter"
#define BITS_PER_CHANNEL 16 #define BITS_PER_CHANNEL 16
WavFileWriter::WavFileWriter() WavFileWriter::WavFileWriter() : mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0) {}
:mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
{}
WavFileWriter::~WavFileWriter() WavFileWriter::~WavFileWriter()
{ {
@@ -358,7 +352,8 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
writeBuffer(wavefmt, 8); writeBuffer(wavefmt, 8);
// Set the format description // Set the format description
uint32_t dwFmtSize = 16; /*= 16L*/; uint32_t dwFmtSize = 16; /*= 16L*/
;
writeBuffer(&dwFmtSize, sizeof(dwFmtSize)); writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
WaveFormatEx format; WaveFormatEx format;
@@ -384,7 +379,7 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
writeBuffer(data, 4); writeBuffer(data, 4);
mPath = p; mPath = p;
mWritten = 0; mWritten = 0;
mLengthOffset = mOutput->tellp(); mLengthOffset = mOutput->tellp();
writeBuffer(&mWritten, sizeof mWritten); writeBuffer(&mWritten, sizeof mWritten);
@@ -435,4 +430,3 @@ std::filesystem::path WavFileWriter::path() const
LOCK; LOCK;
return mPath; return mPath;
} }
+40 -40
View File
@@ -22,45 +22,45 @@ namespace Audio
class WavFileReader class WavFileReader
{ {
protected: protected:
uint16_t mChannels = 0; uint16_t mChannels = 0;
uint16_t mBits = 0; uint16_t mBits = 0;
int mSamplerate = 0; int mSamplerate = 0;
std::filesystem::path mPath; std::filesystem::path mPath;
mutable std::recursive_mutex mFileMtx; mutable std::recursive_mutex mFileMtx;
size_t mDataOffset = 0; size_t mDataOffset = 0;
size_t mDataLength = 0; size_t mDataLength = 0;
Resampler mResampler; Resampler mResampler;
unsigned mLastError = 0; unsigned mLastError = 0;
std::unique_ptr<std::ifstream> mInput; std::unique_ptr<std::ifstream> mInput;
uint8_t mTempBuffer[16384]; uint8_t mTempBuffer[16384];
std::string readChunk(); std::string readChunk();
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
public: public:
WavFileReader(); WavFileReader();
~WavFileReader(); ~WavFileReader();
bool open(const std::filesystem::path& p); bool open(const std::filesystem::path& p);
void close(); void close();
bool isOpened(); bool isOpened();
void rewind(); void rewind();
int samplerate() const; int samplerate() const;
int channels() const; int channels() const;
// This method returns number of read bytes // This method returns number of read bytes
size_t read(void* buffer, size_t bytes); size_t read(void* buffer, size_t bytes);
size_t readRaw(void* buffer, size_t bytes); size_t readRaw(void* buffer, size_t bytes);
// This method returns number of read samples // This method returns number of read samples
size_t read(short* buffer, size_t samples); size_t read(short* buffer, size_t samples);
size_t readRaw(short* buffer, size_t samples); size_t readRaw(short* buffer, size_t samples);
std::filesystem::path path() const; std::filesystem::path path() const;
size_t size() const; size_t size() const;
unsigned lastError() const; unsigned lastError() const;
}; };
typedef std::shared_ptr<WavFileReader> PWavFileReader; typedef std::shared_ptr<WavFileReader> PWavFileReader;
@@ -68,29 +68,29 @@ typedef std::shared_ptr<WavFileReader> PWavFileReader;
class WavFileWriter class WavFileWriter
{ {
protected: protected:
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file. std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
std::filesystem::path mPath; /// Path to requested audio file. std::filesystem::path mPath; /// Path to requested audio file.
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance. mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
size_t mWritten = 0; /// Amount of written data (in bytes) size_t mWritten = 0; /// Amount of written data (in bytes)
size_t mLengthOffset = 0; /// Position of length field. size_t mLengthOffset = 0; /// Position of length field.
int mSamplerate = 0, int mSamplerate = 0, mChannels = 0;
mChannels = 0;
void checkWriteResult(int result);
void writeBuffer(const void* buffer, size_t sz);
void checkWriteResult(int result);
void writeBuffer(const void* buffer, size_t sz);
public: public:
WavFileWriter(); WavFileWriter();
~WavFileWriter(); ~WavFileWriter();
bool open(const std::filesystem::path& p, int samplerate, int channels); bool open(const std::filesystem::path& p, int samplerate, int channels);
void close(); void close();
bool isOpened() const; bool isOpened() const;
size_t write(const void* buffer, size_t bytes); size_t write(const void* buffer, size_t bytes);
std::filesystem::path path() const; std::filesystem::path path() const;
}; };
typedef std::shared_ptr<WavFileWriter> PWavFileWriter; typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
} } // namespace Audio
#endif #endif
+346 -347
View File
@@ -15,541 +15,540 @@ using namespace Audio;
WmmeInputDevice::Buffer::Buffer() WmmeInputDevice::Buffer::Buffer()
{ {
// Do not use WAVEHDR allocated on stack! // Do not use WAVEHDR allocated on stack!
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof WAVEHDR); mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof WAVEHDR);
if (!mHeaderHandle) if (!mHeaderHandle)
throw Exception(ERR_WMME_FAILED, GetLastError()); throw Exception(ERR_WMME_FAILED, GetLastError());
mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle); mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle);
mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_MIC_BUFFER_SIZE); mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_MIC_BUFFER_SIZE);
if (!mDataHandle) if (!mDataHandle)
throw Exception(ERR_WMME_FAILED, GetLastError()); throw Exception(ERR_WMME_FAILED, GetLastError());
mData = GlobalLock(mDataHandle); mData = GlobalLock(mDataHandle);
memset(mHeader, 0, sizeof *mHeader); memset(mHeader, 0, sizeof *mHeader);
mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE; mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE;
mHeader->dwFlags = 0; mHeader->dwFlags = 0;
mHeader->lpData = (LPSTR)mData; mHeader->lpData = (LPSTR)mData;
} }
WmmeInputDevice::Buffer::~Buffer() WmmeInputDevice::Buffer::~Buffer()
{ {
if (mDataHandle) if (mDataHandle)
{ {
GlobalUnlock(mDataHandle); GlobalUnlock(mDataHandle);
GlobalFree(mDataHandle); GlobalFree(mDataHandle);
} }
if (mHeaderHandle) if (mHeaderHandle)
{ {
GlobalUnlock(mHeaderHandle); GlobalUnlock(mHeaderHandle);
GlobalFree(mHeaderHandle); GlobalFree(mHeaderHandle);
} }
} }
bool WmmeInputDevice::Buffer::prepare(HWAVEIN device) bool WmmeInputDevice::Buffer::prepare(HWAVEIN device)
{ {
MMRESULT resCode = MMSYSERR_NOERROR; MMRESULT resCode = MMSYSERR_NOERROR;
mHeader->dwFlags = 0; mHeader->dwFlags = 0;
mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE; mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE;
mHeader->lpData = (LPSTR)mData; mHeader->lpData = (LPSTR)mData;
resCode = waveInPrepareHeader(device, mHeader, sizeof *mHeader); resCode = waveInPrepareHeader(device, mHeader, sizeof *mHeader);
//if (resCode != MMSYSERR_NOERROR) // if (resCode != MMSYSERR_NOERROR)
// LogCritical("Audio", << "Failed to prepare source header. Error code " << resCode << "."); // LogCritical("Audio", << "Failed to prepare source header. Error code " << resCode << ".");
return resCode == MMSYSERR_NOERROR; return resCode == MMSYSERR_NOERROR;
} }
bool WmmeInputDevice::Buffer::unprepare(HWAVEIN device) bool WmmeInputDevice::Buffer::unprepare(HWAVEIN device)
{ {
if (mHeader->dwFlags & WHDR_PREPARED) if (mHeader->dwFlags & WHDR_PREPARED)
{ {
MMRESULT resCode = waveInUnprepareHeader(device, mHeader, sizeof *mHeader); MMRESULT resCode = waveInUnprepareHeader(device, mHeader, sizeof *mHeader);
//if (resCode != MMSYSERR_NOERROR) // if (resCode != MMSYSERR_NOERROR)
// LogCritical("Audio", << "Failed to unprepare source header. Error code " << resCode << "."); // LogCritical("Audio", << "Failed to unprepare source header. Error code " << resCode << ".");
return resCode == MMSYSERR_NOERROR; return resCode == MMSYSERR_NOERROR;
} }
return true; return true;
} }
bool WmmeInputDevice::Buffer::isFinished() bool WmmeInputDevice::Buffer::isFinished()
{ {
return (mHeader->dwFlags & WHDR_DONE) != 0; return (mHeader->dwFlags & WHDR_DONE) != 0;
} }
bool WmmeInputDevice::Buffer::addToDevice(HWAVEIN device) bool WmmeInputDevice::Buffer::addToDevice(HWAVEIN device)
{ {
MMRESULT resCode = waveInAddBuffer(device, mHeader, sizeof(*mHeader)); MMRESULT resCode = waveInAddBuffer(device, mHeader, sizeof(*mHeader));
//if (resCode != MMSYSERR_NOERROR) // if (resCode != MMSYSERR_NOERROR)
// LogCritical("Audio", << "Failed to add buffer to source audio device. Error code is " << resCode << "."); // LogCritical("Audio", << "Failed to add buffer to source audio device. Error code is " << resCode << ".");
return resCode == MMSYSERR_NOERROR; return resCode == MMSYSERR_NOERROR;
} }
void* WmmeInputDevice::Buffer::data() void* WmmeInputDevice::Buffer::data()
{ {
return mData; return mData;
} }
WmmeInputDevice::WmmeInputDevice(int deviceId) WmmeInputDevice::WmmeInputDevice(int deviceId)
:mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false), : mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false), mBufferIndex(0), mDeviceIndex(deviceId),
mBufferIndex(0), mDeviceIndex(deviceId), mThreadHandle(0) mThreadHandle(0)
{ {
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mRefCount = 0; mRefCount = 0;
} }
WmmeInputDevice::~WmmeInputDevice() WmmeInputDevice::~WmmeInputDevice()
{ {
close(); close();
::CloseHandle(mDoneSignal); ::CloseHandle(mDoneSignal);
::CloseHandle(mShutdownSignal); ::CloseHandle(mShutdownSignal);
} }
bool WmmeInputDevice::fakeMode() bool WmmeInputDevice::fakeMode()
{ {
return mFakeMode; return mFakeMode;
} }
void CALLBACK WmmeInputDevice::callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) void CALLBACK WmmeInputDevice::callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
DWORD_PTR dwParam2)
{ {
WmmeInputDevice* impl; WmmeInputDevice* impl;
switch(uMsg) switch (uMsg)
{ {
case WIM_DATA: case WIM_DATA:
impl = (WmmeInputDevice*)dwInstance; impl = (WmmeInputDevice*)dwInstance;
SetEvent(impl->mDoneSignal); SetEvent(impl->mDoneSignal);
break; break;
case WIM_CLOSE: case WIM_CLOSE:
break; break;
case WIM_OPEN: case WIM_OPEN:
break; break;
} }
} }
void WmmeInputDevice::openDevice() void WmmeInputDevice::openDevice()
{ {
// Build WAVEFORMATEX structure // Build WAVEFORMATEX structure
WAVEFORMATEX wfx; WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(wfx)); memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = AUDIO_CHANNELS; wfx.nChannels = AUDIO_CHANNELS;
wfx.nSamplesPerSec = AUDIO_SAMPLERATE; wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
wfx.wBitsPerSample = 16; wfx.wBitsPerSample = 16;
wfx.cbSize = 0; wfx.cbSize = 0;
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8; wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
// Open wavein
MMRESULT mmres = waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mmres != MMSYSERR_NOERROR)
{
mFakeMode = true;
return;
}
else
mFakeMode = false;
// Create the buffers for running // Open wavein
mBufferIndex = 0;
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].prepare(mDevHandle);
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].addToDevice(mDevHandle);
/*mmres = */waveInStart(mDevHandle); MMRESULT mmres =
waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mmres != MMSYSERR_NOERROR)
{
mFakeMode = true;
return;
}
else
mFakeMode = false;
// Create the buffers for running
mBufferIndex = 0;
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].prepare(mDevHandle);
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].addToDevice(mDevHandle);
/*mmres = */ waveInStart(mDevHandle);
} }
bool WmmeInputDevice::open() bool WmmeInputDevice::open()
{ {
Lock lock(mGuard); Lock lock(mGuard);
mRefCount++; mRefCount++;
if (mRefCount > 1) if (mRefCount > 1)
return true;
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
return true; return true;
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
return true;
} }
void WmmeInputDevice::closeDevice() void WmmeInputDevice::closeDevice()
{ {
// Stop device // Stop device
if (mDevHandle) if (mDevHandle)
{ {
MMRESULT mmres = MMSYSERR_NOERROR; MMRESULT mmres = MMSYSERR_NOERROR;
waveInReset(mDevHandle); waveInReset(mDevHandle);
waveInStop(mDevHandle); waveInStop(mDevHandle);
} }
// Close buffers // Close buffers
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].unprepare(mDevHandle); mBufferList[i].unprepare(mDevHandle);
// Close device // Close device
if (mDevHandle) if (mDevHandle)
{ {
waveInClose(mDevHandle); waveInClose(mDevHandle);
mDevHandle = NULL; mDevHandle = NULL;
} }
} }
void WmmeInputDevice::close() void WmmeInputDevice::close()
{ {
Lock l(mGuard); Lock l(mGuard);
mRefCount--; mRefCount--;
if (mRefCount != 0) if (mRefCount != 0)
return; return;
// Set shutdown signal
if (!mThreadHandle)
return;
::SetEvent(mShutdownSignal); // Set shutdown signal
::WaitForSingleObject(mThreadHandle, INFINITE); if (!mThreadHandle)
mThreadHandle = 0; return;
::SetEvent(mShutdownSignal);
::WaitForSingleObject(mThreadHandle, INFINITE);
mThreadHandle = 0;
} }
bool WmmeInputDevice::tryReadBuffer(void* buffer) bool WmmeInputDevice::tryReadBuffer(void* buffer)
{ {
Buffer& devBuffer = mBufferList[mBufferIndex]; Buffer& devBuffer = mBufferList[mBufferIndex];
if (!devBuffer.isFinished()) if (!devBuffer.isFinished())
return false; return false;
memcpy(buffer, devBuffer.data(), AUDIO_MIC_BUFFER_SIZE); memcpy(buffer, devBuffer.data(), AUDIO_MIC_BUFFER_SIZE);
devBuffer.unprepare(mDevHandle); devBuffer.unprepare(mDevHandle);
devBuffer.prepare(mDevHandle); devBuffer.prepare(mDevHandle);
if (!devBuffer.addToDevice(mDevHandle)) if (!devBuffer.addToDevice(mDevHandle))
setFakeMode(true); setFakeMode(true);
else else
{ {
} }
mBufferIndex = (mBufferIndex + 1) % AUDIO_MIC_BUFFER_COUNT; mBufferIndex = (mBufferIndex + 1) % AUDIO_MIC_BUFFER_COUNT;
return true; return true;
} }
void WmmeInputDevice::setFakeMode(bool fakeMode) void WmmeInputDevice::setFakeMode(bool fakeMode)
{ {
mFakeMode = fakeMode; mFakeMode = fakeMode;
} }
int WmmeInputDevice::readBuffer(void* buffer) int WmmeInputDevice::readBuffer(void* buffer)
{ {
//Lock lock(mGuard); // Lock lock(mGuard);
if (mRefCount <= 0 || mFakeMode) if (mRefCount <= 0 || mFakeMode)
return 0; return 0;
// Check for finished buffer // Check for finished buffer
while (!tryReadBuffer(buffer)) while (!tryReadBuffer(buffer))
WaitForSingleObject(mDoneSignal, 50); WaitForSingleObject(mDoneSignal, 50);
return AUDIO_MIC_BUFFER_SIZE; return AUDIO_MIC_BUFFER_SIZE;
} }
HWAVEIN WmmeInputDevice::handle() HWAVEIN WmmeInputDevice::handle()
{ {
Lock lock(mGuard); Lock lock(mGuard);
return mDevHandle; return mDevHandle;
} }
void WmmeInputDevice::threadProc(void* arg) void WmmeInputDevice::threadProc(void* arg)
{ {
WmmeInputDevice* impl = (WmmeInputDevice*)arg; WmmeInputDevice* impl = (WmmeInputDevice*)arg;
impl->openDevice(); impl->openDevice();
void* buffer = _alloca(AUDIO_MIC_BUFFER_SIZE); void* buffer = _alloca(AUDIO_MIC_BUFFER_SIZE);
DWORD waitResult = 0; DWORD waitResult = 0;
HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal}; HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal};
DWORD wr; DWORD wr;
do do
{
wr = ::WaitForMultipleObjects(2, waitArray, FALSE, INFINITE);
if (wr == WAIT_OBJECT_0)
{ {
impl->readBuffer(buffer); wr = ::WaitForMultipleObjects(2, waitArray, FALSE, INFINITE);
if (impl->connection())
impl->connection()->onMicData(Format(), buffer, AUDIO_MIC_BUFFER_SIZE);
}
} while (wr == WAIT_OBJECT_0);
impl->closeDevice(); if (wr == WAIT_OBJECT_0)
{
impl->readBuffer(buffer);
if (impl->connection())
impl->connection()->onMicData(Format(), buffer, AUDIO_MIC_BUFFER_SIZE);
}
} while (wr == WAIT_OBJECT_0);
impl->closeDevice();
} }
// --- WmmeOutputDevice --- // --- WmmeOutputDevice ---
WmmeOutputDevice::Buffer::Buffer() WmmeOutputDevice::Buffer::Buffer() : mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL)
:mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL)
{ {
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE); mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
if (!mHeaderHandle) if (!mHeaderHandle)
throw Exception(ERR_NOMEM); throw Exception(ERR_NOMEM);
mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE); mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
if (!mDataHandle) if (!mDataHandle)
throw Exception(ERR_NOMEM); throw Exception(ERR_NOMEM);
mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle); mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle);
mData = GlobalLock(mDataHandle); mData = GlobalLock(mDataHandle);
memset(mHeader, 0, sizeof *mHeader); memset(mHeader, 0, sizeof *mHeader);
mHeader->dwBufferLength = AUDIO_SPK_BUFFER_SIZE; mHeader->dwBufferLength = AUDIO_SPK_BUFFER_SIZE;
mHeader->lpData = (LPSTR)mData; mHeader->lpData = (LPSTR)mData;
} }
WmmeOutputDevice::Buffer::~Buffer() WmmeOutputDevice::Buffer::~Buffer()
{ {
if (mHeaderHandle) if (mHeaderHandle)
{ {
GlobalUnlock(mHeaderHandle); GlobalUnlock(mHeaderHandle);
GlobalFree(mHeaderHandle); GlobalFree(mHeaderHandle);
} }
if (mDataHandle) if (mDataHandle)
{ {
GlobalUnlock(mDataHandle); GlobalUnlock(mDataHandle);
GlobalFree(mDataHandle); GlobalFree(mDataHandle);
} }
} }
bool WmmeOutputDevice::Buffer::prepare(HWAVEOUT device) bool WmmeOutputDevice::Buffer::prepare(HWAVEOUT device)
{ {
MMRESULT result; MMRESULT result;
result = ::waveOutPrepareHeader(device, mHeader, sizeof *mHeader); result = ::waveOutPrepareHeader(device, mHeader, sizeof *mHeader);
return result == MMSYSERR_NOERROR; return result == MMSYSERR_NOERROR;
} }
bool WmmeOutputDevice::Buffer::unprepare(HWAVEOUT device) bool WmmeOutputDevice::Buffer::unprepare(HWAVEOUT device)
{ {
MMRESULT result; MMRESULT result;
result = ::waveOutUnprepareHeader(device, mHeader, sizeof *mHeader); result = ::waveOutUnprepareHeader(device, mHeader, sizeof *mHeader);
return result == MMSYSERR_NOERROR; return result == MMSYSERR_NOERROR;
} }
bool WmmeOutputDevice::Buffer::write(HWAVEOUT device) bool WmmeOutputDevice::Buffer::write(HWAVEOUT device)
{ {
MMRESULT result; MMRESULT result;
result = ::waveOutWrite(device, mHeader, sizeof *mHeader); result = ::waveOutWrite(device, mHeader, sizeof *mHeader);
return result == MMSYSERR_NOERROR; return result == MMSYSERR_NOERROR;
} }
WmmeOutputDevice::WmmeOutputDevice(int index) WmmeOutputDevice::WmmeOutputDevice(int index)
:mDevice(NULL), mDeviceIndex(index), mPlayedTime(0), mPlayedCount(0), mBufferIndex(0), mThreadHandle(NULL), : mDevice(NULL), mDeviceIndex(index), mPlayedTime(0), mPlayedCount(0), mBufferIndex(0), mThreadHandle(NULL),
mFailed(false), mShutdownMarker(false) mFailed(false), mShutdownMarker(false)
{ {
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
} }
WmmeOutputDevice::~WmmeOutputDevice() WmmeOutputDevice::~WmmeOutputDevice()
{ {
close(); close();
// Destroy used signals // Destroy used signals
CloseHandle(mDoneSignal); CloseHandle(mShutdownSignal); CloseHandle(mDoneSignal);
CloseHandle(mShutdownSignal);
} }
bool WmmeOutputDevice::open() bool WmmeOutputDevice::open()
{ {
// Start thread // Start thread
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this); mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
return true; return true;
} }
void WmmeOutputDevice::close() void WmmeOutputDevice::close()
{ {
// Tell the thread to exit // Tell the thread to exit
SetEvent(mShutdownSignal); SetEvent(mShutdownSignal);
mShutdownMarker = true; mShutdownMarker = true;
// Wait for thread // Wait for thread
if (mThreadHandle) if (mThreadHandle)
WaitForSingleObject(mThreadHandle, INFINITE); WaitForSingleObject(mThreadHandle, INFINITE);
mThreadHandle = 0; mThreadHandle = 0;
} }
void WmmeOutputDevice::openDevice() void WmmeOutputDevice::openDevice()
{ {
mClosing = false; mClosing = false;
MMRESULT mmres = 0; MMRESULT mmres = 0;
WAVEFORMATEX wfx; WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(wfx)); memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = 0x0001; wfx.wFormatTag = 0x0001;
wfx.nChannels = AUDIO_CHANNELS; wfx.nChannels = AUDIO_CHANNELS;
wfx.nSamplesPerSec = AUDIO_SAMPLERATE; wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
wfx.wBitsPerSample = 16; wfx.wBitsPerSample = 16;
wfx.cbSize = 0; wfx.cbSize = 0;
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8; wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
mmres = waveOutOpen(&mDevice, mDeviceIndex, &wfx, (DWORD_PTR)&callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION); mmres = waveOutOpen(&mDevice, mDeviceIndex, &wfx, (DWORD_PTR)&callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mmres != MMSYSERR_NOERROR) if (mmres != MMSYSERR_NOERROR)
throw Exception(ERR_WMME_FAILED, mmres); throw Exception(ERR_WMME_FAILED, mmres);
// Prebuffer silence // Prebuffer silence
for (unsigned i=0; i<AUDIO_SPK_BUFFER_COUNT; i++) for (unsigned i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
{ {
//bool dumb = false; // bool dumb = false;
//mCallback(mBufferList[i].mData, SPK_BUFFER_SIZE, dumb, dumb); // mCallback(mBufferList[i].mData, SPK_BUFFER_SIZE, dumb, dumb);
memset(mBufferList[i].mData, 0, AUDIO_SPK_BUFFER_SIZE); memset(mBufferList[i].mData, 0, AUDIO_SPK_BUFFER_SIZE);
mBufferList[i].prepare(mDevice); mBufferList[i].prepare(mDevice);
mBufferList[i].write(mDevice); mBufferList[i].write(mDevice);
} }
} }
void WmmeOutputDevice::closeDevice() void WmmeOutputDevice::closeDevice()
{ {
Lock l(mGuard); Lock l(mGuard);
mClosing = true; mClosing = true;
bool finished = false; bool finished = false;
while (!finished) while (!finished)
{ {
WaitForSingleObject(mDoneSignal, 10); WaitForSingleObject(mDoneSignal, 10);
finished = areBuffersFinished(); finished = areBuffersFinished();
} }
if (mDevice) if (mDevice)
{ {
waveOutReset(mDevice); waveOutReset(mDevice);
waveOutClose(mDevice); waveOutClose(mDevice);
} }
mDevice = NULL; mDevice = NULL;
} }
bool WmmeOutputDevice::areBuffersFinished() bool WmmeOutputDevice::areBuffersFinished()
{ {
Lock l(mGuard); Lock l(mGuard);
bool result = true; bool result = true;
for (unsigned i=0; i<AUDIO_SPK_BUFFER_COUNT && result; i++) for (unsigned i = 0; i < AUDIO_SPK_BUFFER_COUNT && result; i++)
{
bool finished = mBufferList[i].mHeader->dwFlags & WHDR_DONE ||
!mBufferList[i].mHeader->dwFlags;
if (finished)
{ {
/* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED) bool finished = mBufferList[i].mHeader->dwFlags & WHDR_DONE || !mBufferList[i].mHeader->dwFlags;
mBufferList[i].Unprepare(mDevice); */ if (finished)
{
/* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED)
mBufferList[i].Unprepare(mDevice); */
}
result &= finished;
} }
result &= finished;
}
return result; return result;
} }
void WmmeOutputDevice::threadProc(void* arg) void WmmeOutputDevice::threadProc(void* arg)
{ {
WmmeOutputDevice* impl = (WmmeOutputDevice*)arg; WmmeOutputDevice* impl = (WmmeOutputDevice*)arg;
impl->openDevice(); impl->openDevice();
DWORD waitResult = 0; DWORD waitResult = 0;
HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal}; HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal};
unsigned index, i; unsigned index, i;
unsigned exitCount = 0; unsigned exitCount = 0;
bool exitSignal = false; bool exitSignal = false;
do do
{
// Poll for exit signal
if (!exitSignal)
exitSignal = impl->mShutdownMarker;
// Wait for played buffer
WaitForSingleObject(impl->mDoneSignal, 500);
// Iterate buffers to find played
for (i=0; i<AUDIO_SPK_BUFFER_COUNT; i++)
{ {
index = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT; // Poll for exit signal
Buffer& buffer = impl->mBufferList[index]; if (!exitSignal)
if (!(buffer.mHeader->dwFlags & WHDR_DONE)) exitSignal = impl->mShutdownMarker;
break;
// Wait for played buffer
buffer.unprepare(impl->mDevice); WaitForSingleObject(impl->mDoneSignal, 500);
if (!exitSignal)
{ // Iterate buffers to find played
bool useAEC = true; for (i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
if (impl->connection()) {
impl->connection()->onSpkData(Format(), buffer.mData, AUDIO_SPK_BUFFER_SIZE); index = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
else Buffer& buffer = impl->mBufferList[index];
memset(buffer.mData, 0, AUDIO_SPK_BUFFER_SIZE); if (!(buffer.mHeader->dwFlags & WHDR_DONE))
break;
buffer.prepare(impl->mDevice);
buffer.write(impl->mDevice); buffer.unprepare(impl->mDevice);
} if (!exitSignal)
else {
exitCount++; bool useAEC = true;
} if (impl->connection())
impl->mBufferIndex = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT; impl->connection()->onSpkData(Format(), buffer.mData, AUDIO_SPK_BUFFER_SIZE);
} else
while (!exitSignal || exitCount < AUDIO_SPK_BUFFER_COUNT); memset(buffer.mData, 0, AUDIO_SPK_BUFFER_SIZE);
impl->closeDevice();
buffer.prepare(impl->mDevice);
buffer.write(impl->mDevice);
}
else
exitCount++;
}
impl->mBufferIndex = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
} while (!exitSignal || exitCount < AUDIO_SPK_BUFFER_COUNT);
impl->closeDevice();
} }
HWAVEOUT WmmeOutputDevice::handle() HWAVEOUT WmmeOutputDevice::handle()
{ {
return mDevice; return mDevice;
} }
unsigned WmmeOutputDevice::playedTime() unsigned WmmeOutputDevice::playedTime()
{ {
if (!mDevice) if (!mDevice)
return 0; return 0;
unsigned result = 0; unsigned result = 0;
MMTIME mmt; MMTIME mmt;
memset(&mmt, 0, sizeof(mmt)); memset(&mmt, 0, sizeof(mmt));
mmt.wType = TIME_SAMPLES; mmt.wType = TIME_SAMPLES;
MMRESULT rescode = waveOutGetPosition(mDevice, &mmt, sizeof(mmt)); MMRESULT rescode = waveOutGetPosition(mDevice, &mmt, sizeof(mmt));
if (rescode != MMSYSERR_NOERROR || mmt.wType != TIME_SAMPLES) if (rescode != MMSYSERR_NOERROR || mmt.wType != TIME_SAMPLES)
closeDevice(); closeDevice();
else
{
if (mmt.u.ms < mPlayedTime)
result = 0;
else else
{ {
result = mmt.u.ms - mPlayedTime; if (mmt.u.ms < mPlayedTime)
mPlayedTime = mmt.u.ms - result % 8; result = 0;
else
{
result = mmt.u.ms - mPlayedTime;
mPlayedTime = mmt.u.ms - result % 8;
}
} }
}
return result / 8; return result / 8;
} }
void WmmeOutputDevice::setFakeMode(bool fakemode) void WmmeOutputDevice::setFakeMode(bool fakemode)
{ {
closeDevice(); closeDevice();
} }
bool WmmeOutputDevice::fakeMode() bool WmmeOutputDevice::fakeMode()
{ {
return mFailed; return mFailed;
} }
bool WmmeOutputDevice::closing() bool WmmeOutputDevice::closing()
{ {
return mClosing; return mClosing;
} }
void CALLBACK WmmeOutputDevice::callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) void CALLBACK WmmeOutputDevice::callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
DWORD_PTR dwParam2)
{ {
WmmeOutputDevice* impl; WmmeOutputDevice* impl;
if (msg == WOM_DONE) if (msg == WOM_DONE)
{ {
impl = (WmmeOutputDevice*)dwInstance; impl = (WmmeOutputDevice*)dwInstance;
InterlockedIncrement(&impl->mPlayedCount); InterlockedIncrement(&impl->mPlayedCount);
SetEvent(impl->mDoneSignal); SetEvent(impl->mDoneSignal);
} }
} }
#endif #endif
+84 -84
View File
@@ -30,118 +30,118 @@
namespace Audio namespace Audio
{ {
class WmmeInputDevice: public InputDevice class WmmeInputDevice : public InputDevice
{ {
public: public:
WmmeInputDevice(int index); WmmeInputDevice(int index);
~WmmeInputDevice(); ~WmmeInputDevice();
bool open(); bool open();
void close(); void close();
bool fakeMode(); bool fakeMode();
void setFakeMode(bool fakeMode); void setFakeMode(bool fakeMode);
int readBuffer(void* buffer); int readBuffer(void* buffer);
HWAVEIN handle(); HWAVEIN handle();
protected: protected:
class Buffer class Buffer
{ {
public: public:
Buffer(); Buffer();
~Buffer(); ~Buffer();
bool prepare(HWAVEIN device); bool prepare(HWAVEIN device);
bool unprepare(HWAVEIN device); bool unprepare(HWAVEIN device);
bool isFinished(); bool isFinished();
bool addToDevice(HWAVEIN device); bool addToDevice(HWAVEIN device);
void* data(); void* data();
protected: protected:
HGLOBAL mDataHandle; HGLOBAL mDataHandle;
void* mData; void* mData;
HGLOBAL mHeaderHandle; HGLOBAL mHeaderHandle;
WAVEHDR* mHeader; WAVEHDR* mHeader;
}; };
Mutex mGuard; /// Mutex to protect this instance. Mutex mGuard; /// Mutex to protect this instance.
HWAVEIN mDevHandle; /// Handle of opened capture device. HWAVEIN mDevHandle; /// Handle of opened capture device.
HANDLE mThreadHandle; HANDLE mThreadHandle;
HANDLE mShutdownSignal; HANDLE mShutdownSignal;
HANDLE mDoneSignal; /// Event handle to signal about finished capture. HANDLE mDoneSignal; /// Event handle to signal about finished capture.
Buffer mBufferList[AUDIO_MIC_BUFFER_COUNT]; Buffer mBufferList[AUDIO_MIC_BUFFER_COUNT];
unsigned mBufferIndex; unsigned mBufferIndex;
int mDeviceIndex; /// Index of capture device. int mDeviceIndex; /// Index of capture device.
volatile bool mFakeMode; /// Marks if fake mode is active. volatile bool mFakeMode; /// Marks if fake mode is active.
int mRefCount; int mRefCount;
bool tryReadBuffer(void* buffer); bool tryReadBuffer(void* buffer);
void openDevice(); void openDevice();
void closeDevice(); void closeDevice();
static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
static void threadProc(void* arg); DWORD_PTR dwParam2);
}; static void threadProc(void* arg);
};
class WmmeOutputDevice: public OutputDevice class WmmeOutputDevice : public OutputDevice
{ {
public: public:
WmmeOutputDevice(int index); WmmeOutputDevice(int index);
~WmmeOutputDevice(); ~WmmeOutputDevice();
bool open();
void close();
HWAVEOUT handle(); bool open();
unsigned playedTime(); void close();
void setFakeMode(bool fakemode);
bool fakeMode();
bool closing();
protected: HWAVEOUT handle();
unsigned playedTime();
void setFakeMode(bool fakemode);
bool fakeMode();
bool closing();
protected:
class Buffer class Buffer
{ {
friend class WmmeOutputDevice; friend class WmmeOutputDevice;
public: public:
Buffer(); Buffer();
~Buffer(); ~Buffer();
bool prepare(HWAVEOUT device); bool prepare(HWAVEOUT device);
bool unprepare(HWAVEOUT device); bool unprepare(HWAVEOUT device);
bool write(HWAVEOUT device); bool write(HWAVEOUT device);
protected: protected:
WAVEHDR* mHeader; WAVEHDR* mHeader;
void* mData; void* mData;
HGLOBAL mHeaderHandle; HGLOBAL mHeaderHandle;
HGLOBAL mDataHandle; HGLOBAL mDataHandle;
}; };
Mutex mGuard; /// Mutex to protect this instance Mutex mGuard; /// Mutex to protect this instance
int mDeviceIndex; int mDeviceIndex;
HWAVEOUT mDevice; /// Handle of opened audio device HWAVEOUT mDevice; /// Handle of opened audio device
Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT]; Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT];
unsigned mPlayedTime; /// Amount of played time in milliseconds unsigned mPlayedTime; /// Amount of played time in milliseconds
bool mClosing; bool mClosing;
HANDLE mDoneSignal, HANDLE mDoneSignal, mShutdownSignal, mThreadHandle;
mShutdownSignal, volatile bool mShutdownMarker;
mThreadHandle;
volatile bool mShutdownMarker;
volatile LONG mPlayedCount;
unsigned mBufferIndex;
bool mFailed;
void openDevice(); volatile LONG mPlayedCount;
void closeDevice(); unsigned mBufferIndex;
bool areBuffersFinished(); bool mFailed;
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); void openDevice();
static void threadProc(void* arg); void closeDevice();
}; bool areBuffersFinished();
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
DWORD_PTR dwParam2);
static void threadProc(void* arg);
};
} // namespace Audio
}
#endif #endif
-1
View File
@@ -1,2 +1 @@
#include "Audio_iOS.h" #include "Audio_iOS.h"
+22 -24
View File
@@ -1,38 +1,36 @@
#ifndef __AUDIO_IOS #ifndef __AUDIO_IOS
#define __AUDIO_IOS #define __AUDIO_IOS
class IosInputDevice: public InputDevice class IosInputDevice : public InputDevice
{ {
protected: protected:
public: public:
IosInputDevice(); IosInputDevice();
~IosInputDevice(); ~IosInputDevice();
void open();
void open(); void close();
void close();
}; };
class IosOutputDevice: public OutputDevice class IosOutputDevice : public OutputDevice
{ {
protected: protected:
public: public:
IosOutputDevice(); IosOutputDevice();
~IosOutputDevice(); ~IosOutputDevice();
enum enum
{ {
Receiver, Receiver,
Speaker, Speaker,
Bluetooth Bluetooth
}; };
int route(); int route();
void setRoute(int route); void setRoute(int route);
void open(); void open();
void close(); void close();
}; };
#endif #endif
+95 -99
View File
@@ -15,49 +15,46 @@
#define CONFIG(X) mConfig->at(X) #define CONFIG(X) mConfig->at(X)
#define CONFIG_EXISTS(X) mConfig->exists(X) #define CONFIG_EXISTS(X) mConfig->exists(X)
//#define MODIFY_VIA_BEHIND_NAT // #define MODIFY_VIA_BEHIND_NAT
// NAT decorator // NAT decorator
class NATDecorator: public resip::MessageDecorator class NATDecorator : public resip::MessageDecorator
{ {
protected: protected:
UserAgent& mUserAgent; UserAgent& mUserAgent;
resip::SipMessage mMessage; resip::SipMessage mMessage;
resip::Data mViaHost; resip::Data mViaHost;
unsigned short mViaPort; unsigned short mViaPort;
resip::Data mContactsHost; resip::Data mContactsHost;
resip::Data mContactsScheme; resip::Data mContactsScheme;
unsigned short mContactsPort; unsigned short mContactsPort;
public: public:
NATDecorator(UserAgent& endpoint); NATDecorator(UserAgent& endpoint);
virtual ~NATDecorator(); virtual ~NATDecorator();
virtual void decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId); virtual void decorateMessage(resip::SipMessage& msg, const resip::Tuple& source, const resip::Tuple& destination,
virtual void rollbackMessage(resip::SipMessage& msg); const resip::Data& sigcompId);
virtual void rollbackMessage(resip::SipMessage& msg);
virtual MessageDecorator* clone() const; virtual MessageDecorator* clone() const;
}; };
NATDecorator::NATDecorator(UserAgent& ua) NATDecorator::NATDecorator(UserAgent& ua) : mUserAgent(ua), mViaPort(0), mContactsPort(0) {}
:mUserAgent(ua), mViaPort(0), mContactsPort(0)
{
}
NATDecorator::~NATDecorator() NATDecorator::~NATDecorator() {}
{
}
void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId) void NATDecorator::decorateMessage(resip::SipMessage& msg, const resip::Tuple& source, const resip::Tuple& destination,
const resip::Data& sigcompId)
{ {
// Make a copy to allow rollback // Make a copy to allow rollback
mMessage = msg; mMessage = msg;
std::stringstream dump; std::stringstream dump;
mMessage.encode(dump); mMessage.encode(dump);
//ICELogDebug(<< "Decorating message: \n" << dump.str()); // ICELogDebug(<< "Decorating message: \n" << dump.str());
// Check From: header and find the account // Check From: header and find the account
resip::NameAddr from; resip::NameAddr from;
@@ -101,11 +98,11 @@ void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &s
uri.port() = account->mExternalAddress.port(); uri.port() = account->mExternalAddress.port();
if (account->mConfig->at(CONFIG_SIPS).asBool()) if (account->mConfig->at(CONFIG_SIPS).asBool())
{ {
//uri.scheme() = "sips"; // uri.scheme() = "sips";
//uri.param(resip::p_transport) = "tls"; // uri.param(resip::p_transport) = "tls";
} }
//uri.scheme() = account->mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip"; // uri.scheme() = account->mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
} }
} }
} }
@@ -114,7 +111,7 @@ void NATDecorator::rollbackMessage(resip::SipMessage& msg)
{ {
// Check From: header and find the account // Check From: header and find the account
resip::NameAddr from = msg.header(resip::h_From); resip::NameAddr from = msg.header(resip::h_From);
PAccount account = mUserAgent.getAccount(from); PAccount account = mUserAgent.getAccount(from);
if (!account) if (!account)
return; return;
@@ -141,7 +138,7 @@ void NATDecorator::rollbackMessage(resip::SipMessage& msg)
{ {
uri.host() = mContactsHost; uri.host() = mContactsHost;
uri.port() = mContactsPort; uri.port() = mContactsPort;
//uri.scheme() = mContactsScheme; // uri.scheme() = mContactsScheme;
} }
} }
} }
@@ -153,19 +150,16 @@ resip::MessageDecorator* NATDecorator::clone() const
} }
Account::Account(PVariantMap config, UserAgent& agent) Account::Account(PVariantMap config, UserAgent& agent)
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None), : mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None), mRegistration(NULL)
mRegistration(NULL)
{ {
mProfile = std::make_shared<resip::UserProfile>(agent.mProfile); mProfile = std::make_shared<resip::UserProfile>(agent.mProfile);
mId = Account::generateId(); mId = Account::generateId();
setup(*config); setup(*config);
} }
Account::~Account() Account::~Account() {}
{
}
void Account::setup(VariantMap &config) void Account::setup(VariantMap& config)
{ {
// Credentials // Credentials
@@ -176,8 +170,8 @@ void Account::setup(VariantMap &config)
mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()), mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()),
resip::Data(config[CONFIG_USERNAME].asStdString()), resip::Data(config[CONFIG_USERNAME].asStdString()),
resip::Data(config[CONFIG_PASSWORD].asStdString())); resip::Data(config[CONFIG_PASSWORD].asStdString()));
ICELogInfo( << "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() << ICELogInfo(<< "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() << ", username to "
", username to " << config[CONFIG_USERNAME].asStdString()); << config[CONFIG_USERNAME].asStdString());
// Proxy // Proxy
mProfile->unsetOutboundProxy(); mProfile->unsetOutboundProxy();
@@ -234,7 +228,7 @@ void Account::setup(VariantMap &config)
if (config[CONFIG_DOMAINPORT].asInt() != 0) if (config[CONFIG_DOMAINPORT].asInt() != 0)
from.uri().port() = config[CONFIG_DOMAINPORT].asInt(); from.uri().port() = config[CONFIG_DOMAINPORT].asInt();
else else
from.uri().port();// = 5060; from.uri().port(); // = 5060;
from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString()); from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString());
from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString()); from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString());
@@ -266,10 +260,12 @@ void Account::start()
// Create registration // Create registration
mRegistration = new ResipSession(*mAgent.mDum); mRegistration = new ResipSession(*mAgent.mDum);
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration); auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile,
mConfig->at(CONFIG_REGISTERDURATION).asInt(), mRegistration);
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++) for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str())); regmessage->header(resip::ExtensionHeader(iter->first.c_str()))
.push_back(resip::StringCategory(iter->second.c_str()));
mRegistrationState = RegistrationState::Registering; mRegistrationState = RegistrationState::Registering;
@@ -278,10 +274,10 @@ void Account::start()
// Check if STUN IP is required // Check if STUN IP is required
bool noStunServerIp = !CONFIG_EXISTS(CONFIG_STUNSERVER_IP); bool noStunServerIp = !CONFIG_EXISTS(CONFIG_STUNSERVER_IP);
//bool hasStunServerName = !CONFIG(CONFIG_STUNSERVER_NAME).asStdString().empty(); // bool hasStunServerName = !CONFIG(CONFIG_STUNSERVER_NAME).asStdString().empty();
if (noStunServerIp) if (noStunServerIp)
{ {
ICELogInfo(<<"No STUN server name or IP is not specified. Has to resolve/discover STUN server IP."); ICELogInfo(<< "No STUN server name or IP is not specified. Has to resolve/discover STUN server IP.");
mRefreshStunServerIpTimer.start(CONFIG(CONFIG_DNS_CACHE_TIME).asInt() * 1000); mRefreshStunServerIpTimer.start(CONFIG(CONFIG_DNS_CACHE_TIME).asInt() * 1000);
mRefreshStunServerIpTimer.isTimeToSend(); mRefreshStunServerIpTimer.isTimeToSend();
queryStunServerIp(); queryStunServerIp();
@@ -305,11 +301,10 @@ void Account::stop()
mRegistrationHandle->removeAll(); mRegistrationHandle->removeAll();
mRegistrationHandle = resip::ClientRegistrationHandle(); mRegistrationHandle = resip::ClientRegistrationHandle();
} }
else else if (mRegistration)
if (mRegistration) {
{ mRegistration->end();
mRegistration->end(); }
}
mRegistration = NULL; mRegistration = NULL;
mRegistrationState = RegistrationState::None; mRegistrationState = RegistrationState::None;
} }
@@ -361,7 +356,8 @@ void Account::publishPresence(bool online, const std::string& content, int secon
if (mPublication.isValid()) if (mPublication.isValid())
mPublication->update(&p); mPublication->update(&p);
else else
mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p, resip::Symbols::Presence, seconds)); mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p,
resip::Symbols::Presence, seconds));
} }
void Account::stopPublish() void Account::stopPublish()
@@ -381,14 +377,14 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
observer->mPeer = target; observer->mPeer = target;
std::shared_ptr<resip::SipMessage> msg; std::shared_ptr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME; int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME)) if (mConfig->exists(CONFIG_SUBSCRIPTION_TIME))
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt(); expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME)) if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt(); refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt();
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile, msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile, resip::Data(package), expires,
resip::Data(package), expires, refresh, observer->mSession); refresh, observer->mSession);
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY); msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
mAgent.mClientObserverMap[observer->mSessionId] = observer; mAgent.mClientObserverMap[observer->mSessionId] = observer;
@@ -406,15 +402,17 @@ int Account::sendMsg(const std::string& peer, const void* ptr, unsigned length,
s->setRemoteAddress(peer); s->setRemoteAddress(peer);
// Find MIME type // Find MIME type
resip::Mime type; resip::Mime type;
std::string::size_type p = mime.find('/'); std::string::size_type p = mime.find('/');
if (p != std::string::npos) if (p != std::string::npos)
type = resip::Mime(resip::Data(mime.substr(0, p)), resip::Data(mime.substr(p+1))); type = resip::Mime(resip::Data(mime.substr(0, p)), resip::Data(mime.substr(p + 1)));
else else
type = resip::Mime(resip::Data(mime), resip::Data()); type = resip::Mime(resip::Data(mime), resip::Data());
resip::ClientPagerMessageHandle msgHandle = mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s); resip::ClientPagerMessageHandle msgHandle =
unique_ptr<resip::Contents> contentPtr(new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)),type)); mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s);
unique_ptr<resip::Contents> contentPtr(
new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)), type));
int result = s->sessionId(); int result = s->sessionId();
msgHandle->page(std::move(contentPtr)); msgHandle->page(std::move(contentPtr));
@@ -451,12 +449,13 @@ resip::NameAddr Account::contact(SecureScheme ss)
void Account::queryStunServerIp() void Account::queryStunServerIp()
{ {
ICELogInfo(<<"Looking for STUN/TURN server IP"); ICELogInfo(<< "Looking for STUN/TURN server IP");
if (!mConfig->exists(CONFIG_STUNSERVER_NAME)) if (!mConfig->exists(CONFIG_STUNSERVER_NAME))
{ {
// Send request to find STUN or TURN service // Send request to find STUN or TURN service
std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." + mConfig->at(CONFIG_DOMAIN).asStdString(); std::string target = std::string(mConfig->at(CONFIG_RELAY).asBool() ? "_turn" : "_stun") + "._udp." +
mConfig->at(CONFIG_DOMAIN).asStdString();
// Start lookup // Start lookup
mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this); mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this);
@@ -475,9 +474,9 @@ void Account::queryStunServerIp()
} }
} }
void Account::prepareIceStack(Session *session, ice::AgentRole icerole) void Account::prepareIceStack(Session* session, ice::AgentRole icerole)
{ {
ice::ServerConfig config; ice::ServerConfig config;
ice::NetworkAddress addr; ice::NetworkAddress addr;
addr.setIp(mConfig->at(CONFIG_STUNSERVER_IP).asStdString()); addr.setIp(mConfig->at(CONFIG_STUNSERVER_IP).asStdString());
if (mConfig->at(CONFIG_STUNSERVER_PORT).asInt()) if (mConfig->at(CONFIG_STUNSERVER_PORT).asInt())
@@ -495,8 +494,8 @@ void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
config.mUseIPv4 = mAgent.config()[CONFIG_IPV4].asBool(); config.mUseIPv4 = mAgent.config()[CONFIG_IPV4].asBool();
config.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool(); config.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool();
//config.mDetectNetworkChange = true; // config.mDetectNetworkChange = true;
//config.mNetworkCheckInterval = 5000; // config.mNetworkCheckInterval = 5000;
session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config)); session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config));
session->mIceStack->setEventHandler(session, this); session->mIceStack->setEventHandler(session, this);
@@ -509,7 +508,7 @@ void Account::process()
queryStunServerIp(); queryStunServerIp();
} }
void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage &response) void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
{ {
// Save registration handle // Save registration handle
mRegistrationHandle = h; mRegistrationHandle = h;
@@ -519,16 +518,16 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
mRegistrationHandle->setCustomHeader(resip::Data(iter->first.c_str()), resip::Data(iter->second.c_str())); mRegistrationHandle->setCustomHeader(resip::Data(iter->first.c_str()), resip::Data(iter->second.c_str()));
// Get the Via // Get the Via
const resip::Via& via = response.header(resip::h_Vias).front(); const resip::Via& via = response.header(resip::h_Vias).front();
// Get the sent host // Get the sent host
const resip::Data& sentHost = via.sentHost();//response.header(h_Contacts).front().uri().host(); const resip::Data& sentHost = via.sentHost(); // response.header(h_Contacts).front().uri().host();
// Get the sentPort // Get the sentPort
int sentPort = via.sentPort(); int sentPort = via.sentPort();
const resip::Data& sourceHost = response.getSource().toData(resip::UDP); const resip::Data& sourceHost = response.getSource().toData(resip::UDP);
int rport = 0; int rport = 0;
if (via.exists(resip::p_rport)) if (via.exists(resip::p_rport))
rport = via.param(resip::p_rport).port(); rport = via.param(resip::p_rport).port();
@@ -553,13 +552,14 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
} }
mUsedTransport = response.getReceivedTransportTuple().getType(); mUsedTransport = response.getReceivedTransportTuple().getType();
//bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS; // bool streamTransport = mUsedTransport == resip::TCP || mUsedTransport == resip::TLS;
// Retry registration for stream based transport too // Retry registration for stream based transport too
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool()) if ((hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ &&
mConfig->at(CONFIG_EXTERNALIP).asBool())
{ {
//mRegistrationHandle->requestRefresh(); // mRegistrationHandle->requestRefresh();
// Unregister at first // Unregister at first
mRegistrationHandle->removeAll(); mRegistrationHandle->removeAll();
mRegistrationState = RegistrationState::Reregistering; mRegistrationState = RegistrationState::Reregistering;
return; return;
@@ -570,12 +570,12 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
mAgent.onAccountStart(mAgent.getAccount(this)); mAgent.onAccountStart(mAgent.getAccount(this));
} }
void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage &response) void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
{ {
// Check if this unregistering is a part of rport pr // Check if this unregistering is a part of rport pr
if (mRegistrationState == RegistrationState::Reregistering) if (mRegistrationState == RegistrationState::Reregistering)
{ {
//if (/*this->mUseExternalIP && */response.getSource().getType() == resip::UDP) // if (/*this->mUseExternalIP && */response.getSource().getType() == resip::UDP)
{ {
resip::Uri hostport(contact(SecureScheme::TlsOnly).uri()); resip::Uri hostport(contact(SecureScheme::TlsOnly).uri());
hostport.host() = resip::Data(mExternalAddress.ip()); hostport.host() = resip::Data(mExternalAddress.ip());
@@ -585,19 +585,24 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
const char* transportName = nullptr; const char* transportName = nullptr;
switch (mUsedTransport) switch (mUsedTransport)
{ {
case resip::TCP: transportName = "tcp"; break; case resip::TCP:
case resip::TLS: transportName = "tls"; break; transportName = "tcp";
break;
case resip::TLS:
transportName = "tls";
break;
} }
hostport.param(resip::p_transport) = resip::Data(transportName); hostport.param(resip::p_transport) = resip::Data(transportName);
} }
mProfile->setOverrideHostAndPort(hostport); mProfile->setOverrideHostAndPort(hostport);
//mProfile->setDefaultFrom(from); // mProfile->setDefaultFrom(from);
} }
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt()); mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME); auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++) for (UserInfo::const_iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
regmessage->header(resip::ExtensionHeader(iter->first.c_str())).push_back(resip::StringCategory(iter->second.c_str())); regmessage->header(resip::ExtensionHeader(iter->first.c_str()))
.push_back(resip::StringCategory(iter->second.c_str()));
mAgent.mDum->send(regmessage); mAgent.mDum->send(regmessage);
return; return;
@@ -625,16 +630,17 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
if (result.status == 0) if (result.status == 0)
{ {
resip::Data foundAddress = result.records.front().host(); resip::Data foundAddress = result.records.front().host();
ICELogInfo( << "Success to resolve STUN/TURN address to " << foundAddress.c_str()); ICELogInfo(<< "Success to resolve STUN/TURN address to " << foundAddress.c_str());
mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str()); mConfig->at(CONFIG_STUNSERVER_IP) = std::string(foundAddress.c_str());
// Here the IP address of STUN/TURN server is found. If account is registered already - it means account is ready. // Here the IP address of STUN/TURN server is found. If account is registered already - it means account is
// ready.
if (mRegistrationState == RegistrationState::Registered) if (mRegistrationState == RegistrationState::Registered)
mAgent.onAccountStart(mAgent.getAccount(this)); mAgent.onAccountStart(mAgent.getAccount(this));
} }
else else
{ {
ICELogError( << "Failed to resolve STUN or TURN server IP address."); ICELogError(<< "Failed to resolve STUN or TURN server IP address.");
if (mRegistrationState == RegistrationState::Registered) if (mRegistrationState == RegistrationState::Registered)
{ {
int startCode = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString().empty() ? 0 : 503; int startCode = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString().empty() ? 0 : 503;
@@ -643,10 +649,7 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
} }
} }
void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&) void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&) {}
{
}
void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result) void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
{ {
@@ -654,14 +657,14 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
{ {
// Find lowest priority // Find lowest priority
int priority = 0x7FFFFFFF; int priority = 0x7FFFFFFF;
for (size_t i=0; i<result.records.size(); i++) for (size_t i = 0; i < result.records.size(); i++)
if (result.records[i].priority() < priority) if (result.records[i].priority() < priority)
priority = result.records[i].priority(); priority = result.records[i].priority();
size_t index = 0; size_t index = 0;
int weight = 0; int weight = 0;
for (size_t i=0; i<result.records.size(); i++) for (size_t i = 0; i < result.records.size(); i++)
{ {
if (result.records[i].priority() == priority && result.records[i].weight() >= weight) if (result.records[i].priority() == priority && result.records[i].weight() >= weight)
{ {
@@ -674,15 +677,15 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
const char* host = result.records[index].target().c_str(); const char* host = result.records[index].target().c_str();
ICELogInfo( << "Success to find STUN/TURN server on " << result.records[index].target().c_str() << ICELogInfo(<< "Success to find STUN/TURN server on " << result.records[index].target().c_str() << ":"
":" << (int)result.records[index].port()); << (int)result.records[index].port());
if (inet_addr(host) == INADDR_NONE) if (inet_addr(host) == INADDR_NONE)
{ {
// Try to resolve domain name now // Try to resolve domain name now
mAgent.mStack->getDnsStub().lookup<resip::RR_A>(result.records[index].target(), this); mAgent.mStack->getDnsStub().lookup<resip::RR_A>(result.records[index].target(), this);
//mStack->getDnsStub().lookup<resip::RR_AAAA>(result.records[index].target(), this); // mStack->getDnsStub().lookup<resip::RR_AAAA>(result.records[index].target(), this);
} }
else else
{ {
@@ -691,27 +694,20 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
} }
else else
{ {
ICELogError( << "Failed to find STUN or TURN service for specified domain."); ICELogError(<< "Failed to find STUN or TURN service for specified domain.");
//mAgent::shutdown(); // mAgent::shutdown();
} }
} }
void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&) void Account::onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&) {}
{
} void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&) {}
void Account::onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&) bool Account::isResponsibleFor(const resip::NameAddr& addr)
{
}
bool Account::isResponsibleFor(const resip::NameAddr &addr)
{ {
std::string user = addr.uri().user().c_str(); std::string user = addr.uri().user().c_str();
std::string domain = addr.uri().host().c_str(); std::string domain = addr.uri().host().c_str();
int p = addr.uri().port(); int p = addr.uri().port();
if (mConfig->at(CONFIG_USERNAME).asStdString() == user && mConfig->at(CONFIG_DOMAIN).asStdString() == domain) if (mConfig->at(CONFIG_USERNAME).asStdString() == user && mConfig->at(CONFIG_DOMAIN).asStdString() == domain)
{ {
// Check if ports are the same or port is not specified at all // Check if ports are the same or port is not specified at all
@@ -724,7 +720,7 @@ bool Account::isResponsibleFor(const resip::NameAddr &addr)
return false; return false;
} }
void Account::setUserInfo(const UserInfo &info) void Account::setUserInfo(const UserInfo& info)
{ {
mUserInfo = info; mUserInfo = info;
if (mRegistrationHandle.isValid()) if (mRegistrationHandle.isValid())
@@ -740,7 +736,7 @@ Account::UserInfo Account::getUserInfo() const
} }
std::atomic_int Account::IdGenerator; std::atomic_int Account::IdGenerator;
int Account::generateId() int Account::generateId()
{ {
return ++IdGenerator; return ++IdGenerator;
} }
+35 -34
View File
@@ -22,10 +22,11 @@
class UserAgent; class UserAgent;
class Session; class Session;
class Account: public resip::DnsResultSink class Account : public resip::DnsResultSink
{ {
friend class UserAgent; friend class UserAgent;
friend class NATDecorator; friend class NATDecorator;
public: public:
Account(PVariantMap config, UserAgent& agent); Account(PVariantMap config, UserAgent& agent);
~Account(); ~Account();
@@ -34,7 +35,7 @@ public:
void stop(); void stop();
void refresh(); void refresh();
bool active(); bool active();
int id() const; int id() const;
enum class RegistrationState enum class RegistrationState
{ {
@@ -47,69 +48,69 @@ public:
RegistrationState registrationState(); RegistrationState registrationState();
/* Publishes new presence information */ /* Publishes new presence information */
void publishPresence(bool online, const std::string& content, int seconds = 600); void publishPresence(bool online, const std::string& content, int seconds = 600);
/* Stops publishing of presence */ /* Stops publishing of presence */
void stopPublish(); void stopPublish();
/* Starts observing on specified target / package */ /* Starts observing on specified target / package */
PClientObserver observe(const std::string& target, const std::string& package, void* tag); PClientObserver observe(const std::string& target, const std::string& package, void* tag);
/* Queues message to peer with specified mime type. Returns ID of message. */ /* Queues message to peer with specified mime type. Returns ID of message. */
int sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag); int sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag);
/* Returns name of account - <sip:user@domain> */ /* Returns name of account - <sip:user@domain> */
std::string name(); std::string name();
/* Updates account with configuration */ /* Updates account with configuration */
void setup(VariantMap& config); void setup(VariantMap& config);
/* Returns corresponding resiprocate profile */ /* Returns corresponding resiprocate profile */
std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; } std::shared_ptr<resip::UserProfile> getUserProfile() const { return mProfile; }
typedef std::map<std::string, std::string> UserInfo; typedef std::map<std::string, std::string> UserInfo;
void setUserInfo(const UserInfo& info); void setUserInfo(const UserInfo& info);
UserInfo getUserInfo() const; UserInfo getUserInfo() const;
protected: protected:
PVariantMap mConfig; PVariantMap mConfig;
// Registration // Registration
ResipSession* mRegistration; ResipSession* mRegistration;
resip::ClientRegistrationHandle mRegistrationHandle; resip::ClientRegistrationHandle mRegistrationHandle;
resip::ClientPublicationHandle mPublication; resip::ClientPublicationHandle mPublication;
resip::TransportType mUsedTransport; resip::TransportType mUsedTransport;
RegistrationState mRegistrationState; RegistrationState mRegistrationState;
ice::NetworkAddress mExternalAddress; ice::NetworkAddress mExternalAddress;
std::shared_ptr<resip::UserProfile> mProfile; std::shared_ptr<resip::UserProfile> mProfile;
UserAgent& mAgent; UserAgent& mAgent;
bool mPresenceOnline; bool mPresenceOnline;
std::string mPresenceContent; std::string mPresenceContent;
// Timer to refresh STUN server IP // Timer to refresh STUN server IP
ice::ICEScheduleTimer mRefreshStunServerIpTimer; ice::ICEScheduleTimer mRefreshStunServerIpTimer;
// Cached auth // Cached auth
resip::Auth mCachedAuth; resip::Auth mCachedAuth;
// Id of account // Id of account
int mId; int mId;
// User info about current state // User info about current state
UserInfo mUserInfo; UserInfo mUserInfo;
// List of client subscriptions sent from this account // List of client subscriptions sent from this account
typedef std::set<PClientObserver> ClientObserverSet; typedef std::set<PClientObserver> ClientObserverSet;
ClientObserverSet mClientObserverSet; ClientObserverSet mClientObserverSet;
void process(); void process();
// Method queries new stun server ip from dns (if stun server is specified as dns name) // Method queries new stun server ip from dns (if stun server is specified as dns name)
void queryStunServerIp(); void queryStunServerIp();
bool isResponsibleFor(const resip::NameAddr& addr); bool isResponsibleFor(const resip::NameAddr& addr);
enum class SecureScheme enum class SecureScheme
{ {
SipsAndTls, SipsAndTls,
@@ -121,10 +122,10 @@ protected:
resip::NameAddr contact(SecureScheme ss = SecureScheme::SipsOnly); resip::NameAddr contact(SecureScheme ss = SecureScheme::SipsOnly);
// This method prepares configuration, creates ice stack and sets ownership to session // This method prepares configuration, creates ice stack and sets ownership to session
void prepareIceStack(Session* session, ice::AgentRole role); void prepareIceStack(Session* session, ice::AgentRole role);
void onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response); void onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
void onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response); void onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response); void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response);
#pragma region DnsResultSink implementation #pragma region DnsResultSink implementation
void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&); void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&);
@@ -134,7 +135,7 @@ protected:
void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&); void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&);
#pragma endregion #pragma endregion
static int generateId(); static int generateId();
static std::atomic_int IdGenerator; static std::atomic_int IdGenerator;
}; };
+43 -31
View File
@@ -17,8 +17,7 @@
#define LOG_SUBSYSTEM "engine" #define LOG_SUBSYSTEM "engine"
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal) AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
:mUserAgent(agent), mTerminal(terminal), mState(0), : mUserAgent(agent), mTerminal(terminal), mState(0), mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
{ {
mActive = mfActive; mActive = mfActive;
mRemoteState = msSendRecv; mRemoteState = msSendRecv;
@@ -26,12 +25,11 @@ AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY)) if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap()); mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
mSrtpSuite = SRTP_NONE; mSrtpSuite = SRTP_NONE;
setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending); setStateImpl((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving |
(int)StreamState::Sending);
} }
AudioProvider::~AudioProvider() AudioProvider::~AudioProvider() {}
{
}
std::string AudioProvider::streamName() std::string AudioProvider::streamName()
{ {
@@ -47,7 +45,7 @@ std::string AudioProvider::streamProfile()
} }
// Sets destination IP address // Sets destination IP address
void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr) void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
{ {
if (!mActiveStream) if (!mActiveStream)
return; return;
@@ -55,7 +53,7 @@ void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
mActiveStream->setDestination(addr); mActiveStream->setDestination(addr);
} }
void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer, void* userTag) void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag)
{ {
mMediaObserver = observer; mMediaObserver = observer;
mMediaObserverTag = userTag; mMediaObserverTag = userTag;
@@ -64,20 +62,21 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer,
} }
// Processes incoming data // Processes incoming data
void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) void AudioProvider::processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source)
{ {
if (!mActiveStream) if (!mActiveStream)
return; return;
if (RtpHelper::isRtpOrRtcp(dataBuffer, dataSize)) if (RtpHelper::isRtpOrRtcp(dataBuffer, dataSize))
{ {
ICELogMedia(<<"Adding new data to stream processing"); ICELogMedia(<< "Adding new data to stream processing");
mActiveStream->dataArrived(s, dataBuffer, dataSize, source); mActiveStream->dataArrived(s, dataBuffer, dataSize, source);
} }
} }
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer, unsigned int size) void AudioProvider::sendData(const PDatagramSocket& s, InternetAddress& destination, const void* buffer,
unsigned int size)
{ {
s->sendDatagram(destination, buffer, size); s->sendDatagram(destination, buffer, size);
} }
@@ -105,7 +104,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
// Use CodecListPriority mCodecPriority adapter to work with codec priorities // Use CodecListPriority mCodecPriority adapter to work with codec priorities
if (mAvailableCodecs.empty()) if (mAvailableCodecs.empty())
{ {
for (int i=0; i<mCodecPriority.count(mTerminal.codeclist()); i++) for (int i = 0; i < mCodecPriority.count(mTerminal.codeclist()); i++)
mCodecPriority.codecAt(mTerminal.codeclist(), i).updateSdp(sdp.codecs(), direction); mCodecPriority.codecAt(mTerminal.codeclist(), i).updateSdp(sdp.codecs(), direction);
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent); sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
} }
@@ -122,22 +121,35 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
switch (mActive) switch (mActive)
{ {
case mfActive: case mfActive:
switch(mRemoteState) switch (mRemoteState)
{ {
case msSendonly: attr = "recvonly"; break; case msSendonly:
case msInactive: attr = "recvonly"; break; attr = "recvonly";
break;
case msInactive:
attr = "recvonly";
break;
case msRecvonly: case msRecvonly:
case msSendRecv: break; // Do nothing here case msSendRecv:
break; // Do nothing here
} }
break; break;
case mfPaused: case mfPaused:
switch (mRemoteState) switch (mRemoteState)
{ {
case msRecvonly: attr = "sendonly"; break; case msRecvonly:
case msSendonly: attr = "inactive"; break; attr = "sendonly";
case msInactive: attr = "inactive"; break; break;
case msSendRecv: attr = "sendonly"; break; case msSendonly:
attr = "inactive";
break;
case msInactive:
attr = "inactive";
break;
case msSendRecv:
attr = "sendonly";
break;
} }
break; break;
} }
@@ -227,7 +239,7 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
// Use CodecListPriority mCodecPriority to work with codec priorities // Use CodecListPriority mCodecPriority to work with codec priorities
int pt; int pt;
for (int localIndex=0; localIndex<mCodecPriority.count(mTerminal.codeclist()); localIndex++) for (int localIndex = 0; localIndex < mCodecPriority.count(mTerminal.codeclist()); localIndex++)
{ {
MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex); MT::Codec::Factory& factory = mCodecPriority.codecAt(mTerminal.codeclist(), localIndex);
if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1) if ((pt = factory.processSdp(media.codecs(), sdpDirection)) != -1)
@@ -242,14 +254,14 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
{ {
// Find the most strong crypt suite // Find the most strong crypt suite
const std::list<resip::Data>& vl = media.getValues("crypto"); const std::list<resip::Data>& vl = media.getValues("crypto");
SrtpSuite ss = SRTP_NONE; SrtpSuite ss = SRTP_NONE;
ByteBuffer key; ByteBuffer key;
for (std::list<resip::Data>::const_iterator attrIter = vl.begin(); attrIter != vl.end(); attrIter++) for (std::list<resip::Data>::const_iterator attrIter = vl.begin(); attrIter != vl.end(); attrIter++)
{ {
const resip::Data& attr = *attrIter; const resip::Data& attr = *attrIter;
ByteBuffer tempkey; ByteBuffer tempkey;
int tag = 1; int tag = 1;
SrtpSuite suite = processCryptoAttribute(attr, tempkey, &tag); SrtpSuite suite = processCryptoAttribute(attr, tempkey, &tag);
if (srtpSuiteStrength(suite) > srtpSuiteStrength(ss)) if (srtpSuiteStrength(suite) > srtpSuiteStrength(ss))
{ {
ss = suite; ss = suite;
@@ -316,9 +328,9 @@ std::string AudioProvider::createCryptoAttribute(SrtpSuite suite, int tag)
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag) SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag)
{ {
int srtpTag = 0; int srtpTag = 0;
char suite[64], keyChunk[256]; char suite[64], keyChunk[256];
int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk); int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk);
if (components != 3) if (components != 3)
return SRTP_NONE; return SRTP_NONE;
if (tag) if (tag)
@@ -343,7 +355,7 @@ void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::Codec
for (codecIter = codecs.begin(); codecIter != codecs.end(); codecIter++) for (codecIter = codecs.begin(); codecIter != codecs.end(); codecIter++)
{ {
if (strcmp("TELEPHONE-EVENT", codecIter->getName().c_str()) == 0 || if (strcmp("TELEPHONE-EVENT", codecIter->getName().c_str()) == 0 ||
strcmp("telephone-event", codecIter->getName().c_str()) == 0) strcmp("telephone-event", codecIter->getName().c_str()) == 0)
mRemoteTelephoneCodec = codecIter->payloadType(); mRemoteTelephoneCodec = codecIter->payloadType();
} }
} }
@@ -367,9 +379,9 @@ void AudioProvider::setupMirror(bool enable)
mActiveStream->setupMirror(enable); mActiveStream->setupMirror(enable);
} }
void AudioProvider::setStateImpl(unsigned int state) { void AudioProvider::setStateImpl(unsigned int state)
{
mState = state; mState = state;
if (mActiveStream) if (mActiveStream)
mActiveStream->setState(state); mActiveStream->setState(state);
} }
+74 -75
View File
@@ -18,104 +18,103 @@
class UserAgent; class UserAgent;
class AudioProvider: public DataProvider class AudioProvider : public DataProvider
{ {
public: public:
AudioProvider(UserAgent& agent, MT::Terminal& terminal);
virtual ~AudioProvider();
AudioProvider(UserAgent& agent, MT::Terminal& terminal); // Returns provider RTP name
virtual ~AudioProvider(); std::string streamName() override;
// Returns provider RTP name
std::string streamName() override;
// Returns provider RTP profile name
std::string streamProfile() override;
// Sets destination IP address
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
// Processes incoming data // Returns provider RTP profile name
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override; std::string streamProfile() override;
// This method is called by user agent to send ICE packet from mediasocket // Sets destination IP address
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override; void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
// Updates SDP offer // Processes incoming data
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override; void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
// Called by user agent when session is deleted. // This method is called by user agent to send ICE packet from mediasocket
void sessionDeleted() override; void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer,
unsigned int datasize) override;
// Called by user agent when session is terminated. // Updates SDP offer
void sessionTerminated() override; void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
// Called by user agent when session is started. // Called by user agent when session is deleted.
void sessionEstablished(int conntype) override; void sessionDeleted() override;
// Called by user agent to save media socket for this provider // Called by user agent when session is terminated.
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override; void sessionTerminated() override;
// Called by user agent to get media socket for this provider
RtpPair<PDatagramSocket>& socket(int family) override;
// Called by user agent to process media stream description from remote peer. // Called by user agent when session is started.
// Returns true if description is processed succesfully. Otherwise method returns false. void sessionEstablished(int conntype) override;
// myAnswer sets if the answer will be sent after.
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) override; // Called by user agent to save media socket for this provider
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override;
// Called by user agent to get media socket for this provider
RtpPair<PDatagramSocket>& socket(int family) override;
// Called by user agent to process media stream description from remote peer.
// Returns true if description is processed succesfully. Otherwise method returns false.
// myAnswer sets if the answer will be sent after.
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) override;
void setState(unsigned state) override; void setState(unsigned state) override;
unsigned state() override; unsigned state() override;
MT::Statistics getStatistics() override; MT::Statistics getStatistics() override;
MT::PStream activeStream(); MT::PStream activeStream();
void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction); void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction);
void writeFile(const Audio::PWavFileWriter& stream, MT::Stream::MediaDirection direction); void writeFile(const Audio::PWavFileWriter& stream, MT::Stream::MediaDirection direction);
void setupMirror(bool enable); void setupMirror(bool enable);
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag); void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag = nullptr); static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag = nullptr);
protected: protected:
// SDP's stream name // SDP's stream name
std::string mStreamName; std::string mStreamName;
// Socket handles to operate
RtpPair<PDatagramSocket> mSocket4, mSocket6;
// Destination IP4/6 address // Socket handles to operate
RtpPair<InternetAddress> mDestination; RtpPair<PDatagramSocket> mSocket4, mSocket6;
MT::PStream mActiveStream;
UserAgent& mUserAgent;
MT::Terminal& mTerminal;
MT::Statistics mBackupStats;
unsigned mState; // Destination IP4/6 address
SrtpSuite mSrtpSuite; RtpPair<InternetAddress> mDestination;
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
struct RemoteCodec
{
RemoteCodec(MT::Codec::Factory* factory, int payloadType)
:mFactory(factory), mRemotePayloadType(payloadType)
{ }
MT::Codec::Factory* mFactory; MT::PStream mActiveStream;
int mRemotePayloadType; UserAgent& mUserAgent;
}; MT::Terminal& mTerminal;
std::vector<RemoteCodec> mAvailableCodecs; MT::Statistics mBackupStats;
int mRemoteTelephoneCodec; // Payload type of remote rfc2833 codec
bool mRemoteNoSdp; // Marks if we got no-sdp offer
MT::CodecListPriority mCodecPriority;
MT::Stream::MediaObserver* mMediaObserver = nullptr;
void* mMediaObserverTag = nullptr;
std::string createCryptoAttribute(SrtpSuite suite, int tag); unsigned mState;
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs); SrtpSuite mSrtpSuite;
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
struct RemoteCodec
{
RemoteCodec(MT::Codec::Factory* factory, int payloadType) : mFactory(factory), mRemotePayloadType(payloadType)
{
}
// Implements setState() logic. This allows to be called from constructor (it is not virtual function) MT::Codec::Factory* mFactory;
void setStateImpl(unsigned state); int mRemotePayloadType;
};
std::vector<RemoteCodec> mAvailableCodecs;
int mRemoteTelephoneCodec; // Payload type of remote rfc2833 codec
bool mRemoteNoSdp; // Marks if we got no-sdp offer
MT::CodecListPriority mCodecPriority;
MT::Stream::MediaObserver* mMediaObserver = nullptr;
void* mMediaObserverTag = nullptr;
std::string createCryptoAttribute(SrtpSuite suite, int tag);
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
void setStateImpl(unsigned state);
}; };
#endif #endif
+25 -27
View File
@@ -10,7 +10,7 @@ bool DataProvider::isSupported(const char* name)
{ {
return !strcmp(name, "audio"); return !strcmp(name, "audio");
//return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video")); // return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video"));
} }
void DataProvider::pause() void DataProvider::pause()
@@ -20,7 +20,7 @@ void DataProvider::pause()
// Stop receive RTP stream // Stop receive RTP stream
if (state() & (int)StreamState::Receiving) if (state() & (int)StreamState::Receiving)
setState( state() & ~(int)StreamState::Receiving ); setState(state() & ~(int)StreamState::Receiving);
mActive = mfPaused; mActive = mfPaused;
} }
@@ -28,10 +28,10 @@ void DataProvider::pause()
void DataProvider::resume() void DataProvider::resume()
{ {
// Tell remote peer about resumed receiving in SDP // Tell remote peer about resumed receiving in SDP
//setState( state() | STATE_SIPRECV ); // setState( state() | STATE_SIPRECV );
// Start receive RTP stream // Start receive RTP stream
setState( state() | (int)StreamState::Receiving ); setState(state() | (int)StreamState::Receiving);
mActive = mfActive; mActive = mfActive;
} }
@@ -44,31 +44,29 @@ bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& me
mRemoteState = msSendonly; mRemoteState = msSendonly;
setState(state() & ~(int)StreamState::Sending); setState(state() & ~(int)StreamState::Sending);
} }
else if (media.exists("recvonly"))
{
mRemoteState = msRecvonly;
setState(state() & ~(int)StreamState::Receiving);
}
else if (media.exists("inactive"))
{
mRemoteState = msInactive;
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving));
}
else else
if (media.exists("recvonly")) {
mRemoteState = msSendRecv;
switch (mActive)
{ {
mRemoteState = msRecvonly; case mfActive:
setState(state() & ~(int)StreamState::Receiving); setState(state() | (int)StreamState::Sending | (int)StreamState::Receiving);
} break;
else
if (media.exists("inactive"))
{
mRemoteState = msInactive;
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving) );
}
else
{
mRemoteState = msSendRecv;
switch (mActive)
{
case mfActive:
setState(state() | (int)StreamState::Sending | (int)StreamState::Receiving);
break;
case mfPaused: case mfPaused:
setState(state() | (int)StreamState::Sending ); setState(state() | (int)StreamState::Sending);
break; break;
} }
} }
return true; return true;
} }
+20 -18
View File
@@ -33,59 +33,61 @@ public:
msInactive msInactive
}; };
static bool isSupported(const char* name); static bool isSupported(const char* name);
// Returns provider RTP name // Returns provider RTP name
virtual std::string streamName() = 0; virtual std::string streamName() = 0;
// Returns provider RTP profile name // Returns provider RTP profile name
virtual std::string streamProfile() = 0; virtual std::string streamProfile() = 0;
// Sets destination IP address // Sets destination IP address
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0; virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
// Processes incoming data // Processes incoming data
virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& address) = 0; virtual void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize,
InternetAddress& address) = 0;
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) = 0; virtual void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer,
unsigned int datasize) = 0;
// Updates SDP offer // Updates SDP offer
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0; virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) = 0;
// Called by user agent when session is deleted. Comes after sessionTerminated(). // Called by user agent when session is deleted. Comes after sessionTerminated().
virtual void sessionDeleted() = 0; virtual void sessionDeleted() = 0;
// Called by user agent when session is terminated. // Called by user agent when session is terminated.
virtual void sessionTerminated() = 0; virtual void sessionTerminated() = 0;
// Called by user agent when session is started. // Called by user agent when session is started.
virtual void sessionEstablished(int conntype) = 0; virtual void sessionEstablished(int conntype) = 0;
// Called by user agent to save media socket for this provider // Called by user agent to save media socket for this provider
virtual void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) = 0; virtual void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) = 0;
// Called by user agent to get media socket for this provider // Called by user agent to get media socket for this provider
virtual RtpPair<PDatagramSocket>& socket(int family) = 0; virtual RtpPair<PDatagramSocket>& socket(int family) = 0;
// Called by user agent to process media stream description from remote peer. // Called by user agent to process media stream description from remote peer.
// Returns true if description is processed succesfully. Otherwise method returns false. // Returns true if description is processed succesfully. Otherwise method returns false.
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) = 0; virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) = 0;
virtual unsigned state() = 0; virtual unsigned state() = 0;
virtual void setState(unsigned state) = 0; virtual void setState(unsigned state) = 0;
virtual void pause(); virtual void pause();
virtual void resume(); virtual void resume();
virtual MT::Statistics getStatistics() = 0; virtual MT::Statistics getStatistics() = 0;
protected: protected:
MediaFlow mActive; MediaFlow mActive;
MediaState mRemoteState; MediaState mRemoteState;
}; };
typedef std::shared_ptr<DataProvider> PDataProvider; typedef std::shared_ptr<DataProvider> PDataProvider;
typedef std::vector<PDataProvider> DataProviderVector; typedef std::vector<PDataProvider> DataProviderVector;
#endif #endif
File diff suppressed because it is too large Load Diff
+142 -133
View File
@@ -70,45 +70,47 @@ enum
enum enum
{ {
CONFIG_IPV4 = 0, // Use IP4 CONFIG_IPV4 = 0, // Use IP4
CONFIG_IPV6, // Use IP6. CONFIG_IPV6, // Use IP6.
CONFIG_USERNAME, // Username. String value. CONFIG_USERNAME, // Username. String value.
CONFIG_DOMAIN, // Domain. String value. CONFIG_DOMAIN, // Domain. String value.
CONFIG_PASSWORD, // Password. String value. CONFIG_PASSWORD, // Password. String value.
CONFIG_RINSTANCE, // Determines if SIP rinstance field has to be used during registration. Boolean value. CONFIG_RINSTANCE, // Determines if SIP rinstance field has to be used during registration. Boolean value.
CONFIG_INSTANCE_ID, // Instance id. It is alternative option to rinstance. CONFIG_INSTANCE_ID, // Instance id. It is alternative option to rinstance.
CONFIG_DISPLAYNAME, // Optional user display name. String value. CONFIG_DISPLAYNAME, // Optional user display name. String value.
CONFIG_DOMAINPORT, // Optional domain port number. Integer value. CONFIG_DOMAINPORT, // Optional domain port number. Integer value.
CONFIG_REGISTERDURATION, // Wanted duration for registration. Integer value. It is MANDATORY value. CONFIG_REGISTERDURATION, // Wanted duration for registration. Integer value. It is MANDATORY value.
CONFIG_RPORT, // Use SIP rport field. Recommended to set it to true. Boolean value. CONFIG_RPORT, // Use SIP rport field. Recommended to set it to true. Boolean value.
CONFIG_KEEPALIVETIME, // Interval between UDP keep-alive messages. Boolean value. CONFIG_KEEPALIVETIME, // Interval between UDP keep-alive messages. Boolean value.
CONFIG_RELAY, // Sets if TURN server must be used instead of STUN. Boolean value. CONFIG_RELAY, // Sets if TURN server must be used instead of STUN. Boolean value.
CONFIG_ICETIMEOUT, // Optional timeout for ICE connectivity checks and candidate gathering. Integer value. CONFIG_ICETIMEOUT, // Optional timeout for ICE connectivity checks and candidate gathering. Integer value.
CONFIG_ICEUSERNAME, // Optional username for TURN server. String value. CONFIG_ICEUSERNAME, // Optional username for TURN server. String value.
CONFIG_ICEPASSWORD, // Optional password for TURN server. String value. CONFIG_ICEPASSWORD, // Optional password for TURN server. String value.
CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value. CONFIG_SIPS, // Marks if account credentials are sips: scheme. Boolean value.
CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_NAME. CONFIG_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value. // CONFIG_STUNSERVER_NAME.
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value. CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value. CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
CONFIG_ICEREQUIRED, // ICE MUST be present in remote peer offers and answers. Boolean value. CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
CONFIG_TRANSPORT, // 0 - all transports, 1 - UDP, 2 - TCP, 3 - TLS, CONFIG_ICEREQUIRED, // ICE MUST be present in remote peer offers and answers. Boolean value.
CONFIG_SUBSCRIPTION_TIME, // Subscription time (in seconds) CONFIG_TRANSPORT, // 0 - all transports, 1 - UDP, 2 - TCP, 3 - TLS,
CONFIG_SUBSCRIPTION_TIME, // Subscription time (in seconds)
CONFIG_SUBSCRIPTION_REFRESHTIME, // Refresh interval for subscriptions CONFIG_SUBSCRIPTION_REFRESHTIME, // Refresh interval for subscriptions
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
CONFIG_ROOTCERT, // Additional root cert in PEM format; string. CONFIG_ROOTCERT, // Additional root cert in PEM format; string.
CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to reduce number of steps of SIP transaction CONFIG_CACHECREDENTIALS, // Attempt to cache credentials that comes in response from PBX. Use them when possible to
CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true. // reduce number of steps of SIP transaction
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing CONFIG_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
CONFIG_DEFERRELAYED, // Defer relayed media path CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
CONFIG_PROXY, // Proxy host name or IP address CONFIG_DEFERRELAYED, // Defer relayed media path
CONFIG_PROXYPORT, // Proxy port number CONFIG_PROXY, // Proxy host name or IP address
CONFIG_CODEC_PRIORITY, // Another VariantMap with codec priorities, CONFIG_PROXYPORT, // Proxy port number
CONFIG_ACCOUNT, // VariantMap with account configuration CONFIG_CODEC_PRIORITY, // Another VariantMap with codec priorities,
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests CONFIG_ACCOUNT, // VariantMap with account configuration
CONFIG_OWN_DNS, // Use predefined DNS servers CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
CONFIG_REGID // reg-id value from RFC5626, CONFIG_OWN_DNS, // Use predefined DNS servers
CONFIG_REGID // reg-id value from RFC5626,
}; };
// Conntype parameter for OnSessionEstablished event // Conntype parameter for OnSessionEstablished event
@@ -130,7 +132,7 @@ public:
typedef std::vector<SIPAction*> SIPActionVector; typedef std::vector<SIPAction*> SIPActionVector;
// Session termination reason // Session termination reason
enum enum
{ {
Error, Error,
Timeout, Timeout,
@@ -139,35 +141,37 @@ enum
RemoteBye, RemoteBye,
LocalCancel, LocalCancel,
RemoteCancel, RemoteCancel,
Rejected, //Only as UAS, UAC has distinct onFailure callback Rejected, // Only as UAS, UAC has distinct onFailure callback
Referred Referred
}; };
class UserAgent: public resip::ClientRegistrationHandler, class UserAgent : public resip::ClientRegistrationHandler,
public resip::InviteSessionHandler, public resip::InviteSessionHandler,
public resip::DumShutdownHandler, public resip::DumShutdownHandler,
public resip::ExternalLogger, public resip::ExternalLogger,
public resip::DnsResultSink, public resip::DnsResultSink,
public resip::ClientSubscriptionHandler, public resip::ClientSubscriptionHandler,
public resip::ServerSubscriptionHandler, public resip::ServerSubscriptionHandler,
public resip::ClientPagerMessageHandler, public resip::ClientPagerMessageHandler,
public resip::ServerPagerMessageHandler, public resip::ServerPagerMessageHandler,
public resip::ClientPublicationHandler public resip::ClientPublicationHandler
//public resip::InternalTransport::TransportLogger // public resip::InternalTransport::TransportLogger
{ {
friend class Account; friend class Account;
friend class Session; friend class Session;
friend class ResipSession; friend class ResipSession;
friend class NATDecorator; friend class NATDecorator;
friend class WatcherQueue; friend class WatcherQueue;
public: public:
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */ /* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same.
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2); * Otherwise returns false. */
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
static std::string formatSipAddress(const std::string& sip); static std::string formatSipAddress(const std::string& sip);
static bool isSipAddressValid(const std::string& sip); static bool isSipAddressValid(const std::string& sip);
struct SipAddress struct SipAddress
{ {
bool mValid; bool mValid;
std::string mScheme; std::string mScheme;
std::string mUsername; std::string mUsername;
std::string mDomain; std::string mDomain;
@@ -181,113 +185,118 @@ public:
/* Brings user agent online. Basically it creates a signalling socket(s). /* Brings user agent online. Basically it creates a signalling socket(s).
This is asynchronous method. */ This is asynchronous method. */
void start(); void start();
/* Shutdowns user agent. It closes all sessions, tries to unregister from server and disconnects from it. /* Shutdowns user agent. It closes all sessions, tries to unregister from server and disconnects from it.
This is asynchronous method. onStop() event will be called later */ This is asynchronous method. onStop() event will be called later */
void shutdown(); void shutdown();
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything. onStop() is called in context of this method. */ /* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything.
void stop(); * onStop() is called in context of this method. */
void stop();
/* Checks if user agent is active (started). */ /* Checks if user agent is active (started). */
bool active(); bool active();
/* Used to refresh existing registration(s), publication, subscriptions. */ /* Used to refresh existing registration(s), publication, subscriptions. */
void refresh(); void refresh();
/* Runs sip & ice stacks. Event handlers are called in its context. */ /* Runs sip & ice stacks. Event handlers are called in its context. */
void process(); void process();
/* Adds root cert in PEM format. Usable after start() call. */ /* Adds root cert in PEM format. Usable after start() call. */
void addRootCert(const ByteBuffer& data); void addRootCert(const ByteBuffer& data);
PAccount createAccount(PVariantMap config); PAccount createAccount(PVariantMap config);
void deleteAccount(PAccount account); void deleteAccount(PAccount account);
/* Creates session. Returns session ID. */ /* Creates session. Returns session ID. */
PSession createSession(PAccount account); PSession createSession(PAccount account);
// Must be called when IP interface list is changed // Must be called when IP interface list is changed
void updateInterfaceList(); void updateInterfaceList();
// Called on new incoming session; providers shoukld // Called on new incoming session; providers shoukld
virtual PDataProvider onProviderNeeded(const std::string& name) = 0; virtual PDataProvider onProviderNeeded(const std::string& name) = 0;
// Called on new session offer // Called on new session offer
virtual void onNewSession(PSession s) = 0; virtual void onNewSession(PSession s) = 0;
// Called when session is terminated // Called when session is terminated
virtual void onSessionTerminated(PSession s, int responsecode, int reason) = 0; virtual void onSessionTerminated(PSession s, int responsecode, int reason) = 0;
// Called when session is established ok i.e. after all ICE signalling is finished // Called when session is established ok i.e. after all ICE signalling is finished
// Conntype is type of establish event - EV_SIP or EV_ICE // Conntype is type of establish event - EV_SIP or EV_ICE
virtual void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) = 0; virtual void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) = 0;
// Called when client session gets // Called when client session gets
virtual void onSessionProvisional(PSession s, int code) = 0; virtual void onSessionProvisional(PSession s, int code) = 0;
// Called when user agent started // Called when user agent started
virtual void onStart(int errorcode) = 0; virtual void onStart(int errorcode) = 0;
// Called when user agent stopped // Called when user agent stopped
virtual void onStop() = 0; virtual void onStop() = 0;
// Called when account registered // Called when account registered
virtual void onAccountStart(PAccount account) = 0; virtual void onAccountStart(PAccount account) = 0;
// Called when account removed or failed (non zero error code) // Called when account removed or failed (non zero error code)
virtual void onAccountStop(PAccount account, int error) = 0; virtual void onAccountStop(PAccount account, int error) = 0;
// Called when connectivity checks failed. // Called when connectivity checks failed.
virtual void onConnectivityFailed(PSession s) = 0; virtual void onConnectivityFailed(PSession s) = 0;
// Called when new candidate is gathered // Called when new candidate is gathered
virtual void onCandidateGathered(PSession s, const char* address); virtual void onCandidateGathered(PSession s, const char* address);
// Called when network change detected // Called when network change detected
virtual void onNetworkChange(PSession s) = 0; virtual void onNetworkChange(PSession s) = 0;
// Called when all candidates are gathered // Called when all candidates are gathered
virtual void onGathered(PSession s); virtual void onGathered(PSession s);
// Called when new connectivity check is finished // Called when new connectivity check is finished
virtual void onCheckFinished(PSession s, const char* description); virtual void onCheckFinished(PSession s, const char* description);
// Called when log message must be recorded // Called when log message must be recorded
virtual void onLog(const char* msg); virtual void onLog(const char* msg);
// Called when problem with SIP connection(s) detected // Called when problem with SIP connection(s) detected
virtual void onSipConnectionFailed() = 0; virtual void onSipConnectionFailed() = 0;
// Subscribe/publish presence methods // Subscribe/publish presence methods
virtual void onPublicationSuccess(PAccount acc); virtual void onPublicationSuccess(PAccount acc);
virtual void onPublicationTerminated(PAccount acc, int code); virtual void onPublicationTerminated(PAccount acc, int code);
virtual void onClientObserverStart(PClientObserver observer); virtual void onClientObserverStart(PClientObserver observer);
virtual void onServerObserverStart(PServerObserver observer); virtual void onServerObserverStart(PServerObserver observer);
virtual void onClientObserverStop(PClientObserver observer, int code); virtual void onClientObserverStop(PClientObserver observer, int code);
virtual void onServerObserverStop(PServerObserver observer, int code); virtual void onServerObserverStop(PServerObserver observer, int code);
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online, const std::string& content); virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online,
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length); const std::string& content);
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag); virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag); virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
// Configuration methods // Configuration methods
VariantMap& config(); VariantMap& config();
public: public:
// InviteSessionHandler implementation // InviteSessionHandler implementation
#pragma region InviteSessionHandler implementation #pragma region InviteSessionHandler implementation
/// called when an initial INVITE or the intial response to an outoing invite /// called when an initial INVITE or the intial response to an outoing invite
virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override; virtual void onNewSession(resip::ClientInviteSessionHandle, resip::InviteSession::OfferAnswerType oat,
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override; const resip::SipMessage& msg) override;
virtual void onNewSession(resip::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat,
const resip::SipMessage& msg) override;
/// Received a failure response from UAS /// Received a failure response from UAS
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override; virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an in-dialog provisional response is received that contains an SDP body /// called when an in-dialog provisional response is received that contains an SDP body
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&, const resip::SdpContents&) override; virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage&,
const resip::SdpContents&) override;
/// called when dialog enters the Early state - typically after getting 18x /// called when dialog enters the Early state - typically after getting 18x
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override; virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
@@ -298,7 +307,8 @@ public:
/// called when a dialog initiated as a UAS enters the connected state /// called when a dialog initiated as a UAS enters the connected state
virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override; virtual void onConnected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage* related=0) override; virtual void onTerminated(resip::InviteSessionHandle, resip::InviteSessionHandler::TerminatedReason reason,
const resip::SipMessage* related = 0) override;
/// called when a fork that was created through a 1xx never receives a 2xx /// called when a fork that was created through a 1xx never receives a 2xx
/// because another fork answered and this fork was canceled by a proxy. /// because another fork answered and this fork was canceled by a proxy.
@@ -345,7 +355,8 @@ public:
/// rejected using the server subscription. If the offer is accepted, /// rejected using the server subscription. If the offer is accepted,
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an /// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
/// InviteSession that will send notify messages using the ServerSubscription /// InviteSession that will send notify messages using the ServerSubscription
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage& msg) override; virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle,
const resip::SipMessage& msg) override;
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override; virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
@@ -353,7 +364,8 @@ public:
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override; virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message receives an accepted response /// called when an REFER message receives an accepted response
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage& msg) override; virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle,
const resip::SipMessage& msg) override;
#pragma endregion #pragma endregion
// ClientRegistrationHandler implementation // ClientRegistrationHandler implementation
@@ -367,7 +379,7 @@ public:
/// call on Retry-After failure. /// call on Retry-After failure.
/// return values: -1 = fail, 0 = retry immediately, N = retry in N seconds /// return values: -1 = fail, 0 = retry immediately, N = retry in N seconds
int onRequestRetry(resip::ClientRegistrationHandle, int retrySeconds, const resip::SipMessage& response) override; int onRequestRetry(resip::ClientRegistrationHandle, int retrySeconds, const resip::SipMessage& response) override;
/// Called if registration fails, usage will be destroyed (unless a /// Called if registration fails, usage will be destroyed (unless a
/// Registration retry interval is enabled in the Profile) /// Registration retry interval is enabled in the Profile)
@@ -377,14 +389,9 @@ public:
#pragma region ExternalLogger implementation #pragma region ExternalLogger implementation
/** return true to also do default logging, false to suppress default logging. */ /** return true to also do default logging, false to suppress default logging. */
virtual bool operator()(resip::Log::Level level, virtual bool operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName,
const resip::Subsystem& subsystem, const char* file, int line, const resip::Data& message,
const resip::Data& appName, const resip::Data& messageWithHeaders, const resip::Data& instanceName) override;
const char* file,
int line,
const resip::Data& message,
const resip::Data& messageWithHeaders,
const resip::Data& instanceName) override;
#pragma endregion #pragma endregion
#pragma region DnsResultSink implementation #pragma region DnsResultSink implementation
@@ -399,13 +406,13 @@ public:
#pragma region TransportLogger implementation #pragma region TransportLogger implementation
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen); void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
#pragma endregion #pragma endregion
#pragma region ClientPublicationHandler #pragma region ClientPublicationHandler
void onSuccess(resip::ClientPublicationHandle, const resip::SipMessage& status) override; void onSuccess(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
void onRemove(resip::ClientPublicationHandle, const resip::SipMessage& status) override; void onRemove(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPublicationHandle, const resip::SipMessage& status) override; void onFailure(resip::ClientPublicationHandle, const resip::SipMessage& status) override;
int onRequestRetry(resip::ClientPublicationHandle, int retrySeconds, const resip::SipMessage& status) override; int onRequestRetry(resip::ClientPublicationHandle, int retrySeconds, const resip::SipMessage& status) override;
#pragma endregion #pragma endregion
#pragma region SubscriptionHandler #pragma region SubscriptionHandler
@@ -413,13 +420,13 @@ public:
void onUpdatePending(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override; void onUpdatePending(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
void onUpdateActive(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override; void onUpdateActive(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
//unknown Subscription-State value // unknown Subscription-State value
void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override; void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override; int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override;
//subscription can be ended through a notify or a failure response. // subscription can be ended through a notify or a failure response.
void onTerminated(resip::ClientSubscriptionHandle, const resip::SipMessage* msg) override; void onTerminated(resip::ClientSubscriptionHandle, const resip::SipMessage* msg) override;
//not sure if this has any value. // not sure if this has any value.
void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override; void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override;
/// called to allow app to adorn a message. /// called to allow app to adorn a message.
@@ -438,56 +445,58 @@ public:
#pragma region PagerHandler #pragma region PagerHandler
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override; void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, std::unique_ptr<resip::Contents> contents) override; void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status,
std::unique_ptr<resip::Contents> contents) override;
void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override; void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage& message) override;
#pragma endregion #pragma endregion
void onDumCanBeDeleted() override; void onDumCanBeDeleted() override;
protected: protected:
// Mutex to protect this instance // Mutex to protect this instance
Mutex mGuard; Mutex mGuard;
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here. // Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
std::shared_ptr<resip::MasterProfile> mProfile; std::shared_ptr<resip::MasterProfile> mProfile;
// Resiprocate's SIP stack object pointer // Resiprocate's SIP stack object pointer
resip::SipStack* mStack; resip::SipStack* mStack;
// Resiprocate's dialog usage manager object pointer // Resiprocate's dialog usage manager object pointer
resip::DialogUsageManager* mDum; resip::DialogUsageManager* mDum;
// List of available transports. They are owned by SipStack - so there is no need to delete instances in UserAgent. // List of available transports. They are owned by SipStack - so there is no need to delete instances in UserAgent.
std::vector<resip::InternalTransport*> mTransportList; std::vector<resip::InternalTransport*> mTransportList;
typedef std::map<int, PSession> SessionMap; typedef std::map<int, PSession> SessionMap;
// Session's map // Session's map
SessionMap mSessionMap; SessionMap mSessionMap;
// Used configuration // Used configuration
VariantMap mConfig; VariantMap mConfig;
// Action vector // Action vector
SIPActionVector mActionVector; SIPActionVector mActionVector;
typedef std::map<int, PClientObserver> ClientObserverMap; typedef std::map<int, PClientObserver> ClientObserverMap;
ClientObserverMap mClientObserverMap; ClientObserverMap mClientObserverMap;
typedef std::map<int, PServerObserver> ServerObserverMap; typedef std::map<int, PServerObserver> ServerObserverMap;
ServerObserverMap mServerObserverMap; ServerObserverMap mServerObserverMap;
typedef std::set<PAccount> AccountSet; typedef std::set<PAccount> AccountSet;
AccountSet mAccountSet; AccountSet mAccountSet;
// Constructs and sends INVITE to remote peer. Remote peer address is stored inside session object. // Constructs and sends INVITE to remote peer. Remote peer address is stored inside session object.
void sendOffer(Session* session); void sendOffer(Session* session);
void internalStopSession(Session& session); void internalStopSession(Session& session);
void processWatchingList(); void processWatchingList();
bool handleMultipartRelatedNotify(const resip::SipMessage& notify); bool handleMultipartRelatedNotify(const resip::SipMessage& notify);
PSession getUserSession(int sessionId); PSession getUserSession(int sessionId);
PAccount getAccount(const resip::NameAddr& myAddr); PAccount getAccount(const resip::NameAddr& myAddr);
PAccount getAccount(Account* account); PAccount getAccount(Account* account);
PAccount getAccount(int sessionId); PAccount getAccount(int sessionId);
}; };
#endif #endif
+10 -13
View File
@@ -1,19 +1,16 @@
#include "EP_NetworkQueue.h" #include "EP_NetworkQueue.h"
#include "EP_Engine.h" #include "EP_Engine.h"
WatcherQueue::WatcherQueue(UserAgent& ua) WatcherQueue::WatcherQueue(UserAgent& ua) : mActiveId(0), mAgent(ua) {}
:mActiveId(0), mAgent(ua)
{}
WatcherQueue::~WatcherQueue() WatcherQueue::~WatcherQueue() {}
{}
int WatcherQueue::add(const std::string& peer, const std::string& package, void* tag) int WatcherQueue::add(const std::string& peer, const std::string& package, void* tag)
{ {
ice::Lock l(mGuard); ice::Lock l(mGuard);
// Check if queue has similar item // Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++) for (unsigned i = 0; i < mItemList.size(); i++)
{ {
Item& item = mItemList[i]; Item& item = mItemList[i];
if (item.mTarget == peer && item.mPackage == package && item.mState != Item::State_Deleting) if (item.mTarget == peer && item.mPackage == package && item.mState != Item::State_Deleting)
@@ -43,7 +40,7 @@ void WatcherQueue::remove(int id)
ice::Lock l(mGuard); ice::Lock l(mGuard);
// Check if queue has similar item // Check if queue has similar item
for (auto& item: mItemList) for (auto& item : mItemList)
{ {
if (item.mId == id && id) if (item.mId == id && id)
{ {
@@ -60,7 +57,7 @@ void WatcherQueue::refresh(int id)
ice::Lock l(mGuard); ice::Lock l(mGuard);
// Check if queue has similar item // Check if queue has similar item
for (auto& item: mItemList) for (auto& item : mItemList)
{ {
if (item.mId == id && id) if (item.mId == id && id)
{ {
@@ -77,7 +74,7 @@ void WatcherQueue::process()
{ {
// Find next item to process // Find next item to process
ItemList::iterator i = mItemList.begin(); ItemList::iterator i = mItemList.begin();
for (;i != mItemList.end() && !i->scheduled(); i++) for (; i != mItemList.end() && !i->scheduled(); i++)
; ;
if (i == mItemList.end()) if (i == mItemList.end())
return; return;
@@ -133,7 +130,7 @@ void WatcherQueue::process()
void WatcherQueue::onTerminated(int id, int code) void WatcherQueue::onTerminated(int id, int code)
{ {
ice::Lock l(mGuard); ice::Lock l(mGuard);
ItemList::iterator i = findById(id); ItemList::iterator i = findById(id);
if (i != mItemList.end()) if (i != mItemList.end())
{ {
@@ -148,7 +145,7 @@ void WatcherQueue::onTerminated(int id, int code)
void WatcherQueue::onEstablished(int id, int code) void WatcherQueue::onEstablished(int id, int code)
{ {
ice::Lock l(mGuard); ice::Lock l(mGuard);
ItemList::iterator i = findById(id); ItemList::iterator i = findById(id);
if (i != mItemList.end()) if (i != mItemList.end())
{ {
@@ -161,7 +158,7 @@ void WatcherQueue::onEstablished(int id, int code)
WatcherQueue::ItemList::iterator WatcherQueue::findById(int id) WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
{ {
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++) for (ItemList::iterator i = mItemList.begin(); i != mItemList.end(); i++)
if (i->mId == id) if (i->mId == id)
return i; return i;
return mItemList.end(); return mItemList.end();
@@ -170,7 +167,7 @@ WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
void WatcherQueue::clear() void WatcherQueue::clear()
{ {
ice::Lock l(mGuard); ice::Lock l(mGuard);
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++) for (ItemList::iterator i = mItemList.begin(); i != mItemList.end(); i++)
{ {
if (i->mHandle.isValid()) if (i->mHandle.isValid())
i->mHandle->end(); i->mHandle->end();
+17 -17
View File
@@ -27,26 +27,26 @@ public:
State_Deleting State_Deleting
}; };
resip::ClientSubscriptionHandle mHandle; // Subscription handle resip::ClientSubscriptionHandle mHandle; // Subscription handle
ResipSession* mSession = nullptr; ResipSession* mSession = nullptr;
State mState = State::State_None; 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 = nullptr; // User tag void* mTag = nullptr; // User tag
int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only) int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only)
Item() Item() {}
{}
bool scheduled() bool scheduled()
{ {
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || mState == State_ScheduledToRefresh; return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete ||
mState == State_ScheduledToRefresh;
} }
}; };
WatcherQueue(UserAgent& agent); WatcherQueue(UserAgent& agent);
~WatcherQueue(); ~WatcherQueue();
int add(const std::string& peer, const std::string& package, void* tag); int add(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();
@@ -56,13 +56,13 @@ public:
protected: protected:
typedef std::vector<Item> ItemList; typedef std::vector<Item> ItemList;
ItemList mItemList; ItemList mItemList;
ice::Mutex mGuard; ice::Mutex mGuard;
UserAgent& mAgent; UserAgent& mAgent;
int mActiveId; int mActiveId;
void process(); void process();
ItemList::iterator findById(int id); ItemList::iterator findById(int id);
}; };
#endif #endif
+8 -17
View File
@@ -9,13 +9,9 @@
#include <resip/stack/Pidf.hxx> #include <resip/stack/Pidf.hxx>
#include <resip/dum/ClientSubscription.hxx> #include <resip/dum/ClientSubscription.hxx>
ClientObserver::ClientObserver() ClientObserver::ClientObserver() {}
{
}
ClientObserver::~ClientObserver() ClientObserver::~ClientObserver() {}
{
}
void ClientObserver::refresh() void ClientObserver::refresh()
{ {
@@ -27,13 +23,12 @@ void ClientObserver::stop()
{ {
if (mHandle.isValid()) if (mHandle.isValid())
mHandle->end(); mHandle->end();
else else if (mSession)
{
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
if (mSession) if (mSession)
{ mSession->end();
mSession->runTerminatedEvent(ResipSession::Type_Subscription); }
if (mSession)
mSession->end();
}
mSession = NULL; mSession = NULL;
} }
@@ -42,11 +37,7 @@ std::string ClientObserver::peer()
return mPeer; return mPeer;
} }
ServerObserver::ServerObserver() ServerObserver::ServerObserver() : mState(State_Incoming) {}
:mState(State_Incoming)
{
}
ServerObserver::~ServerObserver() ServerObserver::~ServerObserver()
{ {
+14 -12
View File
@@ -23,19 +23,20 @@ class ClientObserver
{ {
friend class Account; friend class Account;
friend class UserAgent; friend class UserAgent;
public: public:
ClientObserver(); ClientObserver();
~ClientObserver(); ~ClientObserver();
void refresh(); void refresh();
void stop(); void stop();
std::string peer(); std::string peer();
protected: protected:
resip::ClientSubscriptionHandle mHandle; resip::ClientSubscriptionHandle mHandle;
ResipSession* mSession; ResipSession* mSession;
int mSessionId; int mSessionId;
std::string mPeer; std::string mPeer;
}; };
typedef std::shared_ptr<ClientObserver> PClientObserver; typedef std::shared_ptr<ClientObserver> PClientObserver;
@@ -43,6 +44,7 @@ typedef std::shared_ptr<ClientObserver> PClientObserver;
class ServerObserver class ServerObserver
{ {
friend class UserAgent; friend class UserAgent;
public: public:
ServerObserver(); ServerObserver();
~ServerObserver(); ~ServerObserver();
@@ -50,9 +52,9 @@ public:
std::string peer() const; std::string peer() const;
std::string package() const; std::string package() const;
void accept(); void accept();
void update(std::string simpleId, bool online, std::string msg); void update(std::string simpleId, bool online, std::string msg);
void stop(); void stop();
protected: protected:
enum State enum State
@@ -61,11 +63,11 @@ protected:
State_Active, State_Active,
State_Closed State_Closed
}; };
State mState; State mState;
resip::ServerSubscriptionHandle mHandle; resip::ServerSubscriptionHandle mHandle;
std::string mPeer, mPackage; std::string mPeer, mPackage;
resip::Uri mContact; resip::Uri mContact;
int mSessionId; int mSessionId;
}; };
typedef std::shared_ptr<ServerObserver> PServerObserver; typedef std::shared_ptr<ServerObserver> PServerObserver;
+144 -143
View File
@@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2007-2012 Dmytro Bogovych <dmytro.bogovych@gmail.com> * Copyright (C) 2007-2012 Dmytro Bogovych <dmytro.bogovych@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,11 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifdef _WIN32 #ifdef _WIN32
# include <winsock2.h> #include <winsock2.h>
# include <windows.h> #include <windows.h>
#endif #endif
#include <algorithm> #include <algorithm>
@@ -30,8 +30,8 @@
enum enum
{ {
CONFIRMATION_PT = 1, CONFIRMATION_PT = 1,
DATA_PT = 2 DATA_PT = 2
}; };
#define CONFIRMATION_TIMEOUT 500 #define CONFIRMATION_TIMEOUT 500
@@ -39,266 +39,267 @@ enum
ReliableTunnel::ReliableTunnel(const char* streamname) ReliableTunnel::ReliableTunnel(const char* streamname)
{ {
mStack.setEncryption(this); mStack.setEncryption(this);
mStreamName = streamname; mStreamName = streamname;
mBandwidth = 0; mBandwidth = 0;
mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
} }
ReliableTunnel::~ReliableTunnel() ReliableTunnel::~ReliableTunnel()
{ {
::CloseHandle(mDataSignal); ::CloseHandle(mDataSignal);
::CloseHandle(mExitSignal); ::CloseHandle(mExitSignal);
} }
std::string ReliableTunnel::streamName() std::string ReliableTunnel::streamName()
{ {
return mStreamName; return mStreamName;
} }
std::string ReliableTunnel::streamProfile() std::string ReliableTunnel::streamProfile()
{ {
return "RTP/DP"; return "RTP/DP";
} }
void ReliableTunnel::setDestinationAddress(InternetAddress& addr) void ReliableTunnel::setDestinationAddress(InternetAddress& addr)
{ {
mDestination = addr; mDestination = addr;
} }
void ReliableTunnel::queueData(const void* bufferptr, int buffersize) void ReliableTunnel::queueData(const void* bufferptr, int buffersize)
{ {
assert(bufferptr != NULL); assert(bufferptr != NULL);
assert(buffersize != 0); assert(buffersize != 0);
resip::Lock l(mNewQueuedGuard); resip::Lock l(mNewQueuedGuard);
mNewQueued.push_back(std::string((const char*)bufferptr, buffersize)); mNewQueued.push_back(std::string((const char*)bufferptr, buffersize));
::SetEvent(mDataSignal); ::SetEvent(mDataSignal);
} }
// This method is called by user agent to send ICE packet from mediasocket // This method is called by user agent to send ICE packet from mediasocket
void ReliableTunnel::sendData(InternetAddress& addr, const void* dataBuffer, unsigned int datasize) void ReliableTunnel::sendData(InternetAddress& addr, const void* dataBuffer, unsigned int datasize)
{ {
switch (addr.type()) switch (addr.type())
{ {
case AF_INET: case AF_INET:
mSocket4.sendDatagram(addr, dataBuffer, datasize); mSocket4.sendDatagram(addr, dataBuffer, datasize);
return; return;
case AF_INET6: case AF_INET6:
mSocket4.sendDatagram(addr, dataBuffer, datasize); mSocket4.sendDatagram(addr, dataBuffer, datasize);
return; return;
} }
} }
void ReliableTunnel::sessionEstablished(int conntype) void ReliableTunnel::sessionEstablished(int conntype)
{ {
// Start worker thread // Start worker thread
if (conntype == EV_ICE) if (conntype == EV_ICE)
run(); run();
} }
void ReliableTunnel::sessionTerminated() void ReliableTunnel::sessionTerminated()
{ {
// Stop worker thread // Stop worker thread
::SetEvent(mExitSignal); ::SetEvent(mExitSignal);
shutdown(); shutdown();
join(); join();
} }
void ReliableTunnel::updateSdpOffer(resip::SdpContents::Session::Medium& sdp) void ReliableTunnel::updateSdpOffer(resip::SdpContents::Session::Medium& sdp)
{ {
// Get new destination port // Get new destination port
mDestination.setPort((unsigned short)sdp.port()); mDestination.setPort((unsigned short)sdp.port());
sdp.addCodec(resip::SdpContents::Session::Codec("rt", 104)); sdp.addCodec(resip::SdpContents::Session::Codec("rt", 104));
} }
void ReliableTunnel::setSocket(DatagramSocket& socket4, DatagramSocket& socket6) void ReliableTunnel::setSocket(DatagramSocket& socket4, DatagramSocket& socket6)
{ {
mSocket4 = socket4; mSocket4 = socket4;
mSocket6 = socket6; mSocket6 = socket6;
} }
DatagramSocket& ReliableTunnel::socket(int family) DatagramSocket& ReliableTunnel::socket(int family)
{ {
switch (family) switch (family)
{ {
case AF_INET: case AF_INET:
return mSocket4; return mSocket4;
case AF_INET6: case AF_INET6:
return mSocket4; return mSocket4;
default: default:
assert(0); assert(0);
} }
} }
bool ReliableTunnel::processSdpOffer(const resip::SdpContents::Session::Medium& media) bool ReliableTunnel::processSdpOffer(const resip::SdpContents::Session::Medium& media)
{ {
//check for default port number // check for default port number
mDestination.setPort(media.port()); mDestination.setPort(media.port());
return true; return true;
} }
void ReliableTunnel::thread() void ReliableTunnel::thread()
{ {
// Construct event array // Construct event array
while (true) while (true)
{
HANDLE eventarray[2] = { mDataSignal, mExitSignal };
DWORD rescode = ::WaitForMultipleObjects(2, eventarray, FALSE, INFINITE);
if (rescode == WAIT_OBJECT_0)
{ {
resip::Lock l(mNewQueuedGuard); HANDLE eventarray[2] = {mDataSignal, mExitSignal};
for (unsigned i = 0; i<mNewQueued.size(); i++)
mStack.queueOutgoing(mNewQueued[i].c_str(), mNewQueued[i].size()); DWORD rescode = ::WaitForMultipleObjects(2, eventarray, FALSE, INFINITE);
mNewQueued.clear(); if (rescode == WAIT_OBJECT_0)
{
sendOutgoing(); resip::Lock l(mNewQueuedGuard);
for (unsigned i = 0; i < mNewQueued.size(); i++)
mStack.queueOutgoing(mNewQueued[i].c_str(), mNewQueued[i].size());
mNewQueued.clear();
sendOutgoing();
}
else
break;
} }
else
break;
}
} }
void ReliableTunnel::setBandwidth(unsigned int bytesPerSecond) void ReliableTunnel::setBandwidth(unsigned int bytesPerSecond)
{ {
mBandwidth = bytesPerSecond; mBandwidth = bytesPerSecond;
} }
unsigned int ReliableTunnel::bandwidth() unsigned int ReliableTunnel::bandwidth()
{ {
return mBandwidth; return mBandwidth;
} }
void ReliableTunnel::processData(const void* dataptr, int datasize) void ReliableTunnel::processData(const void* dataptr, int datasize)
{ {
resip::Lock l(mStackGuard); resip::Lock l(mStackGuard);
mStack.processIncoming(dataptr, datasize); mStack.processIncoming(dataptr, datasize);
} }
bool ReliableTunnel::hasData() bool ReliableTunnel::hasData()
{ {
resip::Lock l(mStackGuard); resip::Lock l(mStackGuard);
return mIncomingData.size() || mStack.hasAppData(); return mIncomingData.size() || mStack.hasAppData();
} }
unsigned ReliableTunnel::getData(void* ptr, unsigned capacity) unsigned ReliableTunnel::getData(void* ptr, unsigned capacity)
{ {
resip::Lock l(mStackGuard); resip::Lock l(mStackGuard);
char* dataOut = (char*)ptr;
while (capacity && hasData())
{
// Check if mIncomingData is empty
if (!mIncomingData.size())
{
unsigned available = mStack.appData(NULL);
if (!available)
return 0;
mIncomingData.resize(available); char* dataOut = (char*)ptr;
mIncomingData.rewind();
mStack.appData(mIncomingData.mutableData()); while (capacity && hasData())
{
// Check if mIncomingData is empty
if (!mIncomingData.size())
{
unsigned available = mStack.appData(NULL);
if (!available)
return 0;
mIncomingData.resize(available);
mIncomingData.rewind();
mStack.appData(mIncomingData.mutableData());
}
if (mIncomingData.size())
{
unsigned toCopy = min(capacity, mIncomingData.size());
mIncomingData.dequeueBuffer(dataOut, toCopy);
dataOut += toCopy;
capacity -= toCopy;
}
} }
if (mIncomingData.size()) return dataOut - (char*)ptr;
{
unsigned toCopy = min(capacity, mIncomingData.size());
mIncomingData.dequeueBuffer(dataOut, toCopy);
dataOut += toCopy;
capacity -= toCopy;
}
}
return dataOut - (char*)ptr;
} }
// Returns block size for encryption algorythm // Returns block size for encryption algorythm
int ReliableTunnel::blockSize() int ReliableTunnel::blockSize()
{ {
return 8; return 8;
} }
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value. // Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
void ReliableTunnel::encrypt(void* dataPtr, int dataSize) void ReliableTunnel::encrypt(void* dataPtr, int dataSize)
{ {
if (mEncryptionKey.empty()) if (mEncryptionKey.empty())
return; return;
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
for (unsigned i=0; i<dataSize / blockSize(); i++) for (unsigned i = 0; i < dataSize / blockSize(); i++)
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_ENCRYPT); BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher,
BF_ENCRYPT);
#endif #endif
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
for (unsigned i=0; i<dataSize / blockSize(); i++) for (unsigned i = 0; i < dataSize / blockSize(); i++)
mEncryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize()); mEncryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
#endif #endif
} }
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value. // Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
void ReliableTunnel::decrypt(void* dataPtr, int dataSize) void ReliableTunnel::decrypt(void* dataPtr, int dataSize)
{ {
if (mEncryptionKey.empty()) if (mEncryptionKey.empty())
return; return;
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
for (unsigned i=0; i<dataSize / blockSize(); i++) for (unsigned i = 0; i < dataSize / blockSize(); i++)
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_DECRYPT); BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher,
BF_DECRYPT);
#endif #endif
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
for (unsigned i=0; i<dataSize / blockSize(); i++) for (unsigned i = 0; i < dataSize / blockSize(); i++)
mDecryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize()); mDecryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
#endif #endif
} }
// Calculates CRC // Calculates CRC
unsigned ReliableTunnel::crc(const void* dataptr, int datasize) unsigned ReliableTunnel::crc(const void* dataptr, int datasize)
{ {
unsigned long result; unsigned long result;
ICEImpl::CRC32 crc; ICEImpl::CRC32 crc;
crc.fullCrc((const unsigned char*)dataptr, datasize, &result); crc.fullCrc((const unsigned char*)dataptr, datasize, &result);
return result; return result;
} }
void ReliableTunnel::sendOutgoing() void ReliableTunnel::sendOutgoing()
{ {
// Check if stack has to send smth // Check if stack has to send smth
if (mStack.hasPacketToSend()) if (mStack.hasPacketToSend())
{ {
// Get data to send // Get data to send
char buffer[2048]; char buffer[2048];
int length = sizeof(buffer); int length = sizeof(buffer);
mStack.getPacketToSend(buffer, length); mStack.getPacketToSend(buffer, length);
// Send it over UDP // Send it over UDP
sendData(this->mDestination, buffer, length); sendData(this->mDestination, buffer, length);
} }
} }
void ReliableTunnel::setEncryptionKey(void* ptr, unsigned length) void ReliableTunnel::setEncryptionKey(void* ptr, unsigned length)
{ {
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
BF_set_key(&mCipher, length, (const unsigned char*)ptr); BF_set_key(&mCipher, length, (const unsigned char*)ptr);
#endif #endif
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
mEncryptor.SetKey((unsigned char*)ptr, length); mEncryptor.SetKey((unsigned char*)ptr, length);
mDecryptor.SetKey((unsigned char*)ptr, length); mDecryptor.SetKey((unsigned char*)ptr, length);
#endif #endif
// Save key // Save key
mEncryptionKey = std::string((const char*)ptr, length); mEncryptionKey = std::string((const char*)ptr, length);
} }
+86 -87
View File
@@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2007-2010 Dmytro Bogovych <dmytro.bogovych@gmail.com> * Copyright (C) 2007-2010 Dmytro Bogovych <dmytro.bogovych@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -13,7 +13,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifndef __RELIABLE_TUNNEL_H #ifndef __RELIABLE_TUNNEL_H
#define __RELIABLE_TUNNEL_H #define __RELIABLE_TUNNEL_H
@@ -26,122 +26,121 @@
#include "../ICE/ICEReliableTransport.h" #include "../ICE/ICEReliableTransport.h"
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
# include "../Libs/CryptoPP/blowfish.h" #include "../Libs/CryptoPP/blowfish.h"
#endif #endif
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
# include "../Libs/openssl/include/openssl/blowfish.h" #include "../Libs/openssl/include/openssl/blowfish.h"
#endif #endif
class ReliableTunnel: public DataProvider, public resip::ThreadIf, public ICEImpl::ReliableTransport::Encryption class ReliableTunnel : public DataProvider, public resip::ThreadIf, public ICEImpl::ReliableTransport::Encryption
{ {
public: public:
ReliableTunnel(const char* streamname); ReliableTunnel(const char* streamname);
virtual ~ReliableTunnel(); virtual ~ReliableTunnel();
// Returns provider RTP name
virtual std::string streamName();
// Returns provider RTP profile name
virtual std::string streamProfile();
// Sets destination IP address
virtual void setDestinationAddress(InternetAddress& addr);
// Processes incoming data // Returns provider RTP name
virtual void processData(const void* dataBuffer, int dataSize); virtual std::string streamName();
// This method is called by user agent to send ICE packet from mediasocket // Returns provider RTP profile name
virtual void sendData(InternetAddress& destination, const void* dataBuffer, unsigned int datasize); virtual std::string streamProfile();
// Updates SDP offer // Sets destination IP address
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp); virtual void setDestinationAddress(InternetAddress& addr);
// Called by user agent when session is terminated. // Processes incoming data
virtual void sessionTerminated(); virtual void processData(const void* dataBuffer, int dataSize);
// Called by user agent when session is started. // This method is called by user agent to send ICE packet from mediasocket
virtual void sessionEstablished(int conntype); virtual void sendData(InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
// Updates SDP offer
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp);
// Called by user agent when session is terminated.
virtual void sessionTerminated();
// Called by user agent when session is started.
virtual void sessionEstablished(int conntype);
// Called by user agent to save media socket for this provider // Called by user agent to save media socket for this provider
virtual void setSocket(DatagramSocket& socket4, DatagramSocket& socket6); virtual void setSocket(DatagramSocket& socket4, DatagramSocket& socket6);
// Called by user agent to get media socket for this provider
virtual DatagramSocket& socket(int family);
// Called by user agent to process media stream description from remote peer. // Called by user agent to get media socket for this provider
// Returns true if description is processed succesfully. Otherwise method returns false. virtual DatagramSocket& socket(int family);
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media);
virtual void thread(); // Called by user agent to process media stream description from remote peer.
// Returns true if description is processed succesfully. Otherwise method returns false.
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media);
// Enqueues outgoing packet to sending queue virtual void thread();
void queueData(const void* bufferPtr, int bufferSize);
void setBandwidth(unsigned int bytesPerSecond);
unsigned int bandwidth();
// Checks if there is any received application data
bool hasData();
// Reads received data. If ptr is NULL - the length of available data is returned.
unsigned getData(void* ptr, unsigned capacity);
void setEncryptionKey(void* ptr, unsigned length); // Enqueues outgoing packet to sending queue
void queueData(const void* bufferPtr, int bufferSize);
void setBandwidth(unsigned int bytesPerSecond);
unsigned int bandwidth();
// Checks if there is any received application data
bool hasData();
// Reads received data. If ptr is NULL - the length of available data is returned.
unsigned getData(void* ptr, unsigned capacity);
void setEncryptionKey(void* ptr, unsigned length);
protected: protected:
// SDP's stream name // SDP's stream name
std::string mStreamName; std::string mStreamName;
// Transport stack // Transport stack
ICEImpl::ReliableTransport mStack; ICEImpl::ReliableTransport mStack;
// Socket handles to operate
DatagramSocket mSocket4;
DatagramSocket mSocket6;
// Destination IP4/6 address // Socket handles to operate
InternetAddress mDestination; DatagramSocket mSocket4;
DatagramSocket mSocket6;
// Win32 exit signal
HANDLE mExitSignal; // Destination IP4/6 address
InternetAddress mDestination;
// Win32 "new outgoing data" signal
HANDLE mDataSignal; // Win32 exit signal
HANDLE mExitSignal;
// Mutex to protect queuing/sending outgoing data
resip::Mutex mOutgoingMtx; // Win32 "new outgoing data" signal
HANDLE mDataSignal;
std::vector<std::string>
mNewQueued; // Mutex to protect queuing/sending outgoing data
resip::Mutex mNewQueuedGuard; resip::Mutex mOutgoingMtx;
resip::Mutex mStackGuard;
std::vector<std::string> mNewQueued;
resip::Mutex mNewQueuedGuard;
resip::Mutex mStackGuard;
unsigned int mBandwidth;
std::string mEncryptionKey;
unsigned int mBandwidth;
std::string mEncryptionKey;
#ifdef USE_CRYPTOPP #ifdef USE_CRYPTOPP
CryptoPP::BlowfishEncryption mEncryptor; CryptoPP::BlowfishEncryption mEncryptor;
CryptoPP::BlowfishDecryption mDecryptor; CryptoPP::BlowfishDecryption mDecryptor;
#endif #endif
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
BF_KEY mCipher; BF_KEY mCipher;
#endif #endif
ICEImpl::ICEByteBuffer mIncomingData; ICEImpl::ICEByteBuffer mIncomingData;
// Returns block size for encryption algorythm // Returns block size for encryption algorythm
int blockSize(); int blockSize();
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
void encrypt(void* dataPtr, int dataSize);
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value. // Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
void decrypt(void* dataPtr, int dataSize); void encrypt(void* dataPtr, int dataSize);
// Calculates CRC // Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
unsigned crc(const void* dataptr, int datasize); void decrypt(void* dataPtr, int dataSize);
void sendOutgoing(); // Calculates CRC
unsigned crc(const void* dataptr, int datasize);
void sendOutgoing();
}; };
#endif #endif
+115 -109
View File
@@ -13,21 +13,18 @@
#define LOG_SUBSYSTEM "engine" #define LOG_SUBSYSTEM "engine"
typedef resip::SdpContents::Session::Medium Medium; typedef resip::SdpContents::Session::Medium Medium;
typedef resip::SdpContents::Session::MediumContainer MediumContainer; typedef resip::SdpContents::Session::MediumContainer MediumContainer;
#define IS_MULTIPLEX() mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing #define IS_MULTIPLEX() \
mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool() ? SocketHeap::DoMultiplexing : SocketHeap::DontMultiplexing
//------------ ResipSessionAppDialog ------------ //------------ ResipSessionAppDialog ------------
#pragma region ResipSessionAppDialog #pragma region ResipSessionAppDialog
ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham) ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham) {}
{
}
ResipSessionAppDialog::~ResipSessionAppDialog() ResipSessionAppDialog::~ResipSessionAppDialog() {}
{
}
#pragma endregion #pragma endregion
@@ -35,7 +32,7 @@ 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(nullptr), mType(Type_None), mSessionId(0), mSession(0) : resip::AppDialogSet(dum), mUserAgent(nullptr), mType(Type_None), mSessionId(0), mSession(0)
{ {
ResipSession::InstanceCounter++; ResipSession::InstanceCounter++;
@@ -54,7 +51,7 @@ ResipSession::~ResipSession()
mSession->mResipSession = nullptr; mSession->mResipSession = nullptr;
runTerminatedEvent(Type_Auto, 0, 0); runTerminatedEvent(Type_Auto, 0, 0);
} }
catch(...) catch (...)
{ {
} }
@@ -94,7 +91,8 @@ void ResipSession::runTerminatedEvent(Type type, int code, int reason)
case Type_Subscription: case Type_Subscription:
if (mSession) if (mSession)
{ {
UserAgent::ClientObserverMap::iterator observerIter = mUserAgent->mClientObserverMap.find(mSession->sessionId()); UserAgent::ClientObserverMap::iterator observerIter =
mUserAgent->mClientObserverMap.find(mSession->sessionId());
if (observerIter != mUserAgent->mClientObserverMap.end()) if (observerIter != mUserAgent->mClientObserverMap.end())
mUserAgent->onClientObserverStop(observerIter->second, code); mUserAgent->onClientObserverStop(observerIter->second, code);
} }
@@ -186,14 +184,9 @@ std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const res
#pragma endregion #pragma endregion
#pragma region Session::Stream #pragma region Session::Stream
Session::Stream::Stream() Session::Stream::Stream() : mRtcpAttr(false), mRtcpMuxAttr(false) {}
:mRtcpAttr(false), mRtcpMuxAttr(false)
{
}
Session::Stream::~Stream() Session::Stream::~Stream() {}
{
}
void Session::Stream::setProvider(PDataProvider provider) void Session::Stream::setProvider(PDataProvider provider)
{ {
@@ -261,7 +254,7 @@ void Session::Stream::setRtcpMuxAttr(bool value)
std::atomic_int Session::InstanceCounter; std::atomic_int Session::InstanceCounter;
Session::Session(PAccount account) Session::Session(PAccount account)
{ {
InstanceCounter++; InstanceCounter++;
mAccount = account; mAccount = account;
mSessionId = Session::generateId(); mSessionId = Session::generateId();
@@ -290,14 +283,15 @@ Session::~Session()
mResipSession->setSession(NULL); mResipSession->setSession(NULL);
clearProvidersAndSockets(); clearProvidersAndSockets();
} }
catch(...) catch (...)
{} {
}
InstanceCounter--; InstanceCounter--;
} }
void Session::start(const std::string& peer) void Session::start(const std::string& peer)
{ {
ICELogInfo( << "Attempt to start session to " << peer); ICELogInfo(<< "Attempt to start session to " << peer);
Lock l(mGuard); Lock l(mGuard);
if (mResipSession) if (mResipSession)
@@ -320,7 +314,7 @@ void Session::start(const std::string& peer)
// Mark session as Initiator // Mark session as Initiator
mRole = Session::Initiator; mRole = Session::Initiator;
resip::Data addrData(peer); resip::Data addrData(peer);
resip::NameAddr addr(addrData); resip::NameAddr addr(addrData);
// Save target address // Save target address
@@ -334,7 +328,7 @@ void Session::stop()
{ {
ICELogInfo(<< "Stopping session " << mSessionId); ICELogInfo(<< "Stopping session " << mSessionId);
Lock l(mGuard); Lock l(mGuard);
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
{ {
Session::Stream& dataStream = mStreamList[i]; Session::Stream& dataStream = mStreamList[i];
@@ -344,8 +338,8 @@ void Session::stop()
dataStream.provider()->sessionTerminated(); dataStream.provider()->sessionTerminated();
// Free socket // Free socket
SocketHeap::instance().freeSocketPair( dataStream.socket4() ); SocketHeap::instance().freeSocketPair(dataStream.socket4());
SocketHeap::instance().freeSocketPair( dataStream.socket6() ); SocketHeap::instance().freeSocketPair(dataStream.socket6());
// Drop the references so the destructor's cleanup does not free them again // Drop the references so the destructor's cleanup does not free them again
dataStream.setSocket4(RtpPair<PDatagramSocket>()); dataStream.setSocket4(RtpPair<PDatagramSocket>());
@@ -357,7 +351,7 @@ void Session::stop()
mResipSession->runTerminatedEvent(ResipSession::Type_Call, 0, LocalBye); mResipSession->runTerminatedEvent(ResipSession::Type_Call, 0, LocalBye);
if (mResipSession) if (mResipSession)
mResipSession->end(); // Stop SIP session mResipSession->end(); // Stop SIP session
} }
void Session::accept() void Session::accept()
@@ -366,7 +360,8 @@ void Session::accept()
Lock locksession(mGuard); Lock locksession(mGuard);
// If ICE candidate gathering is not finished - just mark session as accepted. It will be accepted in ICE handling code. // If ICE candidate gathering is not finished - just mark session as accepted. It will be accepted in ICE handling
// code.
mAcceptedByUser = true; mAcceptedByUser = true;
if (mGatheredCandidates || mIceStack->state() == ice::IceNone) if (mGatheredCandidates || mIceStack->state() == ice::IceNone)
@@ -405,7 +400,7 @@ void Session::accept()
void Session::reject(int code) void Session::reject(int code)
{ {
ICELogInfo( << "Attempt to reject session " << mSessionId); ICELogInfo(<< "Attempt to reject session " << mSessionId);
Lock l(mGuard); Lock l(mGuard);
@@ -449,8 +444,8 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
MT::Statistics stat; MT::Statistics stat;
// Iterate all session providers // Iterate all session providers
Stream* media = nullptr; Stream* media = nullptr;
for (Stream& stream: mStreamList) for (Stream& stream : mStreamList)
{ {
if (!stream.provider()) if (!stream.provider())
continue; continue;
@@ -473,7 +468,9 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp); info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp); info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
if (stat.mFirstRtpTime) if (stat.mFirstRtpTime)
info[SessionInfo_Duration] = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - *(stat.mFirstRtpTime)).count()); info[SessionInfo_Duration] = static_cast<int>(
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - *(stat.mFirstRtpTime))
.count());
else else
info[SessionInfo_Duration] = 0; info[SessionInfo_Duration] = 0;
@@ -481,7 +478,8 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
info[SessionInfo_PacketLoss] = static_cast<int>((stat.mPacketLoss * 1000) / stat.mReceivedRtp); info[SessionInfo_PacketLoss] = static_cast<int>((stat.mPacketLoss * 1000) / stat.mReceivedRtp);
if (media && mIceStack) if (media && mIceStack)
info[SessionInfo_AudioPeer] = mIceStack->remoteAddress(media->iceInfo().mStreamId, media->iceInfo().mComponentId.mRtp).toStdString(); info[SessionInfo_AudioPeer] =
mIceStack->remoteAddress(media->iceInfo().mStreamId, media->iceInfo().mComponentId.mRtp).toStdString();
info[SessionInfo_Jitter] = stat.mJitter; info[SessionInfo_Jitter] = stat.mJitter;
if (stat.mRttDelay.is_initialized()) if (stat.mRttDelay.is_initialized())
@@ -505,7 +503,8 @@ PAccount Session::account()
return mAccount; return mAccount;
} }
void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize) void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr,
unsigned receivedSize)
{ {
Lock l(mGuard); Lock l(mGuard);
@@ -513,7 +512,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
return; return;
// Check if it STUN packet and must be processed by ICE stack // Check if it STUN packet and must be processed by ICE stack
//ICELogDebug (<< "Received UDP packet from " << src.ip() << ":" << src.port()); // ICELogDebug (<< "Received UDP packet from " << src.ip() << ":" << src.port());
ice::ByteBuffer received(receivedPtr, receivedSize); ice::ByteBuffer received(receivedPtr, receivedSize);
received.setRemoteAddress(src); received.setRemoteAddress(src);
@@ -531,7 +530,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
if (received.size() >= 4) if (received.size() >= 4)
{ {
bool turnPrefix = false; bool turnPrefix = false;
for (unsigned i=0; i<mTurnPrefixList.size() && !turnPrefix; i++) for (unsigned i = 0; i < mTurnPrefixList.size() && !turnPrefix; i++)
turnPrefix |= ice::Stack::isChannelData(received, mTurnPrefixList[i]); turnPrefix |= ice::Stack::isChannelData(received, mTurnPrefixList[i]);
if (turnPrefix) if (turnPrefix)
received.erase(0, 4); received.erase(0, 4);
@@ -546,7 +545,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
{ {
ice::ByteBuffer buffer(receivedPtr, receivedSize); ice::ByteBuffer buffer(receivedPtr, receivedSize);
buffer.setRemoteAddress(src); buffer.setRemoteAddress(src);
/*bool processed = */mIceStack->processIncomingData(stream, component, buffer); /*bool processed = */ mIceStack->processIncomingData(stream, component, buffer);
} }
} }
else else
@@ -594,20 +593,19 @@ void Session::onGathered(ice::Stack* stack, void* tag)
if (mRole == Initiator) if (mRole == Initiator)
mUserAgent->sendOffer(this); mUserAgent->sendOffer(this);
else else if (mRole == Acceptor)
if (mRole == Acceptor) {
{ // Mark session as gathered ICE candidates
// Mark session as gathered ICE candidates mGatheredCandidates = true;
mGatheredCandidates = true;
// if AcceptSession was already called() on session - recall it again to make real work // if AcceptSession was already called() on session - recall it again to make real work
if (mAcceptedByUser) if (mAcceptedByUser)
{ {
// Check if session is needed here - because session can be terminated already // Check if session is needed here - because session can be terminated already
if (mResipSession && mInviteHandle.isValid()) if (mResipSession && mInviteHandle.isValid())
accept(); accept();
}
} }
}
} }
void Session::onSuccess(ice::Stack* stack, void* tag) void Session::onSuccess(ice::Stack* stack, void* tag)
@@ -616,7 +614,7 @@ void Session::onSuccess(ice::Stack* stack, void* tag)
RtpPair<InternetAddress> t; RtpPair<InternetAddress> t;
for (unsigned i=0; i<this->mStreamList.size(); i++) for (unsigned i = 0; i < this->mStreamList.size(); i++)
{ {
PDataProvider p = mStreamList[i].provider(); PDataProvider p = mStreamList[i].provider();
if (p) if (p)
@@ -638,8 +636,8 @@ void Session::onSuccess(ice::Stack* stack, void* tag)
mUserAgent->onSessionEstablished(mUserAgent->getUserSession(mSessionId), EV_ICE, t); mUserAgent->onSessionEstablished(mUserAgent->getUserSession(mSessionId), EV_ICE, t);
//time to resend updated media info over SIP // time to resend updated media info over SIP
//TODO: // TODO:
} }
void Session::onFailed(ice::Stack* stack, void* tag) void Session::onFailed(ice::Stack* stack, void* tag)
@@ -647,17 +645,17 @@ void Session::onFailed(ice::Stack* stack, void* tag)
ICELogError(<< "ICE connectivity check failed for session " << mSessionId); ICELogError(<< "ICE connectivity check failed for session " << mSessionId);
mUserAgent->onConnectivityFailed(mUserAgent->getUserSession(mSessionId)); mUserAgent->onConnectivityFailed(mUserAgent->getUserSession(mSessionId));
//if (mInviteHandle.isValid()) // if (mInviteHandle.isValid())
// mInviteHandle->end(); // mInviteHandle->end();
} }
void Session::onNetworkChange(ice::Stack *stack, void *tag) void Session::onNetworkChange(ice::Stack* stack, void* tag)
{ {
ICELogInfo(<< "Network change detected by ICE stack for session " << mSessionId); ICELogInfo(<< "Network change detected by ICE stack for session " << mSessionId);
mUserAgent->onNetworkChange(mUserAgent->getUserSession(mSessionId)); mUserAgent->onNetworkChange(mUserAgent->getUserSession(mSessionId));
} }
void Session::buildSdp(resip::SdpContents &sdp, SdpDirection sdpDirection) void Session::buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection)
{ {
sdp.session().name() = "ICE_UA"; sdp.session().name() = "ICE_UA";
sdp.session().origin().user() = "user"; sdp.session().origin().user() = "user";
@@ -675,8 +673,12 @@ void Session::buildSdp(resip::SdpContents &sdp, SdpDirection sdpDirection)
ice::NetworkAddress defaultAddr = mIceStack->defaultAddress(mStreamList.front().iceInfo().mStreamId, ICE_RTP_ID); ice::NetworkAddress defaultAddr = mIceStack->defaultAddress(mStreamList.front().iceInfo().mStreamId, ICE_RTP_ID);
// Set IP address for origin and connection // Set IP address for origin and connection
sdp.session().origin().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET ? resip::SdpContents::IP4 : resip::SdpContents::IP6); sdp.session().origin().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET
sdp.session().connection().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET ? resip::SdpContents::IP4 : resip::SdpContents::IP6); ? resip::SdpContents::IP4
: resip::SdpContents::IP6);
sdp.session().connection().setAddress(resip::Data(defaultAddr.ip()), defaultAddr.family() == AF_INET
? resip::SdpContents::IP4
: resip::SdpContents::IP6);
// Add ICE credentials // Add ICE credentials
if (mIceStack->state() > ice::IceNone) if (mIceStack->state() > ice::IceNone)
@@ -686,30 +688,30 @@ void Session::buildSdp(resip::SdpContents &sdp, SdpDirection sdpDirection)
} }
// Iterate media streams // Iterate media streams
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
{ {
Stream& stream = mStreamList[i]; Stream& stream = mStreamList[i];
if (!stream.provider()) if (!stream.provider())
continue; continue;
DataProvider& provider = *mStreamList[i].provider(); DataProvider& provider = *mStreamList[i].provider();
// Get default stream port // Get default stream port
ice::NetworkAddress rtpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTP_ID), ice::NetworkAddress rtpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTP_ID),
rtcpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTCP_ID); rtcpPort = mIceStack->defaultAddress(mStreamList[i].iceInfo().mStreamId, ICE_RTCP_ID);
// Define media stream SDP's header // Define media stream SDP's header
resip::SdpContents::Session::Medium media(resip::Data(provider.streamName()), rtpPort.port(), 0, resip::Data(provider.streamProfile())); resip::SdpContents::Session::Medium media(resip::Data(provider.streamName()), rtpPort.port(), 0,
resip::Data(provider.streamProfile()));
// Add "rtcp" attribute // Add "rtcp" attribute
if (mUserAgent->mConfig[CONFIG_RTCP_ATTR].asBool()) if (mUserAgent->mConfig[CONFIG_RTCP_ATTR].asBool())
{ {
if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool()) if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
rtcpPort = rtpPort; rtcpPort = rtpPort;
else else if (rtcpPort.isEmpty())
if (rtcpPort.isEmpty())
{ {
rtcpPort = rtpPort; rtcpPort = rtpPort;
rtcpPort.setPort( rtpPort.port() + 1); rtcpPort.setPort(rtpPort.port() + 1);
} }
media.addAttribute("rtcp", resip::Data(rtcpPort.port())); media.addAttribute("rtcp", resip::Data(rtcpPort.port()));
@@ -732,7 +734,7 @@ void Session::buildSdp(resip::SdpContents &sdp, SdpDirection sdpDirection)
if (mIceStack->hasComponent(ii.mStreamId, ii.mComponentId.mRtcp)) if (mIceStack->hasComponent(ii.mStreamId, ii.mComponentId.mRtcp))
mIceStack->fillCandidateList(ii.mStreamId, ii.mComponentId.mRtcp, candidates); mIceStack->fillCandidateList(ii.mStreamId, ii.mComponentId.mRtcp, candidates);
for (unsigned c=0; c<candidates.size(); c++) for (unsigned c = 0; c < candidates.size(); c++)
media.addAttribute("candidate", candidates[c].c_str()); media.addAttribute("candidate", candidates[c].c_str());
} }
@@ -766,7 +768,7 @@ void Session::addProvider(PDataProvider provider)
return; return;
// Avoid duplicating providers // Avoid duplicating providers
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
if (mStreamList[i].provider() == provider) if (mStreamList[i].provider() == provider)
return; return;
@@ -777,18 +779,18 @@ void Session::addProvider(PDataProvider provider)
{ {
if (!streamIter->provider() && (streamIter->socket4().mRtp->isValid() || streamIter->socket6().mRtp->isValid())) if (!streamIter->provider() && (streamIter->socket4().mRtp->isValid() || streamIter->socket6().mRtp->isValid()))
{ {
streamIter->setProvider( provider ); streamIter->setProvider(provider);
provider->setSocket(streamIter->socket4(), streamIter->socket6()); provider->setSocket(streamIter->socket4(), streamIter->socket6());
return; return;
} }
} }
Stream s; Stream s;
s.setProvider( provider ); s.setProvider(provider);
// Allocate socket for provider // Allocate socket for provider
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()) ); s.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()) ); s.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
s.provider()->setSocket(s.socket4(), s.socket6()); s.provider()->setSocket(s.socket4(), s.socket6());
// Create ICE stream/component // Create ICE stream/component
@@ -797,10 +799,11 @@ void Session::addProvider(PDataProvider provider)
ii.mPort4 = s.socket4().mRtp->localport(); ii.mPort4 = s.socket4().mRtp->localport();
ii.mPort6 = s.socket6().mRtp->localport(); ii.mPort6 = s.socket6().mRtp->localport();
ii.mComponentId.mRtp = mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(), ii.mComponentId.mRtp =
s.socket6().mRtp->localport()); mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(), s.socket6().mRtp->localport());
if (!mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool()) if (!mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtcp->localport(), s.socket6().mRtcp->localport()); ii.mComponentId.mRtcp =
mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtcp->localport(), s.socket6().mRtcp->localport());
s.setIceInfo(ii); s.setIceInfo(ii);
@@ -825,7 +828,7 @@ int Session::sessionId()
} }
std::atomic_int Session::IdGenerator; std::atomic_int Session::IdGenerator;
int Session::generateId() int Session::generateId()
{ {
return ++IdGenerator; return ++IdGenerator;
} }
@@ -857,7 +860,7 @@ void Session::setTag(void* tag)
void Session::pause() void Session::pause()
{ {
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
{ {
Stream& s = mStreamList[i]; Stream& s = mStreamList[i];
if (s.provider()) if (s.provider())
@@ -868,7 +871,7 @@ void Session::pause()
void Session::resume() void Session::resume()
{ {
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
{ {
Stream& s = mStreamList[i]; Stream& s = mStreamList[i];
if (s.provider()) if (s.provider())
@@ -880,9 +883,9 @@ void Session::resume()
void Session::refreshMediaPath() void Session::refreshMediaPath()
{ {
// Recreate media sockets // Recreate media sockets
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
{ {
Stream& s= mStreamList[i]; Stream& s = mStreamList[i];
PDataProvider p = s.provider(); PDataProvider p = s.provider();
if (!p) if (!p)
continue; continue;
@@ -891,7 +894,7 @@ void Session::refreshMediaPath()
SocketHeap::instance().freeSocketPair(p->socket(AF_INET)); SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
// Bring new socket to provider and stream // Bring new socket to provider and stream
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ), RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()),
s6 = SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()); s6 = SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX());
p->setSocket(s4, s6); p->setSocket(s4, s6);
@@ -912,11 +915,11 @@ void Session::refreshMediaPath()
int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd, std::string iceUfrag, int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media) std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media)
{ {
bool iceRestart = false; bool iceRestart = false;
int mediaCompatible = 0; int mediaCompatible = 0;
MediumContainer::const_iterator mediaIter; MediumContainer::const_iterator mediaIter;
unsigned streamIndex = 0; unsigned streamIndex = 0;
for (mediaIter = media.begin(); mediaIter != media.end(); ++mediaIter, ++streamIndex) for (mediaIter = media.begin(); mediaIter != media.end(); ++mediaIter, ++streamIndex)
{ {
// Get reference to SDP description of remote stream // Get reference to SDP description of remote stream
@@ -929,7 +932,7 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
// Ask about provider if needed // Ask about provider if needed
if (!stream.provider()) if (!stream.provider())
stream.setProvider( mUserAgent->onProviderNeeded(remoteStream.name().c_str()) ); stream.setProvider(mUserAgent->onProviderNeeded(remoteStream.name().c_str()));
// Check the stream validity // Check the stream validity
if (!stream.provider()) if (!stream.provider())
@@ -938,8 +941,8 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
continue; continue;
// See for rtcp & rtcp-mux attribute // See for rtcp & rtcp-mux attribute
stream.setRtcpAttr( remoteStream.exists("rtcp") ); stream.setRtcpAttr(remoteStream.exists("rtcp"));
stream.setRtcpMuxAttr( remoteStream.exists("rtcp-mux") ); stream.setRtcpMuxAttr(remoteStream.exists("rtcp-mux"));
// Set destination address // Set destination address
if (!remoteStream.getConnections().empty()) if (!remoteStream.getConnections().empty())
@@ -951,12 +954,12 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
targetAddr.mRtcp.setIp(remoteIp); targetAddr.mRtcp.setIp(remoteIp);
if (stream.rtcpMuxAttr()) if (stream.rtcpMuxAttr())
targetAddr.mRtcp.setPort( remoteStream.port() ); targetAddr.mRtcp.setPort(remoteStream.port());
else if (stream.rtcpAttr())
targetAddr.mRtcp.setPort(
strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1));
else else
if (stream.rtcpAttr()) targetAddr.mRtcp.setPort(remoteStream.port() + 1);
targetAddr.mRtcp.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
else
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
stream.provider()->setDestinationAddress(targetAddr); stream.provider()->setDestinationAddress(targetAddr);
@@ -974,9 +977,9 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX())); stream.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX())); stream.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
} }
catch(...) catch (...)
{ {
ICELogError( << "Cannot create media socket."); ICELogError(<< "Cannot create media socket.");
return 503; return 503;
} }
@@ -1005,13 +1008,15 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
// See what remote peer offers - offer only single ice component if it relies on multiplexing // See what remote peer offers - offer only single ice component if it relies on multiplexing
if (!targetAddr.multiplexed() && !mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool()) if (!targetAddr.multiplexed() && !mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, stream.socket4().mRtcp->localport(), stream.socket6().mRtcp->localport()); ii.mComponentId.mRtcp = mIceStack->addComponent(ii.mStreamId, NULL, stream.socket4().mRtcp->localport(),
stream.socket6().mRtcp->localport());
stream.setIceInfo(ii); stream.setIceInfo(ii);
} }
if (iceAvailable) if (iceAvailable)
{ {
if (mIceStack->remotePassword(stream.iceInfo().mStreamId) != icePwd || mIceStack->remoteUfrag(stream.iceInfo().mStreamId) != iceUfrag) if (mIceStack->remotePassword(stream.iceInfo().mStreamId) != icePwd ||
mIceStack->remoteUfrag(stream.iceInfo().mStreamId) != iceUfrag)
{ {
iceRestart = true; iceRestart = true;
mIceStack->setRemotePassword(icePwd, stream.iceInfo().mStreamId); mIceStack->setRemotePassword(icePwd, stream.iceInfo().mStreamId);
@@ -1020,10 +1025,10 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
} }
// Get remote ICE candidates vector // Get remote ICE candidates vector
const std::list<resip::Data> candidateList = remoteStream.getValues("candidate"); const std::list<resip::Data> candidateList = remoteStream.getValues("candidate");
// Repackage information about remote candidates // Repackage information about remote candidates
std::vector<std::string> candidateVector; std::vector<std::string> candidateVector;
std::list<resip::Data>::const_iterator cit = candidateList.begin(); std::list<resip::Data>::const_iterator cit = candidateList.begin();
for (; cit != candidateList.end(); ++cit) for (; cit != candidateList.end(); ++cit)
@@ -1032,9 +1037,11 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
if (candidateVector.empty()) if (candidateVector.empty())
iceAvailable = false; iceAvailable = false;
// Ask ICE stack to process this information. This call will remove also second component if it is not defined in remote sdp. // Ask ICE stack to process this information. This call will remove also second component if it is not defined
// in remote sdp.
if (iceAvailable) if (iceAvailable)
iceAvailable = mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteIp, remotePort, mUserAgent->mConfig[CONFIG_DEFERRELAYED].asBool()); iceAvailable = mIceStack->processSdpOffer(stream.iceInfo().mStreamId, candidateVector, remoteIp, remotePort,
mUserAgent->mConfig[CONFIG_DEFERRELAYED].asBool());
} }
// See if there are compatible media streams // See if there are compatible media streams
@@ -1081,22 +1088,22 @@ int Session::release()
void Session::clearProvidersAndSockets() void Session::clearProvidersAndSockets()
{ {
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
{ {
Session::Stream& ds = mStreamList[i]; Session::Stream& ds = mStreamList[i];
if (ds.provider()) if (ds.provider())
{ {
ds.provider()->sessionDeleted(); ds.provider()->sessionDeleted();
SocketHeap::instance().freeSocketPair( ds.socket4() ); SocketHeap::instance().freeSocketPair(ds.socket4());
SocketHeap::instance().freeSocketPair( ds.socket6() ); SocketHeap::instance().freeSocketPair(ds.socket6());
} }
} }
} }
void Session::clearProviders() void Session::clearProviders()
{ {
for (unsigned i=0; i<mStreamList.size(); i++) for (unsigned i = 0; i < mStreamList.size(); i++)
{ {
Session::Stream& ds = mStreamList[i]; Session::Stream& ds = mStreamList[i];
@@ -1132,14 +1139,13 @@ void Session::processQueuedOffer()
//-------------- ResipSessionFactory --------- //-------------- ResipSessionFactory ---------
#pragma region ResipSessionFactory #pragma region ResipSessionFactory
ResipSessionFactory::ResipSessionFactory(UserAgent* agent) ResipSessionFactory::ResipSessionFactory(UserAgent* agent) : mAgent(agent) {}
:mAgent(agent)
{}
resip::AppDialogSet* ResipSessionFactory::createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg) resip::AppDialogSet* ResipSessionFactory::createAppDialogSet(resip::DialogUsageManager& dum,
{ const resip::SipMessage& msg)
{
ResipSession* s = new ResipSession(dum); ResipSession* s = new ResipSession(dum);
s->setUa( mAgent ); s->setUa(mAgent);
return s; return s;
} }
+257 -259
View File
@@ -49,365 +49,363 @@ class ResipSession;
enum SessionInfo enum SessionInfo
{ {
SessionInfo_RemoteSipAddress, // remote sip address SessionInfo_RemoteSipAddress, // remote sip address
SessionInfo_ReceivedTraffic, // amount of received traffic in session in bytes SessionInfo_ReceivedTraffic, // amount of received traffic in session in bytes
SessionInfo_SentTraffic, // amount of sent traffic in session in bytes SessionInfo_SentTraffic, // amount of sent traffic in session in bytes
SessionInfo_PacketLoss, // lost packets counter; returns number of 1/1000 fractions (0.1%) SessionInfo_PacketLoss, // lost packets counter; returns number of 1/1000 fractions (0.1%)
SessionInfo_AudioPeer, // remote peer rtp address in text SessionInfo_AudioPeer, // remote peer rtp address in text
SessionInfo_AudioCodec, // selected audio codec as text SessionInfo_AudioCodec, // selected audio codec as text
SessionInfo_DtmfInterface, // Pointer to DtmfQueue class; returned as void* SessionInfo_DtmfInterface, // Pointer to DtmfQueue class; returned as void*
SessionInfo_IceState, SessionInfo_IceState,
SessionInfo_NetworkMos, SessionInfo_NetworkMos,
SessionInfo_PvqaMos, SessionInfo_PvqaMos,
SessionInfo_PvqaReport, SessionInfo_PvqaReport,
SessionInfo_SentRtp, SessionInfo_SentRtp,
SessionInfo_SentRtcp, SessionInfo_SentRtcp,
SessionInfo_ReceivedRtp, SessionInfo_ReceivedRtp,
SessionInfo_ReceivedRtcp, SessionInfo_ReceivedRtcp,
SessionInfo_LostRtp, SessionInfo_LostRtp,
SessionInfo_DroppedRtp, SessionInfo_DroppedRtp,
SessionInfo_Duration, SessionInfo_Duration,
SessionInfo_Jitter, SessionInfo_Jitter,
SessionInfo_Rtt, SessionInfo_Rtt,
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
SessionInfo_RemotePeer, SessionInfo_RemotePeer,
SessionInfo_SSRC, SessionInfo_SSRC,
SessionInfo_CngCounter, // For AMR codecs only SessionInfo_CngCounter, // For AMR codecs only
SessionInfo_ReceivedRtpTraffic // amount of received RTP traffic in bytes, RTCP excluded SessionInfo_ReceivedRtpTraffic // amount of received RTP traffic in bytes, RTCP excluded
}; };
class Session : class Session : public SocketSink, public ice::StageHandler
public SocketSink,
public ice::StageHandler
{ {
public: public:
class Command class Command
{
public:
virtual void run(Session& s) = 0;
};
// Describes ice stream/component
struct IceInfo
{
IceInfo()
:mStreamId(-1)
{ {
mPort4 = mPort6 = 0; public:
mComponentId.mRtp = mComponentId.mRtcp = -1; virtual void run(Session& s) = 0;
} };
RtpPair<int> mComponentId; // Describes ice stream/component
int mStreamId; struct IceInfo
unsigned short mPort4; {
unsigned short mPort6; IceInfo() : mStreamId(-1)
}; {
mPort4 = mPort6 = 0;
mComponentId.mRtp = mComponentId.mRtcp = -1;
}
// Describes media stream (audio/video) in session RtpPair<int> mComponentId;
class Stream int mStreamId;
{ unsigned short mPort4;
public: unsigned short mPort6;
Stream(); };
~Stream();
void setProvider(PDataProvider provider); // Describes media stream (audio/video) in session
PDataProvider provider(); class Stream
{
public:
Stream();
~Stream();
void setSocket4(const RtpPair<PDatagramSocket>& socket); void setProvider(PDataProvider provider);
RtpPair<PDatagramSocket>& socket4(); PDataProvider provider();
void setSocket6(const RtpPair<PDatagramSocket>& socket); void setSocket4(const RtpPair<PDatagramSocket>& socket);
RtpPair<PDatagramSocket>& socket6(); RtpPair<PDatagramSocket>& socket4();
void setIceInfo(const IceInfo& info); void setSocket6(const RtpPair<PDatagramSocket>& socket);
IceInfo iceInfo() const; RtpPair<PDatagramSocket>& socket6();
// rtcpAttr/rtcpMuxAttr signals about corresponding sip attribute in offer/answer from remote peer void setIceInfo(const IceInfo& info);
bool rtcpAttr() const; IceInfo iceInfo() const;
void setRtcpAttr(bool value);
bool rtcpMuxAttr() const; // rtcpAttr/rtcpMuxAttr signals about corresponding sip attribute in offer/answer from remote peer
void setRtcpMuxAttr(bool value); bool rtcpAttr() const;
void setRtcpAttr(bool value);
protected: bool rtcpMuxAttr() const;
// Provider for corresponding stream void setRtcpMuxAttr(bool value);
PDataProvider mProvider;
// Socket for stream protected:
RtpPair<PDatagramSocket> mSocket4, mSocket6; // Provider for corresponding stream
PDataProvider mProvider;
bool mRtcpAttr; // Socket for stream
bool mRtcpMuxAttr; RtpPair<PDatagramSocket> mSocket4, mSocket6;
IceInfo mIceInfo;
};
Session(PAccount account); bool mRtcpAttr;
virtual ~Session(); bool mRtcpMuxAttr;
IceInfo mIceInfo;
};
// Starts call to specified peer Session(PAccount account);
void start(const std::string& peer); virtual ~Session();
// Stops call // Starts call to specified peer
void stop(); void start(const std::string& peer);
// Accepts call // Stops call
void accept(); void stop();
// Rejects call // Accepts call
void reject(int code); void accept();
enum class InfoOptions // Rejects call
{ void reject(int code);
Standard = 0,
Detailed = 1,
};
void getSessionInfo(InfoOptions options, VariantMap& result); enum class InfoOptions
{
Standard = 0,
Detailed = 1,
};
// Returns integer identifier of the session; it is unique amongst all session in application void getSessionInfo(InfoOptions options, VariantMap& result);
int id() const;
// Returns owning account // Returns integer identifier of the session; it is unique amongst all session in application
PAccount account(); int id() const;
typedef std::map<std::string, std::string> UserHeaders; // Returns owning account
void setUserHeaders(const UserHeaders& headers); PAccount account();
// Called when new media data are available for this session typedef std::map<std::string, std::string> UserHeaders;
void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize); void setUserHeaders(const UserHeaders& headers);
// Called when new candidate is gathered // Called when new media data are available for this session
void onCandidateGathered(ice::Stack* stack, void* tag, const char* address); void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize);
// Called when connectivity check is finished // Called when new candidate is gathered
void onCheckFinished(ice::Stack* stack, void* tag, const char* checkDescription); void onCandidateGathered(ice::Stack* stack, void* tag, const char* address);
// Called when ICE candidates are gathered - with success or timeout. // Called when connectivity check is finished
void onGathered(ice::Stack* stack, void* tag); void onCheckFinished(ice::Stack* stack, void* tag, const char* checkDescription);
// Called when ICE connectivity check is good at least for one of required streams // Called when ICE candidates are gathered - with success or timeout.
void onSuccess(ice::Stack* stack, void* tag); void onGathered(ice::Stack* stack, void* tag);
// Called when ICE connectivity check is failed for all of required streams // Called when ICE connectivity check is good at least for one of required streams
void onFailed(ice::Stack* stack, void* tag); void onSuccess(ice::Stack* stack, void* tag);
// Called when ICE stack detects network change during the call // Called when ICE connectivity check is failed for all of required streams
void onNetworkChange(ice::Stack* stack, void* tag); void onFailed(ice::Stack* stack, void* tag);
// Fills SDP according to ICE and provider's data // Called when ICE stack detects network change during the call
void buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection); void onNetworkChange(ice::Stack* stack, void* tag);
// Searches provider by its local port number // Fills SDP according to ICE and provider's data
PDataProvider findProviderByPort(int family, unsigned short port); void buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection);
// Add provider to internal list // Searches provider by its local port number
void addProvider(PDataProvider provider); PDataProvider findProviderByPort(int family, unsigned short port);
PDataProvider providerAt(int index);
int getProviderCount();
void setUserAgent(UserAgent* agent); // Add provider to internal list
UserAgent* userAgent(); void addProvider(PDataProvider provider);
PDataProvider providerAt(int index);
int getProviderCount();
// Pauses and resumes all providers; updates states void setUserAgent(UserAgent* agent);
void pause(); UserAgent* userAgent();
void resume();
void refreshMediaPath();
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error). // Pauses and resumes all providers; updates states
// There are passing string objects by value; this is correct; this values will modified on the stack. void pause();
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag, void resume();
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media); void refreshMediaPath();
// Session ID // Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
int mSessionId; // There are passing string objects by value; this is correct; this values will modified on the stack.
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
// Media streams collection // Session ID
std::vector<Stream> mStreamList; int mSessionId;
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method // Media streams collection
std::shared_ptr<ice::Stack> mIceStack; std::vector<Stream> mStreamList;
// Pointer to owner user agent instance // Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
UserAgent* mUserAgent; std::shared_ptr<ice::Stack> mIceStack;
// Remote peer SIP address // Pointer to owner user agent instance
resip::NameAddr mRemotePeer; UserAgent* mUserAgent;
// Mutex to protect this instance // Remote peer SIP address
Mutex mGuard; resip::NameAddr mRemotePeer;
// SDP's origin version for sending // Mutex to protect this instance
int mOriginVersion; Mutex mGuard;
uint64_t mRemoteOriginVersion;
// SDP's session version // SDP's origin version for sending
int mSessionVersion; int mOriginVersion;
uint64_t mRemoteOriginVersion;
// Marks if this session does not need OnNewSession event // SDP's session version
bool mAcceptedByEngine; int mSessionVersion;
bool mAcceptedByUser;
// Invite session handle // Marks if this session does not need OnNewSession event
resip::InviteSessionHandle mInviteHandle; bool mAcceptedByEngine;
bool mAcceptedByUser;
// Dialog set object pointer // Invite session handle
ResipSession* mResipSession; resip::InviteSessionHandle mInviteHandle;
// Reference counter // Dialog set object pointer
int mRefCount; ResipSession* mResipSession;
enum // Reference counter
{ int mRefCount;
Initiator = 1,
Acceptor = 2
};
// Specifies session role - caller (Initiator) or callee (Acceptor) enum
volatile int mRole; {
Initiator = 1,
Acceptor = 2
};
// Marks if candidates are gather already // Specifies session role - caller (Initiator) or callee (Acceptor)
volatile bool mGatheredCandidates; volatile int mRole;
// Marks if OnTerminated event was called already on session // Marks if candidates are gather already
volatile bool mTerminated; volatile bool mGatheredCandidates;
// User friend remote peer's sip address // Marks if OnTerminated event was called already on session
std::string mRemoteAddress; volatile bool mTerminated;
// Application specific data // User friend remote peer's sip address
void* mTag; std::string mRemoteAddress;
// Used to count number of transistions to Connected state and avoid multiple onEstablished events.
int mOfferAnswerCounter;
// List of turn prefixes related to sessioj // Application specific data
std::vector<int> mTurnPrefixList; void* mTag;
// True if user agent has to send offer // Used to count number of transistions to Connected state and avoid multiple onEstablished events.
bool mHasToSendOffer; int mOfferAnswerCounter;
// True if user agent has to enqueue offer after ice gather finished // List of turn prefixes related to sessioj
bool mSendOfferUpdateAfterIceGather; std::vector<int> mTurnPrefixList;
// Related sip account // True if user agent has to send offer
PAccount mAccount; bool mHasToSendOffer;
// User headers for INVITE transaction // True if user agent has to enqueue offer after ice gather finished
UserHeaders mUserHeaders; bool mSendOfferUpdateAfterIceGather;
std::string remoteAddress() const; // Related sip account
void setRemoteAddress(const std::string& address); PAccount mAccount;
void* tag(); // User headers for INVITE transaction
void setTag(void* tag); UserHeaders mUserHeaders;
int sessionId();
int increaseSdpVersion();
int addRef();
int release();
// Deletes providers and media sockets std::string remoteAddress() const;
void clearProvidersAndSockets(); void setRemoteAddress(const std::string& address);
// Deletes providers void* tag();
void clearProviders(); void setTag(void* tag);
int sessionId();
int increaseSdpVersion();
int addRef();
int release();
// Helper method to find audio provider for active sip stream // Deletes providers and media sockets
AudioProvider* findProviderForActiveAudio(); void clearProvidersAndSockets();
void processCommandList(); // Deletes providers
void addCommand(Command* cmd); void clearProviders();
void enqueueOffer();
void processQueuedOffer(); // Helper method to find audio provider for active sip stream
static int generateId(); AudioProvider* findProviderForActiveAudio();
static std::atomic_int IdGenerator;
static std::atomic_int InstanceCounter; void processCommandList();
void addCommand(Command* cmd);
void enqueueOffer();
void processQueuedOffer();
static int generateId();
static std::atomic_int IdGenerator;
static std::atomic_int InstanceCounter;
}; };
typedef std::shared_ptr<Session> PSession; typedef std::shared_ptr<Session> PSession;
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
// //
// Classes that provide the mapping between Application Data and DUM // Classes that provide the mapping between Application Data and DUM
// dialogs/dialogsets // dialogs/dialogsets
// //
// The DUM layer creates an AppDialog/AppDialogSet object for inbound/outbound // The DUM layer creates an AppDialog/AppDialogSet object for inbound/outbound
// SIP Request that results in Dialog creation. // SIP Request that results in Dialog creation.
// //
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
class ResipSessionAppDialog : public resip::AppDialog class ResipSessionAppDialog : public resip::AppDialog
{ {
public: public:
ResipSessionAppDialog(resip::HandleManager& ham); ResipSessionAppDialog(resip::HandleManager& ham);
virtual ~ResipSessionAppDialog(); virtual ~ResipSessionAppDialog();
}; };
class ResipSession: public resip::AppDialogSet class ResipSession : public resip::AppDialogSet
{ {
friend class UserAgent; friend class UserAgent;
friend class Account; friend class Account;
public: public:
enum Type enum Type
{ {
Type_None, Type_None,
Type_Registration, Type_Registration,
Type_Subscription, Type_Subscription,
Type_Call, Type_Call,
Type_Auto Type_Auto
}; };
static std::atomic_int InstanceCounter; static std::atomic_int InstanceCounter;
ResipSession(resip::DialogUsageManager& dum); ResipSession(resip::DialogUsageManager& dum);
virtual ~ResipSession(); virtual ~ResipSession();
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg); virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg); virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
void setType(Type type);
Type type();
Session* session();
void setSession(Session* session);
UserAgent* ua();
void setUa(UserAgent* ua);
// Used for subscriptions/messages
int sessionId();
// Used for subscriptions/messages
void* tag() const;
void setTag(void* tag);
// Used for subscriptions/messages void setType(Type type);
std::string remoteAddress() const; Type type();
void setRemoteAddress(std::string address);
void runTerminatedEvent(Type type, int code = 0, int reason = 0); Session* session();
void setSession(Session* session);
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile); UserAgent* ua();
void setUa(UserAgent* ua);
// Used for subscriptions/messages
int sessionId();
// Used for subscriptions/messages
void* tag() const;
void setTag(void* tag);
// Used for subscriptions/messages
std::string remoteAddress() const;
void setRemoteAddress(std::string address);
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
protected: protected:
bool mTerminated; bool mTerminated;
UserAgent* mUserAgent; UserAgent* mUserAgent;
Type mType; Type mType;
Session* mSession; Session* mSession;
int mSessionId; int mSessionId;
std::string mRemoteAddress; std::string mRemoteAddress;
void* mTag; void* mTag;
bool mOnWatchingStartSent; bool mOnWatchingStartSent;
std::shared_ptr<resip::UserProfile> mUASProfile; std::shared_ptr<resip::UserProfile> mUASProfile;
}; };
class ResipSessionFactory : public resip::AppDialogSetFactory class ResipSessionFactory : public resip::AppDialogSetFactory
{ {
public: public:
ResipSessionFactory(UserAgent* agent); ResipSessionFactory(UserAgent* agent);
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg); virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
protected: protected:
UserAgent* mAgent; UserAgent* mAgent;
}; };
#endif #endif
+16 -16
View File
@@ -9,7 +9,7 @@
#define USE_SPEEX_AEC #define USE_SPEEX_AEC
// TODO: test implementation with webrtc aec; be careful - it needs fixes! // TODO: test implementation with webrtc aec; be careful - it needs fixes!
//#define USE_WEBRTC_AEC // #define USE_WEBRTC_AEC
#define USER #define USER
@@ -20,7 +20,7 @@
#define AUDIO_SAMPLERATE 48000 #define AUDIO_SAMPLERATE 48000
#define AUDIO_MIC_BUFFER_COUNT 16 #define AUDIO_MIC_BUFFER_COUNT 16
#define AUDIO_MIC_BUFFER_LENGTH 10 #define AUDIO_MIC_BUFFER_LENGTH 10
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS) #define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
#define AUDIO_SPK_BUFFER_COUNT 16 #define AUDIO_SPK_BUFFER_COUNT 16
#define AUDIO_SPK_BUFFER_LENGTH 10 #define AUDIO_SPK_BUFFER_LENGTH 10
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS) #define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
@@ -36,25 +36,25 @@
// Defined these two lines to get dumping of audio input/output // Defined these two lines to get dumping of audio input/output
//#define AUDIO_DUMPINPUT // #define AUDIO_DUMPINPUT
//#define AUDIO_DUMPOUTPUT // #define AUDIO_DUMPOUTPUT
#define UA_REGISTRATION_TIME 3600 #define UA_REGISTRATION_TIME 3600
#define UA_MEDIA_PORT_START 20000 #define UA_MEDIA_PORT_START 20000
#define UA_MEDIA_PORT_FINISH 30000 #define UA_MEDIA_PORT_FINISH 30000
#define UA_MAX_UDP_PACKET_SIZE 576 #define UA_MAX_UDP_PACKET_SIZE 576
#define UA_PUBLICATION_ID "314" #define UA_PUBLICATION_ID "314"
#define MT_SAMPLERATE AUDIO_SAMPLERATE #define MT_SAMPLERATE AUDIO_SAMPLERATE
#define MT_MAXAUDIOFRAME 1440 #define MT_MAXAUDIOFRAME 1440
#define MT_MAXRTPPACKET 1500 #define MT_MAXRTPPACKET 1500
#define MT_DTMF_END_PACKETS 3 #define MT_DTMF_END_PACKETS 3
// Milliseconds before // Milliseconds before
#define RTP_BUFFER_HIGH (2000) #define RTP_BUFFER_HIGH (2000)
#define RTP_BUFFER_LOW (0) #define RTP_BUFFER_LOW (0)
#define RTP_BUFFER_PREBUFFER (100) #define RTP_BUFFER_PREBUFFER (100)
#define RTP_DECODED_CAPACITY 2048 #define RTP_DECODED_CAPACITY 2048
@@ -107,13 +107,13 @@
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10) #define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
#if defined(TARGET_OSX) || defined(TARGET_LINUX) #if defined(TARGET_OSX) || defined(TARGET_LINUX)
# define TEXT(X) X #define TEXT(X) X
#endif #endif
// In milliseconds // In milliseconds
#define MT_SEVANA_FRAME_TIME 680 #define MT_SEVANA_FRAME_TIME 680
// Number of samples // Number of samples
#define MT_MAX_DECODEBUFFER 32768 #define MT_MAX_DECODEBUFFER 32768
#endif #endif
+2 -8
View File
@@ -5,12 +5,6 @@
#include "HL_AsyncCommand.h" #include "HL_AsyncCommand.h"
AsyncCommand::AsyncCommand() AsyncCommand::AsyncCommand() {}
{
} AsyncCommand::~AsyncCommand() {}
AsyncCommand::~AsyncCommand()
{
}
+4 -4
View File
@@ -9,11 +9,11 @@
class AsyncCommand class AsyncCommand
{ {
public: public:
AsyncCommand(); AsyncCommand();
virtual ~AsyncCommand(); virtual ~AsyncCommand();
virtual void run(void* environment) = 0; virtual void run(void* environment) = 0;
virtual bool finished() = 0; virtual bool finished() = 0;
}; };
#endif // HL_ASYNCCOMMAND_H #endif // HL_ASYNCCOMMAND_H
+239 -191
View File
@@ -4,253 +4,301 @@
#include <string> #include <string>
const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; "0123456789+/";
class Base64 { class Base64
public: {
static bool Encode(const std::string &in, std::string *out) { public:
int i = 0, j = 0; static bool Encode(const std::string& in, std::string* out)
size_t enc_len = 0; {
unsigned char a3[3]; int i = 0, j = 0;
unsigned char a4[4]; size_t enc_len = 0;
unsigned char a3[3];
unsigned char a4[4];
out->resize(EncodedLength(in)); out->resize(EncodedLength(in));
int input_len = in.size(); int input_len = in.size();
std::string::const_iterator input = in.begin(); std::string::const_iterator input = in.begin();
while (input_len--) { while (input_len--)
a3[i++] = *(input++); {
if (i == 3) { a3[i++] = *(input++);
a3_to_a4(a4, a3); if (i == 3)
{
a3_to_a4(a4, a3);
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++)
(*out)[enc_len++] = kBase64Alphabet[a4[i]]; {
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
}
i = 0;
}
} }
i = 0; if (i)
} {
} for (j = i; j < 3; j++)
{
a3[j] = '\0';
}
if (i) { a3_to_a4(a4, a3);
for (j = i; j < 3; j++) {
a3[j] = '\0';
}
a3_to_a4(a4, a3); for (j = 0; j < i + 1; j++)
{
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
}
for (j = 0; j < i + 1; j++) { while ((i++ < 3))
(*out)[enc_len++] = kBase64Alphabet[a4[j]]; {
} (*out)[enc_len++] = '=';
}
while ((i++ < 3)) {
(*out)[enc_len++] = '=';
}
}
return (enc_len == out->size());
}
static bool Encode(const char *input, size_t input_length, char *out, size_t out_length) {
int i = 0, j = 0;
char *out_begin = out;
unsigned char a3[3];
unsigned char a4[4];
size_t encoded_length = EncodedLength(input_length);
if (out_length < encoded_length) return false;
while (input_length--) {
a3[i++] = *input++;
if (i == 3) {
a3_to_a4(a4, a3);
for (i = 0; i < 4; i++) {
*out++ = kBase64Alphabet[a4[i]];
} }
i = 0; return (enc_len == out->size());
}
} }
if (i) { static bool Encode(const char* input, size_t input_length, char* out, size_t out_length)
for (j = i; j < 3; j++) { {
a3[j] = '\0'; int i = 0, j = 0;
} char* out_begin = out;
unsigned char a3[3];
unsigned char a4[4];
a3_to_a4(a4, a3); size_t encoded_length = EncodedLength(input_length);
for (j = 0; j < i + 1; j++) { if (out_length < encoded_length)
*out++ = kBase64Alphabet[a4[j]]; return false;
}
while ((i++ < 3)) { while (input_length--)
*out++ = '='; {
} a3[i++] = *input++;
} if (i == 3)
{
a3_to_a4(a4, a3);
return (out == (out_begin + encoded_length)); for (i = 0; i < 4; i++)
} {
*out++ = kBase64Alphabet[a4[i]];
}
static bool Decode(const std::string &in, std::string *out) { i = 0;
int i = 0, j = 0; }
size_t dec_len = 0;
unsigned char a3[3];
unsigned char a4[4];
int input_len = in.size();
std::string::const_iterator input = in.begin();
out->resize(DecodedLength(in));
while (input_len--) {
if (*input == '=') {
break;
}
a4[i++] = *(input++);
if (i == 4) {
for (i = 0; i <4; i++) {
a4[i] = b64_lookup(a4[i]);
} }
a4_to_a3(a3,a4); if (i)
{
for (j = i; j < 3; j++)
{
a3[j] = '\0';
}
for (i = 0; i < 3; i++) { a3_to_a4(a4, a3);
(*out)[dec_len++] = a3[i];
for (j = 0; j < i + 1; j++)
{
*out++ = kBase64Alphabet[a4[j]];
}
while ((i++ < 3))
{
*out++ = '=';
}
} }
i = 0; return (out == (out_begin + encoded_length));
}
} }
if (i) { static bool Decode(const std::string& in, std::string* out)
for (j = i; j < 4; j++) { {
a4[j] = '\0'; int i = 0, j = 0;
} size_t dec_len = 0;
unsigned char a3[3];
unsigned char a4[4];
for (j = 0; j < 4; j++) { int input_len = in.size();
a4[j] = b64_lookup(a4[j]); std::string::const_iterator input = in.begin();
}
a4_to_a3(a3,a4); out->resize(DecodedLength(in));
for (j = 0; j < i - 1; j++) { while (input_len--)
(*out)[dec_len++] = a3[j]; {
} if (*input == '=')
} {
break;
}
return (dec_len == out->size()); a4[i++] = *(input++);
} if (i == 4)
{
for (i = 0; i < 4; i++)
{
a4[i] = b64_lookup(a4[i]);
}
static bool Decode(const char *input, size_t input_length, char *out, size_t out_length) { a4_to_a3(a3, a4);
int i = 0, j = 0;
char *out_begin = out;
unsigned char a3[3];
unsigned char a4[4];
size_t decoded_length = DecodedLength(input, input_length); for (i = 0; i < 3; i++)
{
(*out)[dec_len++] = a3[i];
}
if (out_length < decoded_length) return false; i = 0;
}
while (input_length--) {
if (*input == '=') {
break;
}
a4[i++] = *(input++);
if (i == 4) {
for (i = 0; i <4; i++) {
a4[i] = b64_lookup(a4[i]);
} }
a4_to_a3(a3,a4); if (i)
{
for (j = i; j < 4; j++)
{
a4[j] = '\0';
}
for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++)
*out++ = a3[i]; {
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3, a4);
for (j = 0; j < i - 1; j++)
{
(*out)[dec_len++] = a3[j];
}
} }
i = 0; return (dec_len == out->size());
}
} }
if (i) { static bool Decode(const char* input, size_t input_length, char* out, size_t out_length)
for (j = i; j < 4; j++) { {
a4[j] = '\0'; int i = 0, j = 0;
} char* out_begin = out;
unsigned char a3[3];
unsigned char a4[4];
for (j = 0; j < 4; j++) { size_t decoded_length = DecodedLength(input, input_length);
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3,a4); if (out_length < decoded_length)
return false;
for (j = 0; j < i - 1; j++) { while (input_length--)
*out++ = a3[j]; {
} if (*input == '=')
{
break;
}
a4[i++] = *(input++);
if (i == 4)
{
for (i = 0; i < 4; i++)
{
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3, a4);
for (i = 0; i < 3; i++)
{
*out++ = a3[i];
}
i = 0;
}
}
if (i)
{
for (j = i; j < 4; j++)
{
a4[j] = '\0';
}
for (j = 0; j < 4; j++)
{
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3, a4);
for (j = 0; j < i - 1; j++)
{
*out++ = a3[j];
}
}
return (out == (out_begin + decoded_length));
} }
return (out == (out_begin + decoded_length)); static int DecodedLength(const char* in, size_t in_length)
} {
int numEq = 0;
static int DecodedLength(const char *in, size_t in_length) { const char* in_end = in + in_length;
int numEq = 0; while (*--in_end == '=')
++numEq;
const char *in_end = in + in_length; return ((6 * in_length) / 8) - numEq;
while (*--in_end == '=') ++numEq;
return ((6 * in_length) / 8) - numEq;
}
static int DecodedLength(const std::string &in) {
int numEq = 0;
int n = in.size();
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) {
++numEq;
} }
return ((6 * n) / 8) - numEq; static int DecodedLength(const std::string& in)
} {
int numEq = 0;
int n = in.size();
inline static int EncodedLength(size_t length) { for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it)
return (length + 2 - ((length + 2) % 3)) / 3 * 4; {
} ++numEq;
}
inline static int EncodedLength(const std::string &in) { return ((6 * n) / 8) - numEq;
return EncodedLength(in.length()); }
}
inline static void StripPadding(std::string *in) { inline static int EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; }
while (!in->empty() && *(in->rbegin()) == '=') in->resize(in->size() - 1);
}
private: inline static int EncodedLength(const std::string& in) { return EncodedLength(in.length()); }
static inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
a4[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f);
}
static inline void a4_to_a3(unsigned char * a3, unsigned char * a4) { inline static void StripPadding(std::string* in)
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); {
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); while (!in->empty() && *(in->rbegin()) == '=')
a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; in->resize(in->size() - 1);
} }
static inline unsigned char b64_lookup(unsigned char c) { private:
if(c >='A' && c <='Z') return c - 'A'; static inline void a3_to_a4(unsigned char* a4, unsigned char* a3)
if(c >='a' && c <='z') return c - 71; {
if(c >='0' && c <='9') return c + 4; a4[0] = (a3[0] & 0xfc) >> 2;
if(c == '+') return 62; a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
if(c == '/') return 63; a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
return 255; a4[3] = (a3[2] & 0x3f);
} }
static inline void a4_to_a3(unsigned char* a3, unsigned char* a4)
{
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
}
static inline unsigned char b64_lookup(unsigned char c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 71;
if (c >= '0' && c <= '9')
return c + 4;
if (c == '+')
return 62;
if (c == '/')
return 63;
return 255;
}
}; };
#endif // HL_BASE64_H #endif // HL_BASE64_H
+4 -4
View File
@@ -8,10 +8,10 @@
#include "ice/ICEByteBuffer.h" #include "ice/ICEByteBuffer.h"
typedef ice::ByteBuffer ByteBuffer; typedef ice::ByteBuffer ByteBuffer;
typedef ice::PByteBuffer PByteBuffer; typedef ice::PByteBuffer PByteBuffer;
typedef ice::BitReader BitReader; typedef ice::BitReader BitReader;
typedef ice::BitWriter BitWriter; typedef ice::BitWriter BitWriter;
typedef ice::BufferReader BufferReader; typedef ice::BufferReader BufferReader;
typedef ice::BufferWriter BufferWriter; typedef ice::BufferWriter BufferWriter;
File diff suppressed because it is too large Load Diff
+80 -85
View File
@@ -14,64 +14,65 @@
BOOL WINAPI CrashReporter::Callback(LPVOID arg) BOOL WINAPI CrashReporter::Callback(LPVOID arg)
{ {
return TRUE; return TRUE;
} }
typedef int(__stdcall *CrInstallProc)(__in PCR_INSTALL_INFOW pInfo); typedef int(__stdcall* CrInstallProc)(__in PCR_INSTALL_INFOW pInfo);
static CrInstallProc CrInstall = nullptr; static CrInstallProc CrInstall = nullptr;
typedef int(__stdcall *CrUninstallProc)(); typedef int(__stdcall* CrUninstallProc)();
static CrUninstallProc CrUninstall = nullptr; static CrUninstallProc CrUninstall = nullptr;
typedef int(__stdcall *CrInstallIntoCurrentThreadProc)(DWORD dwFlags); typedef int(__stdcall* CrInstallIntoCurrentThreadProc)(DWORD dwFlags);
static CrInstallIntoCurrentThreadProc CrInstallIntoCurrentThread = nullptr; static CrInstallIntoCurrentThreadProc CrInstallIntoCurrentThread = nullptr;
typedef int(__stdcall *CrUninstallFromCurrentThreadProc)(); typedef int(__stdcall* CrUninstallFromCurrentThreadProc)();
static CrUninstallFromCurrentThreadProc CrUninstallFromCurrentThread = nullptr; static CrUninstallFromCurrentThreadProc CrUninstallFromCurrentThread = nullptr;
typedef int(__stdcall *CrGetLastErrorMsgProc)(LPWSTR buffer, UINT size); typedef int(__stdcall* CrGetLastErrorMsgProc)(LPWSTR buffer, UINT size);
static CrGetLastErrorMsgProc CrGetLastErrorMsg = nullptr; static CrGetLastErrorMsgProc CrGetLastErrorMsg = nullptr;
static HMODULE CrLibraryHandle = NULL; static HMODULE CrLibraryHandle = NULL;
#endif #endif
void CrashReporter::init(const std::string& appname, const std::string& version, const std::string& url) void CrashReporter::init(const std::string& appname, const std::string& version, const std::string& url)
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
#if defined(CRASHRPT_DYNAMIC) #if defined(CRASHRPT_DYNAMIC)
// Check if DLL functions are here // Check if DLL functions are here
if (CrLibraryHandle) if (CrLibraryHandle)
return; // Library is loaded already - so initialized already return; // Library is loaded already - so initialized already
CrLibraryHandle = ::LoadLibrary(TEXT("crashrpt.dll")); CrLibraryHandle = ::LoadLibrary(TEXT("crashrpt.dll"));
if (!CrLibraryHandle) if (!CrLibraryHandle)
return; // No logging here - initialization happens on very first stages, no chance to log anything return; // No logging here - initialization happens on very first stages, no chance to log anything
CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW"); CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW");
CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall"); CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall");
CrInstallIntoCurrentThread = (CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2"); CrInstallIntoCurrentThread =
CrUninstallFromCurrentThread = (CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread"); (CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2");
CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW"); CrUninstallFromCurrentThread =
(CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread");
CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW");
#else #else
CrInstall = &crInstallW; CrInstall = &crInstallW;
CrUninstall = &crUninstall; CrUninstall = &crUninstall;
CrInstallIntoCurrentThread = &crInstallToCurrentThread2; CrInstallIntoCurrentThread = &crInstallToCurrentThread2;
CrUninstallFromCurrentThread = &crUninstallFromCurrentThread; CrUninstallFromCurrentThread = &crUninstallFromCurrentThread;
CrGetLastErrorMsg = &crGetLastErrorMsgW; CrGetLastErrorMsg = &crGetLastErrorMsgW;
#endif #endif
if (!isLoaded()) if (!isLoaded())
return; return;
CR_INSTALL_INFO info; CR_INSTALL_INFO info;
memset(&info, 0, sizeof(CR_INSTALL_INFO)); memset(&info, 0, sizeof(CR_INSTALL_INFO));
info.cb = sizeof(CR_INSTALL_INFO); info.cb = sizeof(CR_INSTALL_INFO);
struct struct
{ {
std::wstring appname, version, url; std::wstring appname, version, url;
} unicode; } unicode;
unicode.appname = StringHelper::makeTstring(appname), unicode.appname = StringHelper::makeTstring(appname), unicode.version = StringHelper::makeTstring(version),
unicode.version = StringHelper::makeTstring(version),
unicode.url = StringHelper::makeTstring(url); unicode.url = StringHelper::makeTstring(url);
if (unicode.appname.empty()) if (unicode.appname.empty())
@@ -83,111 +84,105 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
if (unicode.version.empty()) if (unicode.version.empty())
unicode.version = L"General version"; unicode.version = L"General version";
info.pszAppName = unicode.appname.c_str(); info.pszAppName = unicode.appname.c_str();
info.pszAppVersion = unicode.version.c_str(); info.pszAppVersion = unicode.version.c_str();
info.pszEmailSubject = TEXT("Crash report"); info.pszEmailSubject = TEXT("Crash report");
//info.pszEmailTo = L"amegyeri@minerva-soft.com"; // info.pszEmailTo = L"amegyeri@minerva-soft.com";
//info.pszUrl = L"http://ftp.minerva-soft.com/crashlog/crashrpt.php"; // info.pszUrl = L"http://ftp.minerva-soft.com/crashlog/crashrpt.php";
//info.pszUrl = L"http://sip.crypttalk.com/crashlog/crashrpt.php"; // info.pszUrl = L"http://sip.crypttalk.com/crashlog/crashrpt.php";
info.pszUrl = unicode.url.c_str(); info.pszUrl = unicode.url.c_str();
info.pfnCrashCallback = Callback; info.pfnCrashCallback = Callback;
info.uPriorities[CR_HTTP] = 1; info.uPriorities[CR_HTTP] = 1;
info.uPriorities[CR_SMTP] = CR_NEGATIVE_PRIORITY; info.uPriorities[CR_SMTP] = CR_NEGATIVE_PRIORITY;
info.uPriorities[CR_SMAPI] = CR_NEGATIVE_PRIORITY; info.uPriorities[CR_SMAPI] = CR_NEGATIVE_PRIORITY;
info.dwFlags = 0; info.dwFlags = 0;
info.pszCrashSenderPath = TEXT("."); info.pszCrashSenderPath = TEXT(".");
int nResult = CrInstall(&info); int nResult = CrInstall(&info);
if (nResult) if (nResult)
{ {
wchar_t errorMsg[512] = L""; wchar_t errorMsg[512] = L"";
CrGetLastErrorMsg(errorMsg, 512); CrGetLastErrorMsg(errorMsg, 512);
OutputDebugStringW(errorMsg); OutputDebugStringW(errorMsg);
//LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " << errorMsg); // LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " <<
} // errorMsg);
}
#endif #endif
} }
void CrashReporter::free() void CrashReporter::free()
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
if (isLoaded()) if (isLoaded())
{ {
CrUninstall(); CrUninstall();
CrInstall = nullptr; CrInstall = nullptr;
CrUninstall = nullptr; CrUninstall = nullptr;
CrInstallIntoCurrentThread = nullptr; CrInstallIntoCurrentThread = nullptr;
CrUninstallFromCurrentThread = nullptr; CrUninstallFromCurrentThread = nullptr;
CrGetLastErrorMsg = nullptr; CrGetLastErrorMsg = nullptr;
#if defined(CRASHRPT_DYNAMIC) #if defined(CRASHRPT_DYNAMIC)
::FreeLibrary(CrLibraryHandle); CrLibraryHandle = NULL; ::FreeLibrary(CrLibraryHandle);
CrLibraryHandle = NULL;
#endif #endif
} }
#endif #endif
} }
bool CrashReporter::isLoaded() bool CrashReporter::isLoaded()
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg || return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg || !CrInstallIntoCurrentThread ||
!CrInstallIntoCurrentThread || !CrUninstallFromCurrentThread); !CrUninstallFromCurrentThread);
#else #else
return false; return false;
#endif #endif
} }
void CrashReporter::initThread() void CrashReporter::initThread()
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
if (isLoaded()) if (isLoaded())
CrInstallIntoCurrentThread(0); CrInstallIntoCurrentThread(0);
#endif #endif
} }
void CrashReporter::freeThread() void CrashReporter::freeThread()
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
if (isLoaded()) if (isLoaded())
CrUninstallFromCurrentThread(); CrUninstallFromCurrentThread();
#endif #endif
} }
CrashReporterThreadPoint::CrashReporterThreadPoint() CrashReporterThreadPoint::CrashReporterThreadPoint()
{ {
CrashReporter::initThread(); CrashReporter::initThread();
} }
CrashReporterThreadPoint::~CrashReporterThreadPoint() CrashReporterThreadPoint::~CrashReporterThreadPoint()
{ {
CrashReporter::freeThread(); CrashReporter::freeThread();
} }
CrashReporterGuard::CrashReporterGuard() CrashReporterGuard::CrashReporterGuard()
{ {
CrashReporter::init("generic"); CrashReporter::init("generic");
} }
CrashReporterGuard::~CrashReporterGuard() CrashReporterGuard::~CrashReporterGuard()
{ {
CrashReporter::free(); CrashReporter::free();
} }
#else #else
CrashReporterThreadPoint::CrashReporterThreadPoint() CrashReporterThreadPoint::CrashReporterThreadPoint() {}
{
}
CrashReporterThreadPoint::~CrashReporterThreadPoint() CrashReporterThreadPoint::~CrashReporterThreadPoint() {}
{
}
CrashReporterGuard::CrashReporterGuard() CrashReporterGuard::CrashReporterGuard() {}
{
}
CrashReporterGuard::~CrashReporterGuard() CrashReporterGuard::~CrashReporterGuard() {}
{
}
#endif #endif
+20 -19
View File
@@ -4,8 +4,8 @@
#include <string> #include <string>
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
# include <WinSock2.h> #include <WinSock2.h>
# include <Windows.h> #include <Windows.h>
#endif #endif
// Helper class to translate SEH exceptions to C++ - sometimes it is needed // Helper class to translate SEH exceptions to C++ - sometimes it is needed
@@ -14,35 +14,36 @@
class SE_Exception class SE_Exception
{ {
private: private:
unsigned int nSE; unsigned int nSE;
public: public:
SE_Exception() {} SE_Exception() {}
SE_Exception(unsigned int n) : nSE(n) {} SE_Exception(unsigned int n) : nSE(n) {}
~SE_Exception() {} ~SE_Exception() {}
unsigned int getSeNumber() { return nSE; } unsigned int getSeNumber() { return nSE; }
}; };
extern void SEHToCpp(unsigned int, EXCEPTION_POINTERS*); extern void SEHToCpp(unsigned int, EXCEPTION_POINTERS*);
// Although better way is to have _set_se_translator set - in our case we do not call it. // Although better way is to have _set_se_translator set - in our case we do not call it.
// The cause is usage of CrashRpt libraries - it gives better control on exception reporting. // The cause is usage of CrashRpt libraries - it gives better control on exception reporting.
# define SET_SEH_TO_CPP #define SET_SEH_TO_CPP
//_set_se_translator(&SEHToCpp) //_set_se_translator(&SEHToCpp)
#else #else
# define SET_SEH_TO_CPP #define SET_SEH_TO_CPP
#endif #endif
class CrashReporter class CrashReporter
{ {
public: public:
static void init(const std::string& appname, const std::string& version = "", const std::string& url = ""); static void init(const std::string& appname, const std::string& version = "", const std::string& url = "");
static void free(); static void free();
static void initThread(); static void initThread();
static void freeThread(); static void freeThread();
static bool isLoaded(); static bool isLoaded();
#ifdef TARGET_WIN #ifdef TARGET_WIN
static BOOL WINAPI Callback(LPVOID /*lpvState*/); static BOOL WINAPI Callback(LPVOID /*lpvState*/);
#endif #endif
}; };
@@ -50,15 +51,15 @@ public:
class CrashReporterThreadPoint class CrashReporterThreadPoint
{ {
public: public:
CrashReporterThreadPoint(); CrashReporterThreadPoint();
~CrashReporterThreadPoint(); ~CrashReporterThreadPoint();
}; };
class CrashReporterGuard class CrashReporterGuard
{ {
public: public:
CrashReporterGuard(); CrashReporterGuard();
~CrashReporterGuard(); ~CrashReporterGuard();
}; };
#endif #endif
+12 -15
View File
@@ -2,28 +2,25 @@
#include "HL_String.h" #include "HL_String.h"
// --------- CsvFile ---------------- // --------- CsvFile ----------------
CsvReader::CsvReader(std::istream& stream) CsvReader::CsvReader(std::istream& stream) : mInputStream(stream) {}
:mInputStream(stream)
{}
CsvReader::~CsvReader() CsvReader::~CsvReader() {}
{}
std::istream& CsvReader::stream() const std::istream& CsvReader::stream() const
{ {
return mInputStream; return mInputStream;
} }
bool CsvReader::readLine(std::vector<std::string>& cells) bool CsvReader::readLine(std::vector<std::string>& cells)
{ {
cells.clear(); cells.clear();
std::string line; std::string line;
if (!std::getline(mInputStream, line)) if (!std::getline(mInputStream, line))
return false; return false;
strx::trim(line); strx::trim(line);
if (line.empty()) if (line.empty())
return false; return false;
strx::split(line, cells, ",;"); strx::split(line, cells, ",;");
return true; return true;
} }
+6 -6
View File
@@ -8,16 +8,16 @@
class CsvReader class CsvReader
{ {
public: public:
CsvReader(std::istream& stream); CsvReader(std::istream& stream);
~CsvReader(); ~CsvReader();
void setStream(std::istream& input); void setStream(std::istream& input);
std::istream& stream() const; std::istream& stream() const;
bool readLine(std::vector<std::string>& cells); bool readLine(std::vector<std::string>& cells);
protected: protected:
std::istream& mInputStream; std::istream& mInputStream;
}; };
#endif #endif
+31 -43
View File
@@ -13,36 +13,35 @@
#include <cstdio> #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)
{ {
std::snprintf(mMessage, sizeof(mMessage), "%d-%d", code, subcode); std::snprintf(mMessage, sizeof(mMessage), "%d-%d", code, subcode);
} }
@@ -50,35 +49,24 @@ public:
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 = 0, mSubcode = 0; int mCode = 0, mSubcode = 0;
char mMessage[256] = {0}; char mMessage[256] = {0};
}; };
+12 -10
View File
@@ -2,9 +2,9 @@
#include <fstream> #include <fstream>
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
# include <unistd.h> #include <unistd.h>
# include <sys/statvfs.h> #include <sys/statvfs.h>
# include <memory.h> #include <memory.h>
#endif #endif
bool FileHelper::exists(const std::string& s) bool FileHelper::exists(const std::string& s)
@@ -102,7 +102,8 @@ size_t FileHelper::getFreespace(const std::string& path)
size_t r = static_cast<size_t>(-1); size_t r = static_cast<size_t>(-1);
#if defined(TARGET_LINUX) #if defined(TARGET_LINUX)
struct statvfs stats; memset(&stats, 0, sizeof stats); struct statvfs stats;
memset(&stats, 0, sizeof stats);
int retcode = statvfs(path.c_str(), &stats); int retcode = statvfs(path.c_str(), &stats);
if (retcode == 0) if (retcode == 0)
@@ -111,11 +112,11 @@ size_t FileHelper::getFreespace(const std::string& path)
return r; return r;
} }
std::string FileHelper::expandUserHome(const std::string &path) std::string FileHelper::expandUserHome(const std::string& path)
{ {
if (path.empty() || path[0] != '~') if (path.empty() || path[0] != '~')
return path; // No expansion needed return path; // No expansion needed
const char* home_dir = nullptr; const char* home_dir = nullptr;
#ifdef TARGET_WIN #ifdef TARGET_WIN
@@ -124,7 +125,8 @@ std::string FileHelper::expandUserHome(const std::string &path)
{ {
home_dir = std::getenv("HOMEDRIVE"); home_dir = std::getenv("HOMEDRIVE");
const char* homepath = std::getenv("HOMEPATH"); const char* homepath = std::getenv("HOMEPATH");
if (home_dir && homepath) { if (home_dir && homepath)
{
std::string fullpath(home_dir); std::string fullpath(home_dir);
fullpath += homepath; fullpath += homepath;
return fullpath + path.substr(1); return fullpath + path.substr(1);
@@ -133,9 +135,9 @@ std::string FileHelper::expandUserHome(const std::string &path)
#else #else
home_dir = std::getenv("HOME"); home_dir = std::getenv("HOME");
#endif #endif
if (!home_dir) if (!home_dir)
throw std::runtime_error("Unable to determine the home directory"); throw std::runtime_error("Unable to determine the home directory");
return std::string(home_dir) + path.substr(1); return std::string(home_dir) + path.substr(1);
} }
+6 -6
View File
@@ -6,14 +6,14 @@
class FileHelper class FileHelper
{ {
public: public:
static bool exists(const std::string& s); static bool exists(const std::string& s);
static bool exists(const char* s); static bool exists(const char* s);
static void remove(const std::string& s); static void remove(const std::string& s);
static void remove(const char* s); static void remove(const char* s);
// static std::string gettempname(); // static std::string gettempname();
static bool isAbsolute(const std::string& s); static bool isAbsolute(const std::string& s);
static std::string getCurrentDir(); static std::string getCurrentDir();
@@ -22,7 +22,7 @@ public:
// Returns free space on volume for path // Returns free space on volume for path
// Works for Linux only. For other systems (size_t)-1 is returned (for errors too) // Works for Linux only. For other systems (size_t)-1 is returned (for errors too)
static size_t getFreespace(const std::string& path); static size_t getFreespace(const std::string& path);
static std::string expandUserHome(const std::string& path); static std::string expandUserHome(const std::string& path);
}; };
+58 -23
View File
@@ -7,13 +7,13 @@ static const uint32_t HEPID2 = 0x021002;
static const uint32_t HEPID3 = 0x48455033; static const uint32_t HEPID3 = 0x48455033;
bool Packet::parseV3(const ByteBuffer& packet) bool Packet::parseV3(const ByteBuffer& packet)
{ {
if (packet.size() < 30) if (packet.size() < 30)
return false; return false;
BufferReader r(packet); BufferReader r(packet);
char signature[4]; char signature[4];
r.readBuffer(signature, 4); r.readBuffer(signature, 4);
if (signature[0] != 'H' || signature[1] != 'E' || signature[2] != 'P' || signature[3] != '3') if (signature[0] != 'H' || signature[1] != 'E' || signature[2] != 'P' || signature[3] != '3')
@@ -24,12 +24,12 @@ bool Packet::parseV3(const ByteBuffer& packet)
l -= 6; l -= 6;
InternetAddress sourceAddr4, destAddr4, sourceAddr6, destAddr6; InternetAddress sourceAddr4, destAddr4, sourceAddr6, destAddr6;
uint16_t sourcePort = 0, destPort = 0; uint16_t sourcePort = 0, destPort = 0;
while (r.count() < packet.size()) while (r.count() < packet.size())
{ {
mVendorId = (VendorId)r.readUShort(); mVendorId = (VendorId)r.readUShort();
ChunkType chunkType = (ChunkType)r.readUShort(); ChunkType chunkType = (ChunkType)r.readUShort();
int chunkLength = r.readUShort(); int chunkLength = r.readUShort();
switch (chunkType) switch (chunkType)
{ {
@@ -102,8 +102,7 @@ bool Packet::parseV3(const ByteBuffer& packet)
if (!sourceAddr4.isEmpty()) if (!sourceAddr4.isEmpty())
mSourceAddress = sourceAddr4; mSourceAddress = sourceAddr4;
else else if (!sourceAddr6.isEmpty())
if (!sourceAddr6.isEmpty())
mSourceAddress = sourceAddr6; mSourceAddress = sourceAddr6;
if (!mSourceAddress.isEmpty()) if (!mSourceAddress.isEmpty())
@@ -111,8 +110,7 @@ bool Packet::parseV3(const ByteBuffer& packet)
if (!destAddr4.isEmpty()) if (!destAddr4.isEmpty())
mDestinationAddress = destAddr4; mDestinationAddress = destAddr4;
else else if (!destAddr6.isEmpty())
if (!destAddr6.isEmpty())
mDestinationAddress = destAddr6; mDestinationAddress = destAddr6;
if (!mDestinationAddress.isEmpty()) if (!mDestinationAddress.isEmpty())
@@ -121,7 +119,7 @@ bool Packet::parseV3(const ByteBuffer& packet)
return true; return true;
} }
bool Packet::parseV2(const ByteBuffer &packet) bool Packet::parseV2(const ByteBuffer& packet)
{ {
if (packet.size() < 31) if (packet.size() < 31)
return false; return false;
@@ -148,16 +146,53 @@ bool Packet::parseV2(const ByteBuffer &packet)
return true; return true;
} }
#define WRITE_CHUNK_UCHAR(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(1); w.writeUChar((uint8_t)V);} #define WRITE_CHUNK_UCHAR(T, V) \
#define WRITE_CHUNK_USHORT(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(2); w.writeUShort((uint16_t)V);} { \
#define WRITE_CHUNK_UINT(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(4); w.writeUInt((uint32_t)V);} w.writeUShort((uint16_t)mVendorId); \
#define WRITE_CHUNK_IP4(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(4); w.writeIp(V);} w.writeUShort((uint16_t)T); \
#define WRITE_CHUNK_IP6(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(8); w.writeIp(V);} w.writeUShort(1); \
#define WRITE_CHUNK_BUFFER(T, V) {w.writeUShort((uint16_t)mVendorId); w.writeUShort((uint16_t)T); w.writeUShort(8); w.writeBuffer(V.data(), V.size());} w.writeUChar((uint8_t)V); \
}
#define WRITE_CHUNK_USHORT(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(2); \
w.writeUShort((uint16_t)V); \
}
#define WRITE_CHUNK_UINT(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(4); \
w.writeUInt((uint32_t)V); \
}
#define WRITE_CHUNK_IP4(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(4); \
w.writeIp(V); \
}
#define WRITE_CHUNK_IP6(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(8); \
w.writeIp(V); \
}
#define WRITE_CHUNK_BUFFER(T, V) \
{ \
w.writeUShort((uint16_t)mVendorId); \
w.writeUShort((uint16_t)T); \
w.writeUShort(8); \
w.writeBuffer(V.data(), V.size()); \
}
ByteBuffer Packet::buildV3() ByteBuffer Packet::buildV3()
{ {
ByteBuffer r; r.resize(mBody.size() + 512); ByteBuffer r;
r.resize(mBody.size() + 512);
BufferWriter w(r); BufferWriter w(r);
// Signature // Signature
@@ -174,9 +209,8 @@ ByteBuffer Packet::buildV3()
{ {
if (mSourceAddress.isV4()) if (mSourceAddress.isV4())
WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress) WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress)
else else if (mSourceAddress.isV6())
if (mSourceAddress.isV6()) WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port()); WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port());
} }
@@ -186,9 +220,8 @@ ByteBuffer Packet::buildV3()
{ {
if (mDestinationAddress.isV4()) if (mDestinationAddress.isV4())
WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress) WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress)
else else if (mDestinationAddress.isV6())
if (mDestinationAddress.isV6()) WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port()); WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port());
} }
@@ -216,7 +249,9 @@ ByteBuffer Packet::buildV3()
r.resize(w.offset()); r.resize(w.offset());
w.rewind(); w.skip(4); w.writeUShort((uint16_t)r.size()); w.rewind();
w.skip(4);
w.writeUShort((uint16_t)r.size());
return r; return r;
} }
+27 -31
View File
@@ -6,8 +6,8 @@
namespace HEP namespace HEP
{ {
enum class ChunkType enum class ChunkType
{ {
None = 0, None = 0,
IPProtocolFamily, IPProtocolFamily,
IPProtocolID, IPProtocolID,
@@ -19,17 +19,17 @@ namespace HEP
DestinationPort, DestinationPort,
Timestamp, Timestamp,
TimestampMicro, TimestampMicro,
ProtocolType, // Maps to Protocol Types below ProtocolType, // Maps to Protocol Types below
CaptureAgentID, CaptureAgentID,
KeepAliveTimer, KeepAliveTimer,
AuthenticationKey, AuthenticationKey,
PacketPayload, PacketPayload,
CompressedPayload, CompressedPayload,
InternalC InternalC
}; };
enum class VendorId enum class VendorId
{ {
None, None,
FreeSwitch, FreeSwitch,
Kamailio, Kamailio,
@@ -37,10 +37,10 @@ namespace HEP
Asterisk, Asterisk,
Homer, Homer,
SipXecs SipXecs
}; };
enum class ProtocolId enum class ProtocolId
{ {
Reserved = 0, Reserved = 0,
SIP, SIP,
XMPP, XMPP,
@@ -54,32 +54,28 @@ namespace HEP
IAX, IAX,
H322, H322,
H321 H321
}; };
struct Packet struct Packet
{ {
bool parseV3(const ByteBuffer& packet); bool parseV3(const ByteBuffer& packet);
bool parseV2(const ByteBuffer& packet); bool parseV2(const ByteBuffer& packet);
ByteBuffer buildV3(); ByteBuffer buildV3();
uint8_t uint8_t mIpProtocolFamily, mIpProtocolId;
mIpProtocolFamily,
mIpProtocolId;
InternetAddress InternetAddress mSourceAddress, mDestinationAddress;
mSourceAddress,
mDestinationAddress;
timeval mTimestamp; timeval mTimestamp;
ProtocolId mProtocolType; ProtocolId mProtocolType;
uint16_t mCaptureAgentId; uint16_t mCaptureAgentId;
uint16_t mKeepAliveTimer; uint16_t mKeepAliveTimer;
ByteBuffer mAuthenticateKey; ByteBuffer mAuthenticateKey;
ByteBuffer mBody; ByteBuffer mBody;
VendorId mVendorId; VendorId mVendorId;
uint32_t mBodyOffset = 0; uint32_t mBodyOffset = 0;
}; };
} } // namespace HEP
#endif #endif
File diff suppressed because it is too large Load Diff
+23 -24
View File
@@ -6,34 +6,33 @@
class IuUP class IuUP
{ {
public: public:
enum class PduType enum class PduType
{ {
DataWithCrc = 0, DataWithCrc = 0,
DataNoCrc = 1, DataNoCrc = 1,
ControlProc = 14 ControlProc = 14
}; };
struct Frame struct Frame
{ {
PduType mPduType; PduType mPduType;
uint8_t mFrameNumber; uint8_t mFrameNumber;
uint8_t mFqc; uint8_t mFqc;
uint8_t mRfci; uint8_t mRfci;
uint8_t mHeaderCrc; uint8_t mHeaderCrc;
bool mHeaderCrcOk; bool mHeaderCrcOk;
uint16_t mPayloadCrc; uint16_t mPayloadCrc;
bool mPayloadCrcOk; bool mPayloadCrcOk;
const uint8_t* mPayload; const uint8_t* mPayload;
uint16_t mPayloadSize; uint16_t mPayloadSize;
}; };
/* Default value is false */ /* Default value is false */
static bool TwoBytePseudoheader; static bool TwoBytePseudoheader;
static bool parse(const uint8_t* packet, int size, Frame& result); static bool parse(const uint8_t* packet, int size, Frame& result);
static bool parse2(const uint8_t* packet, int size, Frame& result); static bool parse2(const uint8_t* packet, int size, Frame& result);
}; };
#endif // HL_IUUP_H #endif // HL_IUUP_H
-1
View File
@@ -2,4 +2,3 @@
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+4 -4
View File
@@ -11,13 +11,13 @@
using ice::GLogger; using ice::GLogger;
using ice::LogLock; using ice::LogLock;
using ice::LL_MEDIA;
using ice::LL_DEBUG;
using ice::LL_INFO;
using ice::LL_CRITICAL; using ice::LL_CRITICAL;
using ice::LL_DEBUG;
using ice::LL_ERROR;
using ice::LL_INFO;
using ice::LL_MEDIA;
using ice::LL_NONE; using ice::LL_NONE;
using ice::LL_SPECIAL; using ice::LL_SPECIAL;
using ice::LL_ERROR;
using ice::LogLevelHelper; using ice::LogLevelHelper;
#endif #endif
+24 -29
View File
@@ -3,10 +3,10 @@
#include "HL_NetworkFrame.h" #include "HL_NetworkFrame.h"
#include "HL_InternetAddress.h" #include "HL_InternetAddress.h"
#define ETHERTYPE_MPLS_UC (0x8847) #define ETHERTYPE_MPLS_UC (0x8847)
#define ETHERTYPE_MPLS_MC (0x8848) #define ETHERTYPE_MPLS_MC (0x8848)
#define ETHERTYPE_IPV6 (0x86dd) #define ETHERTYPE_IPV6 (0x86dd)
#define ETHERTYPE_IP (0x0800) #define ETHERTYPE_IP (0x0800)
#define MPLS_STACK_MASK (0x00000100) #define MPLS_STACK_MASK (0x00000100)
#define MPLS_STACK_SHIFT (8) #define MPLS_STACK_SHIFT (8)
@@ -34,7 +34,7 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForRaw(const Packet& data)
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
{ {
Packet result(data); Packet result(data);
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(data.mData); const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(data.mData);
@@ -53,8 +53,7 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
result.mData += sizeof(VlanHeader); result.mData += sizeof(VlanHeader);
result.mLength -= sizeof(VlanHeader); result.mLength -= sizeof(VlanHeader);
proto = ntohs(vlan->mData); proto = ntohs(vlan->mData);
} } while (proto == 0x8100);
while (proto == 0x8100);
} }
// Skip MPLS headers // Skip MPLS headers
@@ -63,11 +62,11 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
case ETHERTYPE_MPLS_UC: case ETHERTYPE_MPLS_UC:
case ETHERTYPE_MPLS_MC: case ETHERTYPE_MPLS_MC:
// Parse MPLS here until marker "bottom of mpls stack" // Parse MPLS here until marker "bottom of mpls stack"
for(bool bottomOfStack = false; !bottomOfStack; for (bool bottomOfStack = false; !bottomOfStack;
bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0) bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
{ {
result.mData += 4; result.mData += 4;
result.mLength -=4; result.mLength -= 4;
} }
break; break;
@@ -156,7 +155,7 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForLoopback(const Packet& data)
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
{ {
Packet result(data); Packet result(data);
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData); const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(data.mData);
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return Payload(); return Payload();
@@ -187,40 +186,36 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
struct Ip6Header struct Ip6Header
{ {
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t traffic_class_hi:4, uint8_t traffic_class_hi : 4, version : 4;
version:4; uint8_t flow_label_hi : 4, traffic_class_lo : 4;
uint8_t flow_label_hi:4, uint16_t flow_label_lo;
traffic_class_lo:4;
uint16_t flow_label_lo;
#elif __BYTE_ORDER == __BIG_ENDIAN #elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t version:4, uint8_t version : 4, traffic_class_hi : 4;
traffic_class_hi:4; uint8_t traffic_class_lo : 4, flow_label_hi : 4;
uint8_t traffic_class_lo:4, uint16_t flow_label_lo;
flow_label_hi:4;
uint16_t flow_label_lo;
#else #else
# error "Please fix endianness defines" #error "Please fix endianness defines"
#endif #endif
uint16_t payload_len; uint16_t payload_len;
uint8_t next_header; uint8_t next_header;
uint8_t hop_limit; uint8_t hop_limit;
struct in6_addr src_ip; struct in6_addr src_ip;
struct in6_addr dst_ip; struct in6_addr dst_ip;
}; };
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data) NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
{ {
Packet result(data); Packet result(data);
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData); const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData);
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0) /*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
return PacketData(nullptr, 0); return PacketData(nullptr, 0);
*/ */
result.mData += sizeof(Ip6Header); result.mData += sizeof(Ip6Header);
result.mLength -= sizeof(Ip6Header); result.mLength -= sizeof(Ip6Header);
//std::cout << sizeof(Ip6Header) << std::endl; // std::cout << sizeof(Ip6Header) << std::endl;
const UdpHeader* udp = reinterpret_cast<const UdpHeader*>(result.mData); const UdpHeader* udp = reinterpret_cast<const UdpHeader*>(result.mData);
result.mData += sizeof(UdpHeader); result.mData += sizeof(UdpHeader);
+42 -57
View File
@@ -10,25 +10,18 @@ public:
struct Packet struct Packet
{ {
const uint8_t* mData; const uint8_t* mData;
size_t mLength; size_t mLength;
Packet(const uint8_t* data, size_t length) Packet(const uint8_t* data, size_t length) : mData(data), mLength(length) {}
:mData(data), mLength(length)
{}
Packet() Packet() : mData(nullptr), mLength(0) {}
:mData(nullptr), mLength(0)
{}
bool is_empty() const bool is_empty() const { return mData == nullptr || mLength == 0; }
{
return mData == nullptr || mLength == 0;
}
}; };
struct Payload struct Payload
{ {
Packet data; Packet data;
InternetAddress source; InternetAddress source;
InternetAddress dest; InternetAddress dest;
}; };
@@ -44,14 +37,14 @@ public:
{ {
/* Ethernet addresses are 6 bytes */ /* Ethernet addresses are 6 bytes */
static const int AddressLength = 6; static const int AddressLength = 6;
uint8_t mEtherDHost[AddressLength]; /* Destination host address */ uint8_t mEtherDHost[AddressLength]; /* Destination host address */
uint8_t mEtherSHost[AddressLength]; /* Source host address */ uint8_t mEtherSHost[AddressLength]; /* Source host address */
uint16_t mEtherType; /* IP? ARP? RARP? etc */ uint16_t mEtherType; /* IP? ARP? RARP? etc */
}; };
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
struct /*__attribute__((packed))*/ LinuxSllHeader struct /*__attribute__((packed))*/ LinuxSllHeader
#else #else
struct __attribute__((packed)) LinuxSllHeader struct __attribute__((packed)) LinuxSllHeader
#endif #endif
{ {
@@ -70,55 +63,48 @@ public:
struct Ip4Header struct Ip4Header
{ {
uint8_t mVhl; /* version << 4 | header length >> 2 */ uint8_t mVhl; /* version << 4 | header length >> 2 */
uint8_t mTos; /* type of service */ uint8_t mTos; /* type of service */
uint16_t mLen; /* total length */ uint16_t mLen; /* total length */
uint16_t mId; /* identification */ uint16_t mId; /* identification */
uint16_t mOffset; /* fragment offset field */ uint16_t mOffset; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */ #define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */ #define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
uint8_t mTtl; /* time to live */ uint8_t mTtl; /* time to live */
uint8_t mProtocol; /* protocol */ uint8_t mProtocol; /* protocol */
uint16_t mChecksum; /* checksum */ uint16_t mChecksum; /* checksum */
in_addr mSource, in_addr mSource, mDestination; /* source and dest address */
mDestination; /* source and dest address */
int headerLength() const int headerLength() const { return (mVhl & 0x0f) * 4; }
{
return (mVhl & 0x0f) * 4;
}
int version() const int version() const { return mVhl >> 4; }
{
return mVhl >> 4;
}
const in_addr& source4() const { return mSource; } const in_addr& source4() const { return mSource; }
const in_addr& dest4() const { return mDestination; } const in_addr& dest4() const { return mDestination; }
const in6_addr& source6() const { return (const in6_addr&)mSource; } const in6_addr& source6() const { return (const in6_addr&)mSource; }
const in6_addr& dest6() const { return (const in6_addr&)mDestination; } const in6_addr& dest6() const { return (const in6_addr&)mDestination; }
}; };
struct UdpHeader struct UdpHeader
{ {
uint16_t mSourcePort; /* source port */ uint16_t mSourcePort; /* source port */
uint16_t mDestinationPort; uint16_t mDestinationPort;
uint16_t mDatagramLength; /* datagram length */ uint16_t mDatagramLength; /* datagram length */
uint16_t mDatagramChecksum; /* datagram checksum */ uint16_t mDatagramChecksum; /* datagram checksum */
}; };
struct TcpHeader struct TcpHeader
{ {
uint16_t mSourcePort; /* source port */ uint16_t mSourcePort; /* source port */
uint16_t mDestinationPort; /* destination port */ uint16_t mDestinationPort; /* destination port */
uint32_t mSeqNo; /* sequence number */ uint32_t mSeqNo; /* sequence number */
uint32_t mAckNo; /* acknowledgement number */ uint32_t mAckNo; /* acknowledgement number */
uint32_t mDataOffset; /* data offset, rsvd */ uint32_t mDataOffset; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4) #define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
uint8_t mFlags; uint8_t mFlags;
#define TH_FIN 0x01 #define TH_FIN 0x01
#define TH_SYN 0x02 #define TH_SYN 0x02
#define TH_RST 0x04 #define TH_RST 0x04
@@ -127,11 +113,10 @@ public:
#define TH_URG 0x20 #define TH_URG 0x20
#define TH_ECE 0x40 #define TH_ECE 0x40
#define TH_CWR 0x80 #define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) #define TH_FLAGS (TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR)
uint16_t mWindow; /* window */ uint16_t mWindow; /* window */
uint16_t mChecksum; /* checksum */ uint16_t mChecksum; /* checksum */
uint16_t mUrgentPointer; /* urgent pointer */ uint16_t mUrgentPointer; /* urgent pointer */
}; };
}; };
#endif #endif
+27 -22
View File
@@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
# include <asm/ioctls.h> #include <asm/ioctls.h>
#endif #endif
#include "../engine_config.h" #include "../engine_config.h"
@@ -12,19 +12,17 @@
#include "HL_Log.h" #include "HL_Log.h"
#if defined(TARGET_OSX) || defined(TARGET_LINUX) #if defined(TARGET_OSX) || defined(TARGET_LINUX)
# include <fcntl.h> #include <fcntl.h>
#endif #endif
#if !defined(TARGET_WIN) #if !defined(TARGET_WIN)
# include <unistd.h> #include <unistd.h>
#endif #endif
#include <assert.h> #include <assert.h>
#define LOG_SUBSYSTEM "network" #define LOG_SUBSYSTEM "network"
DatagramSocket::DatagramSocket() DatagramSocket::DatagramSocket() : mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0) {}
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
{}
DatagramSocket::~DatagramSocket() DatagramSocket::~DatagramSocket()
{ {
@@ -41,10 +39,12 @@ void DatagramSocket::open(int family)
mHandle = ::socket(mFamily, SOCK_DGRAM, IPPROTO_UDP); mHandle = ::socket(mFamily, SOCK_DGRAM, IPPROTO_UDP);
if (mHandle != INVALID_SOCKET) if (mHandle != INVALID_SOCKET)
{ {
sockaddr_in addr4; sockaddr_in6 addr6; sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4)); memset(&addr6, 0, sizeof(addr6)); sockaddr_in6 addr6;
memset(&addr4, 0, sizeof(addr4));
memset(&addr6, 0, sizeof(addr6));
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6); socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l); int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
if (!retcode) if (!retcode)
{ {
mLocalPort = ntohs(mFamily == AF_INET ? addr4.sin_port : addr6.sin6_port); mLocalPort = ntohs(mFamily == AF_INET ? addr4.sin_port : addr6.sin6_port);
@@ -57,27 +57,34 @@ 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* addr = nullptr; sockaddr* addr = nullptr;
socklen_t addrLen = 0; socklen_t addrLen = 0;
sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}}; sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}};
sockaddr_in6 addr_6 = {AF_INET6, 0, 0, {0}, 0}; sockaddr_in6 addr_6 = {AF_INET6, 0, 0, {0}, 0};
switch (mFamily) switch (mFamily)
{ {
case AF_INET: addr = (sockaddr*)&addr_4; addrLen = sizeof(addr_4); break; case AF_INET:
case AF_INET6: addr = (sockaddr*)&addr_6; addrLen = sizeof(addr_6); break; addr = (sockaddr*)&addr_4;
addrLen = sizeof(addr_4);
break;
case AF_INET6:
addr = (sockaddr*)&addr_6;
addrLen = sizeof(addr_6);
break;
default: default:
assert(0); assert(0);
} }
@@ -131,7 +138,7 @@ bool DatagramSocket::setBlocking(bool blocking)
int flags = fcntl(mHandle, F_GETFL, 0); int flags = fcntl(mHandle, F_GETFL, 0);
if (flags < 0) if (flags < 0)
return false; return false;
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK); flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return (fcntl(mHandle, F_SETFL, flags) == 0) ? true : false; return (fcntl(mHandle, F_SETFL, flags) == 0) ? true : false;
#endif #endif
#if defined(TARGET_ANDROID) #if defined(TARGET_ANDROID)
@@ -152,9 +159,7 @@ DatagramAgreggator::DatagramAgreggator()
mMaxHandle = 0; mMaxHandle = 0;
} }
DatagramAgreggator::~DatagramAgreggator() DatagramAgreggator::~DatagramAgreggator() {}
{
}
void DatagramAgreggator::addSocket(PDatagramSocket socket) void DatagramAgreggator::addSocket(PDatagramSocket socket)
{ {
@@ -196,6 +201,6 @@ bool DatagramAgreggator::waitForData(std::chrono::milliseconds timeout)
tv.tv_sec = timeout.count() / 1000; tv.tv_sec = timeout.count() / 1000;
tv.tv_usec = (timeout.count() % 1000) * 1000; tv.tv_usec = (timeout.count() % 1000) * 1000;
int rescode = ::select(mMaxHandle+1, &mReadSet, nullptr, nullptr, &tv); int rescode = ::select(mMaxHandle + 1, &mReadSet, nullptr, nullptr, &tv);
return rescode > 0; return rescode > 0;
} }
+20 -20
View File
@@ -15,34 +15,34 @@ 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;
@@ -52,18 +52,18 @@ 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(std::chrono::milliseconds timeout); 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
File diff suppressed because it is too large Load Diff
+61 -61
View File
@@ -10,79 +10,79 @@
#include <windows.h> #include <windows.h>
#if defined(USE_MINIDUMP) #if defined(USE_MINIDUMP)
# include <DbgHelp.h> #include <DbgHelp.h>
#endif #endif
int winVersion() int winVersion()
{ {
DWORD dwVersion = 0; DWORD dwVersion = 0;
DWORD dwMajorVersion = 0; DWORD dwMajorVersion = 0;
DWORD dwMinorVersion = 0; DWORD dwMinorVersion = 0;
DWORD dwBuild = 0; DWORD dwBuild = 0;
dwVersion = GetVersion(); dwVersion = GetVersion();
// Get the Windows version. // Get the Windows version.
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
// Get the build number. // Get the build number.
if (dwVersion < 0x80000000) if (dwVersion < 0x80000000)
dwBuild = (DWORD)(HIWORD(dwVersion)); dwBuild = (DWORD)(HIWORD(dwVersion));
if (dwMajorVersion == 5)
return Win_Xp;
if (dwMinorVersion == 1) if (dwMajorVersion == 5)
return Win_Seven; return Win_Xp;
else
return Win_Vista; if (dwMinorVersion == 1)
} return Win_Seven;
else
return Win_Vista;
}
// ----------------- CrashMiniDump ----------------- // ----------------- CrashMiniDump -----------------
#if defined(USE_MINIDUMP) #if defined(USE_MINIDUMP)
static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{ {
// Open the file // Open the file
HANDLE hFile = CreateFile( L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE, HANDLE hFile =
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); CreateFile(L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) ) if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{ {
// Create the minidump // Create the minidump
MINIDUMP_EXCEPTION_INFORMATION mdei; MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId(); mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = ExceptionInfo; mdei.ExceptionPointers = ExceptionInfo;
mdei.ClientPointers = FALSE; mdei.ClientPointers = FALSE;
MINIDUMP_TYPE mdt = MiniDumpWithFullMemory; MINIDUMP_TYPE mdt = MiniDumpWithFullMemory;
BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt,
hFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, 0 ); (ExceptionInfo != 0) ? &mdei : 0, 0, 0);
// Close the file // Close the file
CloseHandle( hFile ); CloseHandle(hFile);
} }
else else
{ {
} }
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
static LPTOP_LEVEL_EXCEPTION_FILTER OldExceptionHandler = nullptr; static LPTOP_LEVEL_EXCEPTION_FILTER OldExceptionHandler = nullptr;
void CrashMiniDump::registerHandler() void CrashMiniDump::registerHandler()
{ {
OldExceptionHandler = ::SetUnhandledExceptionFilter(&MyExceptionHandler); OldExceptionHandler = ::SetUnhandledExceptionFilter(&MyExceptionHandler);
} }
void CrashMiniDump::unregisterHandler() void CrashMiniDump::unregisterHandler()
{ {
::SetUnhandledExceptionFilter(nullptr); ::SetUnhandledExceptionFilter(nullptr);
} }
#endif #endif
@@ -124,29 +124,29 @@ int _kbhit()
ioctl(STDIN, FIONREAD, &bytesWaiting); ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;*/ return bytesWaiting;*/
static const int STDIN_FILENO = 0; static const int STDIN_FILENO = 0;
struct termios oldt, newt; struct termios oldt, newt;
int ch; int ch;
int oldf; int oldf;
tcgetattr(STDIN_FILENO, &oldt); tcgetattr(STDIN_FILENO, &oldt);
newt = oldt; newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO); newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt); tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0); oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar(); ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf); fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF) if (ch != EOF)
{ {
ungetc(ch, stdin); ungetc(ch, stdin);
return 1; return 1;
} }
return 0; return 0;
} }
#endif #endif
+9 -9
View File
@@ -10,11 +10,11 @@
enum enum
{ {
Win_Xp = 0, Win_Xp = 0,
Win_Vista = 1, Win_Vista = 1,
Win_Seven = 2, Win_Seven = 2,
Win_Eight = 3, Win_Eight = 3,
Win_Ten = 4 Win_Ten = 4
}; };
extern int winVersion(); extern int winVersion();
@@ -22,8 +22,8 @@ extern int winVersion();
class CrashMiniDump class CrashMiniDump
{ {
public: public:
static void registerHandler(); static void registerHandler();
static void unregisterHandler(); static void unregisterHandler();
}; };
extern void writeMiniDump(); extern void writeMiniDump();
@@ -41,7 +41,7 @@ int iosVersion();
#include <sys/select.h> #include <sys/select.h>
#include <termios.h> #include <termios.h>
#if defined(TARGET_LINUX) #if defined(TARGET_LINUX)
//# include <stropts.h> // # include <stropts.h>
#endif #endif
extern int _kbhit(); extern int _kbhit();
@@ -49,6 +49,6 @@ extern int _kbhit();
#endif #endif
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
# include <conio.h> #include <conio.h>
#endif #endif
#endif #endif
+27 -29
View File
@@ -5,51 +5,49 @@
#include "HL_Pointer.h" #include "HL_Pointer.h"
UsageCounter::UsageCounter() UsageCounter::UsageCounter() {}
{}
UsageCounter::~UsageCounter() UsageCounter::~UsageCounter() {}
{}
int UsageCounter::obtain(int usageId) int UsageCounter::obtain(int usageId)
{ {
Lock l(mGuard); Lock l(mGuard);
UsageMap::iterator usageIter = mUsage.find(usageId); UsageMap::iterator usageIter = mUsage.find(usageId);
if (usageIter != mUsage.end()) if (usageIter != mUsage.end())
usageIter->second = usageIter->second + 1; usageIter->second = usageIter->second + 1;
else else
mUsage[usageId] = 1; mUsage[usageId] = 1;
return usageCount(); return usageCount();
} }
int UsageCounter::release(int usageId) int UsageCounter::release(int usageId)
{ {
Lock l(mGuard); Lock l(mGuard);
UsageMap::iterator usageIter = mUsage.find(usageId); UsageMap::iterator usageIter = mUsage.find(usageId);
if (usageIter == mUsage.end()) if (usageIter == mUsage.end())
return usageCount(); return usageCount();
usageIter->second = usageIter->second - 1; usageIter->second = usageIter->second - 1;
if (!usageIter->second) if (!usageIter->second)
mUsage.erase(usageIter); mUsage.erase(usageIter);
return usageCount(); return usageCount();
} }
int UsageCounter::usageCount() int UsageCounter::usageCount()
{ {
Lock l(mGuard); Lock l(mGuard);
UsageMap::const_iterator usageIter; UsageMap::const_iterator usageIter;
int result = 0; int result = 0;
for (usageIter = mUsage.begin(); usageIter != mUsage.end(); usageIter++) for (usageIter = mUsage.begin(); usageIter != mUsage.end(); usageIter++)
result += usageIter->second; result += usageIter->second;
return result; return result;
} }
void UsageCounter::clear() void UsageCounter::clear()
{ {
Lock l(mGuard); Lock l(mGuard);
mUsage.clear(); mUsage.clear();
} }
+9 -9
View File
@@ -13,17 +13,17 @@
class UsageCounter class UsageCounter
{ {
public: public:
UsageCounter(); UsageCounter();
~UsageCounter(); ~UsageCounter();
int obtain(int usageId); int obtain(int usageId);
int release(int usageId); int release(int usageId);
int usageCount(); int usageCount();
void clear(); void clear();
protected: protected:
typedef std::map<int, int> UsageMap; typedef std::map<int, int> UsageMap;
UsageMap mUsage; UsageMap mUsage;
Mutex mGuard; Mutex mGuard;
}; };
#endif #endif
+327 -300
View File
@@ -19,354 +19,381 @@
#include <vector> #include <vector>
#ifndef HL_RTP_POOL #ifndef HL_RTP_POOL
# define HL_RTP_POOL 1 #define HL_RTP_POOL 1
#endif #endif
namespace hl namespace hl
{ {
#if HL_RTP_POOL #if HL_RTP_POOL
/// @class FixedBlockPool /// @class FixedBlockPool
/// A process-wide, fixed-block pool with a lock-free thread-local fast path: uniform 256-byte /// A process-wide, fixed-block pool with a lock-free thread-local fast path: uniform 256-byte
/// blocks carved from 64 KB chunks, an intrusive thread-local free list, and a per-block header /// blocks carved from 64 KB chunks, an intrusive thread-local free list, and a per-block header
/// tag so deallocate() is O(1) and lock-free for any block (and can tell pooled blocks from the /// tag so deallocate() is O(1) and lock-free for any block (and can tell pooled blocks from the
/// global-allocator fallback used for oversized requests) regardless of the freeing thread. /// global-allocator fallback used for oversized requests) regardless of the freeing thread.
/// ///
/// CROSS-THREAD RECLAIM (2026-07-26) — fixes an unbounded capacity ratchet. A uniform block size /// CROSS-THREAD RECLAIM (2026-07-26) — fixes an unbounded capacity ratchet. A uniform block size
/// makes a block allocated on one thread safe to free on another, but "safe" is not "reclaimed". /// makes a block allocated on one thread safe to free on another, but "safe" is not "reclaimed".
/// With a purely thread-local free list the block joins the FREEING thread's list, so a /// With a purely thread-local free list the block joins the FREEING thread's list, so a
/// producer/consumer split strands it: vq-core allocates RTPPacket / jitter-buffer nodes on the /// producer/consumer split strands it: vq-core allocates RTPPacket / jitter-buffer nodes on the
/// capture threads and frees them on the interval/reap side, so the capture threads' lists were /// capture threads and frees them on the interval/reap side, so the capture threads' lists were
/// perpetually empty and refill() carved a fresh chunk every BlocksPerChunk allocations, forever. /// perpetually empty and refill() carved a fresh chunk every BlocksPerChunk allocations, forever.
/// Observed on vqmonitor (the only box with >1 capture thread): capacityBlocks() 256 -> 11.8M /// Observed on vqmonitor (the only box with >1 capture thread): capacityBlocks() 256 -> 11.8M
/// = 2.7 GiB over 4 days at ~25 MiB/h, while activeBlocks() drained to 0 every idle interval. /// = 2.7 GiB over 4 days at ~25 MiB/h, while activeBlocks() drained to 0 every idle interval.
/// ///
/// The fix is a process-wide depot of free-block batches. A thread whose free list grows past /// The fix is a process-wide depot of free-block batches. A thread whose free list grows past
/// ThreadCacheHighWater hands a batch down to the depot; a thread that runs dry takes a batch /// ThreadCacheHighWater hands a batch down to the depot; a thread that runs dry takes a batch
/// from the depot before carving a new chunk; a thread flushes its remainder to the depot on /// from the depot before carving a new chunk; a thread flushes its remainder to the depot on
/// exit. The depot is touched once per BlocksPerChunk operations and the hysteresis between /// exit. The depot is touched once per BlocksPerChunk operations and the hysteresis between
/// "empty" and the high-water keeps it off the per-packet path, so the fast path stays lock-free. /// "empty" and the high-water keeps it off the per-packet path, so the fast path stays lock-free.
/// capacityBlocks() now plateaus at the true concurrent high-water instead of ratcheting. /// capacityBlocks() now plateaus at the true concurrent high-water instead of ratcheting.
class FixedBlockPool class FixedBlockPool
{ {
public: public:
/// Usable bytes handed back to the caller from a pooled block. Comfortably covers the /// Usable bytes handed back to the caller from a pooled block. Comfortably covers the
/// shared_ptr nodes we pool (control block + RTPPacket / RtpBuffer::Packet, ~90-130 bytes). /// shared_ptr nodes we pool (control block + RTPPacket / RtpBuffer::Packet, ~90-130 bytes).
static constexpr std::size_t PayloadSize = 240; static constexpr std::size_t PayloadSize = 240;
static constexpr std::size_t BlocksPerChunk = 256; static constexpr std::size_t BlocksPerChunk = 256;
/// Free blocks a thread keeps to itself before handing a BlocksPerChunk batch to the depot. /// Free blocks a thread keeps to itself before handing a BlocksPerChunk batch to the depot.
/// Must exceed BlocksPerChunk so a thread that churns around the boundary does not bounce /// Must exceed BlocksPerChunk so a thread that churns around the boundary does not bounce
/// on the depot mutex; 2x leaves a full batch in hand after every release. /// on the depot mutex; 2x leaves a full batch in hand after every release.
static constexpr std::size_t ThreadCacheHighWater = 2 * BlocksPerChunk; static constexpr std::size_t ThreadCacheHighWater = 2 * BlocksPerChunk;
static void* allocate(std::size_t size) static void* allocate(std::size_t size)
{ {
if (size > PayloadSize) if (size > PayloadSize)
{ {
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize)); uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
tagOf(raw) = TagGlobal; tagOf(raw) = TagGlobal;
s_activeGlobal.fetch_add(1, std::memory_order_relaxed); s_activeGlobal.fetch_add(1, std::memory_order_relaxed);
return raw + HeaderSize; return raw + HeaderSize;
} }
ThreadCache& tc = cache(); ThreadCache& tc = cache();
if (tc.head == nullptr) if (tc.head == nullptr)
{ {
// Reclaim from the depot before asking the OS for more memory. Only when the // Reclaim from the depot before asking the OS for more memory. Only when the
// depot is dry as well has the pool genuinely outgrown its current capacity. // depot is dry as well has the pool genuinely outgrown its current capacity.
std::size_t taken = 0; std::size_t taken = 0;
tc.head = depot().takeBatch(taken); tc.head = depot().takeBatch(taken);
if (tc.head == nullptr) if (tc.head == nullptr)
{ {
tc.head = registry().refill(); tc.head = registry().refill();
taken = BlocksPerChunk; taken = BlocksPerChunk;
} }
tc.count = taken; tc.count = taken;
} }
uint8_t* block = static_cast<uint8_t*>(tc.head); uint8_t* block = static_cast<uint8_t*>(tc.head);
tc.head = nextOf(block); tc.head = nextOf(block);
--tc.count; --tc.count;
s_activeBlocks.fetch_add(1, std::memory_order_relaxed); s_activeBlocks.fetch_add(1, std::memory_order_relaxed);
return block + HeaderSize; return block + HeaderSize;
} }
static void deallocate(void* ptr) noexcept static void deallocate(void* ptr) noexcept
{ {
if (ptr == nullptr) if (ptr == nullptr)
return; return;
uint8_t* block = static_cast<uint8_t*>(ptr) - HeaderSize; uint8_t* block = static_cast<uint8_t*>(ptr) - HeaderSize;
if (tagOf(block) == TagPool) if (tagOf(block) == TagPool)
{ {
ThreadCache& tc = cache(); ThreadCache& tc = cache();
nextOf(block) = tc.head; nextOf(block) = tc.head;
tc.head = block; tc.head = block;
++tc.count; ++tc.count;
s_activeBlocks.fetch_sub(1, std::memory_order_relaxed); s_activeBlocks.fetch_sub(1, std::memory_order_relaxed);
// The one line that closes the ratchet: surplus goes back to the depot, where // The one line that closes the ratchet: surplus goes back to the depot, where
// the thread that actually allocates can reach it. // the thread that actually allocates can reach it.
if (tc.count >= ThreadCacheHighWater) if (tc.count >= ThreadCacheHighWater)
releaseBatch(tc, BlocksPerChunk); releaseBatch(tc, BlocksPerChunk);
} }
else else
{ {
::operator delete(static_cast<void*>(block)); ::operator delete(static_cast<void*>(block));
s_activeGlobal.fetch_sub(1, std::memory_order_relaxed); s_activeGlobal.fetch_sub(1, std::memory_order_relaxed);
} }
} }
/// @name Diagnostics (relaxed gauges — not synchronization) /// @name Diagnostics (relaxed gauges — not synchronization)
/// @{ /// @{
/// Blocks currently handed out from the pool = pooled allocate() minus /// Blocks currently handed out from the pool = pooled allocate() minus
/// pooled deallocate(). Unlike capacityBlocks() (the chunk high-water, /// pooled deallocate(). Unlike capacityBlocks() (the chunk high-water,
/// which only ever grows) this DROPS when objects are freed, so a rising /// which only ever grows) this DROPS when objects are freed, so a rising
/// activeBlocks() is a genuine leak of referenced objects rather than a /// activeBlocks() is a genuine leak of referenced objects rather than a
/// traffic peak that merely carved extra chunks. Covers every pooled /// traffic peak that merely carved extra chunks. Covers every pooled
/// shared_ptr node (allocate_shared<RTPPacket> + jitter-buffer packets). /// shared_ptr node (allocate_shared<RTPPacket> + jitter-buffer packets).
static std::int64_t activeBlocks() noexcept static std::int64_t activeBlocks() noexcept { return s_activeBlocks.load(std::memory_order_relaxed); }
{ /// Live oversized allocations that overflowed to ::operator new.
return s_activeBlocks.load(std::memory_order_relaxed); static std::int64_t activeGlobalAllocations() noexcept { return s_activeGlobal.load(std::memory_order_relaxed); }
} /// Total blocks ever carved (chunks x BlocksPerChunk) = high-water capacity.
/// Live oversized allocations that overflowed to ::operator new. /// With cross-thread reclaim this PLATEAUS once the pool has covered the
static std::int64_t activeGlobalAllocations() noexcept /// concurrent high-water; sustained linear growth means reclaim is not working.
{ static std::int64_t capacityBlocks() noexcept
return s_activeGlobal.load(std::memory_order_relaxed); {
} return s_chunks.load(std::memory_order_relaxed) * static_cast<std::int64_t>(BlocksPerChunk);
/// Total blocks ever carved (chunks x BlocksPerChunk) = high-water capacity. }
/// With cross-thread reclaim this PLATEAUS once the pool has covered the /// Free blocks parked in the process-wide depot, i.e. reclaimed from a freeing
/// concurrent high-water; sustained linear growth means reclaim is not working. /// thread and available to any allocating thread. Healthy steady state is a
static std::int64_t capacityBlocks() noexcept /// non-zero, bounded value: it is the surplus that used to be stranded.
{ static std::int64_t depotBlocks() noexcept { return s_depotBlocks.load(std::memory_order_relaxed); }
return s_chunks.load(std::memory_order_relaxed) * static_cast<std::int64_t>(BlocksPerChunk); /// Approximate live pooled payload bytes (excludes per-block header).
} static std::int64_t activeBytes() noexcept { return activeBlocks() * static_cast<std::int64_t>(PayloadSize); }
/// Free blocks parked in the process-wide depot, i.e. reclaimed from a freeing /// @}
/// thread and available to any allocating thread. Healthy steady state is a
/// non-zero, bounded value: it is the surplus that used to be stranded.
static std::int64_t depotBlocks() noexcept
{
return s_depotBlocks.load(std::memory_order_relaxed);
}
/// Approximate live pooled payload bytes (excludes per-block header).
static std::int64_t activeBytes() noexcept
{
return activeBlocks() * static_cast<std::int64_t>(PayloadSize);
}
/// @}
private: private:
inline static std::atomic<std::int64_t> s_activeBlocks{0}; inline static std::atomic<std::int64_t> s_activeBlocks{0};
inline static std::atomic<std::int64_t> s_activeGlobal{0}; inline static std::atomic<std::int64_t> s_activeGlobal{0};
inline static std::atomic<std::int64_t> s_chunks{0}; inline static std::atomic<std::int64_t> s_chunks{0};
inline static std::atomic<std::int64_t> s_depotBlocks{0}; inline static std::atomic<std::int64_t> s_depotBlocks{0};
static constexpr std::size_t HeaderSize = static constexpr std::size_t HeaderSize =
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t); alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize; static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
static constexpr uint64_t TagPool = 0x504F4F4C52545008ULL; // "POOLRTP\b" static constexpr uint64_t TagPool = 0x504F4F4C52545008ULL; // "POOLRTP\b"
static constexpr uint64_t TagGlobal = 0x474C4F42524C0808ULL; // "GLOBRL\b\b" static constexpr uint64_t TagGlobal = 0x474C4F42524C0808ULL; // "GLOBRL\b\b"
/// A free block's payload is dead space, so the list links live there. Slot 0 chains /// A free block's payload is dead space, so the list links live there. Slot 0 chains
/// blocks within a list/batch; slots 1-2 are used only on a batch's head block, to chain /// blocks within a list/batch; slots 1-2 are used only on a batch's head block, to chain
/// batches inside the depot and to carry the batch length (batches from a thread-exit /// batches inside the depot and to carry the batch length (batches from a thread-exit
/// flush are shorter than BlocksPerChunk). /// flush are shorter than BlocksPerChunk).
static_assert(PayloadSize >= 2 * sizeof(void*) + sizeof(std::size_t), static_assert(PayloadSize >= 2 * sizeof(void*) + sizeof(std::size_t),
"pooled block payload must hold the free-list, depot and length slots"); "pooled block payload must hold the free-list, depot and length slots");
static uint64_t& tagOf(void* block) noexcept static uint64_t& tagOf(void* block) noexcept { return *reinterpret_cast<uint64_t*>(block); }
{
return *reinterpret_cast<uint64_t*>(block);
}
static void*& nextOf(void* block) noexcept static void*& nextOf(void* block) noexcept
{ {
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize); return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize);
} }
static void*& batchNextOf(void* block) noexcept static void*& batchNextOf(void* block) noexcept
{ {
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize + sizeof(void*)); return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize + sizeof(void*));
} }
static std::size_t& batchLenOf(void* block) noexcept static std::size_t& batchLenOf(void* block) noexcept
{ {
return *reinterpret_cast<std::size_t*>(static_cast<uint8_t*>(block) + HeaderSize + 2 * sizeof(void*)); return *reinterpret_cast<std::size_t*>(static_cast<uint8_t*>(block) + HeaderSize + 2 * sizeof(void*));
} }
/// Per-thread free list. The destructor hands the remainder to the depot so a thread that /// Per-thread free list. The destructor hands the remainder to the depot so a thread that
/// exits does not strand its blocks — the same ratchet as the cross-thread case, just /// exits does not strand its blocks — the same ratchet as the cross-thread case, just
/// triggered once per thread instead of continuously. /// triggered once per thread instead of continuously.
struct ThreadCache struct ThreadCache
{ {
void* head = nullptr; void* head = nullptr;
std::size_t count = 0; std::size_t count = 0;
~ThreadCache() ~ThreadCache()
{ {
while (count > 0 && head != nullptr) while (count > 0 && head != nullptr)
releaseBatch(*this, count); releaseBatch(*this, count);
} }
}; };
static ThreadCache& cache() noexcept static ThreadCache& cache() noexcept
{ {
static thread_local ThreadCache tc; static thread_local ThreadCache tc;
return tc; return tc;
} }
/// Detach the first `blocks` entries of the thread list and park them in the depot. /// Detach the first `blocks` entries of the thread list and park them in the depot.
static void releaseBatch(ThreadCache& tc, std::size_t blocks) noexcept static void releaseBatch(ThreadCache& tc, std::size_t blocks) noexcept
{ {
if (blocks == 0 || tc.head == nullptr) if (blocks == 0 || tc.head == nullptr)
return; return;
void* batchHead = tc.head; void* batchHead = tc.head;
void* batchTail = batchHead; void* batchTail = batchHead;
std::size_t length = 1; std::size_t length = 1;
for (; length < blocks && nextOf(batchTail) != nullptr; ++length) for (; length < blocks && nextOf(batchTail) != nullptr; ++length)
batchTail = nextOf(batchTail); batchTail = nextOf(batchTail);
tc.head = nextOf(batchTail); tc.head = nextOf(batchTail);
tc.count -= length; tc.count -= length;
nextOf(batchTail) = nullptr; nextOf(batchTail) = nullptr;
batchLenOf(batchHead) = length; batchLenOf(batchHead) = length;
depot().giveBatch(batchHead, length); depot().giveBatch(batchHead, length);
} }
/// Process-wide store of reclaimed batches. Intrusive (the links live in the free blocks /// Process-wide store of reclaimed batches. Intrusive (the links live in the free blocks
/// themselves) so neither path can allocate or throw — deallocate() is noexcept. The mutex /// themselves) so neither path can allocate or throw — deallocate() is noexcept. The mutex
/// is taken once per BlocksPerChunk operations, not per packet. /// is taken once per BlocksPerChunk operations, not per packet.
class Depot class Depot
{ {
public: public:
void* takeBatch(std::size_t& lengthOut) noexcept void* takeBatch(std::size_t& lengthOut) noexcept
{ {
std::lock_guard<std::mutex> lock(m_Mutex); std::lock_guard<std::mutex> lock(m_Mutex);
void* batch = m_Head; void* batch = m_Head;
if (batch == nullptr) if (batch == nullptr)
{ {
lengthOut = 0; lengthOut = 0;
return nullptr; return nullptr;
} }
m_Head = batchNextOf(batch); m_Head = batchNextOf(batch);
lengthOut = batchLenOf(batch); lengthOut = batchLenOf(batch);
batchNextOf(batch) = nullptr; batchNextOf(batch) = nullptr;
s_depotBlocks.fetch_sub(static_cast<std::int64_t>(lengthOut), std::memory_order_relaxed); s_depotBlocks.fetch_sub(static_cast<std::int64_t>(lengthOut), std::memory_order_relaxed);
return batch; return batch;
} }
void giveBatch(void* batch, std::size_t length) noexcept void giveBatch(void* batch, std::size_t length) noexcept
{ {
std::lock_guard<std::mutex> lock(m_Mutex); std::lock_guard<std::mutex> lock(m_Mutex);
batchNextOf(batch) = m_Head; batchNextOf(batch) = m_Head;
m_Head = batch; m_Head = batch;
s_depotBlocks.fetch_add(static_cast<std::int64_t>(length), std::memory_order_relaxed); s_depotBlocks.fetch_add(static_cast<std::int64_t>(length), std::memory_order_relaxed);
} }
private: private:
std::mutex m_Mutex; std::mutex m_Mutex;
void* m_Head = nullptr; void* m_Head = nullptr;
}; };
class ChunkRegistry class ChunkRegistry
{ {
public: public:
void* refill() void* refill()
{ {
const std::size_t chunkBytes = BlockSize * BlocksPerChunk; const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes)); uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
s_chunks.fetch_add(1, std::memory_order_relaxed); s_chunks.fetch_add(1, std::memory_order_relaxed);
{ {
std::lock_guard<std::mutex> lock(m_Mutex); std::lock_guard<std::mutex> lock(m_Mutex);
m_Chunks.push_back(chunk); m_Chunks.push_back(chunk);
} }
void* list = nullptr; void* list = nullptr;
for (std::size_t i = 0; i < BlocksPerChunk; ++i) for (std::size_t i = 0; i < BlocksPerChunk; ++i)
{ {
uint8_t* block = chunk + i * BlockSize; uint8_t* block = chunk + i * BlockSize;
tagOf(block) = TagPool; tagOf(block) = TagPool;
nextOf(block) = list; nextOf(block) = list;
list = block; list = block;
} }
return list; return list;
} }
private: private:
std::mutex m_Mutex; std::mutex m_Mutex;
std::vector<uint8_t*> m_Chunks; std::vector<uint8_t*> m_Chunks;
}; };
/// Both singletons are deliberately immortal (leaked at exit, reclaimed by the OS). /// Both singletons are deliberately immortal (leaked at exit, reclaimed by the OS).
/// A thread_local ThreadCache is destroyed at thread exit — for the main thread that is /// A thread_local ThreadCache is destroyed at thread exit — for the main thread that is
/// during static destruction — and its destructor touches the depot, so a depot with a /// during static destruction — and its destructor touches the depot, so a depot with a
/// destructor could be used after being destroyed. The same applies to the chunk memory: /// destructor could be used after being destroyed. The same applies to the chunk memory:
/// any pooled object outliving the registry would free into deleted chunks. Never /// any pooled object outliving the registry would free into deleted chunks. Never
/// destroying them removes both hazards; the process is exiting either way. /// destroying them removes both hazards; the process is exiting either way.
static Depot& depot() noexcept static Depot& depot() noexcept
{ {
static Depot* instance = new Depot(); static Depot* instance = new Depot();
return *instance; return *instance;
} }
static ChunkRegistry& registry() static ChunkRegistry& registry()
{ {
static ChunkRegistry* instance = new ChunkRegistry(); static ChunkRegistry* instance = new ChunkRegistry();
return *instance; return *instance;
} }
}; };
#endif // HL_RTP_POOL #endif // HL_RTP_POOL
/// Mode-independent accessors for the pool diagnostics, so callers need not know /// Mode-independent accessors for the pool diagnostics, so callers need not know
/// whether HL_RTP_POOL is compiled in. When pooling is disabled they return -1 /// whether HL_RTP_POOL is compiled in. When pooling is disabled they return -1
/// ("not applicable") since allocate_shared then behaves like make_shared. /// ("not applicable") since allocate_shared then behaves like make_shared.
#if HL_RTP_POOL #if HL_RTP_POOL
inline std::int64_t poolActiveBlocks() noexcept { return FixedBlockPool::activeBlocks(); } inline std::int64_t poolActiveBlocks() noexcept
inline std::int64_t poolCapacityBlocks() noexcept { return FixedBlockPool::capacityBlocks(); } {
inline std::int64_t poolDepotBlocks() noexcept { return FixedBlockPool::depotBlocks(); } return FixedBlockPool::activeBlocks();
inline std::int64_t poolActiveGlobal() noexcept { return FixedBlockPool::activeGlobalAllocations(); } }
inline std::int64_t poolActiveBytes() noexcept { return FixedBlockPool::activeBytes(); } inline std::int64_t poolCapacityBlocks() noexcept
{
return FixedBlockPool::capacityBlocks();
}
inline std::int64_t poolDepotBlocks() noexcept
{
return FixedBlockPool::depotBlocks();
}
inline std::int64_t poolActiveGlobal() noexcept
{
return FixedBlockPool::activeGlobalAllocations();
}
inline std::int64_t poolActiveBytes() noexcept
{
return FixedBlockPool::activeBytes();
}
#else #else
inline std::int64_t poolActiveBlocks() noexcept { return -1; } inline std::int64_t poolActiveBlocks() noexcept
inline std::int64_t poolCapacityBlocks() noexcept { return -1; } {
inline std::int64_t poolDepotBlocks() noexcept { return -1; } return -1;
inline std::int64_t poolActiveGlobal() noexcept { return -1; } }
inline std::int64_t poolActiveBytes() noexcept { return -1; } inline std::int64_t poolCapacityBlocks() noexcept
{
return -1;
}
inline std::int64_t poolDepotBlocks() noexcept
{
return -1;
}
inline std::int64_t poolActiveGlobal() noexcept
{
return -1;
}
inline std::int64_t poolActiveBytes() noexcept
{
return -1;
}
#endif #endif
/// @class PoolAllocator /// @class PoolAllocator
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is /// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request /// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
/// that does not fit a pooled block) it delegates to the global allocator, matching make_shared. /// that does not fit a pooled block) it delegates to the global allocator, matching make_shared.
template <class T> struct PoolAllocator template<class T>
{ struct PoolAllocator
using value_type = T; {
using value_type = T;
PoolAllocator() noexcept = default; PoolAllocator() noexcept = default;
template <class U> PoolAllocator(const PoolAllocator<U>&) noexcept {} template<class U>
PoolAllocator(const PoolAllocator<U>&) noexcept
{
}
T* allocate(std::size_t n) T* allocate(std::size_t n)
{ {
#if HL_RTP_POOL #if HL_RTP_POOL
return static_cast<T*>(FixedBlockPool::allocate(n * sizeof(T))); return static_cast<T*>(FixedBlockPool::allocate(n * sizeof(T)));
#else #else
return static_cast<T*>(::operator new(n * sizeof(T))); return static_cast<T*>(::operator new(n * sizeof(T)));
#endif #endif
} }
void deallocate(T* p, std::size_t /*n*/) noexcept void deallocate(T* p, std::size_t /*n*/) noexcept
{ {
#if HL_RTP_POOL #if HL_RTP_POOL
FixedBlockPool::deallocate(p); FixedBlockPool::deallocate(p);
#else #else
::operator delete(static_cast<void*>(p)); ::operator delete(static_cast<void*>(p));
#endif #endif
} }
template <class U> bool operator==(const PoolAllocator<U>&) const noexcept { return true; } template<class U>
template <class U> bool operator!=(const PoolAllocator<U>&) const noexcept { return false; } bool operator==(const PoolAllocator<U>&) const noexcept
}; {
} // namespace hl return true;
}
template<class U>
bool operator!=(const PoolAllocator<U>&) const noexcept
{
return false;
}
};
} // namespace hl
+172 -170
View File
@@ -4,8 +4,8 @@
#include <algorithm> #include <algorithm>
#ifdef TARGET_WIN #ifdef TARGET_WIN
# define popen _popen #define popen _popen
# define pclose _pclose #define pclose _pclose
#endif #endif
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
@@ -19,46 +19,46 @@ int OsProcess::execSystem(const std::string& cmd)
std::string OsProcess::execCommand(const std::string& cmd) std::string OsProcess::execCommand(const std::string& cmd)
{ {
std::string output; std::string output;
HANDLE hPipeRead, hPipeWrite; HANDLE hPipeRead, hPipeWrite;
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) }; SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process. saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor = NULL; saAttr.lpSecurityDescriptor = NULL;
// Create a pipe to get results from child's stdout. // Create a pipe to get results from child's stdout.
if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) ) if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
return output; return output;
STARTUPINFOA si = { sizeof(STARTUPINFOA) }; STARTUPINFOA si = {sizeof(STARTUPINFOA)};
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite; si.hStdOutput = hPipeWrite;
si.hStdError = hPipeWrite; si.hStdError = hPipeWrite;
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags. si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi = { 0 }; PROCESS_INFORMATION pi = {0};
char* cmdline = (char*)_alloca(cmd.size()+1); char* cmdline = (char*)_alloca(cmd.size() + 1);
strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str()); strcpy(cmdline, strx::replace(cmd, "/", "\\").c_str());
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); BOOL fSuccess = CreateProcessA(nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (! fSuccess) if (!fSuccess)
{ {
CloseHandle( hPipeWrite ); CloseHandle(hPipeWrite);
CloseHandle( hPipeRead ); CloseHandle(hPipeRead);
return output; return output;
} }
bool bProcessEnded = false; bool bProcessEnded = false;
for (; !bProcessEnded ;) for (; !bProcessEnded;)
{ {
// Give some timeslice (50ms), so we won't waste 100% cpu. // Give some timeslice (50ms), so we won't waste 100% cpu.
bProcessEnded = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0; bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
// Even if process exited - we continue reading, if there is some data available over pipe. // Even if process exited - we continue reading, if there is some data available over pipe.
for (;;) for (;;)
{ {
char buf[1024]; char buf[1024];
DWORD dwRead = 0; DWORD dwRead = 0;
DWORD dwAvail = 0; DWORD dwAvail = 0;
@@ -75,119 +75,122 @@ std::string OsProcess::execCommand(const std::string& cmd)
buf[dwRead] = 0; buf[dwRead] = 0;
output += buf; output += buf;
} }
} //for } // for
CloseHandle( hPipeWrite ); CloseHandle(hPipeWrite);
CloseHandle( hPipeRead ); CloseHandle(hPipeRead);
CloseHandle( pi.hProcess ); CloseHandle(pi.hProcess);
CloseHandle( pi.hThread ); CloseHandle(pi.hThread);
return output; return output;
} }
std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline, std::shared_ptr<std::thread>
std::function<void(const std::string& line)> callback, OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> callback,
std::function<void(const std::string& reason)> finished_callback, std::function<void(const std::string& reason)> finished_callback, bool& finish_flag)
bool& finish_flag)
{ {
// std::cout << cmdline << std::endl; // std::cout << cmdline << std::endl;
std::string output; std::string output;
HANDLE hPipeRead, hPipeWrite; HANDLE hPipeRead, hPipeWrite;
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE }; SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE};
saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process. saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor = nullptr; saAttr.lpSecurityDescriptor = nullptr;
// Create a pipe to get results from child's stdout. // Create a pipe to get results from child's stdout.
if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) ) if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
return std::shared_ptr<std::thread>(); return std::shared_ptr<std::thread>();
STARTUPINFOA si; memset(&si, 0, sizeof si); STARTUPINFOA si;
memset(&si, 0, sizeof si);
si.cb = sizeof(STARTUPINFOA); si.cb = sizeof(STARTUPINFOA);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite; si.hStdOutput = hPipeWrite;
si.hStdError = hPipeWrite; si.hStdError = hPipeWrite;
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags. si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof pi); memset(&pi, 0, sizeof pi);
char* cmdbuffer = (char*)_alloca(cmdline.size()+1); char* cmdbuffer = (char*)_alloca(cmdline.size() + 1);
strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str()); strcpy(cmdbuffer, strx::replace(cmdline, "/", "\\").c_str());
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE, BOOL fSuccess =
CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi); CreateProcessA(nullptr, cmdbuffer, nullptr, nullptr, TRUE, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi);
if (! fSuccess) if (!fSuccess)
{ {
CloseHandle( hPipeWrite ); CloseHandle(hPipeWrite);
CloseHandle( hPipeRead ); CloseHandle(hPipeRead);
return std::shared_ptr<std::thread>(); return std::shared_ptr<std::thread>();
} }
std::shared_ptr<std::thread> r = std::make_shared<std::thread>( std::shared_ptr<std::thread> r = std::make_shared<std::thread>(
[&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]() [&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]()
{
char buf[4096]; memset(buf, 0, sizeof buf);
for (; !finish_flag ;)
{ {
// Give some timeslice (50ms), so we won't waste 100% cpu. char buf[4096];
bool timeouted = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0; memset(buf, 0, sizeof buf);
for (; !finish_flag;)
// Even if process exited - we continue reading, if there is some data available over pipe.
for (;;)
{ {
DWORD dwRead = 0; // Give some timeslice (50ms), so we won't waste 100% cpu.
DWORD dwAvail = 0; bool timeouted = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
if (!::PeekNamedPipe(hPipeRead, nullptr, 0, nullptr, &dwAvail, nullptr)) // Even if process exited - we continue reading, if there is some data available over pipe.
break; for (;;)
if (!dwAvail) // no data available, return
break;
int filled = strlen(buf);
if (!::ReadFile(hPipeRead, buf + filled, std::min(sizeof(buf) - 1 - filled, (size_t)dwAvail), &dwRead, nullptr) || !dwRead)
// error, the child process might ended
break;
buf[dwRead] = 0;
// Split to lines and send to callback
const char* cr;
while ((cr = strchr(buf, '\n')) != nullptr)
{ {
std::string line(buf, cr - buf -1); DWORD dwRead = 0;
if (callback) DWORD dwAvail = 0;
callback(strx::trim(line));
memmove(buf, cr + 1, strlen(cr+1) + 1); if (!::PeekNamedPipe(hPipeRead, nullptr, 0, nullptr, &dwAvail, nullptr))
break;
if (!dwAvail) // no data available, return
break;
int filled = strlen(buf);
if (!::ReadFile(hPipeRead, buf + filled, std::min(sizeof(buf) - 1 - filled, (size_t)dwAvail),
&dwRead, nullptr) ||
!dwRead)
// error, the child process might ended
break;
buf[dwRead] = 0;
// Split to lines and send to callback
const char* cr;
while ((cr = strchr(buf, '\n')) != nullptr)
{
std::string line(buf, cr - buf - 1);
if (callback)
callback(strx::trim(line));
memmove(buf, cr + 1, strlen(cr + 1) + 1);
}
} }
} // for
if (buf[0])
callback(strx::trim(std::string(buf)));
char ctrlc = 3;
// if (finish_flag)
// ::WriteFile(hPipeWrite, &ctrlc, 1, nullptr, nullptr);
// GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId);
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
if (finish_flag)
{
// GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
// Close underlying process
// TerminateProcess(pi.hProcess, 3);
} }
} //for CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (buf[0]) if (finished_callback)
callback(strx::trim(std::string(buf))); finished_callback(std::string());
});
char ctrlc = 3;
//if (finish_flag)
// ::WriteFile(hPipeWrite, &ctrlc, 1, nullptr, nullptr);
// GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId);
CloseHandle( hPipeWrite );
CloseHandle( hPipeRead );
if (finish_flag)
{
//GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
// Close underlying process
//TerminateProcess(pi.hProcess, 3);
}
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
if (finished_callback)
finished_callback(std::string());
});
return r; return r;
} }
@@ -201,12 +204,12 @@ std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdl
std::string OsProcess::execCommand(const std::string& cmd) std::string OsProcess::execCommand(const std::string& cmd)
{ {
std::string cp = cmd; std::string cp = cmd;
std::shared_ptr<FILE> pipe(popen(cp.c_str(), "r"), pclose); std::shared_ptr<FILE> pipe(popen(cp.c_str(), "r"), pclose);
if (!pipe) if (!pipe)
throw std::runtime_error("Failed to run."); throw std::runtime_error("Failed to run.");
char buffer[1024]; char buffer[1024];
std::string result = ""; std::string result = "";
while (!feof(pipe.get())) while (!feof(pipe.get()))
{ {
@@ -229,84 +232,83 @@ int OsProcess::execSystem(const std::string& cmd)
#include "helper/HL_String.h" #include "helper/HL_String.h"
#include "helper/HL_Sync.h" #include "helper/HL_Sync.h"
std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline, std::shared_ptr<std::thread>
std::function<void(const std::string& line)> line_callback, OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback,
std::function<void(const std::string& reason)> finished_callback, std::function<void(const std::string& reason)> finished_callback, bool& finish_flag)
bool& finish_flag)
{ {
std::shared_ptr<std::thread> t = std::make_shared<std::thread>([cmdline, line_callback, finished_callback, &finish_flag]() std::shared_ptr<std::thread> t = std::make_shared<std::thread>(
{ [cmdline, line_callback, finished_callback, &finish_flag]()
ThreadHelper::setName("OsProcess::asyncExecCommand");
std::string cp = cmdline;
FILE* pipe = popen(cp.c_str(), "r");
if (!pipe)
{ {
if (finished_callback) ThreadHelper::setName("OsProcess::asyncExecCommand");
finished_callback("Failed to open pipe"); std::string cp = cmdline;
return; FILE* pipe = popen(cp.c_str(), "r");
} if (!pipe)
char buffer[1024];
std::string lines;
std::string result = "";
int fno = fileno(pipe);
// Make it non blocking
fcntl(fno, F_SETFL, O_NONBLOCK);
while (!feof(pipe) && !finish_flag)
{
// Wait for more data
struct pollfd pfd{ .fd = fno, .events = POLLIN };
while (poll(&pfd, 1, 0) == 0 && !finish_flag)
;
// Read data
if (finish_flag)
continue;
int r;
do
{ {
r = static_cast<int>(read(fno, buffer, sizeof(buffer) - 1)); if (finished_callback)
if (r > 0) finished_callback("Failed to open pipe");
{ return;
buffer[r] = 0;
lines += std::string(buffer);
}
} }
while (r == sizeof(buffer) - 1);
if (lines.find('\n') != std::string::npos && line_callback) char buffer[1024];
std::string lines;
std::string result = "";
int fno = fileno(pipe);
// Make it non blocking
fcntl(fno, F_SETFL, O_NONBLOCK);
while (!feof(pipe) && !finish_flag)
{ {
std::string::size_type p = 0; // Wait for more data
while (p < lines.size()) struct pollfd pfd{.fd = fno, .events = POLLIN};
while (poll(&pfd, 1, 0) == 0 && !finish_flag)
;
// Read data
if (finish_flag)
continue;
int r;
do
{ {
std::string::size_type d = lines.find('\n', p); r = static_cast<int>(read(fno, buffer, sizeof(buffer) - 1));
if (d != std::string::npos) if (r > 0)
{ {
if (line_callback) buffer[r] = 0;
line_callback(strx::trim(lines.substr(p, d-p))); lines += std::string(buffer);
p = d + 1;
} }
} while (r == sizeof(buffer) - 1);
if (lines.find('\n') != std::string::npos && line_callback)
{
std::string::size_type p = 0;
while (p < lines.size())
{
std::string::size_type d = lines.find('\n', p);
if (d != std::string::npos)
{
if (line_callback)
line_callback(strx::trim(lines.substr(p, d - p)));
p = d + 1;
}
}
lines.erase(0, p);
} }
lines.erase(0, p);
} }
}
if (finish_flag) if (finish_flag)
{ {
// Send SIGINT to process // Send SIGINT to process
} }
if (pipe) if (pipe)
pclose(pipe); pclose(pipe);
finish_flag = true; finish_flag = true;
if (finished_callback) if (finished_callback)
finished_callback(std::string()); finished_callback(std::string());
}); });
return t; return t;
} }
@@ -322,7 +324,7 @@ pid_t OsProcess::findPid(const std::string& cmdline)
std::string output = execCommand(oss.str()); std::string output = execCommand(oss.str());
return std::atoi(output.c_str()); return std::atoi(output.c_str());
} }
catch(...) catch (...)
{ {
return 0; return 0;
} }
+5 -7
View File
@@ -10,16 +10,14 @@ class OsProcess
{ {
public: public:
static std::string execCommand(const std::string& cmdline); static std::string execCommand(const std::string& cmdline);
static int execSystem(const std::string& cmdline); static int execSystem(const std::string& cmdline);
static std::shared_ptr<std::thread> asyncExecCommand(const std::string& cmdline, static std::shared_ptr<std::thread>
std::function<void(const std::string& line)> line_callback, asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback,
std::function<void(const std::string& reason)> finished_callback, std::function<void(const std::string& reason)> finished_callback, bool& finish_flag);
bool& finish_flag);
#if defined(TARGET_OSX) || defined(TARGET_LINUX) #if defined(TARGET_OSX) || defined(TARGET_LINUX)
static pid_t findPid(const std::string& cmdline); static pid_t findPid(const std::string& cmdline);
static void killByPid(pid_t pid); static void killByPid(pid_t pid);
#endif #endif
}; };
#endif #endif
+69 -58
View File
@@ -4,12 +4,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
# include <WinSock2.h> #include <WinSock2.h>
# include <Windows.h> #include <Windows.h>
#endif #endif
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX) #if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX)
# include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#include "HL_Rtp.h" #include "HL_Rtp.h"
@@ -28,30 +28,30 @@
#define LOG_SUBSYSTEM "network" #define LOG_SUBSYSTEM "network"
static constexpr size_t MAX_RTP_PACKET_SIZE = 65535; static constexpr size_t MAX_RTP_PACKET_SIZE = 65535;
static const char RTPDUMP_SHEBANG[] = "#!rtpplay1.0"; static const char RTPDUMP_SHEBANG[] = "#!rtpplay1.0";
// RTP fixed header (little-endian bit-field layout) // RTP fixed header (little-endian bit-field layout)
struct RtpHeader struct RtpHeader
{ {
unsigned char cc:4; /* CSRC count */ unsigned char cc : 4; /* CSRC count */
unsigned char x:1; /* header extension flag */ unsigned char x : 1; /* header extension flag */
unsigned char p:1; /* padding flag */ unsigned char p : 1; /* padding flag */
unsigned char version:2; /* protocol version */ unsigned char version : 2; /* protocol version */
unsigned char pt:7; /* payload type */ unsigned char pt : 7; /* payload type */
unsigned char m:1; /* marker bit */ unsigned char m : 1; /* marker bit */
unsigned short seq; /* sequence number */ unsigned short seq; /* sequence number */
unsigned int ts; /* timestamp */ unsigned int ts; /* timestamp */
unsigned int ssrc; /* synchronization source */ unsigned int ssrc; /* synchronization source */
}; };
struct RtcpHeader struct RtcpHeader
{ {
unsigned char rc:5; /* reception report count */ unsigned char rc : 5; /* reception report count */
unsigned char p:1; /* padding flag */ unsigned char p : 1; /* padding flag */
unsigned char version:2; /* protocol version */ unsigned char version : 2; /* protocol version */
unsigned char pt; /* payload type */ unsigned char pt; /* payload type */
uint16_t len; /* length */ uint16_t len; /* length */
uint32_t ssrc; /* synchronization source */ uint32_t ssrc; /* synchronization source */
}; };
// --- IPv4 address helpers --- // --- IPv4 address helpers ---
@@ -59,10 +59,8 @@ struct RtcpHeader
static std::string ipToString(uint32_t ip) static std::string ipToString(uint32_t ip)
{ {
// ip in host byte order → dotted-decimal // ip in host byte order → dotted-decimal
return std::to_string((ip >> 24) & 0xFF) + "." + return std::to_string((ip >> 24) & 0xFF) + "." + std::to_string((ip >> 16) & 0xFF) + "." +
std::to_string((ip >> 16) & 0xFF) + "." + std::to_string((ip >> 8) & 0xFF) + "." + std::to_string(ip & 0xFF);
std::to_string((ip >> 8) & 0xFF) + "." +
std::to_string( ip & 0xFF);
} }
static uint32_t stringToIp(const std::string& s) static uint32_t stringToIp(const std::string& s)
@@ -87,7 +85,7 @@ bool RtpHelper::isRtp(const void* buffer, size_t length)
return false; return false;
unsigned char pt = h->pt; unsigned char pt = h->pt;
bool rtp = (pt >= 96 && pt <= 127) || (pt < 35); bool rtp = (pt >= 96 && pt <= 127) || (pt < 35);
return rtp; return rtp;
} }
@@ -130,7 +128,7 @@ int RtpHelper::findPtype(const void* buffer, size_t length)
return -1; return -1;
} }
int RtpHelper::findPacketNo(const void *buffer, size_t length) int RtpHelper::findPacketNo(const void* buffer, size_t length)
{ {
if (isRtp(buffer, length)) if (isRtp(buffer, length))
return ntohs(reinterpret_cast<const RtpHeader*>(buffer)->seq); return ntohs(reinterpret_cast<const RtpHeader*>(buffer)->seq);
@@ -144,15 +142,16 @@ int RtpHelper::findPayloadLength(const void* buffer, size_t length)
return -1; return -1;
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer); const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
const uint8_t* p = static_cast<const uint8_t*>(buffer); const uint8_t* p = static_cast<const uint8_t*>(buffer);
// Fixed header (12 bytes) + CSRC list (4 * CC bytes) // Fixed header (12 bytes) + CSRC list (4 * CC bytes)
size_t offset = 12 + 4u * h->cc; size_t offset = 12 + 4u * h->cc;
if (offset > length) if (offset > length)
return -1; return -1;
// Header extension // Header extension
if (h->x) { if (h->x)
{
if (offset + 4 > length) if (offset + 4 > length)
return -1; return -1;
uint16_t extWords = (static_cast<uint16_t>(p[offset + 2]) << 8) | p[offset + 3]; uint16_t extWords = (static_cast<uint16_t>(p[offset + 2]) << 8) | p[offset + 3];
@@ -164,7 +163,8 @@ int RtpHelper::findPayloadLength(const void* buffer, size_t length)
size_t payloadLen = length - offset; size_t payloadLen = length - offset;
// Padding // Padding
if (h->p && payloadLen > 0) { if (h->p && payloadLen > 0)
{
uint8_t padBytes = p[length - 1]; uint8_t padBytes = p[length - 1];
if (padBytes > payloadLen) if (padBytes > payloadLen)
return -1; return -1;
@@ -186,29 +186,29 @@ std::shared_ptr<jrtplib::RTPPacket> RtpDump::parseRtpData(const uint8_t* data, s
if (!data || len < 12 || !RtpHelper::isRtp(data, len)) if (!data || len < 12 || !RtpHelper::isRtp(data, len))
return nullptr; return nullptr;
try { try
{
// Both are heap-allocated; RTPRawPacket takes ownership and deletes them // Both are heap-allocated; RTPRawPacket takes ownership and deletes them
jrtplib::RTPIPAddress senderAddress = {jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0))}; jrtplib::RTPIPAddress senderAddress = {jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0))};
uint8_t* dataCopy = new uint8_t[len]; uint8_t* dataCopy = new uint8_t[len];
std::memcpy(dataCopy, data, len); std::memcpy(dataCopy, data, len);
jrtplib::RTPRawPacket raw(dataCopy, len, senderAddress, jrtplib::RTPTime(0), true); jrtplib::RTPRawPacket raw(dataCopy, len, senderAddress, jrtplib::RTPTime(0), true);
auto packet = std::make_shared<jrtplib::RTPPacket>(raw); auto packet = std::make_shared<jrtplib::RTPPacket>(raw);
if (packet->GetCreationError() != 0) if (packet->GetCreationError() != 0)
return nullptr; return nullptr;
return packet; return packet;
} catch (const std::exception& e) { }
catch (const std::exception& e)
{
ICELogInfo(<< "Failed to parse RTP packet: " << e.what()); ICELogInfo(<< "Failed to parse RTP packet: " << e.what());
return nullptr; return nullptr;
} }
} }
RtpDump::RtpDump(const char* filename) RtpDump::RtpDump(const char* filename) : mFilename(filename ? filename : "") {}
: mFilename(filename ? filename : "")
{
}
RtpDump::~RtpDump() = default; RtpDump::~RtpDump() = default;
@@ -237,14 +237,19 @@ void RtpDump::load()
// Parse source address from the text line // Parse source address from the text line
size_t spacePos = textLine.find(' '); size_t spacePos = textLine.find(' ');
if (spacePos != std::string::npos) { if (spacePos != std::string::npos)
{
std::string addrPart = textLine.substr(spacePos + 1); std::string addrPart = textLine.substr(spacePos + 1);
size_t slashPos = addrPart.find('/'); size_t slashPos = addrPart.find('/');
if (slashPos != std::string::npos) { if (slashPos != std::string::npos)
{
mSourceIp = stringToIp(addrPart.substr(0, slashPos)); mSourceIp = stringToIp(addrPart.substr(0, slashPos));
try { try
{
mSourcePort = static_cast<uint16_t>(std::stoi(addrPart.substr(slashPos + 1))); mSourcePort = static_cast<uint16_t>(std::stoi(addrPart.substr(slashPos + 1)));
} catch (...) { }
catch (...)
{
mSourcePort = 0; mSourcePort = 0;
} }
} }
@@ -260,14 +265,14 @@ void RtpDump::load()
input.read(reinterpret_cast<char*>(&buf32), 4); input.read(reinterpret_cast<char*>(&buf32), 4);
mStartUsec = ntohl(buf32); mStartUsec = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf32), 4); // source IP (already NBO in file) input.read(reinterpret_cast<char*>(&buf32), 4); // source IP (already NBO in file)
// The binary header stores IP in network byte order; convert to host // The binary header stores IP in network byte order; convert to host
mSourceIp = ntohl(buf32); mSourceIp = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf16), 2); input.read(reinterpret_cast<char*>(&buf16), 2);
mSourcePort = ntohs(buf16); mSourcePort = ntohs(buf16);
input.read(reinterpret_cast<char*>(&buf16), 2); // padding — discard input.read(reinterpret_cast<char*>(&buf16), 2); // padding — discard
if (!input.good()) if (!input.good())
throw std::runtime_error("Failed to read rtpdump binary header"); throw std::runtime_error("Failed to read rtpdump binary header");
@@ -275,21 +280,25 @@ void RtpDump::load()
// --- 3. Packet records --- // --- 3. Packet records ---
size_t packetCount = 0; size_t packetCount = 0;
while (input.good() && input.peek() != EOF) { while (input.good() && input.peek() != EOF)
{
// Packet header: length(2) + plen(2) + offset(4) = 8 bytes // Packet header: length(2) + plen(2) + offset(4) = 8 bytes
uint16_t recLength, plen; uint16_t recLength, plen;
uint32_t offsetMs; uint32_t offsetMs;
input.read(reinterpret_cast<char*>(&recLength), 2); input.read(reinterpret_cast<char*>(&recLength), 2);
if (input.gcount() != 2) break; if (input.gcount() != 2)
break;
recLength = ntohs(recLength); recLength = ntohs(recLength);
input.read(reinterpret_cast<char*>(&plen), 2); input.read(reinterpret_cast<char*>(&plen), 2);
if (input.gcount() != 2) break; if (input.gcount() != 2)
break;
plen = ntohs(plen); plen = ntohs(plen);
input.read(reinterpret_cast<char*>(&offsetMs), 4); input.read(reinterpret_cast<char*>(&offsetMs), 4);
if (input.gcount() != 4) break; if (input.gcount() != 4)
break;
offsetMs = ntohl(offsetMs); offsetMs = ntohl(offsetMs);
// All-zeros record signals end of file in some implementations // All-zeros record signals end of file in some implementations
@@ -365,9 +374,10 @@ void RtpDump::add(const void* buffer, size_t len)
return; return;
uint32_t offsetMs = 0; uint32_t offsetMs = 0;
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
if (!mRecording) { if (!mRecording)
{
mRecording = true; mRecording = true;
mRecordStart = now; mRecordStart = now;
@@ -376,9 +386,11 @@ void RtpDump::add(const void* buffer, size_t len)
auto epoch = wallNow.time_since_epoch(); auto epoch = wallNow.time_since_epoch();
auto sec = std::chrono::duration_cast<std::chrono::seconds>(epoch); auto sec = std::chrono::duration_cast<std::chrono::seconds>(epoch);
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(epoch - sec); auto usec = std::chrono::duration_cast<std::chrono::microseconds>(epoch - sec);
mStartSec = static_cast<uint32_t>(sec.count()); mStartSec = static_cast<uint32_t>(sec.count());
mStartUsec = static_cast<uint32_t>(usec.count()); mStartUsec = static_cast<uint32_t>(usec.count());
} else { }
else
{
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - mRecordStart); auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - mRecordStart);
offsetMs = static_cast<uint32_t>(elapsed.count()); offsetMs = static_cast<uint32_t>(elapsed.count());
} }
@@ -396,8 +408,7 @@ void RtpDump::add(const void* buffer, size_t len, uint32_t offsetMs)
throw std::runtime_error("Packet too large: " + std::to_string(len)); throw std::runtime_error("Packet too large: " + std::to_string(len));
RtpData entry; RtpData entry;
entry.mRawData.assign(static_cast<const uint8_t*>(buffer), entry.mRawData.assign(static_cast<const uint8_t*>(buffer), static_cast<const uint8_t*>(buffer) + len);
static_cast<const uint8_t*>(buffer) + len);
entry.mOffsetMs = offsetMs; entry.mOffsetMs = offsetMs;
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size()); entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
@@ -414,9 +425,8 @@ void RtpDump::flush()
throw std::runtime_error("Failed to open file for writing: " + mFilename); throw std::runtime_error("Failed to open file for writing: " + mFilename);
// --- 1. Text header --- // --- 1. Text header ---
std::string textLine = std::string(RTPDUMP_SHEBANG) + " " + std::string textLine =
ipToString(mSourceIp) + "/" + std::string(RTPDUMP_SHEBANG) + " " + ipToString(mSourceIp) + "/" + std::to_string(mSourcePort) + "\n";
std::to_string(mSourcePort) + "\n";
output.write(textLine.data(), static_cast<std::streamsize>(textLine.size())); output.write(textLine.data(), static_cast<std::streamsize>(textLine.size()));
// --- 2. Binary file header (16 bytes) --- // --- 2. Binary file header (16 bytes) ---
@@ -435,13 +445,14 @@ void RtpDump::flush()
buf16 = htons(mSourcePort); buf16 = htons(mSourcePort);
output.write(reinterpret_cast<const char*>(&buf16), 2); output.write(reinterpret_cast<const char*>(&buf16), 2);
buf16 = 0; // padding buf16 = 0; // padding
output.write(reinterpret_cast<const char*>(&buf16), 2); output.write(reinterpret_cast<const char*>(&buf16), 2);
// --- 3. Packet records --- // --- 3. Packet records ---
size_t written = 0; size_t written = 0;
for (const auto& pkt : mPacketList) { for (const auto& pkt : mPacketList)
{
if (pkt.mRawData.empty()) if (pkt.mRawData.empty())
continue; continue;
+35 -38
View File
@@ -22,12 +22,9 @@ struct RtpPair
T mRtp; T mRtp;
T mRtcp; T mRtcp;
RtpPair() RtpPair() {}
{}
RtpPair(const T& rtp, const T& rtcp) RtpPair(const T& rtp, const T& rtcp) : mRtp(rtp), mRtcp(rtcp) {}
:mRtp(rtp), mRtcp(rtcp)
{}
bool multiplexed() const { return mRtp == mRtcp; } bool multiplexed() const { return mRtp == mRtcp; }
}; };
@@ -35,14 +32,14 @@ struct RtpPair
class RtpHelper class RtpHelper
{ {
public: public:
static bool isRtp(const void* buffer, size_t length); static bool isRtp(const void* buffer, size_t length);
static int findPtype(const void* buffer, size_t length); static int findPtype(const void* buffer, size_t length);
static int findPacketNo(const void *buffer, size_t length); static int findPacketNo(const void* buffer, size_t length);
static bool isRtpOrRtcp(const void* buffer, size_t length); static bool isRtpOrRtcp(const void* buffer, size_t length);
static bool isRtcp(const void* buffer, size_t length); static bool isRtcp(const void* buffer, size_t length);
static unsigned findSsrc(const void* buffer, size_t length); static unsigned findSsrc(const void* buffer, size_t length);
static void setSsrc(void* buffer, size_t length, uint32_t ssrc); static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
static int findPayloadLength(const void* buffer, size_t length); static int findPayloadLength(const void* buffer, size_t length);
static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t); static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t);
}; };
@@ -79,72 +76,72 @@ protected:
struct RtpData struct RtpData
{ {
std::shared_ptr<jrtplib::RTPPacket> mPacket; std::shared_ptr<jrtplib::RTPPacket> mPacket;
std::vector<uint8_t> mRawData; std::vector<uint8_t> mRawData;
uint32_t mOffsetMs = 0; uint32_t mOffsetMs = 0;
}; };
typedef std::vector<RtpData> PacketList; typedef std::vector<RtpData> PacketList;
PacketList mPacketList; PacketList mPacketList;
std::string mFilename; std::string mFilename;
bool mLoaded = false; bool mLoaded = false;
// File header fields // File header fields
uint32_t mSourceIp = 0; uint32_t mSourceIp = 0;
uint16_t mSourcePort = 0; uint16_t mSourcePort = 0;
uint32_t mStartSec = 0; uint32_t mStartSec = 0;
uint32_t mStartUsec = 0; uint32_t mStartUsec = 0;
// Auto-compute packet offsets during recording // Auto-compute packet offsets during recording
bool mRecording = false; bool mRecording = false;
std::chrono::steady_clock::time_point mRecordStart; std::chrono::steady_clock::time_point mRecordStart;
std::shared_ptr<jrtplib::RTPPacket> parseRtpData(const uint8_t* data, size_t len); std::shared_ptr<jrtplib::RTPPacket> parseRtpData(const uint8_t* data, size_t len);
public: public:
explicit RtpDump(const char* filename); explicit RtpDump(const char* filename);
~RtpDump(); ~RtpDump();
/** Set source address for the file header (host byte order). */ /** Set source address for the file header (host byte order). */
void setSource(uint32_t ip, uint16_t port); void setSource(uint32_t ip, uint16_t port);
uint32_t sourceIp() const { return mSourceIp; } uint32_t sourceIp() const { return mSourceIp; }
uint16_t sourcePort() const { return mSourcePort; } uint16_t sourcePort() const { return mSourcePort; }
/** /**
* @brief Load packets from an rtpdump file * @brief Load packets from an rtpdump file
* @throws std::runtime_error on file/format error * @throws std::runtime_error on file/format error
*/ */
void load(); void load();
bool isLoaded() const { return mLoaded; } bool isLoaded() const { return mLoaded; }
size_t count() const; size_t count() const;
/** /**
* @brief Get parsed RTP packet at index * @brief Get parsed RTP packet at index
* @throws std::out_of_range if index is invalid * @throws std::out_of_range if index is invalid
* @throws std::runtime_error if packet could not be parsed as RTP * @throws std::runtime_error if packet could not be parsed as RTP
*/ */
jrtplib::RTPPacket& packetAt(size_t index); jrtplib::RTPPacket& packetAt(size_t index);
/** @brief Get raw packet bytes at index */ /** @brief Get raw packet bytes at index */
const std::vector<uint8_t>& rawDataAt(size_t index) const; const std::vector<uint8_t>& rawDataAt(size_t index) const;
/** @brief Get packet time offset in milliseconds */ /** @brief Get packet time offset in milliseconds */
uint32_t offsetAt(size_t index) const; uint32_t offsetAt(size_t index) const;
/** @brief Add a packet; time offset is auto-computed from first add() call */ /** @brief Add a packet; time offset is auto-computed from first add() call */
void add(const void* data, size_t len); void add(const void* data, size_t len);
/** @brief Add a packet with an explicit millisecond offset */ /** @brief Add a packet with an explicit millisecond offset */
void add(const void* data, size_t len, uint32_t offsetMs); void add(const void* data, size_t len, uint32_t offsetMs);
/** /**
* @brief Write all packets to file in rtpdump format * @brief Write all packets to file in rtpdump format
* @throws std::runtime_error on file error * @throws std::runtime_error on file error
*/ */
void flush(); void flush();
void clear(); void clear();
const std::string& filename() const { return mFilename; } const std::string& filename() const { return mFilename; }
}; };
#endif #endif
+5 -4
View File
@@ -4,12 +4,13 @@
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
template <class T> template<class T>
class SafeSingleton class SafeSingleton
{ {
protected: protected:
static std::atomic<T*> SharedInstance; static std::atomic<T*> SharedInstance;
static std::mutex mMutex; static std::mutex mMutex;
public: public:
static T& instance() static T& instance()
{ {
@@ -50,9 +51,9 @@ public:
} }
}; };
template <class T> template<class T>
std::atomic<T*> SafeSingleton<T>::SharedInstance; std::atomic<T*> SafeSingleton<T>::SharedInstance;
template <class T> template<class T>
std::mutex SafeSingleton<T>::mMutex; std::mutex SafeSingleton<T>::mMutex;
#endif #endif
+27 -28
View File
@@ -12,7 +12,7 @@
#include <assert.h> #include <assert.h>
#include <chrono> #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
#include "HL_SocketHeap.h" #include "HL_SocketHeap.h"
@@ -31,14 +31,13 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
// ----------------------------- SocketSink ------------------------- // ----------------------------- SocketSink -------------------------
SocketSink::~SocketSink() SocketSink::~SocketSink() {}
{}
// ----------------------------- SocketHeap ------------------------- // ----------------------------- SocketHeap -------------------------
SocketHeap::SocketHeap(unsigned short start, unsigned short finish) SocketHeap::SocketHeap(unsigned short start, unsigned short finish)
{ {
mStart = start; mStart = start;
mFinish = finish; mFinish = finish;
} }
@@ -74,7 +73,7 @@ void SocketHeap::setRange(unsigned short start, unsigned short finish)
mFinish = finish; mFinish = finish;
} }
void SocketHeap::range(unsigned short &start, unsigned short &finish) void SocketHeap::range(unsigned short& start, unsigned short& finish)
{ {
Lock l(mGuard); Lock l(mGuard);
@@ -82,10 +81,10 @@ void SocketHeap::range(unsigned short &start, unsigned short &finish)
finish = mFinish; finish = mFinish;
} }
RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink *sink, Multiplex m) RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink* sink, Multiplex m)
{ {
PDatagramSocket rtp, rtcp; PDatagramSocket rtp, rtcp;
for (int attempt=0; (!rtp || !rtcp) && attempt < (mFinish - mStart)/2; attempt++) for (int attempt = 0; (!rtp || !rtcp) && attempt < (mFinish - mStart) / 2; attempt++)
{ {
// Allocate RTP // Allocate RTP
try try
@@ -96,7 +95,7 @@ RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink *sin
else else
rtcp = allocSocket(family, sink, rtp->localport() + 1); rtcp = allocSocket(family, sink, rtp->localport() + 1);
} }
catch(...) catch (...)
{ {
// Release a partially allocated pair before retrying - otherwise // Release a partially allocated pair before retrying - otherwise
// the RTP socket from this attempt leaks into the socket map. // the RTP socket from this attempt leaks into the socket map.
@@ -121,14 +120,13 @@ RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink *sin
freeSocket(rtcp); freeSocket(rtcp);
throw Exception(ERR_NET_FAILED); throw Exception(ERR_NET_FAILED);
} }
ICELogInfo(<< "Allocated socket pair " << (family == AF_INET ? "AF_INET" : "AF_INET6") << " " ICELogInfo(<< "Allocated socket pair " << (family == AF_INET ? "AF_INET" : "AF_INET6") << " " << rtp->socket()
<< rtp->socket() << ":" << rtcp->socket() << ":" << rtcp->socket() << " at ports " << rtp->localport() << ":" << rtcp->localport());
<< " at ports " << rtp->localport() << ":"<< rtcp->localport());
return RtpPair<PDatagramSocket>(rtp, rtcp); return RtpPair<PDatagramSocket>(rtp, rtcp);
} }
void SocketHeap::freeSocketPair(const RtpPair<PDatagramSocket> &p) void SocketHeap::freeSocketPair(const RtpPair<PDatagramSocket>& p)
{ {
freeSocket(p.mRtp); freeSocket(p.mRtp);
freeSocket(p.mRtcp); freeSocket(p.mRtcp);
@@ -136,7 +134,7 @@ void SocketHeap::freeSocketPair(const RtpPair<PDatagramSocket> &p)
PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port) PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
{ {
Lock l(mGuard); Lock l(mGuard);
SOCKET sock = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); SOCKET sock = ::socket(family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) if (sock == INVALID_SOCKET)
{ {
@@ -148,13 +146,13 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
} }
// Obtain port number // Obtain port number
sockaddr_in addr; sockaddr_in addr;
sockaddr_in6 addr6; sockaddr_in6 addr6;
int result = 0; int result = 0;
int testport; int testport;
// A fixed port cannot be retried (it would loop forever if the port is // A fixed port cannot be retried (it would loop forever if the port is
// owned by another process); random ports get a bounded number of attempts. // owned by another process); random ports get a bounded number of attempts.
int attemptsLeft = port ? 1 : 100; int attemptsLeft = port ? 1 : 100;
do do
{ {
testport = port ? port : rand() % ((mFinish - mStart) / 2) * 2 + mStart; testport = port ? port : rand() % ((mFinish - mStart) / 2) * 2 + mStart;
@@ -215,7 +213,7 @@ void SocketHeap::freeSocket(PDatagramSocket socket)
void SocketHeap::processDeleted() void SocketHeap::processDeleted()
{ {
Lock l(mDeleteGuard); Lock l(mDeleteGuard);
SocketVector::iterator socketIter = mDeleteVector.begin(); SocketVector::iterator socketIter = mDeleteVector.begin();
while (socketIter != mDeleteVector.end()) while (socketIter != mDeleteVector.end())
@@ -257,10 +255,11 @@ void SocketHeap::thread()
} }
// Update socket set // Update socket set
for (auto& socketIter: mSocketMap) for (auto& socketIter : mSocketMap)
agreggator.addSocket(socketIter.second.mSocket); agreggator.addSocket(socketIter.second.mSocket);
/* for (SocketMap::iterator socketIter = mSocketMap.begin(); socketIter != mSocketMap.end(); ++socketIter) /* for (SocketMap::iterator socketIter = mSocketMap.begin(); socketIter != mSocketMap.end();
++socketIter)
{ {
// Add handle to set // Add handle to set
agreggator.addSocket(socketIter->second.mSocket); agreggator.addSocket(socketIter->second.mSocket);
@@ -278,12 +277,12 @@ void SocketHeap::thread()
// Remove deleted sockets to avoid call non-existant sinks // Remove deleted sockets to avoid call non-existant sinks
processDeleted(); processDeleted();
for (unsigned i=0; i<agreggator.count(); i++) for (unsigned i = 0; i < agreggator.count(); i++)
{ {
if (agreggator.hasDataAtIndex(i)) if (agreggator.hasDataAtIndex(i))
{ {
//ICELogInfo(<<"Got incoming UDP packet at index " << (const int)i); // ICELogInfo(<<"Got incoming UDP packet at index " << (const int)i);
PDatagramSocket sock = agreggator.socketAt(i); PDatagramSocket sock = agreggator.socketAt(i);
// Find corresponding data sink // Find corresponding data sink
SocketMap::iterator socketItemIter = mSocketMap.find(sock->mHandle); SocketMap::iterator socketItemIter = mSocketMap.find(sock->mHandle);
@@ -291,16 +290,16 @@ void SocketHeap::thread()
if (socketItemIter != mSocketMap.end()) if (socketItemIter != mSocketMap.end())
{ {
InternetAddress src; InternetAddress src;
unsigned received = sock->recvDatagram(src, mTempPacket, sizeof mTempPacket); unsigned received = sock->recvDatagram(src, mTempPacket, sizeof mTempPacket);
if ( received > 0 && received <= MAX_VALID_UDPPACKET_SIZE) if (received > 0 && received <= MAX_VALID_UDPPACKET_SIZE)
socketItemIter->second.mSink->onReceivedData(sock, src, mTempPacket, received); socketItemIter->second.mSink->onReceivedData(sock, src, mTempPacket, received);
} }
// There is a call to ProcessDeleted() as OnReceivedData() could delete sockets // There is a call to ProcessDeleted() as OnReceivedData() could delete sockets
processDeleted(); processDeleted();
} }
} //of for } // of for
} }
} }
else else
@@ -308,12 +307,12 @@ void SocketHeap::thread()
} }
mShutdown = false; mShutdown = false;
//#endif // #endif
} }
static SocketHeap GRTPSocketHeap(20002, 25100); static SocketHeap GRTPSocketHeap(20002, 25100);
SocketHeap& SocketHeap::instance() SocketHeap& SocketHeap::instance()
{ {
return GRTPSocketHeap; return GRTPSocketHeap;
} }
+33 -40
View File
@@ -22,7 +22,8 @@ class SocketSink
{ {
public: public:
virtual ~SocketSink(); virtual ~SocketSink();
virtual void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize) = 0; virtual void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr,
unsigned receivedSize) = 0;
}; };
// Class allocates new UDP sockets and tracks incoming packets on them. It runs in separate thread // Class allocates new UDP sockets and tracks incoming packets on them. It runs in separate thread
@@ -38,76 +39,68 @@ public:
SocketHeap(unsigned short start, unsigned short finish); SocketHeap(unsigned short start, unsigned short finish);
virtual ~SocketHeap(); virtual ~SocketHeap();
static SocketHeap& instance(); static SocketHeap& instance();
void start(); void start();
void stop(); void stop();
// Specifies ne\ port number range. The sockets will be allocated in range [start..finish] // Specifies ne\ port number range. The sockets will be allocated in range [start..finish]
void setRange(unsigned short start, unsigned short finish); void setRange(unsigned short start, unsigned short finish);
// Returns used port number range // Returns used port number range
void range(unsigned short& start, unsigned short& finish); void range(unsigned short& start, unsigned short& finish);
// Attempts to allocate and return socket + allocated port number. REQUIRES pointer to data sink - it will be used to process incoming datagrams // Attempts to allocate and return socket + allocated port number. REQUIRES pointer to data sink - it will be used
PDatagramSocket allocSocket(int family, SocketSink* sink, int port = 0); // to process incoming datagrams
RtpPair<PDatagramSocket> allocSocketPair(int family, SocketSink* sink, Multiplex m); PDatagramSocket allocSocket(int family, SocketSink* sink, int port = 0);
RtpPair<PDatagramSocket> allocSocketPair(int family, SocketSink* sink, Multiplex m);
// Stops receiving data for specified socket and frees socket itself. // Stops receiving data for specified socket and frees socket itself.
void freeSocket(PDatagramSocket socket); void freeSocket(PDatagramSocket socket);
void freeSocketPair(const RtpPair<PDatagramSocket>& p); void freeSocketPair(const RtpPair<PDatagramSocket>& p);
// Sends data to specified address on specified socket. // Sends data to specified address on specified socket.
void sendData(DatagramSocket& socket, InternetAddress& dest, const void* dataPtr, int dataSize); void sendData(DatagramSocket& socket, InternetAddress& dest, const void* dataPtr, int dataSize);
protected: protected:
struct SocketItem struct SocketItem
{ {
// Local port number for socket // Local port number for socket
PDatagramSocket mSocket; PDatagramSocket mSocket;
// Data sink pointer // Data sink pointer
SocketSink* mSink; SocketSink* mSink;
SocketItem() SocketItem() : mSink(nullptr) {}
:mSink(nullptr)
{ }
SocketItem(unsigned short portnumber, SocketSink* sink) SocketItem(unsigned short portnumber, SocketSink* sink) : mSink(sink) { mSocket->mLocalPort = portnumber; }
:mSink(sink)
{
mSocket->mLocalPort = portnumber;
}
~SocketItem() ~SocketItem() {}
{ }
}; };
typedef std::map<SOCKET, SocketItem> SocketMap; typedef std::map<SOCKET, SocketItem> SocketMap;
typedef std::vector<unsigned short> PortVector; typedef std::vector<unsigned short> PortVector;
typedef std::vector<PDatagramSocket> SocketVector; typedef std::vector<PDatagramSocket> SocketVector;
Mutex mGuard; Mutex mGuard;
SocketMap mSocketMap; SocketMap mSocketMap;
PortVector mPortVector; PortVector mPortVector;
unsigned short mStart, unsigned short mStart, mFinish;
mFinish; SocketVector mDeleteVector;
SocketVector mDeleteVector; Mutex mDeleteGuard;
Mutex mDeleteGuard;
char mTempPacket[MAX_UDPPACKET_SIZE]; char mTempPacket[MAX_UDPPACKET_SIZE];
std::shared_ptr<std::thread> mWorkerThread; std::shared_ptr<std::thread> mWorkerThread;
std::thread::id mThreadId; std::thread::id mThreadId;
bool mShutdown = false; bool mShutdown = false;
bool isShutdown() const { return mShutdown; } bool isShutdown() const { return mShutdown; }
void thread(); void thread();
// Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector // Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector
void processDeleted(); void processDeleted();
}; };
#endif #endif
+13 -28
View File
@@ -5,18 +5,15 @@ template<typename T>
struct Average struct Average
{ {
int mCount = 0; int mCount = 0;
T mSum = 0; T mSum = 0;
T average() const T average() const
{ {
if (!mCount) if (!mCount)
return 0; return 0;
return mSum / mCount; return mSum / mCount;
} }
T value() const T value() const { return average(); }
{
return average();
}
void process(T value) void process(T value)
{ {
@@ -28,12 +25,12 @@ struct Average
template<typename T, int minimum = 100000, int maximum = 0, int default_value = 0> template<typename T, int minimum = 100000, int maximum = 0, int default_value = 0>
struct TestResult struct TestResult
{ {
T mMin = minimum; T mMin = minimum;
T mMax = maximum; T mMax = maximum;
Average<T> mAverage; Average<T> mAverage;
T mCurrent = default_value; T mCurrent = default_value;
void process(T value) void process(T value)
{ {
if (mMin > value) if (mMin > value)
mMin = value; mMin = value;
@@ -43,12 +40,9 @@ struct TestResult
mAverage.process(value); mAverage.process(value);
} }
bool is_initialized() const bool is_initialized() const { return mAverage.mCount > 0; }
{
return mAverage.mCount > 0;
}
T current() const T current() const
{ {
if (is_initialized()) if (is_initialized())
return mCurrent; return mCurrent;
@@ -56,26 +50,17 @@ struct TestResult
return 0; return 0;
} }
T value() const T value() const { return current(); }
{
return current();
}
T average() const T average() const { return mAverage.average(); }
{
return mAverage.average();
}
TestResult<T>& operator = (T value) TestResult<T>& operator=(T value)
{ {
process(value); process(value);
return *this; return *this;
} }
operator T() operator T() { return mCurrent; }
{
return mCurrent;
}
}; };
+7 -8
View File
@@ -10,15 +10,14 @@
enum class StreamState enum class StreamState
{ {
Sending = 1, // Transmitting RTP. Set this flag to allow outgoing media stream. Sending = 1, // Transmitting RTP. Set this flag to allow outgoing media stream.
Receiving = 2, // Receiving RTP. Set this flag to allow receiving media stream. Receiving = 2, // Receiving RTP. Set this flag to allow receiving media stream.
Playing = 4, // Play to audio. Unmutes the audio from specified stream. Playing = 4, // Play to audio. Unmutes the audio from specified stream.
Grabbing = 8, // Capture audio. Unmutes the audio to specified stream. Grabbing = 8, // Capture audio. Unmutes the audio to specified stream.
Srtp = 16, // Use SRTP. Make attempt Srtp = 16, // Use SRTP. Make attempt
SipSend = 32, // Declare send capability in SDP SipSend = 32, // Declare send capability in SDP
SipRecv = 64 // Declare recv capability in SDP SipRecv = 64 // Declare recv capability in SDP
}; };
#endif #endif
+59 -59
View File
@@ -11,9 +11,9 @@
#include <inttypes.h> #include <inttypes.h>
#ifdef TARGET_WIN #ifdef TARGET_WIN
# include <WinSock2.h> #include <WinSock2.h>
# include <Windows.h> #include <Windows.h>
# include <cctype> #include <cctype>
#endif #endif
std::string strx::extractFilename(const std::string& path) std::string strx::extractFilename(const std::string& path)
@@ -45,7 +45,7 @@ std::string strx::appendPath(const std::string& s1, const std::string& s2)
return result + s2; return result + s2;
} }
std::string strx::makeUtf8(const std::tstring &arg) std::string strx::makeUtf8(const std::tstring& arg)
{ {
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
int required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL); int required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
@@ -60,7 +60,7 @@ std::string strx::makeUtf8(const std::tstring &arg)
#endif #endif
} }
std::string strx::toUtf8(const std::tstring &arg) std::string strx::toUtf8(const std::tstring& arg)
{ {
return makeUtf8(arg); return makeUtf8(arg);
} }
@@ -80,7 +80,7 @@ std::tstring strx::makeTstring(const std::string& arg)
#endif #endif
} }
int strx::toInt(const char *s, int defaultValue, bool* isOk) int strx::toInt(const char* s, int defaultValue, bool* isOk)
{ {
int result; int result;
if (sscanf(s, "%d", &result) != 1) if (sscanf(s, "%d", &result) != 1)
@@ -89,14 +89,13 @@ int strx::toInt(const char *s, int defaultValue, bool* isOk)
*isOk = false; *isOk = false;
result = defaultValue; result = defaultValue;
} }
else else if (isOk)
if (isOk) *isOk = true;
*isOk = true;
return result; return result;
} }
uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk) uint64_t strx::toUint64(const char* s, uint64_t def, bool* isOk)
{ {
uint64_t result = def; uint64_t result = def;
if (sscanf(s, "%" SCNu64, &result) != 1) if (sscanf(s, "%" SCNu64, &result) != 1)
@@ -105,9 +104,8 @@ uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
*isOk = false; *isOk = false;
result = def; result = def;
} }
else else if (isOk)
if (isOk) *isOk = true;
*isOk = true;
return result; return result;
} }
@@ -119,28 +117,29 @@ std::string strx::toHex(unsigned int value)
return buffer; return buffer;
} }
std::string strx::toHex(const void *ptr) std::string strx::toHex(const void* ptr)
{ {
std::ostringstream oss; std::ostringstream oss;
oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr; oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr;
return oss.str(); return oss.str();
} }
//must be lowercase for MD5 // must be lowercase for MD5
static const char hexmap[] = "0123456789abcdef"; static const char hexmap[] = "0123456789abcdef";
std::string strx::toHex(const uint8_t* input, size_t inputLength) std::string strx::toHex(const uint8_t* input, size_t inputLength)
{ {
std::string result; result.resize(inputLength * 2); std::string result;
result.resize(inputLength * 2);
const char* p = (const char*)input; const char* p = (const char*)input;
char* r = &result[0]; char* r = &result[0];
for (size_t i=0; i < inputLength; ++i) for (size_t i = 0; i < inputLength; ++i)
{ {
unsigned char temp = *p++; unsigned char temp = *p++;
int hi = (temp & 0xf0)>>4; int hi = (temp & 0xf0) >> 4;
int low = (temp & 0xf); int low = (temp & 0xf);
*r++ = hexmap[hi]; *r++ = hexmap[hi];
*r++ = hexmap[low]; *r++ = hexmap[low];
@@ -149,13 +148,13 @@ std::string strx::toHex(const uint8_t* input, size_t inputLength)
return result; return result;
} }
std::string strx::prefixLines(const std::string &source, const std::string &prefix) std::string strx::prefixLines(const std::string& source, const std::string& prefix)
{ {
// Read source line by line // Read source line by line
std::istringstream iss(source); std::istringstream iss(source);
std::ostringstream oss; std::ostringstream oss;
std::string line; std::string line;
while (std::getline(iss,line)) while (std::getline(iss, line))
{ {
oss << prefix << line << std::endl; oss << prefix << line << std::endl;
} }
@@ -194,7 +193,7 @@ void strx::split(const std::string& src, std::vector<std::string>& dst, const st
} }
else else
{ {
std::string t = src.substr(p, f-p); std::string t = src.substr(p, f - p);
if (!t.empty()) if (!t.empty())
dst.push_back(t); dst.push_back(t);
p = f + 1; p = f + 1;
@@ -212,7 +211,7 @@ std::vector<std::string> strx::split(const std::string& src, const std::string&
std::pair<std::string, int> strx::parseHost(const std::string& host, int defaultPort) std::pair<std::string, int> strx::parseHost(const std::string& host, int defaultPort)
{ {
std::pair<std::string, int> result; std::pair<std::string, int> result;
std::size_t p = host.find(':'); std::size_t p = host.find(':');
if (p != std::string::npos) if (p != std::string::npos)
{ {
result.first = host.substr(0, p); result.first = host.substr(0, p);
@@ -230,15 +229,15 @@ std::pair<std::string, std::string> strx::parseAssignment(const std::string& s,
{ {
std::pair<std::string, std::string> result; std::pair<std::string, std::string> result;
std::string::size_type p = s.find('='); std::string::size_type p = s.find('=');
if (p != std::string::npos) if (p != std::string::npos)
{ {
result.first = strx::trim(s.substr(0, p)); result.first = strx::trim(s.substr(0, p));
result.second = strx::trim(s.substr(p+1)); result.second = strx::trim(s.substr(p + 1));
if (trimQuotes && result.second.size() >= 2) if (trimQuotes && result.second.size() >= 2)
{ {
if ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') || if ((result.second[0] == '"' && result.second[result.second.size() - 1] == '"') ||
(result.second[0] == '\'' && result.second[result.second.size()-1] == '\'')) (result.second[0] == '\'' && result.second[result.second.size() - 1] == '\''))
result.second = result.second.substr(1, result.second.size() - 2); result.second = result.second.substr(1, result.second.size() - 2);
} }
} }
@@ -255,10 +254,10 @@ std::string strx::intToString(int value)
return buffer; return buffer;
} }
float strx::toFloat(const std::string &s, float v, bool* isOk) float strx::toFloat(const std::string& s, float v, bool* isOk)
{ {
float result = 0.0; float result = 0.0;
int code = sscanf(s.c_str(), "%f", &result); int code = sscanf(s.c_str(), "%f", &result);
if (code == 1) if (code == 1)
{ {
if (isOk) if (isOk)
@@ -274,16 +273,18 @@ float strx::toFloat(const std::string &s, float v, bool* isOk)
return result; return result;
} }
std::string strx::trim(const std::string &s) std::string strx::trim(const std::string& s)
{ {
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }); auto wsfront =
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }).base(); std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; });
return (wsback <= wsfront ? std::string() : std::string(wsfront,wsback)); auto wsback =
std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; }).base();
return (wsback <= wsfront ? std::string() : std::string(wsfront, wsback));
} }
std::string strx::timeToString(time_t t) std::string strx::timeToString(time_t t)
{ {
char buffer[128] = ""; char buffer[128] = "";
struct tm lt; struct tm lt;
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
if (localtime_r(&t, &lt) == nullptr) if (localtime_r(&t, &lt) == nullptr)
@@ -291,16 +292,16 @@ std::string strx::timeToString(time_t t)
#else #else
lt = *localtime(&t); lt = *localtime(&t);
#endif #endif
strftime(buffer, sizeof(buffer)-1, "%Y-%m-%d %H:%M:%S", &lt); strftime(buffer, sizeof(buffer) - 1, "%Y-%m-%d %H:%M:%S", &lt);
return buffer; return buffer;
} }
std::string strx::millisecondsToString(uint64_t t) std::string strx::millisecondsToString(uint64_t t)
{ {
return timeToString(t/1000); return timeToString(t / 1000);
} }
int strx::fromHex2Int(const std::string &s) int strx::fromHex2Int(const std::string& s)
{ {
int result = 0; int result = 0;
int retcode = sscanf(s.c_str(), "%x", &result); int retcode = sscanf(s.c_str(), "%x", &result);
@@ -328,10 +329,11 @@ static int hex2code(char s)
std::string strx::fromHex2String(const std::string& s) std::string strx::fromHex2String(const std::string& s)
{ {
std::string result; result.resize(s.size() / 2); std::string result;
result.resize(s.size() / 2);
const char* t = s.c_str(); const char* t = s.c_str();
for (size_t i = 0; i < result.size(); i++) for (size_t i = 0; i < result.size(); i++)
result[i] = static_cast<char>((hex2code(t[i*2]) << 4) | hex2code(t[i*2+1])); result[i] = static_cast<char>((hex2code(t[i * 2]) << 4) | hex2code(t[i * 2 + 1]));
return result; return result;
} }
@@ -348,9 +350,9 @@ std::string strx::replace(const std::string& s, char f, char r)
std::string strx::replace(const std::string& s, const std::string& tmpl, const std::string& n) std::string strx::replace(const std::string& s, const std::string& tmpl, const std::string& n)
{ {
std::string result(s); std::string result(s);
std::string::size_type p = 0; std::string::size_type p = 0;
while ( (p = result.find(tmpl, p)) != std::string::npos) while ((p = result.find(tmpl, p)) != std::string::npos)
{ {
result.replace(p, tmpl.size(), n); result.replace(p, tmpl.size(), n);
p += n.size(); p += n.size();
@@ -366,12 +368,12 @@ std::string strx::decodeUri(const std::string& s)
char ch; char ch;
int i, ii = 0; int i, ii = 0;
for (i=0; i<(int)s.length(); i++) for (i = 0; i < (int)s.length(); i++)
{ {
if (s[i] == '%' && i + 2 < (int)s.length()) if (s[i] == '%' && i + 2 < (int)s.length())
{ {
if (sscanf(s.substr(i+1,2).c_str(), "%x", &ii) == 1) if (sscanf(s.substr(i + 1, 2).c_str(), "%x", &ii) == 1)
{ {
ch = static_cast<char>(ii); ch = static_cast<char>(ii);
ret += ch; ret += ch;
@@ -403,13 +405,13 @@ bool strx::endsWith(const std::string& s, const std::string& suffix)
int strx::stringToDuration(const std::string& s) int strx::stringToDuration(const std::string& s)
{ {
if (endsWith(s, "ms")) if (endsWith(s, "ms"))
return std::stoi(s.substr(0, s.size()-2)); return std::stoi(s.substr(0, s.size() - 2));
if (endsWith(s, "s")) if (endsWith(s, "s"))
return std::stoi(s.substr(0, s.size()-1)) * 1000; return std::stoi(s.substr(0, s.size() - 1)) * 1000;
if (endsWith(s, "m")) if (endsWith(s, "m"))
return std::stoi(s.substr(0, s.size()-1)) * 60000; return std::stoi(s.substr(0, s.size() - 1)) * 60000;
if (endsWith(s, "h")) if (endsWith(s, "h"))
return std::stoi(s.substr(0, s.size()-1)) * 3600 * 1000; return std::stoi(s.substr(0, s.size() - 1)) * 3600 * 1000;
else else
return std::stoi(s) * 1000; return std::stoi(s) * 1000;
} }
@@ -438,7 +440,7 @@ std::string strx::removeQuotes(const std::string& s)
r = r.substr(1); r = r.substr(1);
if (r.back() == '"') if (r.back() == '"')
r = r.substr(0, r.size()-1); r = r.substr(0, r.size() - 1);
return r; return r;
} }
@@ -446,20 +448,18 @@ std::string strx::removeQuotes(const std::string& s)
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
// MSVC++ lacks memmem support // MSVC++ lacks memmem support
const void *memmem(const void *haystack, size_t haystack_len, const void* memmem(const void* haystack, size_t haystack_len, const void* const needle, const size_t needle_len)
const void * const needle, const size_t needle_len)
{ {
if (!haystack || !haystack_len || !needle || !needle_len) if (!haystack || !haystack_len || !needle || !needle_len)
return nullptr; return nullptr;
for (const char *h = (const char*)haystack; for (const char* h = (const char*)haystack; haystack_len >= needle_len; ++h, --haystack_len)
haystack_len >= needle_len; {
++h, --haystack_len) { if (!memcmp(h, needle, needle_len))
if (!memcmp(h, needle, needle_len)) { {
return h; return h;
} }
} }
return nullptr; return nullptr;
} }
#endif #endif
+31 -32
View File
@@ -20,32 +20,32 @@
class strx class strx
{ {
public: public:
static std::string extractFilename(const std::string& path); static std::string extractFilename(const std::string& path);
static std::string appendPath(const std::string& s1, const std::string& s2); static std::string appendPath(const std::string& s1, const std::string& s2);
static std::string makeUtf8(const std::tstring& arg); static std::string makeUtf8(const std::tstring& arg);
static std::string toUtf8(const std::tstring& arg); static std::string toUtf8(const std::tstring& arg);
static std::tstring makeTstring(const std::string& arg); static std::tstring makeTstring(const std::string& arg);
static int toInt(const char* s, int defaultValue, bool* isOk = nullptr); static int toInt(const char* s, int defaultValue, bool* isOk = nullptr);
static uint64_t toUint64(const char* s, uint64_t def, bool *isOk = nullptr); static uint64_t toUint64(const char* s, uint64_t def, bool* isOk = nullptr);
static std::string toHex(unsigned int value); static std::string toHex(unsigned int value);
static std::string toHex(const void* ptr); static std::string toHex(const void* ptr);
static std::string toHex(const uint8_t* input, size_t inputLength); static std::string toHex(const uint8_t* input, size_t inputLength);
static std::string intToString(int value); static std::string intToString(int value);
static std::string prefixLines(const std::string& source, const std::string& prefix); static std::string prefixLines(const std::string& source, const std::string& prefix);
static std::string doubleToString(double value, int precision); static std::string doubleToString(double value, int precision);
static int fromHex2Int(const std::string& s); static int fromHex2Int(const std::string& s);
static std::string fromHex2String(const std::string& s); static std::string fromHex2String(const std::string& s);
static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr); static float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength); static const char* findSubstring(const char* buffer, const char* substring, size_t bufferLength);
static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims); static void split(const std::string& src, std::vector<std::string>& dst, const std::string& delims);
static std::vector<std::string> split(const std::string& src, const std::string& delims = "\n"); static std::vector<std::string> split(const std::string& src, const std::string& delims = "\n");
template <typename T> template<typename T>
static std::string join(const std::vector<T>& v, const std::string& delimiter) static std::string join(const std::vector<T>& v, const std::string& delimiter)
{ {
std::ostringstream s; std::ostringstream s;
@@ -58,21 +58,21 @@ public:
return s.str(); return s.str();
} }
static std::pair<std::string, int> parseHost(const std::string& host, int defaultPort); static std::pair<std::string, int> parseHost(const std::string& host, int defaultPort);
static std::pair<std::string, std::string> parseAssignment(const std::string& s, bool trimQuotes = true); static std::pair<std::string, std::string> parseAssignment(const std::string& s, bool trimQuotes = true);
static std::string trim(const std::string& s); static std::string trim(const std::string& s);
static std::string timeToString(time_t t); static std::string timeToString(time_t t);
static std::string millisecondsToString(uint64_t t); static std::string millisecondsToString(uint64_t t);
static std::string replace(const std::string& s, char f, char r); static std::string replace(const std::string& s, char f, char r);
static std::string replace(const std::string& s, const std::string& tmpl, const std::string& n); static std::string replace(const std::string& s, const std::string& tmpl, const std::string& n);
static std::string decodeUri(const std::string& s); static std::string decodeUri(const std::string& s);
static bool startsWith(const std::string& s, const std::string& prefix); static bool startsWith(const std::string& s, const std::string& prefix);
static bool endsWith(const std::string& s, const std::string& suffix); static bool endsWith(const std::string& s, const std::string& suffix);
static int stringToDuration(const std::string& s); static int stringToDuration(const std::string& s);
static std::string uppercase(const std::string& s); static std::string uppercase(const std::string& s);
static std::string lowercase(const std::string& s); static std::string lowercase(const std::string& s);
static std::string removeQuotes(const std::string& s); static std::string removeQuotes(const std::string& s);
}; };
class XcapHelper class XcapHelper
@@ -88,8 +88,7 @@ public:
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
// MSVC++ lacks memmem support // MSVC++ lacks memmem support
extern const void *memmem(const void *haystack, size_t haystack_len, extern const void* memmem(const void* haystack, size_t haystack_len, const void* const needle, const size_t needle_len);
const void * const needle, const size_t needle_len);
#endif #endif
#endif #endif
+46 -38
View File
@@ -10,11 +10,11 @@
#include <iostream> #include <iostream>
#ifdef TARGET_OSX #ifdef TARGET_OSX
# include <libkern/OSAtomic.h> #include <libkern/OSAtomic.h>
#endif #endif
#ifdef TARGET_WIN #ifdef TARGET_WIN
# include <Windows.h> #include <Windows.h>
#endif #endif
void SyncHelper::delay(unsigned int microseconds) void SyncHelper::delay(unsigned int microseconds)
@@ -35,7 +35,7 @@ void SyncHelper::delay(unsigned int microseconds)
// ------------------- 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 // The name will be truncated to 8 or 16 characters
@@ -53,7 +53,7 @@ uint64_t ThreadHelper::getCurrentId()
return static_cast<uint64_t>(GetCurrentThreadId()); return static_cast<uint64_t>(GetCurrentThreadId());
#endif #endif
#if defined(TARGET_LINUX)||defined(TARGET_OSX) #if defined(TARGET_LINUX) || defined(TARGET_OSX)
// RPi builds want this! // RPi builds want this!
return (uint64_t)(pthread_self()); return (uint64_t)(pthread_self());
#endif #endif
@@ -70,13 +70,13 @@ using namespace std::chrono;
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count(); static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
// Seconds starting from the epoch // Seconds starting from the epoch
static time_t TimestampBase = time(nullptr); static time_t TimestampBase = time(nullptr);
// Returns number of milliseconds starting from 01 Jan 1970 GMT // Returns number of milliseconds starting from 01 Jan 1970 GMT
std::chrono::milliseconds chronox::getTimestamp() std::chrono::milliseconds chronox::getTimestamp()
{ {
time_point<steady_clock> t = steady_clock::now(); time_point<steady_clock> t = steady_clock::now();
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count(); uint64_t ms = duration_cast<milliseconds>(t.time_since_epoch()).count();
return std::chrono::milliseconds(ms - TimestampStartPoint + TimestampBase * 1000); return std::chrono::milliseconds(ms - TimestampStartPoint + TimestampBase * 1000);
} }
@@ -84,7 +84,7 @@ std::chrono::milliseconds chronox::getUptime()
{ {
time_point<steady_clock> t = steady_clock::now(); time_point<steady_clock> t = steady_clock::now();
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count(); uint64_t ms = duration_cast<milliseconds>(t.time_since_epoch()).count();
return std::chrono::milliseconds(ms - TimestampStartPoint); return std::chrono::milliseconds(ms - TimestampStartPoint);
} }
@@ -140,21 +140,15 @@ std::chrono::milliseconds chronox::ExecutionTime::getSpentTime() const
} }
// --------------- BufferQueue ----------------- // --------------- BufferQueue -----------------
BufferQueue::BufferQueue() BufferQueue::BufferQueue() {}
{
} BufferQueue::~BufferQueue() {}
BufferQueue::~BufferQueue()
{
}
void BufferQueue::push(const void* data, int bytes) void BufferQueue::push(const void* data, int bytes)
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
PBlock b = std::make_shared<Block>(); PBlock b = std::make_shared<Block>();
b->resize(bytes); b->resize(bytes);
memcpy(b->data(), data, bytes); memcpy(b->data(), data, bytes);
mBlockList.push_back(b); mBlockList.push_back(b);
@@ -164,8 +158,7 @@ void BufferQueue::push(const void* data, int bytes)
BufferQueue::PBlock BufferQueue::pull(int milliseconds) BufferQueue::PBlock BufferQueue::pull(int milliseconds)
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
mSignal.wait_for(l, std::chrono::milliseconds(milliseconds), mSignal.wait_for(l, std::chrono::milliseconds(milliseconds), [this]() { return !mBlockList.empty(); });
[this]() { return !mBlockList.empty(); });
PBlock r; PBlock r;
if (!mBlockList.empty()) if (!mBlockList.empty())
@@ -178,9 +171,7 @@ BufferQueue::PBlock BufferQueue::pull(int milliseconds)
} }
// ----------------- Semaphore --------------------- // ----------------- Semaphore ---------------------
Semaphore::Semaphore(unsigned int count) Semaphore::Semaphore(unsigned int count) : m_count(count) {}
: m_count(count)
{}
void Semaphore::notify() void Semaphore::notify()
{ {
@@ -196,7 +187,8 @@ void Semaphore::wait()
m_count--; m_count--;
} }
bool Semaphore::waitFor(std::chrono::milliseconds timeout) { 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, timeout, [this]() { return m_count > 0; })) if (!m_cv.wait_for(lock, timeout, [this]() { return m_count > 0; }))
@@ -231,7 +223,7 @@ uint64_t TimerQueue::add(std::chrono::milliseconds milliseconds, std::function<v
item.handler = std::move(handler); item.handler = std::move(handler);
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
uint64_t id = ++m_idcounter; uint64_t id = ++m_idcounter;
item.id = id; item.id = id;
m_items.push(std::move(item)); m_items.push(std::move(item));
lk.unlock(); lk.unlock();
@@ -246,19 +238,22 @@ uint64_t TimerQueue::add(std::chrono::milliseconds milliseconds, std::function<v
// 1 if the timer was cancelled. // 1 if the timer was cancelled.
// 0 if you were too late to cancel (or the timer ID was never valid to // 0 if you were too late to cancel (or the timer ID was never valid to
// start with) // start with)
size_t TimerQueue::cancel(uint64_t id) { size_t TimerQueue::cancel(uint64_t id)
{
// Instead of removing the item from the container (thus breaking the // Instead of removing the item from the container (thus breaking the
// heap integrity), we set the item as having no handler, and put // heap integrity), we set the item as having no handler, and put
// that handler on a new item at the top for immediate execution // that handler on a new item at the top for immediate execution
// The timer thread will then ignore the original item, since it has no // The timer thread will then ignore the original item, since it has no
// handler. // handler.
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
for (auto&& item : m_items.getContainer()) { for (auto&& item : m_items.getContainer())
if (item.id == id && item.handler) { {
if (item.id == id && item.handler)
{
WorkItem newItem; WorkItem newItem;
// Zero time, so it stays at the top for immediate execution // Zero time, so it stays at the top for immediate execution
newItem.end = Clock::time_point(); newItem.end = Clock::time_point();
newItem.id = 0; // Means it is a canceled item newItem.id = 0; // Means it is a canceled item
// Move the handler from item to newitem. // Move the handler from item to newitem.
// Also, we need to manually set the handler to nullptr, since // Also, we need to manually set the handler to nullptr, since
// the standard does not guarantee moving an std::function will // the standard does not guarantee moving an std::function will
@@ -285,8 +280,10 @@ size_t TimerQueue::cancelAll()
// Setting all "end" to 0 (for immediate execution) is ok, // Setting all "end" to 0 (for immediate execution) is ok,
// since it maintains the heap integrity // since it maintains the heap integrity
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
for (auto&& item : m_items.getContainer()) { for (auto&& item : m_items.getContainer())
if (item.id) { {
if (item.id)
{
item.end = Clock::time_point(); item.end = Clock::time_point();
item.id = 0; item.id = 0;
} }
@@ -307,9 +304,12 @@ void TimerQueue::run()
if (end.first) if (end.first)
{ {
// Timers found, so wait until it expires (or something else changes) // Timers found, so wait until it expires (or something else changes)
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end.second - std::chrono::steady_clock::now()); auto milliseconds =
std::chrono::duration_cast<std::chrono::milliseconds>(end.second - std::chrono::steady_clock::now());
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
m_checkWork.wait(); m_checkWork.wait();
} }
@@ -327,11 +327,15 @@ void TimerQueue::run()
std::pair<bool, TimerQueue::Clock::time_point> TimerQueue::calcWaitTime() std::pair<bool, TimerQueue::Clock::time_point> TimerQueue::calcWaitTime()
{ {
std::lock_guard<std::mutex> lk(m_mtx); std::lock_guard<std::mutex> lk(m_mtx);
while (m_items.size()) { while (m_items.size())
if (m_items.top().handler) { {
if (m_items.top().handler)
{
// Item present, so return the new wait time // Item present, so return the new wait time
return std::make_pair(true, m_items.top().end); return std::make_pair(true, m_items.top().end);
} else { }
else
{
// Discard empty handlers (they were cancelled) // Discard empty handlers (they were cancelled)
m_items.pop(); m_items.pop();
} }
@@ -342,9 +346,11 @@ std::pair<bool, TimerQueue::Clock::time_point> TimerQueue::calcWaitTime()
return std::make_pair(false, Clock::time_point()); return std::make_pair(false, Clock::time_point());
} }
void TimerQueue::checkWork() { void TimerQueue::checkWork()
{
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
while (m_items.size() && m_items.top().end <= Clock::now()) { while (m_items.size() && m_items.top().end <= Clock::now())
{
WorkItem item(std::move(m_items.top())); WorkItem item(std::move(m_items.top()));
m_items.pop(); m_items.pop();
@@ -355,10 +361,12 @@ void TimerQueue::checkWork() {
} }
} }
bool TimerQueue::WorkItem::operator > (const TimerQueue::WorkItem& other) const { bool TimerQueue::WorkItem::operator>(const TimerQueue::WorkItem& other) const
{
return end > other.end; return end > other.end;
} }
std::vector<TimerQueue::WorkItem>& TimerQueue::Queue::getContainer() { std::vector<TimerQueue::WorkItem>& TimerQueue::Queue::getContainer()
{
return this->c; return this->c;
} }
+31 -31
View File
@@ -15,11 +15,11 @@
#include <assert.h> #include <assert.h>
#if defined(TARGET_WIN) #if defined(TARGET_WIN)
# include <WinSock2.h> #include <WinSock2.h>
# include <Windows.h> #include <Windows.h>
#endif #endif
typedef std::recursive_mutex Mutex; typedef std::recursive_mutex Mutex;
typedef std::unique_lock<std::recursive_mutex> Lock; typedef std::unique_lock<std::recursive_mutex> Lock;
class SyncHelper class SyncHelper
@@ -38,15 +38,15 @@ public:
bool waitFor(std::chrono::milliseconds timeout); bool waitFor(std::chrono::milliseconds timeout);
private: private:
std::mutex m_mtx; std::mutex m_mtx;
std::condition_variable m_cv; std::condition_variable m_cv;
unsigned int m_count; unsigned int m_count;
}; };
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();
}; };
@@ -61,21 +61,22 @@ public:
// 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();
std::chrono::milliseconds getSpentTime() const; std::chrono::milliseconds getSpentTime() const;
protected: protected:
std::chrono::milliseconds mStart; std::chrono::milliseconds mStart;
}; };
@@ -88,16 +89,16 @@ public:
BufferQueue(); BufferQueue();
~BufferQueue(); ~BufferQueue();
typedef std::vector<uint8_t> Block; typedef std::vector<uint8_t> Block;
typedef std::shared_ptr<Block> PBlock; typedef std::shared_ptr<Block> PBlock;
void push(const void* data, int bytes); void push(const void* data, int bytes);
PBlock pull(int milliseconds); PBlock pull(int milliseconds);
protected: protected:
std::mutex mMutex; std::mutex mMutex;
std::condition_variable mSignal; std::condition_variable mSignal;
std::deque<PBlock> mBlockList; std::deque<PBlock> mBlockList;
}; };
@@ -106,7 +107,7 @@ protected:
// Allows execution of handlers at a specified time in the future // Allows execution of handlers at a specified time in the future
// Guarantees: // Guarantees:
// - All handlers are executed ONCE, even if canceled (aborted parameter will // - All handlers are executed ONCE, even if canceled (aborted parameter will
//be set to true) // be set to true)
// - If TimerQueue is destroyed, it will cancel all handlers. // - If TimerQueue is destroyed, it will cancel all handlers.
// - Handlers are ALWAYS executed in the Timer Queue worker thread. // - Handlers are ALWAYS executed in the Timer Queue worker thread.
// - Handlers execution order is NOT guaranteed // - Handlers execution order is NOT guaranteed
@@ -128,38 +129,37 @@ public:
// 1 if the timer was cancelled. // 1 if the timer was cancelled.
// 0 if you were too late to cancel (or the timer ID was never valid to // 0 if you were too late to cancel (or the timer ID was never valid to
// start with) // start with)
size_t cancel(uint64_t id); size_t cancel(uint64_t id);
//! Cancels all timers //! Cancels all timers
// \return // \return
// The number of timers cancelled // The number of timers cancelled
size_t cancelAll(); size_t cancelAll();
private: private:
using Clock = std::chrono::steady_clock; using Clock = std::chrono::steady_clock;
TimerQueue(const TimerQueue&) = delete; TimerQueue(const TimerQueue&) = delete;
TimerQueue& operator=(const TimerQueue&) = delete; TimerQueue& operator=(const TimerQueue&) = delete;
void run(); void run();
std::pair<bool, Clock::time_point> calcWaitTime(); std::pair<bool, Clock::time_point> calcWaitTime();
void checkWork(); void checkWork();
Semaphore m_checkWork; Semaphore m_checkWork;
std::thread m_th; std::thread m_th;
bool m_finish = false; bool m_finish = false;
uint64_t m_idcounter = 0; uint64_t m_idcounter = 0;
struct WorkItem struct WorkItem
{ {
Clock::time_point end; Clock::time_point end;
uint64_t id; // id==0 means it was cancelled uint64_t id; // id==0 means it was cancelled
std::function<void(bool)> handler; std::function<void(bool)> handler;
bool operator > (const WorkItem& other) const; bool operator>(const WorkItem& other) const;
}; };
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();
+3 -3
View File
@@ -6,7 +6,7 @@ thread_pool::thread_pool(size_t num_of_threads, const std::string& name)
if (!num_of_threads) if (!num_of_threads)
num_of_threads = std::thread::hardware_concurrency(); num_of_threads = std::thread::hardware_concurrency();
for(size_t idx = 0; idx < num_of_threads; idx++) for (size_t idx = 0; idx < num_of_threads; idx++)
this->workers.emplace_back(std::thread(&thread_pool::run_worker, this)); this->workers.emplace_back(std::thread(&thread_pool::run_worker, this));
} }
@@ -45,7 +45,7 @@ thread_pool::~thread_pool()
stop = true; stop = true;
} }
this->condition.notify_all(); this->condition.notify_all();
for(std::thread &worker: workers) for (std::thread& worker : workers)
worker.join(); worker.join();
} }
@@ -58,7 +58,7 @@ 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; });
if (!tasks.empty()) if (!tasks.empty())
{ {
t = tasks.front(); t = tasks.front();
+10 -10
View File
@@ -21,26 +21,26 @@ public:
thread_pool(size_t num_of_threads, const std::string& thread_name); thread_pool(size_t num_of_threads, const std::string& thread_name);
~thread_pool(); ~thread_pool();
void enqueue(const task& task); void enqueue(const task& task);
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50)); void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
size_t size(); size_t size();
size_t threads(); size_t threads();
private: private:
// need to keep track of threads so we can join them // need to keep track of threads so we can join them
std::vector< std::thread > workers; std::vector<std::thread> workers;
// the task queue // the task queue
std::queue< task > tasks; std::queue<task> tasks;
// synchronization // synchronization
std::mutex queue_mutex; std::mutex queue_mutex;
std::condition_variable condition; std::condition_variable condition;
std::atomic_bool stop = false; std::atomic_bool stop = false;
// thread name prefix for worker threads // thread name prefix for worker threads
std::string name; std::string name;
void run_worker(); void run_worker();
}; };
#endif #endif

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