Compare commits

..

2 Commits

Author SHA1 Message Date
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
206 changed files with 20692 additions and 21019 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
+137 -154
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();
@@ -389,13 +384,12 @@ 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();
} }
@@ -990,54 +990,43 @@ HRESULT DirectSoundPrivateCreate (OUT LPKSPROPERTYSET * ppKsPropertySet)
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;
} }
@@ -1049,20 +1038,14 @@ BOOL GetInfoFromDSoundGUID( GUID i_sGUID, int &dwWaveID)
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;
+75 -75
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(); bool open();
void close(); void close();
unsigned playedTime() const; unsigned playedTime() const;
bool isSimulate() const; bool isSimulate() const;
void setSimulate(bool s); void setSimulate(bool s);
bool closing(); bool closing();
Format getFormat(); 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
+67 -68
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 // find the f
double f = (double)mFreq.QuadPart / 1000.0; double f = (double)mFreq.QuadPart / 1000.0;
//find the difference // find the difference
unsigned __int64 diff = c.QuadPart - mCounter.QuadPart; unsigned __int64 diff = c.QuadPart - mCounter.QuadPart;
mCounter.QuadPart = c.QuadPart; mCounter.QuadPart = c.QuadPart;
diff = (unsigned __int64)((double)diff / f + 0.5); //get ms diff = (unsigned __int64)((double)diff / f + 0.5); // get ms
diff += mTailTime; diff += mTailTime;
if (diff > mDepthTime) if (diff > mDepthTime)
{ {
mTailTime = 0; mTailTime = 0;
return mDepthTime; return mDepthTime;
} }
else else
{ {
mTailTime = (unsigned )(diff % (unsigned __int64)mQuantTime); mTailTime = (unsigned)(diff % (unsigned __int64)mQuantTime);
unsigned int t = (unsigned )(diff / (unsigned __int64)mQuantTime); unsigned int t = (unsigned)(diff / (unsigned __int64)mQuantTime);
return t * 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;
+27 -55
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,18 +70,19 @@ 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();
@@ -120,7 +92,7 @@ public:
}; };
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
+220 -227
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)); Resampler* resampler = (rate == 8000)
size_t inputProcessed = 0; ? &mResampler8
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize); : ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length 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 // Queue data
mData.add(mTempBuffer.data(), outputSize); 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)
throw Exception(ERR_MIXER_OVERFLOW);
}
channel->addPcm(inputRate, inputData, inputLength); 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)
throw Exception(ERR_MIXER_OVERFLOW);
}
channel->addPcm(rate, w.data(), w.filled()); channel->addPcm(rate, w.data(), w.filled());
//ICELogSpecial(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes"); // 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 // No active channels - nothing to mix - exit
if (!activeCounter) 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
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 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();
} }
+30 -33
View File
@@ -16,46 +16,43 @@
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]; Stream mChannelList[AUDIO_MIX_CHANNEL_COUNT];
Mutex mMutex; Mutex mMutex;
DataWindow mOutput; DataWindow mOutput;
std::atomic_int mActiveCounter; std::atomic_int mActiveCounter;
void mix(); void mix();
Stream* allocateChannel(void* context, unsigned ssrc); Stream* allocateChannel(void* context, unsigned ssrc);
public: public:
Mixer(); Mixer();
~Mixer(); ~Mixer();
@@ -65,8 +62,8 @@ namespace Audio
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
+85 -90
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& 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 */
channel.mSampleMax = (unsigned int)sample;
}
channel.mCounter++;
/* Will we get an overflow with the current gain factor? */
if (((sample * channel.mIgain) >> 16) > channel.mIpeak)
{
/* 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 (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);
} }
channel.mCounter ++;
/* Will we get an overflow with the current gain factor? */
if (((sample * channel.mIgain) >> 16) > channel.mIpeak)
{
/* 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 (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);
}
} }
// --- 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;
} }
+33 -32
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); void process(void* pcm, int length);
}; };
class AecFilter class AecFilter
{ {
public: 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: protected:
void* mCtx; /// The echo canceller context's pointer. void* mCtx; /// The echo canceller context's pointer.
Mutex mGuard; /// Mutex to protect this instance. Mutex mGuard; /// Mutex to protect this instance.
int mFrameTime; /// Duration of single audio frame (in milliseconds) int mFrameTime; /// Duration of single audio frame (in milliseconds)
int mRate; int mRate;
}; };
class DenoiseFilter class DenoiseFilter
{ {
public: 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
+73 -74
View File
@@ -7,7 +7,7 @@
#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>
@@ -16,91 +16,90 @@
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
+333 -334
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 // Open wavein
MMRESULT mmres = waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION); MMRESULT mmres =
if (mmres != MMSYSERR_NOERROR) waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
{ if (mmres != MMSYSERR_NOERROR)
mFakeMode = true; {
return; mFakeMode = true;
} return;
else }
mFakeMode = false; else
mFakeMode = false;
// Create the buffers for running // Create the buffers for running
mBufferIndex = 0; mBufferIndex = 0;
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].prepare(mDevHandle); mBufferList[i].prepare(mDevHandle);
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++) for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
mBufferList[i].addToDevice(mDevHandle); mBufferList[i].addToDevice(mDevHandle);
/*mmres = */waveInStart(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 // Set shutdown signal
if (!mThreadHandle) if (!mThreadHandle)
return; return;
::SetEvent(mShutdownSignal);
::WaitForSingleObject(mThreadHandle, INFINITE);
mThreadHandle = 0;
::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;
buffer.unprepare(impl->mDevice); // Wait for played buffer
if (!exitSignal) WaitForSingleObject(impl->mDoneSignal, 500);
{
bool useAEC = true;
if (impl->connection())
impl->connection()->onSpkData(Format(), buffer.mData, AUDIO_SPK_BUFFER_SIZE);
else
memset(buffer.mData, 0, AUDIO_SPK_BUFFER_SIZE);
buffer.prepare(impl->mDevice); // Iterate buffers to find played
buffer.write(impl->mDevice); for (i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
} {
else index = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
exitCount++; Buffer& buffer = impl->mBufferList[index];
} if (!(buffer.mHeader->dwFlags & WHDR_DONE))
impl->mBufferIndex = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT; break;
}
while (!exitSignal || exitCount < AUDIO_SPK_BUFFER_COUNT); buffer.unprepare(impl->mDevice);
impl->closeDevice(); if (!exitSignal)
{
bool useAEC = true;
if (impl->connection())
impl->connection()->onSpkData(Format(), buffer.mData, AUDIO_SPK_BUFFER_SIZE);
else
memset(buffer.mData, 0, AUDIO_SPK_BUFFER_SIZE);
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
+79 -79
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(); bool open();
void close(); void close();
HWAVEOUT handle(); HWAVEOUT handle();
unsigned playedTime(); unsigned playedTime();
void setFakeMode(bool fakemode); void setFakeMode(bool fakemode);
bool fakeMode(); bool fakeMode();
bool closing(); bool closing();
protected: 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; volatile LONG mPlayedCount;
unsigned mBufferIndex; unsigned mBufferIndex;
bool mFailed; bool mFailed;
void openDevice(); void openDevice();
void closeDevice(); void closeDevice();
bool areBuffersFinished(); bool areBuffersFinished();
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
static void threadProc(void* arg); 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"
+18 -20
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);
} }
+70 -71
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 // Returns provider RTP profile name
std::string streamName() override; std::string streamProfile() override;
// Returns provider RTP profile name // Sets destination IP address
std::string streamProfile() override; void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
// Sets destination IP address // Processes incoming data
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override; void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
// Processes incoming data // This method is called by user agent to send ICE packet from mediasocket
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override; void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer,
unsigned int datasize) override;
// This method is called by user agent to send ICE packet from mediasocket // Updates SDP offer
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override; void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
// Updates SDP offer // Called by user agent when session is deleted.
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override; void sessionDeleted() override;
// Called by user agent when session is deleted. // Called by user agent when session is terminated.
void sessionDeleted() override; void sessionTerminated() override;
// Called by user agent when session is terminated. // Called by user agent when session is started.
void sessionTerminated() override; void sessionEstablished(int conntype) override;
// Called by user agent when session is started. // Called by user agent to save media socket for this provider
void sessionEstablished(int conntype) override; void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override;
// Called by user agent to save media socket for this provider // Called by user agent to get media socket for this provider
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override; RtpPair<PDatagramSocket>& socket(int family) override;
// Called by user agent to get media socket for this provider // Called by user agent to process media stream description from remote peer.
RtpPair<PDatagramSocket>& socket(int family) override; // Returns true if description is processed succesfully. Otherwise method returns false.
// myAnswer sets if the answer will be sent after.
// Called by user agent to process media stream description from remote peer. bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) override;
// 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 // Socket handles to operate
RtpPair<PDatagramSocket> mSocket4, mSocket6; RtpPair<PDatagramSocket> mSocket4, mSocket6;
// Destination IP4/6 address // Destination IP4/6 address
RtpPair<InternetAddress> mDestination; RtpPair<InternetAddress> mDestination;
MT::PStream mActiveStream; MT::PStream mActiveStream;
UserAgent& mUserAgent; UserAgent& mUserAgent;
MT::Terminal& mTerminal; MT::Terminal& mTerminal;
MT::Statistics mBackupStats; MT::Statistics mBackupStats;
unsigned mState; unsigned mState;
SrtpSuite mSrtpSuite; SrtpSuite mSrtpSuite;
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
struct RemoteCodec struct RemoteCodec
{ {
RemoteCodec(MT::Codec::Factory* factory, int payloadType) RemoteCodec(MT::Codec::Factory* factory, int payloadType) : mFactory(factory), mRemotePayloadType(payloadType)
:mFactory(factory), mRemotePayloadType(payloadType) {
{ } }
MT::Codec::Factory* mFactory; MT::Codec::Factory* mFactory;
int mRemotePayloadType; int mRemotePayloadType;
}; };
std::vector<RemoteCodec> mAvailableCodecs; std::vector<RemoteCodec> mAvailableCodecs;
int mRemoteTelephoneCodec; // Payload type of remote rfc2833 codec int mRemoteTelephoneCodec; // Payload type of remote rfc2833 codec
bool mRemoteNoSdp; // Marks if we got no-sdp offer bool mRemoteNoSdp; // Marks if we got no-sdp offer
MT::CodecListPriority mCodecPriority; MT::CodecListPriority mCodecPriority;
MT::Stream::MediaObserver* mMediaObserver = nullptr; MT::Stream::MediaObserver* mMediaObserver = nullptr;
void* mMediaObserverTag = nullptr; void* mMediaObserverTag = nullptr;
std::string createCryptoAttribute(SrtpSuite suite, int tag); std::string createCryptoAttribute(SrtpSuite suite, int tag);
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs); void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
void setStateImpl(unsigned state);
// 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
+136 -127
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
@@ -139,11 +141,11 @@ 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,
@@ -153,21 +155,23 @@ class UserAgent: public resip::ClientRegistrationHandler,
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;
+129 -128
View File
@@ -16,8 +16,8 @@
* 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());
mNewQueued.clear();
sendOutgoing(); DWORD rescode = ::WaitForMultipleObjects(2, eventarray, FALSE, INFINITE);
if (rescode == WAIT_OBJECT_0)
{
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; char* dataOut = (char*)ptr;
while (capacity && hasData()) while (capacity && hasData())
{
// Check if mIncomingData is empty
if (!mIncomingData.size())
{ {
unsigned available = mStack.appData(NULL); // Check if mIncomingData is empty
if (!available) if (!mIncomingData.size())
return 0; {
unsigned available = mStack.appData(NULL);
if (!available)
return 0;
mIncomingData.resize(available); mIncomingData.resize(available);
mIncomingData.rewind(); mIncomingData.rewind();
mStack.appData(mIncomingData.mutableData()); 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);
} }
+70 -71
View File
@@ -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 // Returns provider RTP name
virtual std::string streamName(); virtual std::string streamName();
// Returns provider RTP profile name // Returns provider RTP profile name
virtual std::string streamProfile(); virtual std::string streamProfile();
// Sets destination IP address // Sets destination IP address
virtual void setDestinationAddress(InternetAddress& addr); virtual void setDestinationAddress(InternetAddress& addr);
// Processes incoming data // Processes incoming data
virtual void processData(const void* dataBuffer, int dataSize); virtual void processData(const void* dataBuffer, int dataSize);
// 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(InternetAddress& destination, const void* dataBuffer, unsigned int datasize); virtual void sendData(InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
// Updates SDP offer // Updates SDP offer
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp); virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp);
// Called by user agent when session is terminated. // Called by user agent when session is terminated.
virtual void sessionTerminated(); virtual void sessionTerminated();
// Called by user agent when session is started. // Called by user agent when session is started.
virtual void sessionEstablished(int conntype); 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 // Called by user agent to get media socket for this provider
virtual DatagramSocket& socket(int family); virtual DatagramSocket& socket(int family);
// 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); virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media);
virtual void thread(); virtual void thread();
// Enqueues outgoing packet to sending queue // Enqueues outgoing packet to sending queue
void queueData(const void* bufferPtr, int bufferSize); void queueData(const void* bufferPtr, int bufferSize);
void setBandwidth(unsigned int bytesPerSecond); void setBandwidth(unsigned int bytesPerSecond);
unsigned int bandwidth(); unsigned int bandwidth();
// Checks if there is any received application data // Checks if there is any received application data
bool hasData(); bool hasData();
// Reads received data. If ptr is NULL - the length of available data is returned. // Reads received data. If ptr is NULL - the length of available data is returned.
unsigned getData(void* ptr, unsigned capacity); unsigned getData(void* ptr, unsigned capacity);
void setEncryptionKey(void* ptr, unsigned length); 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 // Socket handles to operate
DatagramSocket mSocket4; DatagramSocket mSocket4;
DatagramSocket mSocket6; DatagramSocket mSocket6;
// Destination IP4/6 address // Destination IP4/6 address
InternetAddress mDestination; InternetAddress mDestination;
// Win32 exit signal // Win32 exit signal
HANDLE mExitSignal; HANDLE mExitSignal;
// Win32 "new outgoing data" signal // Win32 "new outgoing data" signal
HANDLE mDataSignal; HANDLE mDataSignal;
// Mutex to protect queuing/sending outgoing data // Mutex to protect queuing/sending outgoing data
resip::Mutex mOutgoingMtx; resip::Mutex mOutgoingMtx;
std::vector<std::string> std::vector<std::string> mNewQueued;
mNewQueued; resip::Mutex mNewQueuedGuard;
resip::Mutex mNewQueuedGuard; resip::Mutex mStackGuard;
resip::Mutex mStackGuard;
unsigned int mBandwidth; unsigned int mBandwidth;
std::string mEncryptionKey; 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. // Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
void encrypt(void* dataPtr, int dataSize); void encrypt(void* dataPtr, int dataSize);
// 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 decrypt(void* dataPtr, int dataSize); void decrypt(void* dataPtr, int dataSize);
// Calculates CRC // Calculates CRC
unsigned crc(const void* dataptr, int datasize); unsigned crc(const void* dataptr, int datasize);
void sendOutgoing(); void sendOutgoing();
}; };
#endif #endif
+112 -106
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
@@ -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)
{ {
@@ -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;
} }
+247 -249
View File
@@ -49,279 +49,276 @@ 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. // Application specific data
int mOfferAnswerCounter; void* mTag;
// List of turn prefixes related to sessioj // Used to count number of transistions to Connected state and avoid multiple onEstablished events.
std::vector<int> mTurnPrefixList; int mOfferAnswerCounter;
// True if user agent has to send offer // List of turn prefixes related to sessioj
bool mHasToSendOffer; std::vector<int> mTurnPrefixList;
// True if user agent has to enqueue offer after ice gather finished // True if user agent has to send offer
bool mSendOfferUpdateAfterIceGather; bool mHasToSendOffer;
// Related sip account // True if user agent has to enqueue offer after ice gather finished
PAccount mAccount; bool mSendOfferUpdateAfterIceGather;
// User headers for INVITE transaction // Related sip account
UserHeaders mUserHeaders; PAccount mAccount;
std::string remoteAddress() const; // User headers for INVITE transaction
void setRemoteAddress(const std::string& address); UserHeaders mUserHeaders;
void* tag(); std::string remoteAddress() const;
void setTag(void* tag); void setRemoteAddress(const std::string& address);
int sessionId();
int increaseSdpVersion();
int addRef();
int release();
// Deletes providers and media sockets void* tag();
void clearProvidersAndSockets(); void setTag(void* tag);
int sessionId();
int increaseSdpVersion();
int addRef();
int release();
// Deletes providers // Deletes providers and media sockets
void clearProviders(); void clearProvidersAndSockets();
// Helper method to find audio provider for active sip stream // Deletes providers
AudioProvider* findProviderForActiveAudio(); void clearProviders();
void processCommandList(); // Helper method to find audio provider for active sip stream
void addCommand(Command* cmd); AudioProvider* findProviderForActiveAudio();
void enqueueOffer();
void processQueuedOffer(); void processCommandList();
static int generateId(); void addCommand(Command* cmd);
static std::atomic_int IdGenerator; void enqueueOffer();
static std::atomic_int InstanceCounter; 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;
@@ -338,76 +335,77 @@ typedef std::shared_ptr<Session> PSession;
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); void setType(Type type);
Type type(); Type type();
Session* session(); Session* session();
void setSession(Session* session); void setSession(Session* session);
UserAgent* ua(); UserAgent* ua();
void setUa(UserAgent* ua); void setUa(UserAgent* ua);
// Used for subscriptions/messages // Used for subscriptions/messages
int sessionId(); int sessionId();
// Used for subscriptions/messages // Used for subscriptions/messages
void* tag() const; void* tag() const;
void setTag(void* tag); void setTag(void* tag);
// Used for subscriptions/messages // Used for subscriptions/messages
std::string remoteAddress() const; std::string remoteAddress() const;
void setRemoteAddress(std::string address); void setRemoteAddress(std::string address);
void runTerminatedEvent(Type type, int code = 0, int reason = 0); void runTerminatedEvent(Type type, int code = 0, int reason = 0);
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile); void setUASProfile(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
+79 -84
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};
}; };
+9 -7
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,10 +112,10 @@ 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;
@@ -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);
+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
+60 -60
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) if (dwMajorVersion == 5)
return Win_Xp; return Win_Xp;
if (dwMinorVersion == 1) if (dwMinorVersion == 1)
return Win_Seven; return Win_Seven;
else else
return Win_Vista; 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
+26 -28
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();
usageIter->second = usageIter->second - 1;
if (!usageIter->second)
mUsage.erase(usageIter);
return usageCount(); return usageCount();
usageIter->second = usageIter->second - 1;
if (!usageIter->second)
mUsage.erase(usageIter);
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
+171 -169
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
+30 -31
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();
+9 -9
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