- clang format + naming improved
This commit is contained in:
@@ -9,14 +9,13 @@
|
||||
#include "HL_String.h"
|
||||
|
||||
#if defined(TARGET_ANDROID)
|
||||
# include "../engine/audio/Audio_Android.h"
|
||||
#include "../engine/audio/Audio_Android.h"
|
||||
#endif
|
||||
|
||||
#define LOG_SUBSYSTEM "audio"
|
||||
|
||||
|
||||
AudioManager::AudioManager()
|
||||
:mTerminal(nullptr), mAudioMonitoring(nullptr)
|
||||
AudioManager::AudioManager() : mTerminal(nullptr), mAudioMonitoring(nullptr)
|
||||
{
|
||||
mPlayer.setDelegate(this);
|
||||
}
|
||||
@@ -92,7 +91,7 @@ void AudioManager::start(int usageId)
|
||||
|
||||
if (!mAudioOutput)
|
||||
{
|
||||
Audio::Enumerator *enumerator = Audio::Enumerator::make(usageId == atNull);
|
||||
Audio::Enumerator* enumerator = Audio::Enumerator::make(usageId == atNull);
|
||||
enumerator->open(Audio::mySpeaker);
|
||||
int outputIndex = enumerator->indexOfDefaultDevice();
|
||||
|
||||
@@ -102,8 +101,7 @@ void AudioManager::start(int usageId)
|
||||
if (outputIndex >= enumerator->count())
|
||||
outputIndex = 0;
|
||||
|
||||
mAudioOutput = Audio::POutputDevice(
|
||||
Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
|
||||
mAudioOutput = Audio::POutputDevice(Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
|
||||
}
|
||||
else
|
||||
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
|
||||
@@ -139,7 +137,7 @@ void AudioManager::stop(int usageId)
|
||||
{
|
||||
LOCK_MANAGER;
|
||||
|
||||
ICELogInfo( << "Stop main audio with usage id " << usageId);
|
||||
ICELogInfo(<< "Stop main audio with usage id " << usageId);
|
||||
if (mTerminal)
|
||||
{
|
||||
if (mTerminal->audio())
|
||||
@@ -196,9 +194,7 @@ void AudioManager::stopPlayFile(int usageId)
|
||||
mPlayer.release(usageId);
|
||||
}
|
||||
|
||||
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item)
|
||||
{
|
||||
}
|
||||
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item) {}
|
||||
|
||||
void AudioManager::process()
|
||||
{
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "../engine/media/MT_Box.h"
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
AudioPrefix_Ring = 1,
|
||||
@@ -31,7 +30,7 @@ enum
|
||||
|
||||
#define AudioSessionCoeff 64
|
||||
|
||||
class AudioManager: public Audio::Player::EndOfAudioDelegate
|
||||
class AudioManager : public Audio::Player::EndOfAudioDelegate
|
||||
{
|
||||
public:
|
||||
AudioManager();
|
||||
@@ -40,24 +39,24 @@ public:
|
||||
// static AudioManager& instance();
|
||||
|
||||
// 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
|
||||
void setTerminal(MT::Terminal* terminal);
|
||||
MT::Terminal* terminal();
|
||||
void setTerminal(MT::Terminal* terminal);
|
||||
MT::Terminal* terminal();
|
||||
|
||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||
Audio::DataConnection* audioMonitoring();
|
||||
|
||||
// Start/stop methods relies on usage counter; only first start and last stop opens/closes devices actually
|
||||
void start(int usageId);
|
||||
void stop(int usageId);
|
||||
void start(int usageId);
|
||||
void stop(int usageId);
|
||||
|
||||
// Inject a custom input device. Must be called before start(): when set,
|
||||
// start() skips construction of the default platform microphone. Pass an
|
||||
// empty pointer to clear the override.
|
||||
void setAudioInput(Audio::PInputDevice input);
|
||||
void setAudioOutput(Audio::POutputDevice output);
|
||||
void setAudioInput(Audio::PInputDevice input);
|
||||
void setAudioOutput(Audio::POutputDevice output);
|
||||
|
||||
enum AudioTarget
|
||||
{
|
||||
@@ -82,14 +81,14 @@ public:
|
||||
void process();
|
||||
|
||||
protected:
|
||||
Audio::PInputDevice mAudioInput;
|
||||
Audio::POutputDevice mAudioOutput;
|
||||
Audio::Player mPlayer;
|
||||
MT::Terminal* mTerminal;
|
||||
Audio::PInputDevice mAudioInput;
|
||||
Audio::POutputDevice mAudioOutput;
|
||||
Audio::Player mPlayer;
|
||||
MT::Terminal* mTerminal;
|
||||
Audio::DataConnection* mAudioMonitoring;
|
||||
|
||||
std::map<int, int> UsageMap;
|
||||
UsageCounter mUsage;
|
||||
std::mutex mGuard;
|
||||
std::map<int, int> UsageMap;
|
||||
UsageCounter mUsage;
|
||||
std::mutex mGuard;
|
||||
};
|
||||
#endif
|
||||
|
||||
+141
-152
@@ -21,8 +21,7 @@ const std::string Status_NoAudioManager = "no audio manager";
|
||||
|
||||
#define LOG_SUBSYSTEM "agent"
|
||||
|
||||
AgentImpl::AgentImpl()
|
||||
:mShutdown(false), mEventListChangeCondVar()
|
||||
AgentImpl::AgentImpl() : mShutdown(false), mEventListChangeCondVar()
|
||||
{
|
||||
#if defined(TARGET_ANDROID) || defined(TARGET_WIN)
|
||||
ice::GLogger.useDebugWindow(true);
|
||||
@@ -84,76 +83,59 @@ std::string AgentImpl::command(const std::string& command)
|
||||
}
|
||||
if (cmd == "config")
|
||||
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
|
||||
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
|
||||
{
|
||||
answer["status"] = Status_NoCommand;
|
||||
}
|
||||
{
|
||||
answer["status"] = Status_NoCommand;
|
||||
}
|
||||
}
|
||||
catch(std::exception& e)
|
||||
catch (std::exception& e)
|
||||
{
|
||||
answer["status"] = e.what();
|
||||
}
|
||||
@@ -172,17 +154,20 @@ std::string AgentImpl::read()
|
||||
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::string transport = d["transport"].asString();
|
||||
config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
|
||||
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_IPV4] = d["ipv4"].asBool();
|
||||
config()[CONFIG_IPV6] = d["ipv6"].asBool();
|
||||
|
||||
// Log file
|
||||
std::string logfile = d["logfile"].asString();
|
||||
std::string logfile = d["logfile"].asString();
|
||||
ice::Logger& logger = ice::GLogger;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (mThread)
|
||||
@@ -220,9 +205,9 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
|
||||
mTerminal = std::make_shared<MT::Terminal>(settings);
|
||||
|
||||
// Enable/disable codecs
|
||||
PVariantMap priorityConfig = std::make_shared<VariantMap>();
|
||||
PVariantMap priorityConfig = std::make_shared<VariantMap>();
|
||||
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;
|
||||
|
||||
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
|
||||
@@ -250,10 +235,10 @@ void AgentImpl::processStop(JsonCpp::Value& /*request*/, JsonCpp::Value& answer)
|
||||
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);
|
||||
PVariantMap c = std::make_shared<VariantMap>();
|
||||
PVariantMap c = std::make_shared<VariantMap>();
|
||||
|
||||
(*c)[CONFIG_USERNAME] = d["username"].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);
|
||||
// Locate account in map
|
||||
auto accountIter = mAccountMap.find(request["account_id"].asInt());
|
||||
auto accountIter = mAccountMap.find(request["account_id"].asInt());
|
||||
if (accountIter != mAccountMap.end())
|
||||
{
|
||||
accountIter->second->start();
|
||||
@@ -286,17 +271,17 @@ void AgentImpl::processStartAccount(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||
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);
|
||||
// Locate account in map
|
||||
auto accountIter = mAccountMap.find(request["account_id"].asInt());
|
||||
auto accountIter = mAccountMap.find(request["account_id"].asInt());
|
||||
if (accountIter != mAccountMap.end())
|
||||
{
|
||||
Account::UserInfo info;
|
||||
JsonCpp::Value& arg = request["userinfo"];
|
||||
Account::UserInfo info;
|
||||
JsonCpp::Value& arg = request["userinfo"];
|
||||
std::vector<std::string> keys = arg.getMemberNames();
|
||||
for (const std::string& k: keys)
|
||||
for (const std::string& k : keys)
|
||||
info[k] = arg[k].asString();
|
||||
accountIter->second->setUserInfo(info);
|
||||
|
||||
@@ -306,10 +291,10 @@ void AgentImpl::processSetUserInfoToAccount(JsonCpp::Value &request, JsonCpp::Va
|
||||
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);
|
||||
auto accountIter = mAccountMap.find(request["account_id"].asInt());
|
||||
auto accountIter = mAccountMap.find(request["account_id"].asInt());
|
||||
if (accountIter != mAccountMap.end())
|
||||
{
|
||||
PSession session = createSession(accountIter->second);
|
||||
@@ -345,9 +330,10 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||
if (sessionIter != mSessionMap.end())
|
||||
{
|
||||
// Ensure audio provider is here
|
||||
PSession session = sessionIter->second;
|
||||
PSession session = sessionIter->second;
|
||||
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)
|
||||
std::string path_faults = request["path_faults"].asString();
|
||||
@@ -372,7 +358,8 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
||||
{ "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())
|
||||
// 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
|
||||
|
||||
// Get user headers
|
||||
Session::UserHeaders info;
|
||||
JsonCpp::Value& arg = request["userinfo"];
|
||||
Session::UserHeaders info;
|
||||
JsonCpp::Value& arg = request["userinfo"];
|
||||
std::vector<std::string> keys = arg.getMemberNames();
|
||||
for (const std::string& k: keys)
|
||||
for (const std::string& k : keys)
|
||||
info[k] = arg[k].asString();
|
||||
session->setUserHeaders(info);
|
||||
|
||||
@@ -413,7 +400,7 @@ void AgentImpl::processStopSession(JsonCpp::Value& request, JsonCpp::Value& answ
|
||||
{
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 (!mAudioManager)
|
||||
@@ -445,13 +432,13 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
|
||||
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
||||
|
||||
// Accept session on SIP level
|
||||
PSession session = sessionIter->second;
|
||||
PSession session = sessionIter->second;
|
||||
|
||||
// Get user headers
|
||||
Session::UserHeaders info;
|
||||
JsonCpp::Value& arg = request["userinfo"];
|
||||
Session::UserHeaders info;
|
||||
JsonCpp::Value& arg = request["userinfo"];
|
||||
std::vector<std::string> keys = arg.getMemberNames();
|
||||
for (const std::string& k: keys)
|
||||
for (const std::string& k : keys)
|
||||
info[k] = arg[k].asString();
|
||||
session->setUserHeaders(info);
|
||||
|
||||
@@ -469,17 +456,17 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||
|
||||
int sessionId = request["session_id"].asInt();
|
||||
auto sessionIter = mSessionMap.find(sessionId);
|
||||
int sessionId = request["session_id"].asInt();
|
||||
auto sessionIter = mSessionMap.find(sessionId);
|
||||
if (sessionIter != mSessionMap.end())
|
||||
mSessionMap.erase(sessionIter);
|
||||
//#if defined(USE_AQUA_LIBRARY)
|
||||
// closeAqua(sessionId);
|
||||
//#endif
|
||||
// #if defined(USE_AQUA_LIBRARY)
|
||||
// closeAqua(sessionId);
|
||||
// #endif
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||
int sessionId = request["session_id"].asInt();
|
||||
SessionMap::iterator sessionIter = mSessionMap.find(sessionId);
|
||||
int sessionId = request["session_id"].asInt();
|
||||
SessionMap::iterator sessionIter = mSessionMap.find(sessionId);
|
||||
if (sessionIter != mSessionMap.end())
|
||||
{
|
||||
PSession session = sessionIter->second;
|
||||
PSession session = sessionIter->second;
|
||||
VariantMap result;
|
||||
session->getSessionInfo(Session::InfoOptions::Detailed,
|
||||
result);
|
||||
session->getSessionInfo(Session::InfoOptions::Detailed, result);
|
||||
|
||||
if (result.exists(SessionInfo_AudioCodec))
|
||||
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 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::string pem = request["cert"].asString();
|
||||
std::string pem = request["cert"].asString();
|
||||
|
||||
std::string::size_type pb = 0, pe = 0;
|
||||
while (pb != std::string::npos && pe != std::string::npos) {
|
||||
std::string::size_type pb = 0, pe = 0;
|
||||
while (pb != std::string::npos && pe != std::string::npos)
|
||||
{
|
||||
pb = pem.find(BeginCertificate, pb);
|
||||
pe = pem.find(EndCertificate, pe);
|
||||
|
||||
if (pb != std::string::npos && pe != std::string::npos && pe > pb) {
|
||||
if (pb != std::string::npos && pe != std::string::npos && pe > pb)
|
||||
{
|
||||
std::string cert = pem.substr(pb, pe - pb + EndCertificate.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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
ICELog(static_cast<ice::LogLevel>(level), "App", << message);
|
||||
@@ -600,7 +588,8 @@ void AgentImpl::stopAgentAndThread()
|
||||
stop();
|
||||
}
|
||||
catch (...)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
// Stop worker thread
|
||||
if (mThread)
|
||||
@@ -634,19 +623,19 @@ void AgentImpl::stopAgentAndThread()
|
||||
void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer)
|
||||
{
|
||||
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())
|
||||
{
|
||||
// Extract ptr to session
|
||||
PSession session = sessionIter->second;
|
||||
PSession session = sessionIter->second;
|
||||
|
||||
// Parse command
|
||||
std::string actionText = request["media_action"].asString(),
|
||||
directionText = request["media_direction"].asString();
|
||||
std::string actionText = request["media_action"].asString(),
|
||||
directionText = request["media_direction"].asString();
|
||||
|
||||
MT::Stream::MediaDirection direction = directionText == "incoming" ? MT::Stream::MediaDirection::Incoming
|
||||
: MT::Stream::MediaDirection::Outgoing;
|
||||
std::string path = request["path"].asString();
|
||||
MT::Stream::MediaDirection direction =
|
||||
directionText == "incoming" ? MT::Stream::MediaDirection::Incoming : MT::Stream::MediaDirection::Outgoing;
|
||||
std::string path = request["path"].asString();
|
||||
|
||||
// Try to open file
|
||||
AudioProvider* prov = session->findProviderForActiveAudio();
|
||||
@@ -672,42 +661,41 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (actionText == "write")
|
||||
else if (actionText == "write")
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Turn off recording from the stream
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Turn off recording from the stream
|
||||
prov->writeFile(Audio::PWavFileWriter(), direction);
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
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;
|
||||
}
|
||||
else
|
||||
answer["status"] = Status_NoCommand;
|
||||
}
|
||||
}
|
||||
else if (actionText == "mirror")
|
||||
{
|
||||
prov->setupMirror(request["enable"].asBool());
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
else
|
||||
answer["status"] = Status_NoCommand;
|
||||
}
|
||||
else
|
||||
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)
|
||||
{
|
||||
@@ -718,7 +706,9 @@ void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -856,8 +846,7 @@ void AgentImpl::onCheckFinished(PSession s, const char* description)
|
||||
}
|
||||
|
||||
// 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
|
||||
void AgentImpl::onSipConnectionFailed()
|
||||
|
||||
@@ -16,72 +16,72 @@
|
||||
#include <atomic>
|
||||
|
||||
|
||||
class AgentImpl: public UserAgent, public MT::Stream::MediaObserver
|
||||
class AgentImpl : public UserAgent, public MT::Stream::MediaObserver
|
||||
{
|
||||
protected:
|
||||
std::recursive_mutex mAgentMutex;
|
||||
std::mutex mEventListMutex;
|
||||
std::condition_variable mEventListChangeCondVar;
|
||||
std::vector<JsonCpp::Value> mEventList;
|
||||
bool mUseNativeAudio = false;
|
||||
std::recursive_mutex mAgentMutex;
|
||||
std::mutex mEventListMutex;
|
||||
std::condition_variable mEventListChangeCondVar;
|
||||
std::vector<JsonCpp::Value> mEventList;
|
||||
bool mUseNativeAudio = false;
|
||||
|
||||
typedef std::map<int, PAccount> AccountMap;
|
||||
AccountMap mAccountMap;
|
||||
AccountMap mAccountMap;
|
||||
|
||||
typedef std::map<int, PSession> SessionMap;
|
||||
SessionMap mSessionMap;
|
||||
SessionMap mSessionMap;
|
||||
|
||||
|
||||
std::shared_ptr<std::thread> mThread;
|
||||
std::atomic<bool> mShutdown;
|
||||
std::shared_ptr<MT::Terminal> mTerminal;
|
||||
std::shared_ptr<AudioManager> mAudioManager;
|
||||
Audio::DataConnection* mAudioMonitoring = nullptr;
|
||||
std::shared_ptr<std::thread> mThread;
|
||||
std::atomic<bool> mShutdown;
|
||||
std::shared_ptr<MT::Terminal> mTerminal;
|
||||
std::shared_ptr<AudioManager> mAudioManager;
|
||||
Audio::DataConnection* mAudioMonitoring = nullptr;
|
||||
|
||||
void run();
|
||||
void addEvent(const JsonCpp::Value& v);
|
||||
void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStart(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStop(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void stopAgentAndThread();
|
||||
void run();
|
||||
void addEvent(const JsonCpp::Value& v);
|
||||
void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStart(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStop(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||
void stopAgentAndThread();
|
||||
|
||||
public:
|
||||
AgentImpl();
|
||||
~AgentImpl();
|
||||
|
||||
std::string command(const std::string& command);
|
||||
bool waitForData(int milliseconds);
|
||||
std::string read();
|
||||
std::string command(const std::string& command);
|
||||
bool waitForData(int milliseconds);
|
||||
std::string read();
|
||||
|
||||
// Get access to internal audio manager. Value can be nullptr.
|
||||
const std::shared_ptr<AudioManager>& audioManager() const;
|
||||
|
||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||
Audio::DataConnection* monitoring() const;
|
||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||
Audio::DataConnection* monitoring() const;
|
||||
|
||||
// UserAgent overrides
|
||||
// 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
|
||||
void onNewSession(PSession s) override;
|
||||
void onNewSession(PSession s) override;
|
||||
|
||||
// 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
|
||||
// Conntype is type of establish event - EV_SIP or EV_ICE
|
||||
@@ -123,7 +123,8 @@ public:
|
||||
void onSipConnectionFailed() override;
|
||||
|
||||
// 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
|
||||
|
||||
@@ -1,27 +1,16 @@
|
||||
#include "Agent_Interface.h"
|
||||
#include "Agent_Impl.h"
|
||||
Agent::Agent()
|
||||
:mContext(new AgentImpl());
|
||||
Agent::Agent() : mContext(new AgentImpl());
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Agent::~Agent()
|
||||
{
|
||||
Agent::~Agent() {}
|
||||
|
||||
}
|
||||
|
||||
void Agent::write(const std::string& command)
|
||||
{
|
||||
|
||||
}
|
||||
void Agent::write(const std::string& command) {}
|
||||
|
||||
bool Agent::waitForData(int milliseconds)
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Agent::read()
|
||||
{
|
||||
|
||||
}
|
||||
std::string Agent::read() {}
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
class Agent
|
||||
{
|
||||
protected:
|
||||
void* mContext;
|
||||
void* mContext;
|
||||
|
||||
public:
|
||||
Agent();
|
||||
~Agent();
|
||||
void write(const std::string& command);
|
||||
bool waitForData(int milliseconds);
|
||||
std::string read();
|
||||
Agent();
|
||||
~Agent();
|
||||
void write(const std::string& command);
|
||||
bool waitForData(int milliseconds);
|
||||
std::string read();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+372
-378
@@ -14,586 +14,580 @@ using namespace Audio;
|
||||
|
||||
// -------------------- AndroidEnumerator -----------------------------
|
||||
|
||||
AndroidEnumerator::AndroidEnumerator()
|
||||
{}
|
||||
AndroidEnumerator::AndroidEnumerator() {}
|
||||
|
||||
AndroidEnumerator::~AndroidEnumerator()
|
||||
{}
|
||||
AndroidEnumerator::~AndroidEnumerator() {}
|
||||
|
||||
int AndroidEnumerator::indexOfDefaultDevice()
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AndroidEnumerator::count()
|
||||
{
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int AndroidEnumerator::idAt(int index)
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (++mUsageCounter == 1)
|
||||
internalOpen();
|
||||
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (++mUsageCounter == 1)
|
||||
internalOpen();
|
||||
}
|
||||
|
||||
void OpenSLEngine::close()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (mUsageCounter == 0)
|
||||
return;
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (mUsageCounter == 0)
|
||||
return;
|
||||
|
||||
if (--mUsageCounter == 0)
|
||||
internalClose();
|
||||
if (--mUsageCounter == 0)
|
||||
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()
|
||||
{
|
||||
SLresult resultCode;
|
||||
SLresult resultCode;
|
||||
|
||||
// Instantiate OpenSL ES engine object
|
||||
resultCode = slCreateEngine(&mEngineObject, 0, nullptr, 0, nullptr, nullptr);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Instantiate OpenSL ES engine object
|
||||
resultCode = slCreateEngine(&mEngineObject, 0, nullptr, 0, nullptr, nullptr);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Bring it online (realize)
|
||||
resultCode = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Bring it online (realize)
|
||||
resultCode = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Get interface finally
|
||||
resultCode = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Get interface finally
|
||||
resultCode = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
ICELogInfo(<< "OpenSL engine object created.");
|
||||
ICELogInfo(<< "OpenSL engine object created.");
|
||||
}
|
||||
|
||||
void OpenSLEngine::internalClose()
|
||||
{
|
||||
if (mEngineObject != nullptr)
|
||||
{
|
||||
ICELogInfo(<< "Destroy OpenSL engine object.");
|
||||
if (mEngineObject != nullptr)
|
||||
{
|
||||
ICELogInfo(<< "Destroy OpenSL engine object.");
|
||||
|
||||
(*mEngineObject)->Destroy(mEngineObject);
|
||||
mEngineObject = nullptr;
|
||||
mEngineInterface = nullptr;
|
||||
}
|
||||
(*mEngineObject)->Destroy(mEngineObject);
|
||||
mEngineObject = nullptr;
|
||||
mEngineInterface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SLEngineItf OpenSLEngine::getNativeEngine() const
|
||||
{
|
||||
return mEngineInterface;
|
||||
return mEngineInterface;
|
||||
}
|
||||
|
||||
static OpenSLEngine OpenSLEngineInstance;
|
||||
|
||||
OpenSLEngine& OpenSLEngine::instance()
|
||||
OpenSLEngine& OpenSLEngine::instance()
|
||||
{
|
||||
return OpenSLEngineInstance;
|
||||
return OpenSLEngineInstance;
|
||||
}
|
||||
|
||||
// --------------- Input implementation ----------------
|
||||
AndroidInputDevice::AndroidInputDevice(int devId)
|
||||
{}
|
||||
AndroidInputDevice::AndroidInputDevice(int devId) {}
|
||||
|
||||
AndroidInputDevice::~AndroidInputDevice()
|
||||
{}
|
||||
AndroidInputDevice::~AndroidInputDevice() {}
|
||||
|
||||
static int RateToProbe[12][2] = {
|
||||
{ SL_SAMPLINGRATE_16, 16000 },
|
||||
{ SL_SAMPLINGRATE_8, 8000 },
|
||||
{ SL_SAMPLINGRATE_32, 32000 },
|
||||
{ SL_SAMPLINGRATE_44_1, 44100 },
|
||||
{ SL_SAMPLINGRATE_11_025, 10025 },
|
||||
{ SL_SAMPLINGRATE_22_05, 22050 },
|
||||
{ SL_SAMPLINGRATE_24, 24000 },
|
||||
{ SL_SAMPLINGRATE_48, 48000 },
|
||||
{ SL_SAMPLINGRATE_64, 64000 },
|
||||
{ SL_SAMPLINGRATE_88_2, 88200 },
|
||||
{ SL_SAMPLINGRATE_96, 96000 },
|
||||
{ SL_SAMPLINGRATE_192, 192000} };
|
||||
{SL_SAMPLINGRATE_16, 16000}, {SL_SAMPLINGRATE_8, 8000}, {SL_SAMPLINGRATE_32, 32000},
|
||||
{SL_SAMPLINGRATE_44_1, 44100}, {SL_SAMPLINGRATE_11_025, 10025}, {SL_SAMPLINGRATE_22_05, 22050},
|
||||
{SL_SAMPLINGRATE_24, 24000}, {SL_SAMPLINGRATE_48, 48000}, {SL_SAMPLINGRATE_64, 64000},
|
||||
{SL_SAMPLINGRATE_88_2, 88200}, {SL_SAMPLINGRATE_96, 96000}, {SL_SAMPLINGRATE_192, 192000}};
|
||||
|
||||
bool AndroidInputDevice::open()
|
||||
{
|
||||
if (active())
|
||||
return true;
|
||||
if (active())
|
||||
return true;
|
||||
|
||||
OpenSLEngine::instance().open();
|
||||
OpenSLEngine::instance().open();
|
||||
|
||||
// Probe few sampling rates
|
||||
bool opened = false;
|
||||
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
|
||||
{
|
||||
try
|
||||
// Probe few sampling rates
|
||||
bool opened = false;
|
||||
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
|
||||
{
|
||||
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1]);
|
||||
mDeviceRate = RateToProbe[rateIndex][1];
|
||||
ICELogInfo(<< "Input Opened with rate " << mDeviceRate << " and rate index " << rateIndex);
|
||||
opened = mDeviceRate != 0;
|
||||
if (!opened)
|
||||
internalClose();
|
||||
try
|
||||
{
|
||||
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1]);
|
||||
mDeviceRate = RateToProbe[rateIndex][1];
|
||||
ICELogInfo(<< "Input Opened with rate " << mDeviceRate << " and rate index " << rateIndex);
|
||||
opened = mDeviceRate != 0;
|
||||
if (!opened)
|
||||
internalClose();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
opened = false;
|
||||
internalClose();
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
opened = false;
|
||||
internalClose();
|
||||
}
|
||||
}
|
||||
mActive = opened;
|
||||
mActive = opened;
|
||||
|
||||
return opened;
|
||||
return opened;
|
||||
}
|
||||
|
||||
void AndroidInputDevice::close()
|
||||
{
|
||||
// There is no check for active() value because close() can be called to cleanup after bad open() call.
|
||||
internalClose();
|
||||
OpenSLEngine::instance().close();
|
||||
mActive = false;
|
||||
// There is no check for active() value because close() can be called to cleanup after bad open() call.
|
||||
internalClose();
|
||||
OpenSLEngine::instance().close();
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
Format AndroidInputDevice::getFormat()
|
||||
{
|
||||
return Format(mDeviceRate, 1);
|
||||
return Format(mDeviceRate, 1);
|
||||
}
|
||||
|
||||
bool AndroidInputDevice::active() const
|
||||
{
|
||||
return mActive;
|
||||
return mActive;
|
||||
}
|
||||
|
||||
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);
|
||||
while (mSdkRateCache.filled() < AUDIO_MIC_BUFFER_SIZE)
|
||||
{
|
||||
mDataCondVar.wait(l);
|
||||
}
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
while (mSdkRateCache.filled() < AUDIO_MIC_BUFFER_SIZE)
|
||||
{
|
||||
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)
|
||||
{
|
||||
SLresult resultCode = 0;
|
||||
SLuint32 nrOfChannels = 1;
|
||||
SLresult resultCode = 0;
|
||||
SLuint32 nrOfChannels = 1;
|
||||
|
||||
// Prepare audio source
|
||||
SLDataLocator_IODevice devDescription = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
|
||||
SLDataSource audioSource = { &devDescription, NULL };
|
||||
// Prepare audio source
|
||||
SLDataLocator_IODevice devDescription = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
|
||||
SLDataSource audioSource = {&devDescription, NULL};
|
||||
|
||||
// Source flags
|
||||
SLuint32 speakersFlags = nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
||||
// Source flags
|
||||
SLuint32 speakersFlags =
|
||||
nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
||||
|
||||
// Buffer queue
|
||||
SLDataLocator_AndroidSimpleBufferQueue queueDescription = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
|
||||
// Buffer queue
|
||||
SLDataLocator_AndroidSimpleBufferQueue queueDescription = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||
|
||||
// Audio format
|
||||
SLDataFormat_PCM formatDescription = { SL_DATAFORMAT_PCM, nrOfChannels, (SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags, SL_BYTEORDER_LITTLEENDIAN };
|
||||
// Audio format
|
||||
SLDataFormat_PCM formatDescription = {SL_DATAFORMAT_PCM, nrOfChannels,
|
||||
(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
|
||||
// Do not forget about RECORD_AUDIO permission
|
||||
const SLInterfaceID interfacesList[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
|
||||
const SLboolean interfacesRequirements[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||
// Create recorder
|
||||
// Do not forget about RECORD_AUDIO permission
|
||||
const SLInterfaceID interfacesList[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
|
||||
const SLboolean interfacesRequirements[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
|
||||
// Get access to OpenSL engine
|
||||
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
|
||||
CHECK_SL_INTERFACE(engine_interface, -1);
|
||||
// Get access to OpenSL engine
|
||||
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
|
||||
CHECK_SL_INTERFACE(engine_interface, -1);
|
||||
|
||||
resultCode = (*engine_interface)->CreateAudioRecorder(
|
||||
OpenSLEngine::instance().getNativeEngine(),
|
||||
&mRecorderObject, &audioSource, &audioSink, 2, interfacesList, interfacesRequirements);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
CHECK_SL_INTERFACE(mRecorderObject, -2);
|
||||
resultCode = (*engine_interface)
|
||||
->CreateAudioRecorder(OpenSLEngine::instance().getNativeEngine(), &mRecorderObject, &audioSource,
|
||||
&audioSink, 2, interfacesList, interfacesRequirements);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
CHECK_SL_INTERFACE(mRecorderObject, -2);
|
||||
|
||||
// Obtain stream type
|
||||
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDCONFIGURATION, &mAndroidCfg);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Obtain stream type
|
||||
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDCONFIGURATION, &mAndroidCfg);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Now audio recorder goes to real world
|
||||
resultCode = (*mRecorderObject)->Realize(mRecorderObject, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Now audio recorder goes to real world
|
||||
resultCode = (*mRecorderObject)->Realize(mRecorderObject, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Get recorder interface
|
||||
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_RECORD, &mRecorderInterface);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
CHECK_SL_INTERFACE(mRecorderInterface, -3);
|
||||
// Get recorder interface
|
||||
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_RECORD, &mRecorderInterface);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
CHECK_SL_INTERFACE(mRecorderInterface, -3);
|
||||
|
||||
// Now buffer queue interface...
|
||||
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
|
||||
// Now buffer queue interface...
|
||||
resultCode =
|
||||
(*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
|
||||
|
||||
// Resampler is needed to provide SDK's rate
|
||||
mResampler = std::make_shared<Resampler>();
|
||||
mResampler->start(nrOfChannels, rate, AUDIO_SAMPLERATE);
|
||||
// Resampler is needed to provide SDK's rate
|
||||
mResampler = std::make_shared<Resampler>();
|
||||
mResampler->start(nrOfChannels, rate, AUDIO_SAMPLERATE);
|
||||
|
||||
// Allocate recorder buffer size
|
||||
mBufferSize = (AUDIO_MIC_BUFFER_LENGTH / 10) * (rate / 100) * 2;
|
||||
mRecorderBuffer.setCapacity(mBufferSize * AUDIO_MIC_BUFFER_COUNT);
|
||||
mRecorderBufferIndex = 0;
|
||||
// Allocate recorder buffer size
|
||||
mBufferSize = (AUDIO_MIC_BUFFER_LENGTH / 10) * (rate / 100) * 2;
|
||||
mRecorderBuffer.setCapacity(mBufferSize * AUDIO_MIC_BUFFER_COUNT);
|
||||
mRecorderBufferIndex = 0;
|
||||
|
||||
// Setup data consuming callback
|
||||
resultCode = (*mRecorderBufferInterface)->RegisterCallback(mRecorderBufferInterface, DeviceCallback, (void*)this);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Setup data consuming callback
|
||||
resultCode = (*mRecorderBufferInterface)->RegisterCallback(mRecorderBufferInterface, DeviceCallback, (void*)this);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Setup buffers
|
||||
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
(*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
|
||||
// Setup buffers
|
||||
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
(*mRecorderBufferInterface)
|
||||
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
|
||||
|
||||
// Start finally
|
||||
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Start finally
|
||||
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
}
|
||||
|
||||
void AndroidInputDevice::internalClose()
|
||||
{
|
||||
if (!mRecorderObject)
|
||||
return;
|
||||
if (!mRecorderObject)
|
||||
return;
|
||||
|
||||
if (*mRecorderObject)
|
||||
{
|
||||
if (active())
|
||||
if (*mRecorderObject)
|
||||
{
|
||||
// Stop recording
|
||||
(*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_STOPPED);
|
||||
if (active())
|
||||
{
|
||||
// Stop recording
|
||||
(*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_STOPPED);
|
||||
|
||||
// Wait until recording will not stop really
|
||||
SLuint32 state = SL_RECORDSTATE_STOPPED;
|
||||
do
|
||||
{
|
||||
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
|
||||
SyncHelper::delay(1);
|
||||
}
|
||||
while (state == SL_RECORDSTATE_RECORDING);
|
||||
// Wait until recording will not stop really
|
||||
SLuint32 state = SL_RECORDSTATE_STOPPED;
|
||||
do
|
||||
{
|
||||
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
|
||||
SyncHelper::delay(1);
|
||||
} while (state == SL_RECORDSTATE_RECORDING);
|
||||
}
|
||||
(*mRecorderObject)->Destroy(mRecorderObject);
|
||||
}
|
||||
(*mRecorderObject)->Destroy(mRecorderObject);
|
||||
}
|
||||
|
||||
mRecorderObject = nullptr;
|
||||
mRecorderInterface = nullptr;
|
||||
mRecorderBufferInterface = nullptr;
|
||||
mAndroidCfg = nullptr;
|
||||
mRecorderObject = nullptr;
|
||||
mRecorderInterface = nullptr;
|
||||
mRecorderBufferInterface = nullptr;
|
||||
mAndroidCfg = nullptr;
|
||||
}
|
||||
|
||||
void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Send data to AudioPair
|
||||
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;
|
||||
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)
|
||||
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
|
||||
{
|
||||
if (context)
|
||||
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
|
||||
}
|
||||
catch(...)
|
||||
{}
|
||||
try
|
||||
{
|
||||
if (context)
|
||||
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// ------------ AndroidOutputDevice -----------------
|
||||
AndroidOutputDevice::AndroidOutputDevice(int devId)
|
||||
{
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||
}
|
||||
|
||||
AndroidOutputDevice::~AndroidOutputDevice()
|
||||
{
|
||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||
close();
|
||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||
close();
|
||||
}
|
||||
|
||||
bool AndroidOutputDevice::open()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
bool opened = false;
|
||||
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
|
||||
{
|
||||
try
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
bool opened = false;
|
||||
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
|
||||
{
|
||||
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1], true);
|
||||
opened = true;
|
||||
mDeviceRate = RateToProbe[rateIndex][1];
|
||||
ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex);
|
||||
try
|
||||
{
|
||||
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1], true);
|
||||
opened = true;
|
||||
mDeviceRate = RateToProbe[rateIndex][1];
|
||||
ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
opened = false;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
opened = false;
|
||||
}
|
||||
}
|
||||
if (opened)
|
||||
ICELogInfo(<< "Speaker opened on rate " << mDeviceRate);
|
||||
if (opened)
|
||||
ICELogInfo(<< "Speaker opened on rate " << mDeviceRate);
|
||||
|
||||
return opened;
|
||||
return opened;
|
||||
}
|
||||
|
||||
void AndroidOutputDevice::close()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
internalClose();
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
internalClose();
|
||||
}
|
||||
|
||||
Format AndroidOutputDevice::getFormat()
|
||||
{
|
||||
return Format(mDeviceRate, 1);
|
||||
return Format(mDeviceRate, 1);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
mInShutdown = false;
|
||||
mInShutdown = false;
|
||||
|
||||
SLresult resultCode;
|
||||
SLuint32 channels = 1;
|
||||
SLresult resultCode;
|
||||
SLuint32 channels = 1;
|
||||
|
||||
// Configure audio source
|
||||
SLDataLocator_AndroidSimpleBufferQueue queue_desc = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
|
||||
// Configure audio source
|
||||
SLDataLocator_AndroidSimpleBufferQueue queue_desc = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||
|
||||
const SLInterfaceID interfacesList[] = { SL_IID_VOLUME };
|
||||
const SLboolean interfaceRequirements[] = { SL_BOOLEAN_FALSE };
|
||||
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateOutputMix(
|
||||
OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
|
||||
interfaceRequirements);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
const SLInterfaceID interfacesList[] = {SL_IID_VOLUME};
|
||||
const SLboolean interfaceRequirements[] = {SL_BOOLEAN_FALSE};
|
||||
resultCode = (*OpenSLEngine::instance().getNativeEngine())
|
||||
->CreateOutputMix(OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
|
||||
interfaceRequirements);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Bring mixer online
|
||||
resultCode = (*mMixer)->Realize(mMixer, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Bring mixer online
|
||||
resultCode = (*mMixer)->Realize(mMixer, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Prepare mixer configuration
|
||||
SLuint32 speakers =
|
||||
channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
||||
// Prepare mixer configuration
|
||||
SLuint32 speakers = channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
|
||||
|
||||
// Describe audio format
|
||||
SLDataFormat_PCM pcm_format = {SL_DATAFORMAT_PCM, channels, (SLuint32) rateId,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
speakers, SL_BYTEORDER_LITTLEENDIAN};
|
||||
// Describe audio format
|
||||
SLDataFormat_PCM pcm_format = {
|
||||
SL_DATAFORMAT_PCM, channels, (SLuint32)rateId, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16, speakers, SL_BYTEORDER_LITTLEENDIAN};
|
||||
|
||||
// Describe audio source - buffers + audio format
|
||||
SLDataSource audio_source = { &queue_desc, &pcm_format };
|
||||
// Describe audio source - buffers + audio format
|
||||
SLDataSource audio_source = {&queue_desc, &pcm_format};
|
||||
|
||||
// Describe audio sink
|
||||
SLDataLocator_OutputMix mixer_desc = { SL_DATALOCATOR_OUTPUTMIX, mMixer };
|
||||
SLDataSink audio_sink = { &mixer_desc, NULL };
|
||||
// Describe audio sink
|
||||
SLDataLocator_OutputMix mixer_desc = {SL_DATALOCATOR_OUTPUTMIX, mMixer};
|
||||
SLDataSink audio_sink = {&mixer_desc, NULL};
|
||||
|
||||
// Create player instance
|
||||
const SLInterfaceID playerInterfaces[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
SL_IID_VOLUME,
|
||||
SL_IID_ANDROIDCONFIGURATION };
|
||||
const SLboolean playerInterfacesReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||
// Create player instance
|
||||
const SLInterfaceID playerInterfaces[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME,
|
||||
SL_IID_ANDROIDCONFIGURATION};
|
||||
const SLboolean playerInterfacesReqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
|
||||
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateAudioPlayer(
|
||||
OpenSLEngine::instance().getNativeEngine(), &mPlayer,
|
||||
&audio_source, &audio_sink, 3, playerInterfaces, playerInterfacesReqs);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
resultCode = (*OpenSLEngine::instance().getNativeEngine())
|
||||
->CreateAudioPlayer(OpenSLEngine::instance().getNativeEngine(), &mPlayer, &audio_source,
|
||||
&audio_sink, 3, playerInterfaces, playerInterfacesReqs);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Get android config interface
|
||||
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDCONFIGURATION, &mAndroidConfig);
|
||||
// Get android config interface
|
||||
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDCONFIGURATION, &mAndroidConfig);
|
||||
|
||||
if (resultCode == SL_RESULT_SUCCESS)
|
||||
{
|
||||
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
|
||||
resultCode = (*mAndroidConfig)->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE,
|
||||
&streamType, sizeof(SLint32));
|
||||
if (resultCode != SL_RESULT_SUCCESS)
|
||||
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
|
||||
}
|
||||
else
|
||||
ICELogCritical(<< "Failed to obtain android cfg audio interface with error " << (unsigned)resultCode);
|
||||
if (resultCode == SL_RESULT_SUCCESS)
|
||||
{
|
||||
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
|
||||
resultCode = (*mAndroidConfig)
|
||||
->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
|
||||
if (resultCode != SL_RESULT_SUCCESS)
|
||||
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
|
||||
}
|
||||
else
|
||||
ICELogCritical(<< "Failed to obtain android cfg audio interface with error " << (unsigned)resultCode);
|
||||
|
||||
// Bring player online
|
||||
resultCode = (*mPlayer)->Realize(mPlayer, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Bring player online
|
||||
resultCode = (*mPlayer)->Realize(mPlayer, SL_BOOLEAN_FALSE);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Obtain player control
|
||||
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_PLAY, &mPlayerControl);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Obtain player control
|
||||
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_PLAY, &mPlayerControl);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Get the buffer queue interface
|
||||
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&mBufferQueue);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Get the buffer queue interface
|
||||
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mBufferQueue);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Setup callback
|
||||
resultCode = (*mBufferQueue)->RegisterCallback(mBufferQueue, DeviceCallback, this);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Setup callback
|
||||
resultCode = (*mBufferQueue)->RegisterCallback(mBufferQueue, DeviceCallback, this);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
// Enqueue buffers
|
||||
mBufferSize = (int)Format(rate, channels).sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
|
||||
mPlayBuffer.setCapacity(AUDIO_SPK_BUFFER_COUNT * mBufferSize);
|
||||
// Enqueue buffers
|
||||
mBufferSize = (int)Format(rate, channels).sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
|
||||
mPlayBuffer.setCapacity(AUDIO_SPK_BUFFER_COUNT * mBufferSize);
|
||||
|
||||
mBufferIndex = 0;
|
||||
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
|
||||
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize,
|
||||
(SLuint32)mBufferSize);
|
||||
mBufferIndex = 0;
|
||||
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
|
||||
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize, (SLuint32)mBufferSize);
|
||||
|
||||
// Set the player's state to playing
|
||||
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
// Set the player's state to playing
|
||||
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
|
||||
CHECK_OPENSLES_ERROR;
|
||||
|
||||
ICELogInfo(<< "Android audio output is opened and playing.");
|
||||
ICELogInfo(<< "Android audio output is opened and playing.");
|
||||
}
|
||||
|
||||
void AndroidOutputDevice::internalClose()
|
||||
{
|
||||
if (mPlayer)
|
||||
{
|
||||
if (*mPlayer)
|
||||
if (mPlayer)
|
||||
{
|
||||
mInShutdown = true;
|
||||
ICELogInfo(<< "Stop player");
|
||||
if (mPlayerControl) {
|
||||
if (*mPlayerControl) {
|
||||
SLuint32 state = SL_PLAYSTATE_PLAYING;
|
||||
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
|
||||
if (*mPlayer)
|
||||
{
|
||||
mInShutdown = true;
|
||||
ICELogInfo(<< "Stop player");
|
||||
if (mPlayerControl)
|
||||
{
|
||||
if (*mPlayerControl)
|
||||
{
|
||||
SLuint32 state = SL_PLAYSTATE_PLAYING;
|
||||
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
|
||||
|
||||
while (state != SL_PLAYSTATE_STOPPED) {
|
||||
(*mPlayerControl)->GetPlayState(mPlayerControl, &state);
|
||||
SyncHelper::delay(1);
|
||||
}
|
||||
while (state != SL_PLAYSTATE_STOPPED)
|
||||
{
|
||||
(*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)
|
||||
(*mMixer)->Destroy(mMixer);
|
||||
mMixer = nullptr;
|
||||
}
|
||||
if (mMixer)
|
||||
{
|
||||
if (*mMixer)
|
||||
(*mMixer)->Destroy(mMixer);
|
||||
mMixer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidOutputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
|
||||
{
|
||||
if (mInShutdown)
|
||||
return;
|
||||
/*{
|
||||
char silence[mBufferSize]; memset(silence, 0, mBufferSize);
|
||||
(*mBufferQueue)->Enqueue(mBufferQueue, silence, mBufferSize);
|
||||
return;
|
||||
}*/
|
||||
if (mInShutdown)
|
||||
return;
|
||||
/*{
|
||||
char silence[mBufferSize]; memset(silence, 0, mBufferSize);
|
||||
(*mBufferQueue)->Enqueue(mBufferQueue, silence, mBufferSize);
|
||||
return;
|
||||
}*/
|
||||
|
||||
// Ask producer about data
|
||||
char* buffer = mPlayBuffer.mutableData() + mBufferIndex * mBufferSize;
|
||||
if (mConnection)
|
||||
{
|
||||
Format f = getFormat();
|
||||
if (f.mRate != 0)
|
||||
mConnection->onSpkData(f, buffer, mBufferSize);
|
||||
}
|
||||
(*mBufferQueue)->Enqueue(mBufferQueue, buffer, (SLuint32)mBufferSize);
|
||||
// Ask producer about data
|
||||
char* buffer = mPlayBuffer.mutableData() + mBufferIndex * mBufferSize;
|
||||
if (mConnection)
|
||||
{
|
||||
Format f = getFormat();
|
||||
if (f.mRate != 0)
|
||||
mConnection->onSpkData(f, buffer, mBufferSize);
|
||||
}
|
||||
(*mBufferQueue)->Enqueue(mBufferQueue, buffer, (SLuint32)mBufferSize);
|
||||
|
||||
mBufferIndex++;
|
||||
mBufferIndex %= AUDIO_SPK_BUFFER_COUNT;
|
||||
mBufferIndex++;
|
||||
mBufferIndex %= AUDIO_SPK_BUFFER_COUNT;
|
||||
}
|
||||
|
||||
void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
|
||||
}
|
||||
catch(...)
|
||||
{}
|
||||
try
|
||||
{
|
||||
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TARGET_ANDROID
|
||||
|
||||
@@ -25,121 +25,120 @@
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
class AndroidEnumerator: public Enumerator
|
||||
{
|
||||
public:
|
||||
AndroidEnumerator();
|
||||
~AndroidEnumerator();
|
||||
class AndroidEnumerator : public Enumerator
|
||||
{
|
||||
public:
|
||||
AndroidEnumerator();
|
||||
~AndroidEnumerator();
|
||||
|
||||
void open(int direction);
|
||||
void close();
|
||||
void open(int direction);
|
||||
void close();
|
||||
|
||||
int count();
|
||||
std::string nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
int count();
|
||||
std::string nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
|
||||
protected:
|
||||
};
|
||||
protected:
|
||||
};
|
||||
|
||||
class AndroidInputDevice: public InputDevice
|
||||
{
|
||||
public:
|
||||
AndroidInputDevice(int devId);
|
||||
~AndroidInputDevice();
|
||||
class AndroidInputDevice : public InputDevice
|
||||
{
|
||||
public:
|
||||
AndroidInputDevice(int devId);
|
||||
~AndroidInputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
int readBuffer(void* buffer);
|
||||
bool active() const;
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
int readBuffer(void* buffer);
|
||||
bool active() const;
|
||||
|
||||
protected:
|
||||
bool mActive = false;
|
||||
SLObjectItf mRecorderObject = nullptr;
|
||||
SLRecordItf mRecorderInterface = nullptr;
|
||||
SLAndroidSimpleBufferQueueItf mRecorderBufferInterface = nullptr;
|
||||
SLAndroidConfigurationItf mAndroidCfg = nullptr;
|
||||
protected:
|
||||
bool mActive = false;
|
||||
SLObjectItf mRecorderObject = nullptr;
|
||||
SLRecordItf mRecorderInterface = nullptr;
|
||||
SLAndroidSimpleBufferQueueItf mRecorderBufferInterface = nullptr;
|
||||
SLAndroidConfigurationItf mAndroidCfg = nullptr;
|
||||
|
||||
PResampler mResampler;
|
||||
DataWindow mDeviceRateCache, mSdkRateCache;
|
||||
int mDeviceRate; // Actual rate of opened recorder
|
||||
int mBufferSize; // Size of buffer used for recording (at native sample rate)
|
||||
DataWindow mRecorderBuffer;
|
||||
std::condition_variable mDataCondVar;
|
||||
int mRecorderBufferIndex;
|
||||
std::mutex mMutex;
|
||||
PResampler mResampler;
|
||||
DataWindow mDeviceRateCache, mSdkRateCache;
|
||||
int mDeviceRate; // Actual rate of opened recorder
|
||||
int mBufferSize; // Size of buffer used for recording (at native sample rate)
|
||||
DataWindow mRecorderBuffer;
|
||||
std::condition_variable mDataCondVar;
|
||||
int mRecorderBufferIndex;
|
||||
std::mutex mMutex;
|
||||
|
||||
void internalOpen(int rateCode, int rate);
|
||||
void internalClose();
|
||||
void handleCallback(SLAndroidSimpleBufferQueueItf bq);
|
||||
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||
};
|
||||
void internalOpen(int rateCode, int rate);
|
||||
void internalClose();
|
||||
void handleCallback(SLAndroidSimpleBufferQueueItf bq);
|
||||
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||
};
|
||||
|
||||
class AndroidOutputDevice: public OutputDevice
|
||||
{
|
||||
public:
|
||||
AndroidOutputDevice(int devId);
|
||||
~AndroidOutputDevice();
|
||||
class AndroidOutputDevice : public OutputDevice
|
||||
{
|
||||
public:
|
||||
AndroidOutputDevice(int devId);
|
||||
~AndroidOutputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
|
||||
protected:
|
||||
std::mutex mMutex;
|
||||
int mDeviceRate = 0;
|
||||
SLObjectItf mMixer = nullptr;
|
||||
SLObjectItf mPlayer = nullptr;
|
||||
SLPlayItf mPlayerControl = nullptr;
|
||||
SLAndroidSimpleBufferQueueItf mBufferQueue = nullptr;
|
||||
SLAndroidConfigurationItf mAndroidConfig = nullptr;
|
||||
SLEffectSendItf mEffect = nullptr;
|
||||
protected:
|
||||
std::mutex mMutex;
|
||||
int mDeviceRate = 0;
|
||||
SLObjectItf mMixer = nullptr;
|
||||
SLObjectItf mPlayer = nullptr;
|
||||
SLPlayItf mPlayerControl = nullptr;
|
||||
SLAndroidSimpleBufferQueueItf mBufferQueue = nullptr;
|
||||
SLAndroidConfigurationItf mAndroidConfig = nullptr;
|
||||
SLEffectSendItf mEffect = nullptr;
|
||||
|
||||
DataWindow mPlayBuffer;
|
||||
int mBufferIndex = 0, mBufferSize = 0;
|
||||
bool mInShutdown = false;
|
||||
DataWindow mPlayBuffer;
|
||||
int mBufferIndex = 0, mBufferSize = 0;
|
||||
bool mInShutdown = false;
|
||||
|
||||
void internalOpen(int rateId, int rate, bool voice);
|
||||
void internalClose();
|
||||
void internalOpen(int rateId, int rate, bool voice);
|
||||
void internalClose();
|
||||
|
||||
void handleCallback(SLAndroidSimpleBufferQueueItf bq);
|
||||
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||
void handleCallback(SLAndroidSimpleBufferQueueItf bq);
|
||||
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||
};
|
||||
|
||||
};
|
||||
class OpenSLEngine : public OsEngine
|
||||
{
|
||||
public:
|
||||
OpenSLEngine();
|
||||
~OpenSLEngine();
|
||||
|
||||
class OpenSLEngine: public OsEngine
|
||||
{
|
||||
public:
|
||||
OpenSLEngine();
|
||||
~OpenSLEngine();
|
||||
// open() / close() methods are based on usage counting.
|
||||
// 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;
|
||||
|
||||
// open() / close() methods are based on usage counting.
|
||||
// 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;
|
||||
|
||||
SLEngineItf getNativeEngine() const;
|
||||
static OpenSLEngine& instance();
|
||||
|
||||
static OpenSLEngine& instance();
|
||||
protected:
|
||||
std::mutex mMutex;
|
||||
int mUsageCounter = 0;
|
||||
SLObjectItf mEngineObject = nullptr;
|
||||
SLEngineItf mEngineInterface = nullptr;
|
||||
|
||||
protected:
|
||||
std::mutex mMutex;
|
||||
int mUsageCounter = 0;
|
||||
SLObjectItf mEngineObject = nullptr;
|
||||
SLEngineItf mEngineInterface = nullptr;
|
||||
|
||||
void internalOpen();
|
||||
void internalClose();
|
||||
};
|
||||
}
|
||||
void internalOpen();
|
||||
void internalClose();
|
||||
};
|
||||
} // namespace Audio
|
||||
|
||||
#endif // TARGET_ANDROID
|
||||
|
||||
|
||||
@@ -18,284 +18,279 @@ using namespace Audio;
|
||||
|
||||
// -------------------- AndroidEnumerator -----------------------------
|
||||
|
||||
AndroidEnumerator::AndroidEnumerator()
|
||||
{}
|
||||
AndroidEnumerator::AndroidEnumerator() {}
|
||||
|
||||
AndroidEnumerator::~AndroidEnumerator()
|
||||
{}
|
||||
AndroidEnumerator::~AndroidEnumerator() {}
|
||||
|
||||
int AndroidEnumerator::indexOfDefaultDevice()
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AndroidEnumerator::count()
|
||||
{
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int AndroidEnumerator::idAt(int index)
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 ----------------
|
||||
AndroidInputDevice::AndroidInputDevice(int devId)
|
||||
{}
|
||||
AndroidInputDevice::AndroidInputDevice(int devId) {}
|
||||
|
||||
AndroidInputDevice::~AndroidInputDevice()
|
||||
{
|
||||
close();
|
||||
close();
|
||||
}
|
||||
|
||||
bool AndroidInputDevice::open()
|
||||
{
|
||||
if (active())
|
||||
return true;
|
||||
if (active())
|
||||
return true;
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Input);
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||
builder.setFormat(oboe::AudioFormat::I16);
|
||||
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||
builder.setCallback(this);
|
||||
oboe::Result rescode = builder.openStream(&mRecordingStream);
|
||||
if (rescode != oboe::Result::OK)
|
||||
return false;
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Input);
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||
builder.setFormat(oboe::AudioFormat::I16);
|
||||
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||
builder.setCallback(this);
|
||||
oboe::Result rescode = builder.openStream(&mRecordingStream);
|
||||
if (rescode != oboe::Result::OK)
|
||||
return false;
|
||||
|
||||
mDeviceRate = mRecordingStream->getSampleRate();
|
||||
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
|
||||
mActive = true;
|
||||
mDeviceRate = mRecordingStream->getSampleRate();
|
||||
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
|
||||
mActive = true;
|
||||
|
||||
rescode = mRecordingStream->requestStart();
|
||||
if (rescode != oboe::Result::OK)
|
||||
{
|
||||
close();
|
||||
mActive = false;
|
||||
}
|
||||
return mActive;
|
||||
rescode = mRecordingStream->requestStart();
|
||||
if (rescode != oboe::Result::OK)
|
||||
{
|
||||
close();
|
||||
mActive = false;
|
||||
}
|
||||
return mActive;
|
||||
}
|
||||
|
||||
void AndroidInputDevice::close()
|
||||
{
|
||||
// There is no check for active() value because close() can be called to cleanup after bad open() call.
|
||||
if (mRecordingStream != nullptr)
|
||||
{
|
||||
mRecordingStream->close();
|
||||
delete mRecordingStream; mRecordingStream = nullptr;
|
||||
}
|
||||
mActive = false;
|
||||
// There is no check for active() value because close() can be called to cleanup after bad open() call.
|
||||
if (mRecordingStream != nullptr)
|
||||
{
|
||||
mRecordingStream->close();
|
||||
delete mRecordingStream;
|
||||
mRecordingStream = nullptr;
|
||||
}
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
oboe::DataCallbackResult
|
||||
AndroidInputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
|
||||
oboe::DataCallbackResult AndroidInputDevice::onAudioReady(oboe::AudioStream* audioStream, void* audioData,
|
||||
int32_t numFrames)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
|
||||
// Send data to AudioPair
|
||||
if (mConnection)
|
||||
mConnection->onMicData(getFormat(), audioData, numFrames);
|
||||
// Send data to AudioPair
|
||||
if (mConnection)
|
||||
mConnection->onMicData(getFormat(), audioData, numFrames);
|
||||
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
Format AndroidInputDevice::getFormat()
|
||||
{
|
||||
return Format(mDeviceRate, 1);
|
||||
return Format(mDeviceRate, 1);
|
||||
}
|
||||
|
||||
bool AndroidInputDevice::active() const
|
||||
{
|
||||
return mActive;
|
||||
return mActive;
|
||||
}
|
||||
|
||||
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(int devId)
|
||||
{
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
|
||||
}
|
||||
AndroidOutputDevice::~AndroidOutputDevice()
|
||||
{
|
||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||
// Mark shutdown before closing so a disconnect callback racing with teardown
|
||||
// does not resurrect the stream via onErrorAfterClose()'s restart.
|
||||
mInShutdown = true;
|
||||
close();
|
||||
ICELogDebug(<< "Deleting AndroidOutputDevice.");
|
||||
// Mark shutdown before closing so a disconnect callback racing with teardown
|
||||
// does not resurrect the stream via onErrorAfterClose()'s restart.
|
||||
mInShutdown = true;
|
||||
close();
|
||||
}
|
||||
|
||||
bool AndroidOutputDevice::open()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
|
||||
if (mActive)
|
||||
return true;
|
||||
if (mActive)
|
||||
return true;
|
||||
|
||||
mInShutdown = false;
|
||||
mRequestedFrames = 0;
|
||||
mStartTime = 0.0;
|
||||
mEndTime = 0.0;
|
||||
mInShutdown = false;
|
||||
mRequestedFrames = 0;
|
||||
mStartTime = 0.0;
|
||||
mEndTime = 0.0;
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Output);
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||
builder.setFormat(oboe::AudioFormat::I16);
|
||||
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||
// Route through the platform voice-call path: correct device selection/volume
|
||||
// and platform voice tuning for a softphone.
|
||||
builder.setUsage(oboe::Usage::VoiceCommunication);
|
||||
builder.setContentType(oboe::ContentType::Speech);
|
||||
// builder.setDataCallback(this);
|
||||
builder.setCallback(this);
|
||||
//builder.setErrorCallback(this)
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Output);
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||
builder.setFormat(oboe::AudioFormat::I16);
|
||||
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||
// Route through the platform voice-call path: correct device selection/volume
|
||||
// and platform voice tuning for a softphone.
|
||||
builder.setUsage(oboe::Usage::VoiceCommunication);
|
||||
builder.setContentType(oboe::ContentType::Speech);
|
||||
// builder.setDataCallback(this);
|
||||
builder.setCallback(this);
|
||||
// builder.setErrorCallback(this)
|
||||
|
||||
oboe::Result rescode = builder.openStream(&mPlayingStream);
|
||||
if (rescode != oboe::Result::OK)
|
||||
return false;
|
||||
oboe::Result rescode = builder.openStream(&mPlayingStream);
|
||||
if (rescode != oboe::Result::OK)
|
||||
return false;
|
||||
|
||||
mDeviceRate = mPlayingStream->getSampleRate();
|
||||
ICELogInfo(<< "Output opened with rate " << mDeviceRate);
|
||||
mActive = true;
|
||||
mDeviceRate = mPlayingStream->getSampleRate();
|
||||
ICELogInfo(<< "Output opened with rate " << mDeviceRate);
|
||||
mActive = true;
|
||||
|
||||
rescode = mPlayingStream->requestStart();
|
||||
if (rescode != oboe::Result::OK)
|
||||
{
|
||||
close();
|
||||
mActive = false;
|
||||
return mActive;
|
||||
}
|
||||
rescode = mPlayingStream->requestStart();
|
||||
if (rescode != oboe::Result::OK)
|
||||
{
|
||||
close();
|
||||
mActive = false;
|
||||
return mActive;
|
||||
}
|
||||
|
||||
// Latch the burst size and start from a two-burst buffer. onAudioReady() grows
|
||||
// this on XRuns (up to a cap) so we keep low latency when the device can sustain
|
||||
// it and trade a little latency for glitch-free playback when it can't.
|
||||
mBurstFrames = mPlayingStream->getFramesPerBurst();
|
||||
mXRunLast = 0;
|
||||
mHeartbeatLast = 0.0f;
|
||||
if (mBurstFrames > 0)
|
||||
mPlayingStream->setBufferSizeInFrames(mBurstFrames * 2);
|
||||
// Latch the burst size and start from a two-burst buffer. onAudioReady() grows
|
||||
// this on XRuns (up to a cap) so we keep low latency when the device can sustain
|
||||
// it and trade a little latency for glitch-free playback when it can't.
|
||||
mBurstFrames = mPlayingStream->getFramesPerBurst();
|
||||
mXRunLast = 0;
|
||||
mHeartbeatLast = 0.0f;
|
||||
if (mBurstFrames > 0)
|
||||
mPlayingStream->setBufferSizeInFrames(mBurstFrames * 2);
|
||||
|
||||
return mActive;
|
||||
return mActive;
|
||||
}
|
||||
|
||||
void AndroidOutputDevice::close()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (!mActive)
|
||||
return;
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (!mActive)
|
||||
return;
|
||||
|
||||
if (mPlayingStream != nullptr)
|
||||
{
|
||||
mPlayingStream->close();
|
||||
delete mPlayingStream; mPlayingStream = nullptr;
|
||||
}
|
||||
mEndTime = now_ms();
|
||||
mActive = false;
|
||||
if (mPlayingStream != nullptr)
|
||||
{
|
||||
mPlayingStream->close();
|
||||
delete mPlayingStream;
|
||||
mPlayingStream = nullptr;
|
||||
}
|
||||
mEndTime = now_ms();
|
||||
mActive = false;
|
||||
|
||||
ICELogInfo(<< "For time " << mEndTime - mStartTime << " ms was requested "
|
||||
<< float(mRequestedFrames) / getFormat().mRate * 1000 << " ms");
|
||||
ICELogInfo(<< "For time " << mEndTime - mStartTime << " ms was requested "
|
||||
<< float(mRequestedFrames) / getFormat().mRate * 1000 << " ms");
|
||||
}
|
||||
|
||||
Format AndroidOutputDevice::getFormat()
|
||||
{
|
||||
return {mDeviceRate, 1};
|
||||
return {mDeviceRate, 1};
|
||||
}
|
||||
|
||||
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 (mInShutdown)
|
||||
return oboe::DataCallbackResult::Stop;
|
||||
if (mStartTime == 0.0)
|
||||
mStartTime = now_ms();
|
||||
|
||||
if (mStartTime == 0.0)
|
||||
mStartTime = now_ms();
|
||||
|
||||
// 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)
|
||||
// Ask producer about data
|
||||
memset(audioData, 0, numFrames * 2);
|
||||
if (mConnection)
|
||||
{
|
||||
int32_t cap = mBurstFrames * 8;
|
||||
int32_t cur = audioStream->getBufferSizeInFrames();
|
||||
int32_t next = std::min(cur + mBurstFrames, cap);
|
||||
if (next > cur)
|
||||
audioStream->setBufferSizeInFrames(next);
|
||||
Format f = getFormat();
|
||||
if (f.mRate != 0)
|
||||
mConnection->onSpkData(f, audioData, numFrames * 2);
|
||||
}
|
||||
}
|
||||
mRequestedFrames += numFrames;
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Adaptive buffer sizing: on new XRuns (device-side underruns, i.e. we missed a
|
||||
// callback deadline), grow the buffer one burst at a time up to a cap. Both calls
|
||||
// are documented as safe from within the data callback.
|
||||
auto xrun = audioStream->getXRunCount();
|
||||
if (xrun && xrun.value() > mXRunLast)
|
||||
{
|
||||
mXRunLast = xrun.value();
|
||||
if (mBurstFrames > 0)
|
||||
{
|
||||
int32_t cap = mBurstFrames * 8;
|
||||
int32_t cur = audioStream->getBufferSizeInFrames();
|
||||
int32_t next = std::min(cur + mBurstFrames, cap);
|
||||
if (next > cur)
|
||||
audioStream->setBufferSizeInFrames(next);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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.
|
||||
// See https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
|
||||
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result) {
|
||||
if (result == oboe::Result::ErrorDisconnected && !mInShutdown) {
|
||||
ICELogInfo(<< "Output stream disconnected; restarting on the new route");
|
||||
// close() and open() each take mMutex internally; this callback holds none.
|
||||
close();
|
||||
open();
|
||||
}
|
||||
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream* stream, oboe::Result result)
|
||||
{
|
||||
if (result == oboe::Result::ErrorDisconnected && !mInShutdown)
|
||||
{
|
||||
ICELogInfo(<< "Output stream disconnected; restarting on the new route");
|
||||
// close() and open() each take mMutex internally; this callback holds none.
|
||||
close();
|
||||
open();
|
||||
}
|
||||
}
|
||||
#endif // TARGET_ANDROID
|
||||
|
||||
@@ -25,89 +25,88 @@
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
class AndroidEnumerator: public Enumerator
|
||||
{
|
||||
public:
|
||||
AndroidEnumerator();
|
||||
~AndroidEnumerator();
|
||||
class AndroidEnumerator : public Enumerator
|
||||
{
|
||||
public:
|
||||
AndroidEnumerator();
|
||||
~AndroidEnumerator();
|
||||
|
||||
void open(int direction);
|
||||
void close();
|
||||
void open(int direction);
|
||||
void close();
|
||||
|
||||
int count();
|
||||
std::string nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
int count();
|
||||
std::string nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
|
||||
protected:
|
||||
};
|
||||
protected:
|
||||
};
|
||||
|
||||
class AndroidInputDevice: public InputDevice, public oboe::AudioStreamCallback
|
||||
{
|
||||
public:
|
||||
AndroidInputDevice(int devId);
|
||||
~AndroidInputDevice();
|
||||
class AndroidInputDevice : public InputDevice, public oboe::AudioStreamCallback
|
||||
{
|
||||
public:
|
||||
AndroidInputDevice(int devId);
|
||||
~AndroidInputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
int readBuffer(void* buffer);
|
||||
bool active() const;
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
int readBuffer(void* buffer);
|
||||
bool active() const;
|
||||
|
||||
oboe::DataCallbackResult
|
||||
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames);
|
||||
|
||||
|
||||
protected:
|
||||
bool mActive = false;
|
||||
oboe::AudioStream* mRecordingStream = nullptr;
|
||||
PResampler mResampler;
|
||||
DataWindow mDeviceRateCache, mSdkRateCache;
|
||||
int mDeviceRate; // Actual rate of opened recorder
|
||||
int mBufferSize; // Size of buffer used for recording (at native sample rate)
|
||||
DataWindow mRecorderBuffer;
|
||||
std::condition_variable mDataCondVar;
|
||||
int mRecorderBufferIndex;
|
||||
std::mutex mMutex;
|
||||
};
|
||||
protected:
|
||||
bool mActive = false;
|
||||
oboe::AudioStream* mRecordingStream = nullptr;
|
||||
PResampler mResampler;
|
||||
DataWindow mDeviceRateCache, mSdkRateCache;
|
||||
int mDeviceRate; // Actual rate of opened recorder
|
||||
int mBufferSize; // Size of buffer used for recording (at native sample rate)
|
||||
DataWindow mRecorderBuffer;
|
||||
std::condition_variable mDataCondVar;
|
||||
int mRecorderBufferIndex;
|
||||
std::mutex mMutex;
|
||||
};
|
||||
|
||||
class AndroidOutputDevice: public OutputDevice, public oboe::AudioStreamCallback
|
||||
{
|
||||
public:
|
||||
AndroidOutputDevice(int devId);
|
||||
~AndroidOutputDevice();
|
||||
class AndroidOutputDevice : public OutputDevice, public oboe::AudioStreamCallback
|
||||
{
|
||||
public:
|
||||
AndroidOutputDevice(int devId);
|
||||
~AndroidOutputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
|
||||
void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result);
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* audioStream, void* audioData, int32_t numFrames);
|
||||
void onErrorAfterClose(oboe::AudioStream* stream, oboe::Result result);
|
||||
|
||||
protected:
|
||||
std::mutex mMutex;
|
||||
int mDeviceRate = 0;
|
||||
oboe::AudioStream* mPlayingStream = nullptr;
|
||||
DataWindow mPlayBuffer;
|
||||
int mBufferIndex = 0, mBufferSize = 0;
|
||||
bool mInShutdown = false;
|
||||
bool mActive = false;
|
||||
protected:
|
||||
std::mutex mMutex;
|
||||
int mDeviceRate = 0;
|
||||
oboe::AudioStream* mPlayingStream = nullptr;
|
||||
DataWindow mPlayBuffer;
|
||||
int mBufferIndex = 0, mBufferSize = 0;
|
||||
bool mInShutdown = false;
|
||||
bool mActive = false;
|
||||
|
||||
// Adaptive buffer sizing + device-side diagnostics (Step 0 / #5).
|
||||
int32_t mBurstFrames = 0; // frames per burst, latched at open()
|
||||
int32_t mXRunLast = 0; // last observed cumulative XRun count
|
||||
float mHeartbeatLast = 0.0f; // now_ms() of last device heartbeat log
|
||||
// Adaptive buffer sizing + device-side diagnostics (Step 0 / #5).
|
||||
int32_t mBurstFrames = 0; // frames per burst, latched at open()
|
||||
int32_t mXRunLast = 0; // last observed cumulative XRun count
|
||||
float mHeartbeatLast = 0.0f; // now_ms() of last device heartbeat log
|
||||
|
||||
// Statistics
|
||||
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
|
||||
};
|
||||
}
|
||||
// Statistics
|
||||
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
|
||||
};
|
||||
} // namespace Audio
|
||||
|
||||
#endif // TARGET_ANDROID
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,69 +25,71 @@
|
||||
namespace Audio
|
||||
{
|
||||
|
||||
class AudioException: public Exception
|
||||
class AudioException : public Exception
|
||||
{
|
||||
public:
|
||||
AudioException(int code, OSStatus subcode)
|
||||
:Exception(code, int(subcode))
|
||||
{}
|
||||
AudioException(int code, OSStatus subcode) : Exception(code, int(subcode)) {}
|
||||
};
|
||||
|
||||
//#ifndef AudioDeviceID
|
||||
//# define AudioDeviceID unsigned
|
||||
//#endif
|
||||
class MacEnumerator: public Enumerator
|
||||
// #ifndef AudioDeviceID
|
||||
// # define AudioDeviceID unsigned
|
||||
// #endif
|
||||
class MacEnumerator : public Enumerator
|
||||
{
|
||||
public:
|
||||
MacEnumerator();
|
||||
~MacEnumerator();
|
||||
MacEnumerator();
|
||||
~MacEnumerator();
|
||||
|
||||
void open(int direction);
|
||||
void close();
|
||||
void open(int direction);
|
||||
void close();
|
||||
|
||||
int count();
|
||||
std::tstring nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
int count();
|
||||
std::tstring nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
|
||||
protected:
|
||||
struct DeviceInfo
|
||||
{
|
||||
AudioDeviceID mId;
|
||||
std::string mName;
|
||||
bool mCanChangeOutputVolume;
|
||||
bool mCanChangeInputVolume;
|
||||
int mInputCount, mOutputCount;
|
||||
int mDefaultRate;
|
||||
DeviceInfo(): mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0), mDefaultRate(16000) {}
|
||||
};
|
||||
std::vector<DeviceInfo> mDeviceList;
|
||||
unsigned mDefaultInput, mDefaultOutput;
|
||||
int mDirection;
|
||||
void getInfo(DeviceInfo& di);
|
||||
struct DeviceInfo
|
||||
{
|
||||
AudioDeviceID mId;
|
||||
std::string mName;
|
||||
bool mCanChangeOutputVolume;
|
||||
bool mCanChangeInputVolume;
|
||||
int mInputCount, mOutputCount;
|
||||
int mDefaultRate;
|
||||
DeviceInfo()
|
||||
: mId(0), mCanChangeOutputVolume(false), mCanChangeInputVolume(false), mInputCount(0), mOutputCount(0),
|
||||
mDefaultRate(16000)
|
||||
{
|
||||
}
|
||||
};
|
||||
std::vector<DeviceInfo> mDeviceList;
|
||||
unsigned mDefaultInput, mDefaultOutput;
|
||||
int mDirection;
|
||||
void getInfo(DeviceInfo& di);
|
||||
};
|
||||
|
||||
class CoreAudioUnit
|
||||
{
|
||||
public:
|
||||
CoreAudioUnit();
|
||||
~CoreAudioUnit();
|
||||
CoreAudioUnit();
|
||||
~CoreAudioUnit();
|
||||
|
||||
void open(bool voice);
|
||||
void close();
|
||||
AudioStreamBasicDescription getFormat(int scope, int bus);
|
||||
void setFormat(AudioStreamBasicDescription& format, int scope, int bus);
|
||||
bool getEnabled(int scope, int bus);
|
||||
void setEnabled(bool enabled, int scope, int bus);
|
||||
void makeCurrent(AudioDeviceID deviceId, int scope, int bus);
|
||||
void setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus);
|
||||
void setBufferFrameSizeInMilliseconds(int ms);
|
||||
int getBufferFrameSize();
|
||||
void initialize();
|
||||
AudioUnit getHandle();
|
||||
void open(bool voice);
|
||||
void close();
|
||||
AudioStreamBasicDescription getFormat(int scope, int bus);
|
||||
void setFormat(AudioStreamBasicDescription& format, int scope, int bus);
|
||||
bool getEnabled(int scope, int bus);
|
||||
void setEnabled(bool enabled, int scope, int bus);
|
||||
void makeCurrent(AudioDeviceID deviceId, int scope, int bus);
|
||||
void setCallback(AURenderCallbackStruct cb, int callbackType, int scope, int bus);
|
||||
void setBufferFrameSizeInMilliseconds(int ms);
|
||||
int getBufferFrameSize();
|
||||
void initialize();
|
||||
AudioUnit getHandle();
|
||||
|
||||
protected:
|
||||
AudioUnit mUnit;
|
||||
AudioUnit mUnit;
|
||||
};
|
||||
|
||||
class MacDevice
|
||||
@@ -96,101 +98,92 @@ public:
|
||||
MacDevice(int devId);
|
||||
~MacDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
void setRender(bool render);
|
||||
void setCapture(bool capture);
|
||||
int getId();
|
||||
Format getFormat();
|
||||
bool open();
|
||||
void close();
|
||||
void setRender(bool render);
|
||||
void setCapture(bool capture);
|
||||
int getId();
|
||||
Format getFormat();
|
||||
|
||||
DataConnection* connection();
|
||||
void setConnection(DataConnection* c);
|
||||
void provideAudioToSpeaker(int channels, void* buffer, int length);
|
||||
void obtainAudioFromMic(int channels, const void* buffer, int length);
|
||||
void setConnection(DataConnection* c);
|
||||
void provideAudioToSpeaker(int channels, void* buffer, int length);
|
||||
void obtainAudioFromMic(int channels, const void* buffer, int length);
|
||||
|
||||
protected:
|
||||
AudioDeviceID mDeviceId;
|
||||
bool mCapture, mRender;
|
||||
bool mActive;
|
||||
int mUsageCount;
|
||||
Mutex mGuard;
|
||||
AudioDeviceID mDeviceId;
|
||||
bool mCapture, mRender;
|
||||
bool mActive;
|
||||
int mUsageCount;
|
||||
Mutex mGuard;
|
||||
|
||||
CoreAudioUnit mAudioUnit;
|
||||
AudioComponent mComponent;
|
||||
AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat, mStreamFormat;
|
||||
CoreAudioUnit mAudioUnit;
|
||||
AudioComponent mComponent;
|
||||
AudioStreamBasicDescription mCaptureInputFormat, mCaptureOutputFormat, mRenderInputFormat, mRenderOutputFormat,
|
||||
mStreamFormat;
|
||||
AudioBufferList* mInputBufferList;
|
||||
DataConnection* mConnection;
|
||||
SpeexResampler mCaptureResampler, mRenderResampler;
|
||||
ByteBuffer mTail;
|
||||
DataWindow mInputBuffer, mOutputBuffer;
|
||||
bool createUnit(bool voice);
|
||||
void destroyUnit();
|
||||
void startStream();
|
||||
void stopStream();
|
||||
void setupStreamFormat();
|
||||
bool createResampleUnit(AudioStreamBasicDescription format);
|
||||
DataConnection* mConnection;
|
||||
SpeexResampler mCaptureResampler, mRenderResampler;
|
||||
ByteBuffer mTail;
|
||||
DataWindow mInputBuffer, mOutputBuffer;
|
||||
bool createUnit(bool voice);
|
||||
void destroyUnit();
|
||||
void startStream();
|
||||
void stopStream();
|
||||
void setupStreamFormat();
|
||||
bool createResampleUnit(AudioStreamBasicDescription format);
|
||||
|
||||
static OSStatus outputCallback( void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData );
|
||||
static OSStatus outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList* ioData);
|
||||
|
||||
static OSStatus inputCallback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData);
|
||||
static OSStatus inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList* ioData);
|
||||
#ifdef TARGET_IOS
|
||||
static void propListener(void *inClientData,
|
||||
AudioSessionPropertyID inID,
|
||||
UInt32 inDataSize,
|
||||
const void * inData);
|
||||
static void interruptionListener(void *inClientData, UInt32 inInterruption);
|
||||
static void propListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inData);
|
||||
static void interruptionListener(void* inClientData, UInt32 inInterruption);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<MacDevice> PMacDevice;
|
||||
|
||||
class MacInputDevice: public InputDevice
|
||||
class MacInputDevice : public InputDevice
|
||||
{
|
||||
public:
|
||||
MacInputDevice(int devId);
|
||||
~MacInputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
int readBuffer(void* buffer);
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
int readBuffer(void* buffer);
|
||||
|
||||
protected:
|
||||
PMacDevice mDevice;
|
||||
|
||||
};
|
||||
|
||||
class MacOutputDevice: public OutputDevice
|
||||
class MacOutputDevice : public OutputDevice
|
||||
{
|
||||
public:
|
||||
MacOutputDevice(int devId);
|
||||
~MacOutputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
bool open();
|
||||
void close();
|
||||
Format getFormat();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakemode);
|
||||
|
||||
protected:
|
||||
PMacDevice mDevice;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Audio
|
||||
|
||||
#endif // TARGET_OSX
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
DataWindow::DataWindow()
|
||||
{}
|
||||
DataWindow::DataWindow() {}
|
||||
|
||||
DataWindow::~DataWindow()
|
||||
{
|
||||
@@ -28,8 +27,8 @@ void DataWindow::setCapacity(size_t capacity)
|
||||
if (capacity <= mCapacity)
|
||||
return;
|
||||
|
||||
size_t tail = capacity - mCapacity;
|
||||
char* buffer = mData;
|
||||
size_t tail = capacity - mCapacity;
|
||||
char* buffer = mData;
|
||||
mData = (char*)realloc(mData, capacity);
|
||||
if (!mData)
|
||||
{
|
||||
@@ -145,7 +144,7 @@ size_t DataWindow::read(void* buffer, size_t length)
|
||||
if (buffer)
|
||||
memcpy(buffer, mData, length);
|
||||
if (length < mFilled)
|
||||
memmove(mData, mData+length, mFilled - length);
|
||||
memmove(mData, mData + length, mFilled - length);
|
||||
mFilled -= length;
|
||||
}
|
||||
return length;
|
||||
@@ -181,7 +180,7 @@ void DataWindow::zero(size_t length)
|
||||
|
||||
size_t DataWindow::moveTo(DataWindow& dst, size_t size)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
Lock l(mMutex);
|
||||
|
||||
size_t avail = std::min(size, (size_t)filled());
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
|
||||
|
||||
dst.setCapacity(src.filled()*2);
|
||||
dst.setCapacity(src.filled() * 2);
|
||||
short* input = (short*)src.mutableData();
|
||||
short* output = (short*)dst.mutableData();
|
||||
|
||||
for (int i=0; i<src.filled()/2; i++)
|
||||
output[i*2] = output[i*2+1] = input[i];
|
||||
for (int i = 0; i < src.filled() / 2; i++)
|
||||
output[i * 2] = output[i * 2 + 1] = input[i];
|
||||
dst.mFilled = src.filled() * 2;
|
||||
}
|
||||
|
||||
@@ -18,34 +18,34 @@ public:
|
||||
DataWindow();
|
||||
~DataWindow();
|
||||
|
||||
void setCapacity(size_t capacity);
|
||||
size_t capacity() const;
|
||||
void setCapacity(size_t capacity);
|
||||
size_t capacity() const;
|
||||
|
||||
void addZero(size_t length);
|
||||
void add(const void* data, size_t length);
|
||||
void add(short sample);
|
||||
size_t read(void* buffer, size_t length);
|
||||
void erase(size_t length);
|
||||
const char* data() const;
|
||||
char* mutableData();
|
||||
size_t filled() const;
|
||||
void setFilled(size_t filled);
|
||||
void clear();
|
||||
void addZero(size_t length);
|
||||
void add(const void* data, size_t length);
|
||||
void add(short sample);
|
||||
size_t read(void* buffer, size_t length);
|
||||
void erase(size_t length);
|
||||
const char* data() const;
|
||||
char* mutableData();
|
||||
size_t filled() const;
|
||||
void setFilled(size_t filled);
|
||||
void clear();
|
||||
|
||||
short shortAt(size_t index) const;
|
||||
void setShortAt(short value, size_t index);
|
||||
void zero(size_t length);
|
||||
size_t moveTo(DataWindow& dst, size_t size /* in bytes*/ );
|
||||
short shortAt(size_t index) const;
|
||||
void setShortAt(short value, size_t index);
|
||||
void zero(size_t length);
|
||||
size_t moveTo(DataWindow& dst, size_t size /* in bytes*/);
|
||||
|
||||
std::chrono::milliseconds getTimeLength(const Format& fmt) const;
|
||||
|
||||
static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
|
||||
static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
|
||||
|
||||
protected:
|
||||
mutable Mutex mMutex;
|
||||
char* mData = nullptr;
|
||||
size_t mFilled = 0;
|
||||
size_t mCapacity = 0;
|
||||
char* mData = nullptr;
|
||||
size_t mFilled = 0;
|
||||
size_t mCapacity = 0;
|
||||
};
|
||||
}
|
||||
} // namespace Audio
|
||||
#endif
|
||||
|
||||
@@ -13,278 +13,274 @@ using namespace Audio;
|
||||
|
||||
// --- 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),
|
||||
mMonitoring(nullptr)
|
||||
: mConfig(nullptr), mDelegate(nullptr), mAec(false), mAgc(false),
|
||||
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));
|
||||
mOutputBuffer.setCapacity(AUDIO_SPK_BUFFER_SIZE * (AUDIO_SPK_BUFFER_COUNT + 1));
|
||||
mInputResampingData.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_BUFFER_COUNT + 1));
|
||||
mOutput10msBuffer.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH));
|
||||
mOutputNativeData.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH * AUDIO_SPK_BUFFER_COUNT * 24));
|
||||
mInputBuffer.setCapacity(AUDIO_MIC_BUFFER_SIZE * (AUDIO_MIC_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));
|
||||
mOutput10msBuffer.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH));
|
||||
mOutputNativeData.setCapacity((int)Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH * AUDIO_SPK_BUFFER_COUNT * 24));
|
||||
}
|
||||
|
||||
DevicePair::~DevicePair()
|
||||
{
|
||||
if (mInput)
|
||||
{
|
||||
if (mInput->connection() == this)
|
||||
mInput->setConnection(nullptr);
|
||||
mInput.reset();
|
||||
}
|
||||
if (mInput)
|
||||
{
|
||||
if (mInput->connection() == this)
|
||||
mInput->setConnection(nullptr);
|
||||
mInput.reset();
|
||||
}
|
||||
|
||||
if (mOutput)
|
||||
{
|
||||
if (mOutput->connection() == this)
|
||||
mOutput->setConnection(nullptr);
|
||||
mOutput.reset();
|
||||
}
|
||||
if (mOutput)
|
||||
{
|
||||
if (mOutput->connection() == this)
|
||||
mOutput->setConnection(nullptr);
|
||||
mOutput.reset();
|
||||
}
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setAec(bool aec)
|
||||
{
|
||||
mAec = aec;
|
||||
return *this;
|
||||
mAec = aec;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DevicePair::aec()
|
||||
{
|
||||
return mAec;
|
||||
return mAec;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setAgc(bool agc)
|
||||
{
|
||||
mAgc = agc;
|
||||
return *this;
|
||||
mAgc = agc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DevicePair::agc()
|
||||
{
|
||||
return mAgc;
|
||||
return mAgc;
|
||||
}
|
||||
|
||||
|
||||
VariantMap* DevicePair::config()
|
||||
{
|
||||
return mConfig;
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setConfig(VariantMap* config)
|
||||
{
|
||||
mConfig = config;
|
||||
return *this;
|
||||
mConfig = config;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PInputDevice DevicePair::input()
|
||||
{
|
||||
return mInput;
|
||||
return mInput;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
mInput = input;
|
||||
mInput->setConnection(this);
|
||||
if (mDelegate)
|
||||
mDelegate->deviceChanged(this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
POutputDevice DevicePair::output()
|
||||
{
|
||||
return mOutput;
|
||||
return mOutput;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
mOutput = output;
|
||||
mOutput->setConnection(this);
|
||||
if (mDelegate)
|
||||
mDelegate->deviceChanged(this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DevicePair::start()
|
||||
{
|
||||
bool result = false;
|
||||
if (mInput)
|
||||
result = mInput->open();
|
||||
if (mOutput && result)
|
||||
result &= mOutput->open();
|
||||
bool result = false;
|
||||
if (mInput)
|
||||
result = mInput->open();
|
||||
if (mOutput && result)
|
||||
result &= mOutput->open();
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void DevicePair::stop()
|
||||
{
|
||||
if (mInput)
|
||||
mInput->close();
|
||||
if (mOutput)
|
||||
mOutput->close();
|
||||
if (mInput)
|
||||
mInput->close();
|
||||
if (mOutput)
|
||||
mOutput->close();
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setDelegate(Delegate* dc)
|
||||
{
|
||||
mDelegate = dc;
|
||||
return *this;
|
||||
mDelegate = dc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DevicePair::Delegate* DevicePair::delegate()
|
||||
{
|
||||
return mDelegate;
|
||||
return mDelegate;
|
||||
}
|
||||
|
||||
DevicePair& DevicePair::setMonitoring(DataConnection* monitoring)
|
||||
{
|
||||
mMonitoring = monitoring;
|
||||
return *this;
|
||||
mMonitoring = monitoring;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataConnection* DevicePair::monitoring()
|
||||
{
|
||||
return mMonitoring;
|
||||
return mMonitoring;
|
||||
}
|
||||
|
||||
Player& DevicePair::player()
|
||||
{
|
||||
return mPlayer;
|
||||
return mPlayer;
|
||||
}
|
||||
|
||||
void DevicePair::onMicData(const Format& f, const void* buffer, int length)
|
||||
{
|
||||
#ifdef DUMP_NATIVEINPUT
|
||||
if (!mNativeInputDump)
|
||||
{
|
||||
mNativeInputDump = std::make_shared<WavFileWriter>();
|
||||
mNativeInputDump->open("nativeinput.wav", f.mRate, f.mChannels);
|
||||
}
|
||||
if (mNativeInputDump)
|
||||
mNativeInputDump->write(buffer, length);
|
||||
if (!mNativeInputDump)
|
||||
{
|
||||
mNativeInputDump = std::make_shared<WavFileWriter>();
|
||||
mNativeInputDump->open("nativeinput.wav", f.mRate, f.mChannels);
|
||||
}
|
||||
if (mNativeInputDump)
|
||||
mNativeInputDump->write(buffer, length);
|
||||
#endif
|
||||
|
||||
// send the data to internal queue - it can hold data which were not processed by resampler in last call
|
||||
mInputResampingData.add(buffer, length);
|
||||
// send the data to internal queue - it can hold data which were not processed by resampler in last call
|
||||
mInputResampingData.add(buffer, length);
|
||||
|
||||
// split processing by blocks
|
||||
int blocks = mInputResampingData.filled() / (int)f.sizeFromTime(AUDIO_MIC_BUFFER_LENGTH);
|
||||
// split processing by blocks
|
||||
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
|
||||
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());
|
||||
|
||||
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.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));
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePair::onSpkData(const Format& f, void* buffer, int length)
|
||||
{
|
||||
//ICELogMedia(<< "Audio::DevicePair::onSpkData() begin");
|
||||
// ICELogMedia(<< "Audio::DevicePair::onSpkData() begin");
|
||||
#ifdef DUMP_NATIVEOUTPUT
|
||||
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++)
|
||||
if (!mNativeOutputDump)
|
||||
{
|
||||
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
|
||||
|
||||
// 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
|
||||
mNativeOutputDump = std::make_shared<WavFileWriter>();
|
||||
mNativeOutputDump->open("nativeoutput.wav", f.mRate, f.mChannels);
|
||||
}
|
||||
}
|
||||
|
||||
// assert(mOutputNativeData.filled() >= length);
|
||||
#ifdef DUMP_NATIVEOUTPUT
|
||||
if (mNativeOutputDump)
|
||||
mNativeOutputDump->write(mOutputNativeData.data(), length);
|
||||
#endif
|
||||
#ifdef CONSOLE_LOGGING
|
||||
printf("Speaker requests %d\n", length);
|
||||
#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
|
||||
if (mMonitoring)
|
||||
mMonitoring->onSpkData(f, buffer, length);
|
||||
// Now request data from terminal or whetever delegate is
|
||||
for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
|
||||
{
|
||||
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.
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (mAgc)
|
||||
mAgcFilter.process(buffer, length);
|
||||
if (mAgc)
|
||||
mAgcFilter.process(buffer, length);
|
||||
|
||||
if (mAec)
|
||||
mAecFilter.fromMic(buffer);
|
||||
if (mAec)
|
||||
mAecFilter.fromMic(buffer);
|
||||
|
||||
if (mDelegate)
|
||||
mDelegate->onMicData(f, buffer, length);
|
||||
if (mDelegate)
|
||||
mDelegate->onMicData(f, buffer, length);
|
||||
}
|
||||
|
||||
@@ -11,59 +11,59 @@
|
||||
#include "Audio_Resampler.h"
|
||||
#include "Audio_DataWindow.h"
|
||||
|
||||
//#define DUMP_NATIVEOUTPUT
|
||||
//#define DUMP_NATIVEINPUT
|
||||
// #define DUMP_NATIVEOUTPUT
|
||||
// #define DUMP_NATIVEINPUT
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
|
||||
class DevicePair: protected DataConnection
|
||||
{
|
||||
public:
|
||||
class Delegate: public DataConnection
|
||||
class DevicePair : protected DataConnection
|
||||
{
|
||||
public:
|
||||
class Delegate : public DataConnection
|
||||
{
|
||||
public:
|
||||
virtual void deviceChanged(DevicePair* dpair) = 0;
|
||||
virtual void deviceChanged(DevicePair* dpair) = 0;
|
||||
};
|
||||
|
||||
DevicePair();
|
||||
virtual ~DevicePair();
|
||||
|
||||
DevicePair& setAec(bool aec);
|
||||
bool aec();
|
||||
DevicePair& setAgc(bool agc);
|
||||
bool agc();
|
||||
DevicePair& setAec(bool aec);
|
||||
bool aec();
|
||||
DevicePair& setAgc(bool agc);
|
||||
bool agc();
|
||||
|
||||
VariantMap* config();
|
||||
DevicePair& setConfig(VariantMap* config);
|
||||
VariantMap* config();
|
||||
DevicePair& setConfig(VariantMap* config);
|
||||
|
||||
PInputDevice input();
|
||||
DevicePair& setInput(PInputDevice input);
|
||||
PInputDevice input();
|
||||
DevicePair& setInput(PInputDevice input);
|
||||
|
||||
POutputDevice output();
|
||||
DevicePair& setOutput(POutputDevice output);
|
||||
POutputDevice output();
|
||||
DevicePair& setOutput(POutputDevice output);
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
DevicePair& setDelegate(Delegate* dc);
|
||||
Delegate* delegate();
|
||||
DevicePair& setDelegate(Delegate* dc);
|
||||
Delegate* delegate();
|
||||
|
||||
DevicePair& setMonitoring(DataConnection* monitoring);
|
||||
DevicePair& setMonitoring(DataConnection* monitoring);
|
||||
DataConnection* monitoring();
|
||||
|
||||
Player& player();
|
||||
Player& player();
|
||||
|
||||
protected:
|
||||
VariantMap* mConfig;
|
||||
PInputDevice mInput;
|
||||
POutputDevice mOutput;
|
||||
Delegate* mDelegate;
|
||||
bool mAec;
|
||||
bool mAgc;
|
||||
AgcFilter mAgcFilter;
|
||||
AecFilter mAecFilter;
|
||||
Player mPlayer;
|
||||
protected:
|
||||
VariantMap* mConfig;
|
||||
PInputDevice mInput;
|
||||
POutputDevice mOutput;
|
||||
Delegate* mDelegate;
|
||||
bool mAec;
|
||||
bool mAgc;
|
||||
AgcFilter mAgcFilter;
|
||||
AecFilter mAecFilter;
|
||||
Player mPlayer;
|
||||
UniversalResampler mMicResampler, mSpkResampler;
|
||||
DataWindow mInputBuffer, mOutputBuffer, mAecSpkBuffer, mInputResampingData, mOutputNativeData, mOutput10msBuffer;
|
||||
DataConnection* mMonitoring;
|
||||
@@ -77,9 +77,9 @@ namespace Audio
|
||||
void onMicData(const Format& f, const void* buffer, int length);
|
||||
void onSpkData(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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#define DRVM_MAPPER_CONSOLEVOICECOM_GET (0x2000 + 23)
|
||||
#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 LOG_SUBSYSTEM "audio"
|
||||
@@ -39,32 +39,29 @@ public:
|
||||
{
|
||||
HINSTANCE mInstance;
|
||||
|
||||
HRESULT (WINAPI *DirectSoundCreate8)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN);
|
||||
HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
|
||||
HRESULT(WINAPI* DirectSoundCreate8)(LPGUID, LPDIRECTSOUND8*, LPUNKNOWN);
|
||||
HRESULT(WINAPI* DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
HRESULT(WINAPI* DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
|
||||
|
||||
HRESULT (WINAPI *DirectSoundCaptureCreate8)(LPGUID, LPDIRECTSOUNDCAPTURE8* , LPUNKNOWN);
|
||||
HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
|
||||
HRESULT (WINAPI *GetDeviceID)(LPCGUID src, LPGUID dst);
|
||||
HRESULT(WINAPI* DirectSoundCaptureCreate8)(LPGUID, LPDIRECTSOUNDCAPTURE8*, LPUNKNOWN);
|
||||
HRESULT(WINAPI* DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
HRESULT(WINAPI* DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
|
||||
HRESULT(WINAPI* GetDeviceID)(LPCGUID src, LPGUID dst);
|
||||
} mRoutines;
|
||||
|
||||
protected:
|
||||
LPDIRECTSOUND mDirectSound;
|
||||
Mutex mGuard;
|
||||
Mutex mGuard;
|
||||
unsigned int mRefCount;
|
||||
};
|
||||
|
||||
DSoundInit gDSoundInit;
|
||||
|
||||
DSoundInit::DSoundInit()
|
||||
:mRefCount(0)
|
||||
{
|
||||
}
|
||||
DSoundInit::DSoundInit() : mRefCount(0) {}
|
||||
|
||||
DSoundInit::~DSoundInit()
|
||||
{
|
||||
//Unload();
|
||||
// Unload();
|
||||
}
|
||||
|
||||
void DSoundInit::load()
|
||||
@@ -77,16 +74,21 @@ void DSoundInit::load()
|
||||
|
||||
hr = ::CoInitialize(NULL);
|
||||
|
||||
//load the DirectSound DLL
|
||||
// load the DirectSound DLL
|
||||
mRoutines.mInstance = ::LoadLibraryW(L"dsound.dll");
|
||||
if (!mRoutines.mInstance)
|
||||
throw std::logic_error("Cannot load dsound.dll");
|
||||
|
||||
mRoutines.DirectSoundCaptureCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureCreate8");
|
||||
mRoutines.DirectSoundCaptureEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureEnumerateW");
|
||||
mRoutines.DirectSoundCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCreate8");
|
||||
mRoutines.DirectSoundEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW");
|
||||
mRoutines.GetDeviceID = (HRESULT (WINAPI*) (LPCGUID, LPGUID)) GetProcAddress(mRoutines.mInstance, "GetDeviceID");
|
||||
mRoutines.DirectSoundCaptureCreate8 =
|
||||
(HRESULT(WINAPI*)(LPGUID, LPDIRECTSOUNDCAPTURE8*, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance,
|
||||
"DirectSoundCaptureCreate8");
|
||||
mRoutines.DirectSoundCaptureEnumerateW = (HRESULT(WINAPI*)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(
|
||||
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()
|
||||
:mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture)
|
||||
{
|
||||
}
|
||||
VistaEnumerator::VistaEnumerator() : mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture) {}
|
||||
|
||||
VistaEnumerator::~VistaEnumerator()
|
||||
{
|
||||
@@ -119,14 +118,12 @@ VistaEnumerator::~VistaEnumerator()
|
||||
void VistaEnumerator::open(int direction)
|
||||
{
|
||||
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
|
||||
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
||||
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
||||
|
||||
mDirection = (direction == myMicrophone) ? eCapture : eRender;
|
||||
|
||||
HRESULT hr = CoCreateInstance(
|
||||
CLSID_MMDeviceEnumerator, NULL,
|
||||
CLSCTX_ALL, IID_IMMDeviceEnumerator,
|
||||
(void**)&mEnumerator);
|
||||
HRESULT hr =
|
||||
CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mEnumerator);
|
||||
if (!mEnumerator)
|
||||
return;
|
||||
|
||||
@@ -152,7 +149,7 @@ void VistaEnumerator::close()
|
||||
|
||||
if (mDefaultDevice)
|
||||
{
|
||||
//mDefaultDevice->Release();
|
||||
// mDefaultDevice->Release();
|
||||
mDefaultDevice = NULL;
|
||||
}
|
||||
|
||||
@@ -162,7 +159,7 @@ void VistaEnumerator::close()
|
||||
mEnumerator = NULL;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -175,9 +172,9 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
|
||||
if (index == -1)
|
||||
return mDefaultDevice;
|
||||
|
||||
size_t idSize = 0;
|
||||
MMRESULT mmres = 0;
|
||||
WCHAR* id = NULL;
|
||||
size_t idSize = 0;
|
||||
MMRESULT mmres = 0;
|
||||
WCHAR* id = NULL;
|
||||
if (mDirection == eCapture)
|
||||
{
|
||||
mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEIDSIZE, (DWORD_PTR)&idSize, NULL);
|
||||
@@ -185,7 +182,7 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
|
||||
if (mmres != MMSYSERR_NOERROR)
|
||||
return NULL;
|
||||
|
||||
id = (WCHAR*)_alloca(idSize*sizeof(WCHAR));
|
||||
id = (WCHAR*)_alloca(idSize * sizeof(WCHAR));
|
||||
mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize);
|
||||
}
|
||||
else
|
||||
@@ -195,7 +192,7 @@ IMMDevice* VistaEnumerator::mapIndexToInterface(int index)
|
||||
if (mmres != MMSYSERR_NOERROR)
|
||||
return NULL;
|
||||
|
||||
id = (WCHAR*)_alloca(idSize*sizeof(WCHAR));
|
||||
id = (WCHAR*)_alloca(idSize * sizeof(WCHAR));
|
||||
mmres = waveOutMessage((HWAVEOUT)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize);
|
||||
}
|
||||
|
||||
@@ -213,7 +210,7 @@ void VistaEnumerator::enumerate()
|
||||
mNameList.clear();
|
||||
int res = (int)count();
|
||||
|
||||
for (int i=0; i<res; i++)
|
||||
for (int i = 0; i < res; i++)
|
||||
{
|
||||
IMMDevice* dev = mapIndexToInterface(i);
|
||||
if (dev)
|
||||
@@ -258,26 +255,23 @@ int VistaEnumerator::indexOfDefaultDevice()
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return devID;
|
||||
}
|
||||
|
||||
// -------------- XpEnumerator ---------------
|
||||
XpEnumerator::XpEnumerator()
|
||||
:mDirection(-1)
|
||||
{
|
||||
}
|
||||
XpEnumerator::XpEnumerator() : mDirection(-1) {}
|
||||
|
||||
XpEnumerator::~XpEnumerator()
|
||||
{
|
||||
}
|
||||
XpEnumerator::~XpEnumerator() {}
|
||||
|
||||
void XpEnumerator::open(int direction)
|
||||
{
|
||||
@@ -285,7 +279,7 @@ void XpEnumerator::open(int direction)
|
||||
if (direction == myMicrophone)
|
||||
{
|
||||
int count = waveInGetNumDevs();
|
||||
for (int i=0; i<count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WAVEINCAPSW caps;
|
||||
if (waveInGetDevCapsW(i, &caps, sizeof caps) == MMSYSERR_NOERROR)
|
||||
@@ -297,7 +291,7 @@ void XpEnumerator::open(int direction)
|
||||
else
|
||||
{
|
||||
int count = waveOutGetNumDevs();
|
||||
for (int i=0; i<count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
WAVEOUTCAPSW caps;
|
||||
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();
|
||||
}
|
||||
@@ -333,12 +325,14 @@ int XpEnumerator::indexOfDefaultDevice()
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return devID;
|
||||
@@ -346,11 +340,12 @@ int XpEnumerator::indexOfDefaultDevice()
|
||||
|
||||
// -------- DSoundInputDevice ---------------
|
||||
DSoundInputDevice::DSoundInputDevice(GUID deviceId)
|
||||
:mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE), mEnableDenoiser(true),
|
||||
mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT)
|
||||
#ifdef AUDIO_DUMPINPUT
|
||||
,mDump(AUDIO_SAMPLERATE)
|
||||
#endif
|
||||
: mSimulate(false), mBufferIndex(0), mGUID(deviceId), mThreadHandle(0), mDenoiser(AUDIO_SAMPLERATE),
|
||||
mEnableDenoiser(true), mNullAudio(AUDIO_MIC_BUFFER_LENGTH, AUDIO_MIC_BUFFER_COUNT)
|
||||
#ifdef AUDIO_DUMPINPUT
|
||||
,
|
||||
mDump(AUDIO_SAMPLERATE)
|
||||
#endif
|
||||
{
|
||||
gDSoundInit.load();
|
||||
|
||||
@@ -359,7 +354,7 @@ DSoundInputDevice::DSoundInputDevice(GUID deviceId)
|
||||
mDevice = NULL;
|
||||
mBuffer = 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;
|
||||
mEventSignals[i] = mEventArray[i].hEventNotify = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
@@ -371,7 +366,7 @@ DSoundInputDevice::~DSoundInputDevice()
|
||||
{
|
||||
close();
|
||||
::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);
|
||||
|
||||
gDSoundInit.unload();
|
||||
@@ -388,14 +383,13 @@ bool DSoundInputDevice::isSimulate() const
|
||||
}
|
||||
|
||||
void DSoundInputDevice::openDevice()
|
||||
{
|
||||
ICELogInfo(<< "Open DirectSound audio input.")
|
||||
::CoInitialize(NULL);
|
||||
{
|
||||
ICELogInfo(<< "Open DirectSound audio input.")::CoInitialize(NULL);
|
||||
Lock l(mGuard);
|
||||
// Ensure if GUID is not null
|
||||
if (IsEqualGUID(mGUID, GUID_NULL))
|
||||
{
|
||||
setSimulate( true );
|
||||
setSimulate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -404,24 +398,28 @@ void DSoundInputDevice::openDevice()
|
||||
mDump.open(L"audioinput.wav");
|
||||
#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));
|
||||
|
||||
WAVEFORMATEX wfx;
|
||||
memset(&wfx, 0, sizeof(wfx));
|
||||
|
||||
//wfx.cbSize = sizeof(wfx);
|
||||
// wfx.cbSize = sizeof(wfx);
|
||||
wfx.nChannels = AUDIO_CHANNELS;
|
||||
wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
|
||||
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.wFormatTag = WAVE_FORMAT_PCM;
|
||||
|
||||
DSCBUFFERDESC dsbd;
|
||||
ZeroMemory(&dsbd, sizeof(dsbd));
|
||||
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.lpwfxFormat = &wfx;
|
||||
dsbd.dwFXCount = 0;
|
||||
@@ -434,7 +432,7 @@ void DSoundInputDevice::openDevice()
|
||||
DSoundHelper::checkComResult(mNotifications->SetNotificationPositions(AUDIO_MIC_BUFFER_COUNT, mEventArray));
|
||||
DSoundHelper::checkComResult(mBuffer->Start(DSCBSTART_LOOPING));
|
||||
dscb->Release();
|
||||
setSimulate( false );
|
||||
setSimulate(false);
|
||||
}
|
||||
|
||||
bool DSoundInputDevice::open()
|
||||
@@ -452,7 +450,7 @@ bool DSoundInputDevice::open()
|
||||
|
||||
void DSoundInputDevice::closeDevice()
|
||||
{
|
||||
ICELogInfo(<<"Close DirectSound audio input");
|
||||
ICELogInfo(<< "Close DirectSound audio input");
|
||||
Lock l(mGuard);
|
||||
|
||||
#ifdef AUDIO_DUMPINPUT
|
||||
@@ -505,7 +503,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
|
||||
// Ensure device exists
|
||||
if (!mDevice)
|
||||
{
|
||||
setSimulate( true );
|
||||
setSimulate(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -524,9 +522,10 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -534,7 +533,7 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
|
||||
if (::WaitForMultipleObjects(AUDIO_MIC_BUFFER_COUNT, mEventSignals, TRUE, 0) != WAIT_TIMEOUT)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
@@ -543,11 +542,12 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
|
||||
// Find the buffer start offset
|
||||
mReadOffset = mNextBuffer * AUDIO_MIC_BUFFER_SIZE;
|
||||
|
||||
//increase the buffer's index
|
||||
// increase the buffer's index
|
||||
if (++mNextBuffer == AUDIO_MIC_BUFFER_COUNT)
|
||||
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));
|
||||
|
||||
// Copy&Enqueue captured data to mQueue
|
||||
@@ -574,9 +574,9 @@ bool DSoundInputDevice::tryReadBuffer(void* buffer)
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
setSimulate( true );
|
||||
setSimulate(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -585,9 +585,8 @@ void DSoundInputDevice::setSimulate(bool s)
|
||||
{
|
||||
if (!mSimulate && s)
|
||||
mNullAudio.start();
|
||||
else
|
||||
if (mSimulate && !s)
|
||||
mNullAudio.stop();
|
||||
else if (mSimulate && !s)
|
||||
mNullAudio.stop();
|
||||
|
||||
mSimulate = s;
|
||||
}
|
||||
@@ -599,7 +598,7 @@ Format DSoundInputDevice::getFormat()
|
||||
|
||||
int DSoundInputDevice::readBuffer(void* buffer)
|
||||
{
|
||||
//Lock lock(mGuard);
|
||||
// Lock lock(mGuard);
|
||||
if (mRefCount <= 0 || isSimulate())
|
||||
return 0;
|
||||
|
||||
@@ -642,10 +641,8 @@ void DSoundInputDevice::threadProc(void* arg)
|
||||
|
||||
|
||||
DSoundOutputDevice::DSoundOutputDevice(GUID deviceId)
|
||||
:mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL),
|
||||
mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0),
|
||||
mThreadHandle(0), mSimulate(false), mGUID(deviceId),
|
||||
mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT)
|
||||
: mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL), mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0),
|
||||
mThreadHandle(0), mSimulate(false), mGUID(deviceId), mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT)
|
||||
{
|
||||
gDSoundInit.load();
|
||||
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
@@ -700,7 +697,7 @@ void DSoundOutputDevice::openDevice()
|
||||
ICELogInfo(<< "Open DirectSound audio output");
|
||||
if (IsEqualGUID(mGUID, GUID_NULL))
|
||||
{
|
||||
setSimulate( true );
|
||||
setSimulate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -729,10 +726,10 @@ void DSoundOutputDevice::openDevice()
|
||||
dsbd.dwSize = sizeof(DSBUFFERDESC);
|
||||
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
dsbd.dwBufferBytes = 0;
|
||||
dsbd.lpwfxFormat = NULL;//&wfx;
|
||||
dsbd.lpwfxFormat = NULL; //&wfx;
|
||||
dsbd.guid3DAlgorithm = DS3DALG_DEFAULT;
|
||||
|
||||
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mPrimaryBuffer, NULL ));
|
||||
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mPrimaryBuffer, NULL));
|
||||
DSBCAPS caps;
|
||||
caps.dwSize = sizeof(caps);
|
||||
caps.dwFlags = 0;
|
||||
@@ -751,14 +748,16 @@ void DSoundOutputDevice::openDevice()
|
||||
DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL));
|
||||
|
||||
// Fill the buffer with silence
|
||||
LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0;
|
||||
DSoundHelper::checkComResult(mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0));
|
||||
LPVOID ptr1 = NULL, ptr2 = NULL;
|
||||
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)
|
||||
memset(ptr1, 0, len1);
|
||||
if (len2 && ptr2)
|
||||
memset(ptr2, 0, 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);
|
||||
}
|
||||
|
||||
@@ -804,8 +803,9 @@ bool DSoundOutputDevice::getMediaFrame()
|
||||
if (mConnection)
|
||||
mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame);
|
||||
}
|
||||
catch(...)
|
||||
{}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -833,7 +833,8 @@ bool DSoundOutputDevice::process()
|
||||
offset %= mBufferSize;
|
||||
|
||||
// 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));
|
||||
|
||||
assert(ptr2 == NULL);
|
||||
@@ -858,10 +859,10 @@ void DSoundOutputDevice::threadProc(void* arg)
|
||||
DSoundOutputDevice* impl = (DSoundOutputDevice*)arg;
|
||||
impl->openDevice();
|
||||
|
||||
DWORD waitResult = 0;
|
||||
HANDLE waitArray[2] = {impl->mBufferSignal, impl->mShutdownSignal};
|
||||
DWORD waitResult = 0;
|
||||
HANDLE waitArray[2] = {impl->mBufferSignal, impl->mShutdownSignal};
|
||||
unsigned exitCount = 0;
|
||||
bool exitSignal = false;
|
||||
bool exitSignal = false;
|
||||
while (true)
|
||||
{
|
||||
// Poll for shutdown signal
|
||||
@@ -884,12 +885,12 @@ void DSoundOutputDevice::threadProc(void* arg)
|
||||
impl->restoreBuffer();
|
||||
impl->process();
|
||||
}
|
||||
catch(const Exception& e)
|
||||
catch (const Exception& e)
|
||||
{
|
||||
ICELogError(<< "DirectSound output failed with code = " << e.code() << ", subcode = " << e.subcode());
|
||||
impl->setSimulate(true);
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
ICELogError(<< "DirectSound output failed due to unexpected exception.");
|
||||
impl->setSimulate(true);
|
||||
@@ -925,62 +926,61 @@ bool DSoundOutputDevice::closing()
|
||||
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)
|
||||
{
|
||||
HMODULE hLibDsound = NULL;
|
||||
LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL;
|
||||
LPCLASSFACTORY pClassFactory = NULL;
|
||||
LPKSPROPERTYSET pKsPropertySet = NULL;
|
||||
HRESULT hr = DS_OK;
|
||||
HRESULT DirectSoundPrivateCreate(OUT LPKSPROPERTYSET* ppKsPropertySet)
|
||||
{
|
||||
HMODULE hLibDsound = NULL;
|
||||
LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL;
|
||||
LPCLASSFACTORY pClassFactory = NULL;
|
||||
LPKSPROPERTYSET pKsPropertySet = NULL;
|
||||
HRESULT hr = DS_OK;
|
||||
|
||||
// Load dsound.dll
|
||||
hLibDsound = LoadLibrary(TEXT("dsound.dll"));
|
||||
|
||||
if(!hLibDsound)
|
||||
if (!hLibDsound)
|
||||
{
|
||||
hr = DSERR_GENERIC;
|
||||
}
|
||||
|
||||
// Find DllGetClassObject
|
||||
if(SUCCEEDED(hr))
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
pfnDllGetClassObject =
|
||||
(LPFNDLLGETCLASSOBJECT)GetProcAddress ( hLibDsound, "DllGetClassObject" );
|
||||
pfnDllGetClassObject = (LPFNDLLGETCLASSOBJECT)GetProcAddress(hLibDsound, "DllGetClassObject");
|
||||
|
||||
|
||||
if(!pfnDllGetClassObject)
|
||||
if (!pfnDllGetClassObject)
|
||||
{
|
||||
hr = DSERR_GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
if(pClassFactory)
|
||||
if (pClassFactory)
|
||||
{
|
||||
pClassFactory->Release();
|
||||
}
|
||||
|
||||
// Handle final success or failure
|
||||
if(SUCCEEDED(hr))
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*ppKsPropertySet = pKsPropertySet;
|
||||
}
|
||||
else if(pKsPropertySet)
|
||||
else if (pKsPropertySet)
|
||||
{
|
||||
pKsPropertySet->Release();
|
||||
}
|
||||
@@ -988,56 +988,45 @@ HRESULT DirectSoundPrivateCreate (OUT LPKSPROPERTYSET * ppKsPropertySet)
|
||||
FreeLibrary(hLibDsound);
|
||||
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL GetInfoFromDSoundGUID( GUID i_sGUID, int &dwWaveID)
|
||||
{
|
||||
LPKSPROPERTYSET pKsPropertySet = NULL;
|
||||
HRESULT hr;
|
||||
BOOL retval = FALSE;
|
||||
BOOL GetInfoFromDSoundGUID(GUID i_sGUID, int& dwWaveID)
|
||||
{
|
||||
LPKSPROPERTYSET pKsPropertySet = NULL;
|
||||
HRESULT hr;
|
||||
BOOL retval = FALSE;
|
||||
|
||||
PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL;
|
||||
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription;
|
||||
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription;
|
||||
|
||||
memset(&sDirectSoundDeviceDescription,0,sizeof(sDirectSoundDeviceDescription));
|
||||
hr = DirectSoundPrivateCreate( &pKsPropertySet );
|
||||
if(SUCCEEDED(hr))
|
||||
memset(&sDirectSoundDeviceDescription, 0, sizeof(sDirectSoundDeviceDescription));
|
||||
hr = DirectSoundPrivateCreate(&pKsPropertySet);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ULONG ulBytesReturned = 0;
|
||||
sDirectSoundDeviceDescription.DeviceId = i_sGUID;
|
||||
|
||||
// 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.
|
||||
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
|
||||
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
|
||||
NULL,
|
||||
0,
|
||||
&sDirectSoundDeviceDescription,
|
||||
sizeof(sDirectSoundDeviceDescription),
|
||||
&ulBytesReturned
|
||||
);
|
||||
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0,
|
||||
&sDirectSoundDeviceDescription, sizeof(sDirectSoundDeviceDescription),
|
||||
&ulBytesReturned);
|
||||
|
||||
if (ulBytesReturned)
|
||||
{
|
||||
// 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.
|
||||
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA)new BYTE[ulBytesReturned];
|
||||
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA) new BYTE[ulBytesReturned];
|
||||
*psDirectSoundDeviceDescription = sDirectSoundDeviceDescription;
|
||||
|
||||
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice,
|
||||
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
|
||||
NULL,
|
||||
0,
|
||||
psDirectSoundDeviceDescription,
|
||||
ulBytesReturned,
|
||||
&ulBytesReturned
|
||||
);
|
||||
hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0,
|
||||
psDirectSoundDeviceDescription, ulBytesReturned, &ulBytesReturned);
|
||||
|
||||
dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId;
|
||||
dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId;
|
||||
/*Description = psDirectSoundDeviceDescription->Description;
|
||||
Module = psDirectSoundDeviceDescription->Module;
|
||||
Interface = psDirectSoundDeviceDescription->Interface;*/
|
||||
delete [] psDirectSoundDeviceDescription;
|
||||
delete[] psDirectSoundDeviceDescription;
|
||||
retval = TRUE;
|
||||
}
|
||||
|
||||
@@ -1045,24 +1034,18 @@ BOOL GetInfoFromDSoundGUID( GUID i_sGUID, int &dwWaveID)
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
struct EnumResult
|
||||
{
|
||||
int mDeviceId;
|
||||
int mDeviceId;
|
||||
GUID mGuid;
|
||||
};
|
||||
|
||||
BOOL CALLBACK DSEnumCallback(
|
||||
LPGUID lpGuid,
|
||||
LPCTSTR lpcstrDescription,
|
||||
LPCTSTR lpcstrModule,
|
||||
LPVOID lpContext
|
||||
)
|
||||
BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
|
||||
{
|
||||
if (lpGuid)
|
||||
{
|
||||
|
||||
int devId = -1;
|
||||
GetInfoFromDSoundGUID(*lpGuid, devId);
|
||||
EnumResult* er = (EnumResult*)lpContext;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <EndpointVolume.h>
|
||||
#include <MMDeviceAPI.h>
|
||||
#if defined(_MSC_VER)
|
||||
# include <Functiondiscoverykeys_devpkey.h>
|
||||
#include <Functiondiscoverykeys_devpkey.h>
|
||||
#endif
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -31,48 +31,48 @@
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
class VistaEnumerator: public Enumerator
|
||||
class VistaEnumerator : public Enumerator
|
||||
{
|
||||
public:
|
||||
VistaEnumerator();
|
||||
~VistaEnumerator();
|
||||
|
||||
void open(int direction);
|
||||
void close();
|
||||
void open(int direction);
|
||||
void close();
|
||||
|
||||
int count();
|
||||
int count();
|
||||
std::tstring nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
|
||||
protected:
|
||||
IMMDeviceCollection* mCollection;
|
||||
IMMDevice* mDefaultDevice;
|
||||
IMMDeviceEnumerator* mEnumerator;
|
||||
EDataFlow mDirection;
|
||||
std::vector<std::wstring> mNameList;
|
||||
IMMDeviceCollection* mCollection;
|
||||
IMMDevice* mDefaultDevice;
|
||||
IMMDeviceEnumerator* mEnumerator;
|
||||
EDataFlow mDirection;
|
||||
std::vector<std::wstring> mNameList;
|
||||
|
||||
void enumerate();
|
||||
IMMDevice* mapIndexToInterface(int index);
|
||||
void enumerate();
|
||||
IMMDevice* mapIndexToInterface(int index);
|
||||
};
|
||||
|
||||
class XpEnumerator: public Enumerator
|
||||
class XpEnumerator : public Enumerator
|
||||
{
|
||||
public:
|
||||
XpEnumerator();
|
||||
~XpEnumerator();
|
||||
|
||||
void open(int direction);
|
||||
void close();
|
||||
void open(int direction);
|
||||
void close();
|
||||
|
||||
int count();
|
||||
int count();
|
||||
std::tstring nameAt(int index);
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
int idAt(int index);
|
||||
int indexOfDefaultDevice();
|
||||
|
||||
protected:
|
||||
std::vector<std::wstring> mNameList;
|
||||
int mDirection;
|
||||
int mDirection;
|
||||
};
|
||||
|
||||
class DSoundHelper
|
||||
@@ -83,32 +83,32 @@ public:
|
||||
};
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
typedef struct IDirectSoundNotify8 *LPDIRECTSOUNDNOTIFY8;
|
||||
typedef struct IDirectSoundNotify8* LPDIRECTSOUNDNOTIFY8;
|
||||
#endif
|
||||
|
||||
class DSoundInputDevice: public InputDevice
|
||||
class DSoundInputDevice : public InputDevice
|
||||
{
|
||||
public:
|
||||
DSoundInputDevice(GUID deviceId);
|
||||
~DSoundInputDevice();
|
||||
|
||||
void enableDenoiser(bool enable);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool isSimulate() const;
|
||||
void setSimulate(bool s);
|
||||
|
||||
int readBuffer(void* buffer);
|
||||
|
||||
void enableDenoiser(bool enable);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool isSimulate() const;
|
||||
void setSimulate(bool s);
|
||||
|
||||
int readBuffer(void* buffer);
|
||||
Format getFormat();
|
||||
|
||||
protected:
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
LPDIRECTSOUNDCAPTURE8 mDevice;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER8 mBuffer;
|
||||
LPDIRECTSOUNDNOTIFY8 mNotifications;
|
||||
DSBPOSITIONNOTIFY mEventArray[AUDIO_MIC_BUFFER_COUNT];
|
||||
HANDLE mEventSignals[AUDIO_MIC_BUFFER_COUNT]; // Helper array to make WaitForMultipleObjects in loop
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
LPDIRECTSOUNDCAPTURE8 mDevice;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER8 mBuffer;
|
||||
LPDIRECTSOUNDNOTIFY8 mNotifications;
|
||||
DSBPOSITIONNOTIFY mEventArray[AUDIO_MIC_BUFFER_COUNT];
|
||||
HANDLE mEventSignals[AUDIO_MIC_BUFFER_COUNT]; // Helper array to make WaitForMultipleObjects in loop
|
||||
|
||||
int mBufferIndex;
|
||||
int mNextBuffer;
|
||||
@@ -116,7 +116,7 @@ protected:
|
||||
|
||||
HANDLE mThreadHandle;
|
||||
HANDLE mShutdownSignal;
|
||||
volatile bool mSimulate; /// Marks if simulate mode is active.
|
||||
volatile bool mSimulate; /// Marks if simulate mode is active.
|
||||
int mRefCount;
|
||||
ByteBuffer mQueue;
|
||||
unsigned mReadOffset;
|
||||
@@ -129,59 +129,59 @@ protected:
|
||||
WavFileWriter mDump;
|
||||
#endif
|
||||
|
||||
bool tryReadBuffer(void* buffer);
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
bool tryReadBuffer(void* buffer);
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
|
||||
static void threadProc(void* arg);
|
||||
};
|
||||
|
||||
class DSoundOutputDevice: public OutputDevice
|
||||
class DSoundOutputDevice : public OutputDevice
|
||||
{
|
||||
public:
|
||||
DSoundOutputDevice(GUID deviceId);
|
||||
~DSoundOutputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
unsigned playedTime() const;
|
||||
bool isSimulate() const;
|
||||
void setSimulate(bool s);
|
||||
bool closing();
|
||||
Format getFormat();
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
unsigned playedTime() const;
|
||||
bool isSimulate() const;
|
||||
void setSimulate(bool s);
|
||||
bool closing();
|
||||
Format getFormat();
|
||||
|
||||
protected:
|
||||
Mutex mGuard; /// Mutex to protect this instance
|
||||
int mDeviceID;
|
||||
LPDIRECTSOUND8 mDevice;
|
||||
LPDIRECTSOUNDBUFFER mPrimaryBuffer;
|
||||
LPDIRECTSOUNDBUFFER mBuffer;
|
||||
GUID mGUID;
|
||||
unsigned mWriteOffset;
|
||||
unsigned mPlayedSamples;
|
||||
unsigned mSentBytes;
|
||||
DWORD mPlayCursor; // Measured in bytes
|
||||
unsigned mBufferSize;
|
||||
unsigned mTotalPlayed; // Measured in bytes
|
||||
unsigned mTail; // Measured in bytes
|
||||
HANDLE mShutdownSignal;
|
||||
HANDLE mBufferSignal;
|
||||
HANDLE mThreadHandle;
|
||||
bool mSimulate;
|
||||
StubTimer mNullAudio;
|
||||
DWORD mWriteCursor;
|
||||
char mMediaFrame[AUDIO_SPK_BUFFER_SIZE];
|
||||
unsigned mRefCount;
|
||||
Mutex mGuard; /// Mutex to protect this instance
|
||||
int mDeviceID;
|
||||
LPDIRECTSOUND8 mDevice;
|
||||
LPDIRECTSOUNDBUFFER mPrimaryBuffer;
|
||||
LPDIRECTSOUNDBUFFER mBuffer;
|
||||
GUID mGUID;
|
||||
unsigned mWriteOffset;
|
||||
unsigned mPlayedSamples;
|
||||
unsigned mSentBytes;
|
||||
DWORD mPlayCursor; // Measured in bytes
|
||||
unsigned mBufferSize;
|
||||
unsigned mTotalPlayed; // Measured in bytes
|
||||
unsigned mTail; // Measured in bytes
|
||||
HANDLE mShutdownSignal;
|
||||
HANDLE mBufferSignal;
|
||||
HANDLE mThreadHandle;
|
||||
bool mSimulate;
|
||||
StubTimer mNullAudio;
|
||||
DWORD mWriteCursor;
|
||||
char mMediaFrame[AUDIO_SPK_BUFFER_SIZE];
|
||||
unsigned mRefCount;
|
||||
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
void restoreBuffer();
|
||||
bool process();
|
||||
bool getMediaFrame();
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
void restoreBuffer();
|
||||
bool process();
|
||||
bool getMediaFrame();
|
||||
|
||||
static void threadProc(void* arg);
|
||||
static void threadProc(void* arg);
|
||||
};
|
||||
}
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# include <WinSock2.h>
|
||||
#include <WinSock2.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
@@ -17,93 +17,92 @@ using namespace Audio;
|
||||
TimeSource::TimeSource(int quantTime, int nrOfQuants)
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
mCounter.QuadPart = 0;
|
||||
mCounter.QuadPart = 0;
|
||||
#endif
|
||||
#if defined(TARGET_OSX) || defined(TARGET_IOS)
|
||||
mach_timebase_info(&mTimebase);
|
||||
mRatio = ((double)mTimebase.numer / (double)mTimebase.denom) / 1000000;
|
||||
mach_timebase_info(&mTimebase);
|
||||
mRatio = ((double)mTimebase.numer / (double)mTimebase.denom) / 1000000;
|
||||
|
||||
#endif
|
||||
mQuantTime = quantTime;
|
||||
mDepthTime = quantTime * nrOfQuants;
|
||||
mTailTime = 0;
|
||||
mQuantTime = quantTime;
|
||||
mDepthTime = quantTime * nrOfQuants;
|
||||
mTailTime = 0;
|
||||
}
|
||||
|
||||
void TimeSource::start()
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
if (!QueryPerformanceFrequency(&mFreq))
|
||||
throw Exception(ERR_QPC, GetLastError());
|
||||
if (!QueryPerformanceCounter(&mCounter))
|
||||
throw Exception(ERR_QPC, GetLastError());
|
||||
if (!QueryPerformanceFrequency(&mFreq))
|
||||
throw Exception(ERR_QPC, GetLastError());
|
||||
if (!QueryPerformanceCounter(&mCounter))
|
||||
throw Exception(ERR_QPC, GetLastError());
|
||||
#endif
|
||||
}
|
||||
|
||||
void TimeSource::stop()
|
||||
{
|
||||
}
|
||||
void TimeSource::stop() {}
|
||||
|
||||
unsigned TimeSource::time()
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
LARGE_INTEGER c;
|
||||
if (!QueryPerformanceCounter(&c))
|
||||
throw Exception(ERR_QPC, GetLastError());
|
||||
|
||||
//find the f
|
||||
double f = (double)mFreq.QuadPart / 1000.0;
|
||||
|
||||
//find the difference
|
||||
unsigned __int64 diff = c.QuadPart - mCounter.QuadPart;
|
||||
if (!QueryPerformanceCounter(&c))
|
||||
throw Exception(ERR_QPC, GetLastError());
|
||||
|
||||
mCounter.QuadPart = c.QuadPart;
|
||||
|
||||
diff = (unsigned __int64)((double)diff / f + 0.5); //get ms
|
||||
diff += mTailTime;
|
||||
// find the f
|
||||
double f = (double)mFreq.QuadPart / 1000.0;
|
||||
|
||||
if (diff > mDepthTime)
|
||||
{
|
||||
mTailTime = 0;
|
||||
return mDepthTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTailTime = (unsigned )(diff % (unsigned __int64)mQuantTime);
|
||||
unsigned int t = (unsigned )(diff / (unsigned __int64)mQuantTime);
|
||||
return t * mQuantTime;
|
||||
}
|
||||
// find the difference
|
||||
unsigned __int64 diff = c.QuadPart - mCounter.QuadPart;
|
||||
|
||||
mCounter.QuadPart = c.QuadPart;
|
||||
|
||||
diff = (unsigned __int64)((double)diff / f + 0.5); // get ms
|
||||
diff += mTailTime;
|
||||
|
||||
if (diff > mDepthTime)
|
||||
{
|
||||
mTailTime = 0;
|
||||
return mDepthTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTailTime = (unsigned)(diff % (unsigned __int64)mQuantTime);
|
||||
unsigned int t = (unsigned)(diff / (unsigned __int64)mQuantTime);
|
||||
return t * mQuantTime;
|
||||
}
|
||||
#endif
|
||||
#if defined(TARGET_OSX) || defined(TARGET_IOS)
|
||||
uint64_t t = mach_absolute_time();
|
||||
uint64_t c = uint64_t((double)t * mRatio + 0.5);
|
||||
uint64_t t = mach_absolute_time();
|
||||
uint64_t c = uint64_t((double)t * mRatio + 0.5);
|
||||
|
||||
uint64_t diff = c - this->mTime + mTailTime;
|
||||
mTime = c;
|
||||
if (diff > mDepthTime)
|
||||
{
|
||||
mTailTime = 0;
|
||||
return mDepthTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTailTime = diff % mQuantTime;
|
||||
uint64_t t = diff / mQuantTime;
|
||||
return t * mQuantTime;
|
||||
}
|
||||
uint64_t diff = c - this->mTime + mTailTime;
|
||||
mTime = c;
|
||||
if (diff > mDepthTime)
|
||||
{
|
||||
mTailTime = 0;
|
||||
return mDepthTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTailTime = diff % mQuantTime;
|
||||
uint64_t t = diff / mQuantTime;
|
||||
return t * mQuantTime;
|
||||
}
|
||||
#endif
|
||||
#if defined(TARGET_LINUX)
|
||||
assert(0);
|
||||
assert(0);
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_ANDROID)
|
||||
assert(0);
|
||||
assert(0);
|
||||
#endif
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- StubTimer ---
|
||||
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
|
||||
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
@@ -113,38 +112,38 @@ StubTimer::StubTimer(int bufferTime, int bufferCount)
|
||||
StubTimer::~StubTimer()
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
::CloseHandle(mStubSignal);
|
||||
::CloseHandle(mStubSignal);
|
||||
#endif
|
||||
}
|
||||
|
||||
void StubTimer::start()
|
||||
{
|
||||
mTimeSource.start();
|
||||
mCurrentTime = mTimeSource.time();
|
||||
mActive = true;
|
||||
mTimeSource.start();
|
||||
mCurrentTime = mTimeSource.time();
|
||||
mActive = true;
|
||||
}
|
||||
|
||||
void StubTimer::stop()
|
||||
{
|
||||
mTimeSource.stop();
|
||||
mActive = false;
|
||||
mTimeSource.stop();
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
void StubTimer::waitForBuffer()
|
||||
{
|
||||
if (!mActive)
|
||||
start();
|
||||
if (!mActive)
|
||||
start();
|
||||
|
||||
unsigned t = mTimeSource.time();
|
||||
|
||||
while (!t)
|
||||
{
|
||||
unsigned t = mTimeSource.time();
|
||||
|
||||
while (!t)
|
||||
{
|
||||
#ifdef TARGET_WIN
|
||||
::WaitForSingleObject(mStubSignal, mBufferTime);
|
||||
::WaitForSingleObject(mStubSignal, mBufferTime);
|
||||
#endif
|
||||
#if defined(TARGET_OSX) || defined(TARGET_IOS)
|
||||
usleep(100);
|
||||
usleep(100);
|
||||
#endif
|
||||
t = mTimeSource.time();
|
||||
}
|
||||
t = mTimeSource.time();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
#include <EndpointVolume.h>
|
||||
#include <MMDeviceAPI.h>
|
||||
#if defined(_MSC_VER)
|
||||
# include <Functiondiscoverykeys_devpkey.h>
|
||||
#include <Functiondiscoverykeys_devpkey.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_IOS)
|
||||
# include <AudioUnit/AudioUnit.h>
|
||||
# include <AudioToolbox/AudioConverter.h>
|
||||
# include <AudioToolbox/AudioServices.h>
|
||||
# include <mach/mach_time.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioConverter.h>
|
||||
#include <AudioToolbox/AudioServices.h>
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
@@ -27,52 +27,52 @@
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
class TimeSource
|
||||
{
|
||||
protected:
|
||||
class TimeSource
|
||||
{
|
||||
protected:
|
||||
#ifdef TARGET_WIN
|
||||
LARGE_INTEGER mCounter; /// Current value from QPC.
|
||||
LARGE_INTEGER mFreq; /// Current frequency from QPC.
|
||||
LARGE_INTEGER mCounter; /// Current value from QPC.
|
||||
LARGE_INTEGER mFreq; /// Current frequency from QPC.
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_IOS)
|
||||
uint64_t mTime;
|
||||
uint64_t mTime;
|
||||
struct mach_timebase_info mTimebase;
|
||||
double mRatio;
|
||||
double mRatio;
|
||||
#endif
|
||||
|
||||
unsigned mQuantTime; /// Used time quants length in milliseconds.
|
||||
unsigned mDepthTime; /// Number of available time quants.
|
||||
unsigned mTailTime; /// Not-accounted milliseconds.
|
||||
unsigned mQuantTime; /// Used time quants length in milliseconds.
|
||||
unsigned mDepthTime; /// Number of available time quants.
|
||||
unsigned mTailTime; /// Not-accounted milliseconds.
|
||||
|
||||
public:
|
||||
public:
|
||||
TimeSource(int quantTime, int nrOfQuants);
|
||||
~TimeSource() = default;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void start();
|
||||
void stop();
|
||||
unsigned time();
|
||||
};
|
||||
};
|
||||
|
||||
class StubTimer
|
||||
{
|
||||
public:
|
||||
class StubTimer
|
||||
{
|
||||
public:
|
||||
StubTimer(int bufferTime, int bufferCount);
|
||||
~StubTimer();
|
||||
void start();
|
||||
void stop();
|
||||
void waitForBuffer();
|
||||
|
||||
protected:
|
||||
unsigned mBufferTime;
|
||||
unsigned mBufferCount;
|
||||
unsigned mCurrentTime;
|
||||
TimeSource mTimeSource;
|
||||
protected:
|
||||
unsigned mBufferTime;
|
||||
unsigned mBufferCount;
|
||||
unsigned mCurrentTime;
|
||||
TimeSource mTimeSource;
|
||||
#ifdef TARGET_WIN
|
||||
HANDLE mStubSignal;
|
||||
HANDLE mStubSignal;
|
||||
#endif
|
||||
bool mActive;
|
||||
};
|
||||
}
|
||||
bool mActive;
|
||||
};
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
#include "../helper/HL_OsVersion.h"
|
||||
|
||||
#if !defined(USE_NULL_AUDIO)
|
||||
# ifdef TARGET_WIN
|
||||
# include "Audio_Wmme.h"
|
||||
# include "Audio_DirectSound.h"
|
||||
# endif
|
||||
# ifdef TARGET_OSX
|
||||
# include "Audio_CoreAudio.h"
|
||||
# endif
|
||||
# ifdef TARGET_ANDROID
|
||||
# include "Audio_Android.h"
|
||||
# endif
|
||||
#ifdef TARGET_WIN
|
||||
#include "Audio_Wmme.h"
|
||||
#include "Audio_DirectSound.h"
|
||||
#endif
|
||||
#ifdef TARGET_OSX
|
||||
#include "Audio_CoreAudio.h"
|
||||
#endif
|
||||
#ifdef TARGET_ANDROID
|
||||
#include "Audio_Android.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "Audio_Helper.h"
|
||||
@@ -24,14 +24,9 @@
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
Device::Device()
|
||||
:mConnection(nullptr)
|
||||
{
|
||||
}
|
||||
Device::Device() : mConnection(nullptr) {}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
}
|
||||
Device::~Device() {}
|
||||
|
||||
|
||||
void Device::setConnection(DataConnection* connection)
|
||||
@@ -44,13 +39,9 @@ DataConnection* Device::connection()
|
||||
return mConnection;
|
||||
}
|
||||
|
||||
InputDevice::InputDevice()
|
||||
{
|
||||
}
|
||||
InputDevice::InputDevice() {}
|
||||
|
||||
InputDevice::~InputDevice()
|
||||
{
|
||||
}
|
||||
InputDevice::~InputDevice() {}
|
||||
|
||||
InputDevice* InputDevice::make(int devId)
|
||||
{
|
||||
@@ -71,13 +62,9 @@ InputDevice* InputDevice::make(int devId)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OutputDevice::OutputDevice()
|
||||
{
|
||||
}
|
||||
OutputDevice::OutputDevice() {}
|
||||
|
||||
OutputDevice::~OutputDevice()
|
||||
{
|
||||
}
|
||||
OutputDevice::~OutputDevice() {}
|
||||
|
||||
OutputDevice* OutputDevice::make(int devId)
|
||||
{
|
||||
@@ -85,7 +72,7 @@ OutputDevice* OutputDevice::make(int devId)
|
||||
return new NullOutputDevice();
|
||||
#else
|
||||
#if defined(TARGET_WIN)
|
||||
//return new WmmeOutputDevice(index);
|
||||
// return new WmmeOutputDevice(index);
|
||||
return new DSoundOutputDevice(DSoundHelper::deviceId2Guid(devId, false));
|
||||
#endif
|
||||
#ifdef TARGET_OSX
|
||||
@@ -100,13 +87,9 @@ OutputDevice* OutputDevice::make(int devId)
|
||||
|
||||
|
||||
// --- Enumerator ---
|
||||
Enumerator::Enumerator()
|
||||
{
|
||||
}
|
||||
Enumerator::Enumerator() {}
|
||||
|
||||
Enumerator::~Enumerator()
|
||||
{
|
||||
}
|
||||
Enumerator::~Enumerator() {}
|
||||
|
||||
int Enumerator::nameToIndex(const std::tstring& name)
|
||||
{
|
||||
@@ -119,7 +102,6 @@ int Enumerator::nameToIndex(const std::tstring& name)
|
||||
|
||||
Enumerator* Enumerator::make(bool useNull)
|
||||
{
|
||||
|
||||
if (useNull)
|
||||
return new NullEnumerator();
|
||||
#ifndef USE_NULL_AUDIO
|
||||
@@ -147,7 +129,7 @@ OsEngine* OsEngine::instance()
|
||||
|
||||
#ifdef TARGET_ANDROID
|
||||
return nullptr; // As we use Oboe library for now
|
||||
//return &OpenSLEngine::instance();
|
||||
// return &OpenSLEngine::instance();
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -27,34 +27,18 @@ struct Format
|
||||
int mRate;
|
||||
int mChannels;
|
||||
|
||||
Format()
|
||||
:mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS)
|
||||
{}
|
||||
Format() : mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS) {}
|
||||
|
||||
Format(int rate, int channels)
|
||||
:mRate(rate), mChannels(channels)
|
||||
{}
|
||||
Format(int rate, int channels) : mRate(rate), mChannels(channels) {}
|
||||
|
||||
size_t samplesFromSize(size_t length) const
|
||||
{
|
||||
return length / 2 / mChannels;
|
||||
}
|
||||
size_t samplesFromSize(size_t length) const { return length / 2 / mChannels; }
|
||||
|
||||
// Returns milliseconds
|
||||
float timeFromSize(size_t length) const
|
||||
{
|
||||
return float(samplesFromSize(length) / (mRate / 1000.0));
|
||||
}
|
||||
float timeFromSize(size_t length) const { return float(samplesFromSize(length) / (mRate / 1000.0)); }
|
||||
|
||||
float sizeFromTime(size_t milliseconds) const
|
||||
{
|
||||
return float((milliseconds * mRate) / 500.0 * mChannels);
|
||||
}
|
||||
float sizeFromTime(size_t milliseconds) const { return float((milliseconds * mRate) / 500.0 * mChannels); }
|
||||
|
||||
size_t sizeFromTime(std::chrono::milliseconds ms) const
|
||||
{
|
||||
return sizeFromTime(ms.count());
|
||||
}
|
||||
size_t sizeFromTime(std::chrono::milliseconds ms) const { return sizeFromTime(ms.count()); }
|
||||
|
||||
std::string toString()
|
||||
{
|
||||
@@ -63,26 +47,13 @@ struct Format
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
bool operator == (const Format& rhs) const
|
||||
{
|
||||
return mRate == rhs.mRate && mChannels == rhs.mChannels;
|
||||
}
|
||||
bool operator==(const Format& rhs) const { return mRate == rhs.mRate && mChannels == rhs.mChannels; }
|
||||
|
||||
bool operator != (const Format& rhs) const
|
||||
{
|
||||
return mRate != rhs.mRate || mChannels != rhs.mChannels;
|
||||
}
|
||||
bool operator!=(const Format& rhs) const { return mRate != rhs.mRate || mChannels != rhs.mChannels; }
|
||||
|
||||
int rate() const
|
||||
{
|
||||
return mRate;
|
||||
}
|
||||
|
||||
int channels() const
|
||||
{
|
||||
return mChannels;
|
||||
}
|
||||
int rate() const { return mRate; }
|
||||
|
||||
int channels() const { return mChannels; }
|
||||
};
|
||||
|
||||
class DataConnection
|
||||
@@ -99,28 +70,29 @@ public:
|
||||
Device();
|
||||
virtual ~Device();
|
||||
|
||||
void setConnection(DataConnection* connection);
|
||||
void setConnection(DataConnection* connection);
|
||||
DataConnection* connection();
|
||||
|
||||
virtual bool open() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual Format getFormat() = 0;
|
||||
virtual bool open() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual Format getFormat() = 0;
|
||||
|
||||
protected:
|
||||
DataConnection* mConnection;
|
||||
};
|
||||
|
||||
|
||||
class InputDevice: public Device
|
||||
class InputDevice : public Device
|
||||
{
|
||||
public:
|
||||
InputDevice();
|
||||
virtual ~InputDevice();
|
||||
|
||||
|
||||
static InputDevice* make(int devId);
|
||||
};
|
||||
typedef std::shared_ptr<InputDevice> PInputDevice;
|
||||
|
||||
class OutputDevice: public Device
|
||||
class OutputDevice : public Device
|
||||
{
|
||||
public:
|
||||
OutputDevice();
|
||||
@@ -135,27 +107,27 @@ class Enumerator
|
||||
public:
|
||||
Enumerator();
|
||||
virtual ~Enumerator();
|
||||
int nameToIndex(const std::tstring& name);
|
||||
int nameToIndex(const std::tstring& name);
|
||||
|
||||
virtual void open(int direction) = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual int count() = 0;
|
||||
virtual void open(int direction) = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual int count() = 0;
|
||||
virtual std::tstring nameAt(int index) = 0;
|
||||
virtual int idAt(int index) = 0;
|
||||
virtual int indexOfDefaultDevice() = 0;
|
||||
|
||||
static Enumerator* make(bool useNull = false);
|
||||
virtual int idAt(int index) = 0;
|
||||
virtual int indexOfDefaultDevice() = 0;
|
||||
|
||||
static Enumerator* make(bool useNull = false);
|
||||
};
|
||||
|
||||
class OsEngine
|
||||
{
|
||||
public:
|
||||
virtual void open() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual void open() = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
static OsEngine* instance();
|
||||
};
|
||||
};
|
||||
}; // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
+225
-232
@@ -17,341 +17,334 @@ using namespace Audio;
|
||||
|
||||
Mixer::Stream::Stream()
|
||||
{
|
||||
mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE);
|
||||
mResampler16.start(AUDIO_CHANNELS, 16000, AUDIO_SAMPLERATE);
|
||||
mResampler32.start(AUDIO_CHANNELS, 32000, AUDIO_SAMPLERATE);
|
||||
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
|
||||
mActive = false;
|
||||
mContext = nullptr;
|
||||
mSSRC = 0;
|
||||
mFadeOutCounter = 0;
|
||||
mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT);
|
||||
mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE);
|
||||
mResampler16.start(AUDIO_CHANNELS, 16000, AUDIO_SAMPLERATE);
|
||||
mResampler32.start(AUDIO_CHANNELS, 32000, AUDIO_SAMPLERATE);
|
||||
mResampler48.start(AUDIO_CHANNELS, 48000, AUDIO_SAMPLERATE);
|
||||
mActive = false;
|
||||
mContext = nullptr;
|
||||
mSSRC = 0;
|
||||
mFadeOutCounter = 0;
|
||||
mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT);
|
||||
}
|
||||
|
||||
Mixer::Stream::~Stream()
|
||||
{
|
||||
}
|
||||
Mixer::Stream::~Stream() {}
|
||||
|
||||
void Mixer::Stream::setSsrc(unsigned ssrc)
|
||||
{
|
||||
mSSRC = ssrc;
|
||||
mSSRC = ssrc;
|
||||
}
|
||||
|
||||
unsigned Mixer::Stream::ssrc()
|
||||
{
|
||||
return mSSRC;
|
||||
return mSSRC;
|
||||
}
|
||||
|
||||
void Mixer::Stream::setContext(void* context)
|
||||
{
|
||||
mContext = context;
|
||||
mContext = context;
|
||||
}
|
||||
void* Mixer::Stream::context()
|
||||
{
|
||||
return mContext;
|
||||
return mContext;
|
||||
}
|
||||
|
||||
DataWindow& Mixer::Stream::data()
|
||||
{
|
||||
return mData;
|
||||
return mData;
|
||||
}
|
||||
|
||||
bool Mixer::Stream::active()
|
||||
{
|
||||
return mActive;
|
||||
return mActive;
|
||||
}
|
||||
|
||||
void Mixer::Stream::setActive(bool active)
|
||||
{
|
||||
mActive = active;
|
||||
mActive = active;
|
||||
}
|
||||
|
||||
void Mixer::Stream::addPcm(int rate, const void* input, int length)
|
||||
{
|
||||
// Resample to internal sample rate
|
||||
size_t outputSize = size_t(0.5 + length * ((float)AUDIO_SAMPLERATE / rate));
|
||||
if (mTempBuffer.size() < outputSize)
|
||||
mTempBuffer.resize(outputSize);
|
||||
|
||||
Resampler* resampler = (rate == 8000) ? &mResampler8 : ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
|
||||
size_t inputProcessed = 0;
|
||||
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize);
|
||||
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length
|
||||
// Resample to internal sample rate
|
||||
size_t outputSize = size_t(0.5 + length * ((float)AUDIO_SAMPLERATE / rate));
|
||||
if (mTempBuffer.size() < outputSize)
|
||||
mTempBuffer.resize(outputSize);
|
||||
|
||||
// Queue data
|
||||
mData.add(mTempBuffer.data(), outputSize);
|
||||
Resampler* resampler = (rate == 8000)
|
||||
? &mResampler8
|
||||
: ((rate == 16000) ? &mResampler16 : ((rate == 32000) ? &mResampler32 : &mResampler48));
|
||||
size_t inputProcessed = 0;
|
||||
resampler->processBuffer(input, length, inputProcessed, mTempBuffer.mutableData(), outputSize);
|
||||
// inputProcessed result value is ignored here - rate will be 8/16/32/48k, inputProcessed is equal to length
|
||||
|
||||
// Queue data
|
||||
mData.add(mTempBuffer.data(), outputSize);
|
||||
}
|
||||
|
||||
Mixer::Mixer()
|
||||
{
|
||||
mActiveCounter = 0;
|
||||
mOutput.setCapacity(32768);
|
||||
mActiveCounter = 0;
|
||||
mOutput.setCapacity(32768);
|
||||
}
|
||||
|
||||
Mixer::~Mixer()
|
||||
{
|
||||
}
|
||||
Mixer::~Mixer() {}
|
||||
|
||||
void Mixer::unregisterChannel(void* channel)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == channel)
|
||||
Lock l(mMutex);
|
||||
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
{
|
||||
c.setActive(false); // stream is not active anymore
|
||||
c.data().clear(); // clear data
|
||||
mActiveCounter--;
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == channel)
|
||||
{
|
||||
c.setActive(false); // stream is not active anymore
|
||||
c.data().clear(); // clear data
|
||||
mActiveCounter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::clear(void* context, unsigned ssrc)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == context && c.ssrc() == ssrc)
|
||||
Lock l(mMutex);
|
||||
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
{
|
||||
c.setActive(false);
|
||||
c.data().clear();
|
||||
mActiveCounter--;
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == context && c.ssrc() == ssrc)
|
||||
{
|
||||
c.setActive(false);
|
||||
c.data().clear();
|
||||
mActiveCounter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mixer::Stream* Mixer::allocateChannel(void* context, unsigned ssrc)
|
||||
{
|
||||
// Allocate new channel
|
||||
Lock l(mMutex);
|
||||
Stream* channel;
|
||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT;i++)
|
||||
{
|
||||
channel = &mChannelList[i];
|
||||
if (!channel->active())
|
||||
// Allocate new channel
|
||||
Lock l(mMutex);
|
||||
Stream* channel;
|
||||
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
{
|
||||
channel->setSsrc(ssrc);
|
||||
channel->setContext(context);
|
||||
channel->data().clear();
|
||||
mActiveCounter++;
|
||||
channel->setActive(true);
|
||||
return channel;
|
||||
channel = &mChannelList[i];
|
||||
if (!channel->active())
|
||||
{
|
||||
channel->setSsrc(ssrc);
|
||||
channel->setContext(context);
|
||||
channel->data().clear();
|
||||
mActiveCounter++;
|
||||
channel->setActive(true);
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Mixer::addPcm(void* context, unsigned ssrc,
|
||||
const void* inputData, int inputLength,
|
||||
int inputRate, bool fadeOut)
|
||||
void Mixer::addPcm(void* context, unsigned ssrc, const void* inputData, int inputLength, int inputRate, bool fadeOut)
|
||||
{
|
||||
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
|
||||
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
|
||||
|
||||
Lock l(mMutex);
|
||||
int i;
|
||||
Lock l(mMutex);
|
||||
int i;
|
||||
|
||||
// Locate a channel
|
||||
Stream* channel = NULL;
|
||||
|
||||
for (i=0; i<AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == context && c.ssrc() == ssrc)
|
||||
channel = &c;
|
||||
}
|
||||
if (!channel)
|
||||
{
|
||||
channel = allocateChannel(context, ssrc);
|
||||
// Locate a channel
|
||||
Stream* channel = NULL;
|
||||
|
||||
for (i = 0; i < AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == context && c.ssrc() == ssrc)
|
||||
channel = &c;
|
||||
}
|
||||
if (!channel)
|
||||
throw Exception(ERR_MIXER_OVERFLOW);
|
||||
}
|
||||
|
||||
channel->addPcm(inputRate, inputData, inputLength);
|
||||
{
|
||||
channel = allocateChannel(context, ssrc);
|
||||
if (!channel)
|
||||
throw Exception(ERR_MIXER_OVERFLOW);
|
||||
}
|
||||
|
||||
channel->addPcm(inputRate, inputData, inputLength);
|
||||
}
|
||||
|
||||
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);
|
||||
int i;
|
||||
Lock l(mMutex);
|
||||
int i;
|
||||
|
||||
// Locate a channel
|
||||
Stream* channel = NULL;
|
||||
|
||||
for (i=0; i<AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == context && c.ssrc() == ssrc)
|
||||
channel = &c;
|
||||
}
|
||||
if (!channel)
|
||||
{
|
||||
channel = allocateChannel(context, ssrc);
|
||||
// Locate a channel
|
||||
Stream* channel = NULL;
|
||||
|
||||
for (i = 0; i < AUDIO_MIX_CHANNEL_COUNT && !channel; i++)
|
||||
{
|
||||
Stream& c = mChannelList[i];
|
||||
if (c.active() && c.context() == context && c.ssrc() == ssrc)
|
||||
channel = &c;
|
||||
}
|
||||
if (!channel)
|
||||
throw Exception(ERR_MIXER_OVERFLOW);
|
||||
}
|
||||
|
||||
channel->addPcm(rate, w.data(), w.filled());
|
||||
//ICELogSpecial(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes");
|
||||
{
|
||||
channel = allocateChannel(context, ssrc);
|
||||
if (!channel)
|
||||
throw Exception(ERR_MIXER_OVERFLOW);
|
||||
}
|
||||
|
||||
channel->addPcm(rate, w.data(), w.filled());
|
||||
// ICELogSpecial(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes");
|
||||
}
|
||||
|
||||
void Mixer::mix()
|
||||
{
|
||||
Lock l(mMutex);
|
||||
Lock l(mMutex);
|
||||
|
||||
// Current sample
|
||||
int sample = 0;
|
||||
// Current sample
|
||||
int sample = 0;
|
||||
|
||||
// Counter of processed active channels
|
||||
int processed = 0;
|
||||
// Counter of processed active channels
|
||||
int processed = 0;
|
||||
|
||||
// Samples & sources counters
|
||||
unsigned sampleCounter = 0, sourceCounter;
|
||||
// Samples & sources counters
|
||||
unsigned sampleCounter = 0, sourceCounter;
|
||||
|
||||
short outputBuffer[512];
|
||||
unsigned outputCounter = 0;
|
||||
short outputBuffer[512];
|
||||
unsigned outputCounter = 0;
|
||||
|
||||
// Build active channel map
|
||||
Stream* channelList[AUDIO_MIX_CHANNEL_COUNT];
|
||||
int activeCounter = 0;
|
||||
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
if (mChannelList[i].active())
|
||||
channelList[activeCounter++] = &mChannelList[i];
|
||||
|
||||
// No active channels - nothing to mix - exit
|
||||
if (!activeCounter)
|
||||
{
|
||||
// ICELogDebug(<< "No active channel");
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimized versions for 1& 2 active channels
|
||||
if (activeCounter == 1)
|
||||
{
|
||||
// Copy much samples as we have
|
||||
Stream& audio = *channelList[0];
|
||||
// Build active channel map
|
||||
Stream* channelList[AUDIO_MIX_CHANNEL_COUNT];
|
||||
int activeCounter = 0;
|
||||
for (int i = 0; i < AUDIO_MIX_CHANNEL_COUNT; i++)
|
||||
if (mChannelList[i].active())
|
||||
channelList[activeCounter++] = &mChannelList[i];
|
||||
|
||||
// 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
|
||||
// No active channels - nothing to mix - exit
|
||||
if (!activeCounter)
|
||||
{
|
||||
sample = 0;
|
||||
sourceCounter = 0;
|
||||
processed = 0;
|
||||
for (int i=0; i<activeCounter; i++)
|
||||
{
|
||||
Stream& audio = *channelList[i];
|
||||
processed++;
|
||||
// ICELogDebug(<< "No active channel");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (abs(currentSample) > abs(sample))
|
||||
sample = currentSample;
|
||||
sourceCounter++;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
audio1.data().erase(available * 2);
|
||||
audio2.data().erase(available * 2);
|
||||
}
|
||||
while (sourceCounter);
|
||||
|
||||
processed = 0;
|
||||
for (int i=0; i<activeCounter; i++)
|
||||
else
|
||||
{
|
||||
Stream& audio = *channelList[i];
|
||||
audio.data().erase(sampleCounter*2);
|
||||
do
|
||||
{
|
||||
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)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
Lock l(mMutex);
|
||||
|
||||
if (mOutput.filled() < outputLength)
|
||||
mix();
|
||||
if (mOutput.filled() < outputLength)
|
||||
mix();
|
||||
|
||||
//ICELogSpecial(<<"Mixer has " << mOutput.filled() << " available bytes");
|
||||
memset(outputData, 0, outputLength);
|
||||
return mOutput.read(outputData, outputLength);
|
||||
// ICELogSpecial(<<"Mixer has " << mOutput.filled() << " available bytes");
|
||||
memset(outputData, 0, outputLength);
|
||||
return mOutput.read(outputData, outputLength);
|
||||
}
|
||||
|
||||
int Mixer::mixAndGetPcm(Audio::DataWindow& output)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
Lock l(mMutex);
|
||||
|
||||
// Mix
|
||||
mix();
|
||||
// Mix
|
||||
mix();
|
||||
|
||||
size_t avail = mOutput.filled();
|
||||
if (!avail)
|
||||
{
|
||||
output.setFilled(0);
|
||||
return 0;
|
||||
}
|
||||
size_t avail = mOutput.filled();
|
||||
if (!avail)
|
||||
{
|
||||
output.setFilled(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make sure output has enough space (setCapacity only ever grows the window)
|
||||
if (output.capacity() < avail)
|
||||
output.setCapacity(avail);
|
||||
// Make sure output has enough space (setCapacity only ever grows the window)
|
||||
if (output.capacity() < avail)
|
||||
output.setCapacity(avail);
|
||||
|
||||
// Read mixed data to output and publish the real byte count
|
||||
size_t got = mOutput.read(output.mutableData(), avail);
|
||||
output.setFilled(got);
|
||||
return static_cast<int>(got);
|
||||
// Read mixed data to output and publish the real byte count
|
||||
size_t got = mOutput.read(output.mutableData(), avail);
|
||||
output.setFilled(got);
|
||||
return static_cast<int>(got);
|
||||
}
|
||||
|
||||
int Mixer::available()
|
||||
{
|
||||
return mOutput.filled();
|
||||
return mOutput.filled();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,59 +14,56 @@
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
|
||||
namespace Audio
|
||||
namespace Audio
|
||||
{
|
||||
class Mixer
|
||||
{
|
||||
protected:
|
||||
class Mixer
|
||||
{
|
||||
protected:
|
||||
class Stream
|
||||
{
|
||||
protected:
|
||||
DataWindow mData;
|
||||
Resampler mResampler8,
|
||||
mResampler16,
|
||||
mResampler32,
|
||||
mResampler48;
|
||||
bool mActive;
|
||||
void* mContext;
|
||||
unsigned mSSRC;
|
||||
unsigned mFadeOutCounter;
|
||||
ByteBuffer mTempBuffer;
|
||||
DataWindow mData;
|
||||
Resampler mResampler8, mResampler16, mResampler32, mResampler48;
|
||||
bool mActive;
|
||||
void* mContext;
|
||||
unsigned mSSRC;
|
||||
unsigned mFadeOutCounter;
|
||||
ByteBuffer mTempBuffer;
|
||||
|
||||
public:
|
||||
Stream();
|
||||
~Stream();
|
||||
Stream();
|
||||
~Stream();
|
||||
|
||||
void setSsrc(unsigned ssrc);
|
||||
unsigned ssrc();
|
||||
void setContext(void* context);
|
||||
void* context();
|
||||
DataWindow& data();
|
||||
bool active();
|
||||
void setActive(bool active);
|
||||
void addPcm(int rate, const void* input, int length);
|
||||
void setSsrc(unsigned ssrc);
|
||||
unsigned ssrc();
|
||||
void setContext(void* context);
|
||||
void* context();
|
||||
DataWindow& data();
|
||||
bool active();
|
||||
void setActive(bool active);
|
||||
void addPcm(int rate, const void* input, int length);
|
||||
};
|
||||
|
||||
Stream mChannelList[AUDIO_MIX_CHANNEL_COUNT];
|
||||
Mutex mMutex;
|
||||
DataWindow mOutput;
|
||||
std::atomic_int mActiveCounter;
|
||||
|
||||
void mix();
|
||||
Stream* allocateChannel(void* context, unsigned ssrc);
|
||||
Stream mChannelList[AUDIO_MIX_CHANNEL_COUNT];
|
||||
Mutex mMutex;
|
||||
DataWindow mOutput;
|
||||
std::atomic_int mActiveCounter;
|
||||
|
||||
public:
|
||||
void mix();
|
||||
Stream* allocateChannel(void* context, unsigned ssrc);
|
||||
|
||||
public:
|
||||
Mixer();
|
||||
~Mixer();
|
||||
|
||||
|
||||
void unregisterChannel(void* context);
|
||||
void clear(void* context, unsigned ssrc);
|
||||
void addPcm(void* context, unsigned ssrc, const void* inputData, int inputLength, int inputRate, bool fadeOut);
|
||||
void addPcm(void* context, unsigned ssrc, Audio::DataWindow& w, int rate, bool fadeOut);
|
||||
int getPcm(void* outputData, int outputLength);
|
||||
int mixAndGetPcm(Audio::DataWindow& output);
|
||||
int available();
|
||||
};
|
||||
} //end of namespace
|
||||
int available();
|
||||
};
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
using namespace Audio;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
NullTimer::NullTimer(std::chrono::milliseconds interval, Delegate *delegate, const char* name)
|
||||
:mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
|
||||
NullTimer::NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name)
|
||||
: mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
|
||||
{
|
||||
start();
|
||||
}
|
||||
@@ -49,15 +49,13 @@ void NullTimer::run()
|
||||
// Sleep for mInterval - mTail milliseconds
|
||||
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()
|
||||
:mBuffer(nullptr)
|
||||
{
|
||||
}
|
||||
NullInputDevice::NullInputDevice() : mBuffer(nullptr) {}
|
||||
|
||||
NullInputDevice::~NullInputDevice()
|
||||
{
|
||||
@@ -69,7 +67,8 @@ bool NullInputDevice::open()
|
||||
ICELogInfo(<< "Starting NullInputDevice for " << AUDIO_MIC_BUFFER_LENGTH << "ms buffers");
|
||||
mBuffer = malloc(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.
|
||||
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_MIC_BUFFER_LENGTH), this, "null_mic");
|
||||
@@ -85,7 +84,7 @@ void NullInputDevice::internalClose()
|
||||
free(mBuffer);
|
||||
mBuffer = nullptr;
|
||||
}
|
||||
ICELogInfo( << "Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||
ICELogInfo(<< "Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||
}
|
||||
|
||||
void NullInputDevice::close()
|
||||
@@ -95,7 +94,7 @@ void NullInputDevice::close()
|
||||
|
||||
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
|
||||
}
|
||||
@@ -109,10 +108,7 @@ void NullInputDevice::onTimerSignal(NullTimer& timer)
|
||||
}
|
||||
|
||||
// --------------------- NullOutputDevice --------------------------
|
||||
NullOutputDevice::NullOutputDevice()
|
||||
:mBuffer(nullptr)
|
||||
{
|
||||
}
|
||||
NullOutputDevice::NullOutputDevice() : mBuffer(nullptr) {}
|
||||
|
||||
NullOutputDevice::~NullOutputDevice()
|
||||
{
|
||||
@@ -122,7 +118,8 @@ NullOutputDevice::~NullOutputDevice()
|
||||
|
||||
bool NullOutputDevice::open()
|
||||
{
|
||||
mTimeCounter = 0; mDataCounter = 0;
|
||||
mTimeCounter = 0;
|
||||
mDataCounter = 0;
|
||||
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
|
||||
// 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");
|
||||
@@ -132,7 +129,8 @@ bool NullOutputDevice::open()
|
||||
void NullOutputDevice::internalClose()
|
||||
{
|
||||
mTimer.reset();
|
||||
free(mBuffer); mBuffer = nullptr;
|
||||
free(mBuffer);
|
||||
mBuffer = nullptr;
|
||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||
}
|
||||
|
||||
@@ -143,11 +141,11 @@ void NullOutputDevice::close()
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void NullOutputDevice::onTimerSignal(NullTimer &timer)
|
||||
void NullOutputDevice::onTimerSignal(NullTimer& timer)
|
||||
{
|
||||
mTimeCounter += AUDIO_SPK_BUFFER_LENGTH;
|
||||
mDataCounter += AUDIO_SPK_BUFFER_SIZE;
|
||||
@@ -156,19 +154,15 @@ void NullOutputDevice::onTimerSignal(NullTimer &timer)
|
||||
}
|
||||
|
||||
// ---------------------- 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;
|
||||
}
|
||||
@@ -191,4 +185,3 @@ int NullEnumerator::indexOfDefaultDevice()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,75 +17,77 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
std::thread mWorkerThread;
|
||||
std::atomic_bool mShutdown = {false};
|
||||
Delegate* mDelegate = nullptr;
|
||||
std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds
|
||||
std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink
|
||||
std::string mThreadName;
|
||||
std::thread mWorkerThread;
|
||||
std::atomic_bool mShutdown = {false};
|
||||
Delegate* mDelegate = nullptr;
|
||||
std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds
|
||||
std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink
|
||||
std::string mThreadName;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void run();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void run();
|
||||
public:
|
||||
/* Interval is in milliseconds. */
|
||||
NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name = nullptr);
|
||||
~NullTimer();
|
||||
};
|
||||
|
||||
class NullInputDevice: public InputDevice, public NullTimer::Delegate
|
||||
class NullInputDevice : public InputDevice, public NullTimer::Delegate
|
||||
{
|
||||
protected:
|
||||
void* mBuffer = nullptr;
|
||||
void* mBuffer = nullptr;
|
||||
std::shared_ptr<NullTimer> mTimer;
|
||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||
void internalClose();
|
||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||
void internalClose();
|
||||
|
||||
public:
|
||||
NullInputDevice();
|
||||
virtual ~NullInputDevice();
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
bool open() override;
|
||||
void close() 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:
|
||||
std::shared_ptr<NullTimer> mTimer;
|
||||
void* mBuffer = nullptr;
|
||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||
void* mBuffer = nullptr;
|
||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||
|
||||
void internalClose();
|
||||
|
||||
void internalClose();
|
||||
public:
|
||||
NullOutputDevice();
|
||||
virtual ~NullOutputDevice();
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
bool open() override;
|
||||
void close() override;
|
||||
Format getFormat() override;
|
||||
|
||||
void onTimerSignal(NullTimer& timer) override;
|
||||
void onTimerSignal(NullTimer& timer) override;
|
||||
};
|
||||
|
||||
class NullEnumerator: public Enumerator
|
||||
class NullEnumerator : public Enumerator
|
||||
{
|
||||
public:
|
||||
NullEnumerator();
|
||||
~NullEnumerator();
|
||||
|
||||
void open(int direction) override;
|
||||
void close() override;
|
||||
void open(int direction) override;
|
||||
void close() override;
|
||||
|
||||
int count() override;
|
||||
int count() override;
|
||||
std::tstring nameAt(int index) override;
|
||||
int idAt(int index) override;
|
||||
int indexOfDefaultDevice() override;
|
||||
int idAt(int index) override;
|
||||
int indexOfDefaultDevice() override;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,161 +11,156 @@
|
||||
|
||||
using namespace Audio;
|
||||
// -------------- Player -----------
|
||||
Player::Player()
|
||||
:mDelegate(nullptr), mPlayedTime(0)
|
||||
{
|
||||
}
|
||||
Player::Player() : mDelegate(nullptr), mPlayedTime(0) {}
|
||||
|
||||
Player::~Player()
|
||||
{
|
||||
}
|
||||
Player::~Player() {}
|
||||
|
||||
void Player::setDelegate(EndOfAudioDelegate* d)
|
||||
{
|
||||
mDelegate = d;
|
||||
mDelegate = d;
|
||||
}
|
||||
|
||||
Player::EndOfAudioDelegate* Player::getDelegate() const
|
||||
{
|
||||
return mDelegate;
|
||||
return mDelegate;
|
||||
}
|
||||
|
||||
void Player::setOutput(POutputDevice output)
|
||||
{
|
||||
mOutput = output;
|
||||
if (mOutput)
|
||||
mOutput->setConnection(this);
|
||||
mOutput = output;
|
||||
if (mOutput)
|
||||
mOutput->setConnection(this);
|
||||
}
|
||||
|
||||
POutputDevice Player::getOutput() const
|
||||
{
|
||||
return mOutput;
|
||||
return mOutput;
|
||||
}
|
||||
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)
|
||||
|
||||
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
|
||||
if (mOutput)
|
||||
memset(buffer, 0, length);
|
||||
// Fill buffer by zero if player owns dedicated device
|
||||
if (mOutput)
|
||||
memset(buffer, 0, length);
|
||||
|
||||
// See if there is item in playlist
|
||||
int produced = 0;
|
||||
while (mPlaylist.size() && produced < length)
|
||||
{
|
||||
PlaylistItem& item = mPlaylist.front();
|
||||
// Check for timelength
|
||||
if (item.mTimelength > 0 && item.mTimelength < mPlayedTime)
|
||||
// See if there is item in playlist
|
||||
int produced = 0;
|
||||
while (mPlaylist.size() && produced < length)
|
||||
{
|
||||
onFilePlayed();
|
||||
continue;
|
||||
}
|
||||
PlaylistItem& item = mPlaylist.front();
|
||||
// Check for timelength
|
||||
if (item.mTimelength > 0 && item.mTimelength < mPlayedTime)
|
||||
{
|
||||
onFilePlayed();
|
||||
continue;
|
||||
}
|
||||
|
||||
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));
|
||||
int wasread = item.mFile->read((char*)buffer + produced, length - produced);
|
||||
mPlayedTime += float(wasread) / BYTES_PER_MILLISECOND;
|
||||
produced += wasread;
|
||||
}
|
||||
else
|
||||
onFilePlayed();
|
||||
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;
|
||||
produced += wasread;
|
||||
}
|
||||
else
|
||||
onFilePlayed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::onFilePlayed()
|
||||
{
|
||||
// Save usage id to release later from main loop
|
||||
mFinishedUsages.push_back(mPlaylist.front().mUsageId);
|
||||
|
||||
// Send event
|
||||
if (mDelegate)
|
||||
mDelegate->onFilePlayed(mPlaylist.front());
|
||||
|
||||
// Remove played item & reset played time
|
||||
mPlaylist.pop_front();
|
||||
mPlayedTime = 0;
|
||||
// Save usage id to release later from main loop
|
||||
mFinishedUsages.push_back(mPlaylist.front().mUsageId);
|
||||
|
||||
// Send event
|
||||
if (mDelegate)
|
||||
mDelegate->onFilePlayed(mPlaylist.front());
|
||||
|
||||
// Remove played item & reset played time
|
||||
mPlaylist.pop_front();
|
||||
mPlayedTime = 0;
|
||||
}
|
||||
|
||||
void Player::obtain(int usage)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
auto usageIter = mUsage.find(usage);
|
||||
if (usageIter == mUsage.end())
|
||||
mUsage[usage] = 1;
|
||||
else
|
||||
usageIter->second = usageIter->second + 1;
|
||||
Lock l(mGuard);
|
||||
auto usageIter = mUsage.find(usage);
|
||||
if (usageIter == mUsage.end())
|
||||
mUsage[usage] = 1;
|
||||
else
|
||||
usageIter->second = usageIter->second + 1;
|
||||
|
||||
if (mUsage.size() == 1 && mOutput)
|
||||
mOutput->open();
|
||||
if (mUsage.size() == 1 && mOutput)
|
||||
mOutput->open();
|
||||
}
|
||||
|
||||
void Player::release(int usage)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
UsageMap::iterator usageIter = mUsage.find(usage);
|
||||
if (usageIter == mUsage.end())
|
||||
return;
|
||||
Lock l(mGuard);
|
||||
UsageMap::iterator usageIter = mUsage.find(usage);
|
||||
if (usageIter == mUsage.end())
|
||||
return;
|
||||
|
||||
usageIter->second = usageIter->second - 1;
|
||||
if (!usageIter->second)
|
||||
mUsage.erase(usageIter);
|
||||
usageIter->second = usageIter->second - 1;
|
||||
if (!usageIter->second)
|
||||
mUsage.erase(usageIter);
|
||||
|
||||
for (unsigned i=0; i<mPlaylist.size(); i++)
|
||||
if (mPlaylist[i].mUsageId == usage)
|
||||
mPlaylist.erase(mPlaylist.begin() + i);
|
||||
for (unsigned i = 0; i < mPlaylist.size(); i++)
|
||||
if (mPlaylist[i].mUsageId == usage)
|
||||
mPlaylist.erase(mPlaylist.begin() + i);
|
||||
|
||||
if (mUsage.empty() && mOutput)
|
||||
mOutput->close();
|
||||
if (mUsage.empty() && mOutput)
|
||||
mOutput->close();
|
||||
}
|
||||
|
||||
int Player::releasePlayed()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
int result = mFinishedUsages.size();
|
||||
while (!mFinishedUsages.empty())
|
||||
{
|
||||
release(mFinishedUsages.front());
|
||||
mFinishedUsages.erase(mFinishedUsages.begin());
|
||||
}
|
||||
return result;
|
||||
Lock l(mGuard);
|
||||
int result = mFinishedUsages.size();
|
||||
while (!mFinishedUsages.empty())
|
||||
{
|
||||
release(mFinishedUsages.front());
|
||||
mFinishedUsages.erase(mFinishedUsages.begin());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Player::add(int usageId, PWavFileReader file, bool loop, int timelength)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
PlaylistItem item;
|
||||
item.mFile = file;
|
||||
item.mLoop = loop;
|
||||
item.mTimelength = timelength;
|
||||
item.mUsageId = usageId;
|
||||
mPlaylist.push_back(item);
|
||||
Lock l(mGuard);
|
||||
PlaylistItem item;
|
||||
item.mFile = file;
|
||||
item.mLoop = loop;
|
||||
item.mTimelength = timelength;
|
||||
item.mUsageId = usageId;
|
||||
mPlaylist.push_back(item);
|
||||
|
||||
obtain(usageId);
|
||||
obtain(usageId);
|
||||
}
|
||||
|
||||
void Player::clear()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
while (mPlaylist.size())
|
||||
onFilePlayed();
|
||||
Lock l(mGuard);
|
||||
while (mPlaylist.size())
|
||||
onFilePlayed();
|
||||
}
|
||||
|
||||
void Player::retrieveUsageIds(std::vector<int>& ids)
|
||||
{
|
||||
ids.assign(mFinishedUsages.begin(), mFinishedUsages.end());
|
||||
mFinishedUsages.clear();
|
||||
ids.assign(mFinishedUsages.begin(), mFinishedUsages.end());
|
||||
mFinishedUsages.clear();
|
||||
}
|
||||
|
||||
@@ -16,56 +16,57 @@
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
class Player: public DataConnection
|
||||
{
|
||||
friend class DevicePair;
|
||||
public:
|
||||
class Player : public DataConnection
|
||||
{
|
||||
friend class DevicePair;
|
||||
|
||||
public:
|
||||
struct PlaylistItem
|
||||
{
|
||||
PWavFileReader mFile;
|
||||
bool mLoop;
|
||||
int mTimelength;
|
||||
int mUsageId;
|
||||
PWavFileReader mFile;
|
||||
bool mLoop;
|
||||
int mTimelength;
|
||||
int mUsageId;
|
||||
};
|
||||
typedef std::deque<PlaylistItem> Playlist;
|
||||
|
||||
class EndOfAudioDelegate
|
||||
{
|
||||
public:
|
||||
virtual void onFilePlayed(PlaylistItem& item) = 0;
|
||||
virtual void onFilePlayed(PlaylistItem& item) = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
protected:
|
||||
typedef std::map<int, int> UsageMap;
|
||||
Audio::POutputDevice mOutput;
|
||||
UsageMap mUsage; // References map
|
||||
std::vector<int> mFinishedUsages; // Finished plays
|
||||
Audio::POutputDevice mOutput;
|
||||
UsageMap mUsage; // References map
|
||||
std::vector<int> mFinishedUsages; // Finished plays
|
||||
|
||||
Mutex mGuard;
|
||||
Playlist mPlaylist;
|
||||
float mPlayedTime;
|
||||
EndOfAudioDelegate* mDelegate;
|
||||
Mutex mGuard;
|
||||
Playlist mPlaylist;
|
||||
float mPlayedTime;
|
||||
EndOfAudioDelegate* mDelegate;
|
||||
|
||||
void onMicData(const Format& f, const void* buffer, int length);
|
||||
void onSpkData(const Format& f, void* buffer, int length);
|
||||
void onFilePlayed();
|
||||
void obtain(int usageId);
|
||||
void onMicData(const Format& f, const void* buffer, int length);
|
||||
void onSpkData(const Format& f, void* buffer, int length);
|
||||
void onFilePlayed();
|
||||
void obtain(int usageId);
|
||||
|
||||
public:
|
||||
public:
|
||||
Player();
|
||||
~Player();
|
||||
|
||||
void setDelegate(EndOfAudioDelegate* d);
|
||||
void setDelegate(EndOfAudioDelegate* d);
|
||||
EndOfAudioDelegate* getDelegate() const;
|
||||
|
||||
void setOutput(POutputDevice output);
|
||||
POutputDevice getOutput() const;
|
||||
void setOutput(POutputDevice output);
|
||||
POutputDevice getOutput() const;
|
||||
|
||||
void add(int usageId, PWavFileReader file, bool loop, int timelength);
|
||||
void release(int usageId);
|
||||
void clear();
|
||||
int releasePlayed();
|
||||
void retrieveUsageIds(std::vector<int>& ids);
|
||||
};
|
||||
}
|
||||
void add(int usageId, PWavFileReader file, bool loop, int timelength);
|
||||
void release(int usageId);
|
||||
void clear();
|
||||
int releasePlayed();
|
||||
void retrieveUsageIds(std::vector<int>& ids);
|
||||
};
|
||||
} // namespace Audio
|
||||
#endif
|
||||
|
||||
+122
-126
@@ -10,7 +10,7 @@
|
||||
#include "speex/speex_preprocess.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# include <malloc.h>
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
@@ -18,221 +18,217 @@
|
||||
using namespace Audio;
|
||||
|
||||
#ifndef SHRT_MAX
|
||||
# define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||
#endif
|
||||
|
||||
AgcFilter::AgcFilter(int channels)
|
||||
{
|
||||
static const float DefaultLevel = 0.8f;
|
||||
static const float DefaultLevel = 0.8f;
|
||||
|
||||
for (int i=0; i<channels; i++)
|
||||
{
|
||||
Channel c;
|
||||
float level = DefaultLevel;
|
||||
c.mSampleMax = 1;
|
||||
c.mCounter = 0;
|
||||
c.mIgain = 65536;
|
||||
if (level > 1.0f)
|
||||
level = 1.0f;
|
||||
else
|
||||
if (level < 0.5f)
|
||||
level = 0.5f;
|
||||
for (int i = 0; i < channels; i++)
|
||||
{
|
||||
Channel c;
|
||||
float level = DefaultLevel;
|
||||
c.mSampleMax = 1;
|
||||
c.mCounter = 0;
|
||||
c.mIgain = 65536;
|
||||
if (level > 1.0f)
|
||||
level = 1.0f;
|
||||
else if (level < 0.5f)
|
||||
level = 0.5f;
|
||||
|
||||
c.mIpeak = (int)(SHRT_MAX * level * 65536);
|
||||
c.mIpeak = (int)(SHRT_MAX * level * 65536);
|
||||
|
||||
c.mSilenceCounter = 0;
|
||||
mChannelList.push_back(c);
|
||||
}
|
||||
c.mSilenceCounter = 0;
|
||||
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++)
|
||||
processChannel((short*)pcm, length / (sizeof(short) * mChannelList.size()), i);
|
||||
for (size_t i = 0; i < mChannelList.size(); i++)
|
||||
processChannel((short*)pcm, length / (sizeof(short) * mChannelList.size()), i);
|
||||
}
|
||||
|
||||
void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex)
|
||||
{
|
||||
int i;
|
||||
int 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)
|
||||
for (i = 0; i < nrOfSamples; i++)
|
||||
{
|
||||
/* update the max */
|
||||
channel.mSampleMax = (unsigned int)sample;
|
||||
}
|
||||
channel.mCounter ++;
|
||||
long gain_new;
|
||||
int sample;
|
||||
int sampleIndex = mChannelList.size() * i + channelIndex;
|
||||
Channel& channel = mChannelList[channelIndex];
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* get the abs of buffer[i] */
|
||||
sample = pcm[sampleIndex];
|
||||
sample = (sample < 0 ? -(sample) : sample);
|
||||
|
||||
/* 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;
|
||||
if (sample > (int)channel.mSampleMax)
|
||||
{
|
||||
/* update the max */
|
||||
channel.mSampleMax = (unsigned int)sample;
|
||||
}
|
||||
channel.mCounter++;
|
||||
|
||||
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;
|
||||
/* 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);
|
||||
}
|
||||
pcm[sampleIndex] = (short) ((pcm[sampleIndex] * channel.mIgain) >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
// --- AecFilter ---
|
||||
#ifdef USE_SPEEX_AEC
|
||||
# include "speex/speex_echo.h"
|
||||
#include "speex/speex_echo.h"
|
||||
#include "Audio_Interface.h"
|
||||
|
||||
#if !defined(TARGET_WIN)
|
||||
# include <alloca.h>
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBRTC_AEC
|
||||
# include "aec/echo_cancellation.h"
|
||||
#include "aec/echo_cancellation.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_WEBRTC_AEC
|
||||
static void CheckWRACode(unsigned errorcode)
|
||||
{
|
||||
if (errorcode)
|
||||
throw Exception(ERR_WEBRTC, errorcode);
|
||||
if (errorcode)
|
||||
throw Exception(ERR_WEBRTC, errorcode);
|
||||
}
|
||||
#endif
|
||||
|
||||
AecFilter::AecFilter(int tailTime, int frameTime, int rate)
|
||||
:mCtx(nullptr), mFrameTime(frameTime), mRate(rate)
|
||||
AecFilter::AecFilter(int tailTime, int frameTime, int rate) : mCtx(nullptr), mFrameTime(frameTime), mRate(rate)
|
||||
{
|
||||
#ifdef USE_SPEEX_AEC
|
||||
if (AUDIO_CHANNELS == 2)
|
||||
mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS, AUDIO_CHANNELS );
|
||||
else
|
||||
mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000));
|
||||
int tmp = rate;
|
||||
speex_echo_ctl((SpeexEchoState*)mCtx, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp);
|
||||
if (AUDIO_CHANNELS == 2)
|
||||
mCtx = speex_echo_state_init_mc(frameTime * (mRate / 1000), tailTime * (mRate / 1000), AUDIO_CHANNELS,
|
||||
AUDIO_CHANNELS);
|
||||
else
|
||||
mCtx = speex_echo_state_init(frameTime * (mRate / 1000), tailTime * (mRate / 1000));
|
||||
int tmp = rate;
|
||||
speex_echo_ctl((SpeexEchoState*)mCtx, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp);
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBRTC_AEC
|
||||
CheckWRACode(WebRtcAec_Create(&mCtx));
|
||||
CheckWRACode(WebRtcAec_Init(mCtx, rate, rate));
|
||||
CheckWRACode(WebRtcAec_Create(&mCtx));
|
||||
CheckWRACode(WebRtcAec_Init(mCtx, rate, rate));
|
||||
#endif
|
||||
}
|
||||
|
||||
AecFilter::~AecFilter()
|
||||
{
|
||||
#ifdef USE_SPEEX_AEC
|
||||
if (mCtx)
|
||||
{
|
||||
//speex_echo_state_destroy((SpeexEchoState*)mCtx);
|
||||
mCtx = nullptr;
|
||||
}
|
||||
if (mCtx)
|
||||
{
|
||||
// speex_echo_state_destroy((SpeexEchoState*)mCtx);
|
||||
mCtx = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBRTC_AEC
|
||||
CheckWRACode(WebRtcAec_Free(mCtx));
|
||||
mCtx = NULL;
|
||||
CheckWRACode(WebRtcAec_Free(mCtx));
|
||||
mCtx = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AecFilter::fromMic(void *data)
|
||||
void AecFilter::fromMic(void* data)
|
||||
{
|
||||
#ifdef USE_SPEEX_AEC
|
||||
short* output = (short*)alloca(Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
|
||||
speex_echo_capture((SpeexEchoState*)mCtx, (short*)data, (short*)output);
|
||||
memmove(data, output, AUDIO_MIC_BUFFER_SIZE);
|
||||
short* output = (short*)alloca(Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH));
|
||||
speex_echo_capture((SpeexEchoState*)mCtx, (short*)data, (short*)output);
|
||||
memmove(data, output, AUDIO_MIC_BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEBRTC_AEC
|
||||
short* inputframe = (short*)ALLOCA(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));
|
||||
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));
|
||||
#endif
|
||||
}
|
||||
|
||||
void AecFilter::toSpeaker(void *data)
|
||||
void AecFilter::toSpeaker(void* data)
|
||||
{
|
||||
#ifdef USE_SPEEX_AEC
|
||||
speex_echo_playback((SpeexEchoState*)mCtx, (short*)data);
|
||||
speex_echo_playback((SpeexEchoState*)mCtx, (short*)data);
|
||||
#endif
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
int AecFilter::frametime()
|
||||
{
|
||||
return mFrameTime;
|
||||
return mFrameTime;
|
||||
}
|
||||
|
||||
|
||||
DenoiseFilter::DenoiseFilter(int rate)
|
||||
:mRate(rate)
|
||||
DenoiseFilter::DenoiseFilter(int rate) : mRate(rate)
|
||||
{
|
||||
mCtx = speex_preprocess_state_init(mRate/100, mRate);
|
||||
mCtx = speex_preprocess_state_init(mRate / 100, mRate);
|
||||
}
|
||||
|
||||
DenoiseFilter::~DenoiseFilter()
|
||||
{
|
||||
if (mCtx)
|
||||
speex_preprocess_state_destroy((SpeexPreprocessState*)mCtx);
|
||||
if (mCtx)
|
||||
speex_preprocess_state_destroy((SpeexPreprocessState*)mCtx);
|
||||
}
|
||||
|
||||
void DenoiseFilter::fromMic(void* data, int timelength)
|
||||
{
|
||||
assert(timelength % 10 == 0);
|
||||
|
||||
// Process by 10-ms blocks
|
||||
spx_int16_t* in = (spx_int16_t*)data;
|
||||
|
||||
for (int blockIndex=0; blockIndex<timelength/10; blockIndex++)
|
||||
{
|
||||
spx_int16_t* block = in + blockIndex * (mRate / 100) * AUDIO_CHANNELS;
|
||||
speex_preprocess_run((SpeexPreprocessState*)mCtx, block);
|
||||
}
|
||||
assert(timelength % 10 == 0);
|
||||
|
||||
// Process by 10-ms blocks
|
||||
spx_int16_t* in = (spx_int16_t*)data;
|
||||
|
||||
for (int blockIndex = 0; blockIndex < timelength / 10; blockIndex++)
|
||||
{
|
||||
spx_int16_t* block = in + blockIndex * (mRate / 100) * AUDIO_CHANNELS;
|
||||
speex_preprocess_run((SpeexPreprocessState*)mCtx, block);
|
||||
}
|
||||
}
|
||||
|
||||
int DenoiseFilter::rate()
|
||||
{
|
||||
return mRate;
|
||||
return mRate;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,59 +11,60 @@
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
class AgcFilter
|
||||
{
|
||||
protected:
|
||||
class AgcFilter
|
||||
{
|
||||
protected:
|
||||
struct Channel
|
||||
{
|
||||
unsigned int mSampleMax;
|
||||
int mCounter;
|
||||
long mIgain;
|
||||
int mIpeak;
|
||||
int mSilenceCounter;
|
||||
unsigned int mSampleMax;
|
||||
int mCounter;
|
||||
long mIgain;
|
||||
int mIpeak;
|
||||
int mSilenceCounter;
|
||||
};
|
||||
std::vector<Channel> mChannelList;
|
||||
void processChannel(short* pcm, int nrOfSamples, int channelIndex);
|
||||
public:
|
||||
void processChannel(short* pcm, int nrOfSamples, int channelIndex);
|
||||
|
||||
public:
|
||||
AgcFilter(int channels);
|
||||
~AgcFilter();
|
||||
|
||||
void process(void* pcm, int length);
|
||||
};
|
||||
|
||||
class AecFilter
|
||||
{
|
||||
public:
|
||||
void process(void* pcm, int length);
|
||||
};
|
||||
|
||||
class AecFilter
|
||||
{
|
||||
public:
|
||||
AecFilter(int tailTime, int frameTime, int rate);
|
||||
~AecFilter();
|
||||
|
||||
// These methods accept input block with timelength "frameTime" used in constructor.
|
||||
void toSpeaker(void* data);
|
||||
void fromMic(void* data);
|
||||
int frametime();
|
||||
|
||||
protected:
|
||||
void* mCtx; /// The echo canceller context's pointer.
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
int mFrameTime; /// Duration of single audio frame (in milliseconds)
|
||||
int mRate;
|
||||
};
|
||||
int frametime();
|
||||
|
||||
class DenoiseFilter
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
void* mCtx; /// The echo canceller context's pointer.
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
int mFrameTime; /// Duration of single audio frame (in milliseconds)
|
||||
int mRate;
|
||||
};
|
||||
|
||||
class DenoiseFilter
|
||||
{
|
||||
public:
|
||||
DenoiseFilter(int rate);
|
||||
~DenoiseFilter();
|
||||
|
||||
void fromMic(void* data, int timelength);
|
||||
int rate();
|
||||
int rate();
|
||||
|
||||
protected:
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
void* mCtx; /// The denoiser context pointer.
|
||||
int mRate; /// Duration of single audio frame (in milliseconds)
|
||||
};
|
||||
protected:
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
void* mCtx; /// The denoiser context pointer.
|
||||
int mRate; /// Duration of single audio frame (in milliseconds)
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace Audio
|
||||
{
|
||||
|
||||
|
||||
SpeexResampler::SpeexResampler()
|
||||
{}
|
||||
SpeexResampler::SpeexResampler() {}
|
||||
|
||||
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)
|
||||
{
|
||||
// Defer context creation until first request
|
||||
//mContext = speex_resampler_init(channels, sourceRate, destRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
|
||||
//assert(mContext != NULL);
|
||||
// mContext = speex_resampler_init(channels, sourceRate, destRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
|
||||
// assert(mContext != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +58,8 @@ SpeexResampler::~SpeexResampler()
|
||||
stop();
|
||||
}
|
||||
|
||||
size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed,
|
||||
void* dest, size_t destCapacity)
|
||||
size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_t& sourceProcessed, void* dest,
|
||||
size_t destCapacity)
|
||||
{
|
||||
assert(mSourceRate != 0 && mDestRate != 0);
|
||||
|
||||
@@ -77,8 +76,7 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
|
||||
|
||||
if (!mContext)
|
||||
{
|
||||
mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate,
|
||||
AUDIO_RESAMPLER_QUALITY, &mErrorCode);
|
||||
mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
|
||||
if (!mContext)
|
||||
return 0;
|
||||
}
|
||||
@@ -103,11 +101,9 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
|
||||
unsigned inLen = sourceLength / (sizeof(short) * mChannels);
|
||||
outLen /= sizeof(short) * mChannels;
|
||||
assert(mContext != NULL);
|
||||
spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen),
|
||||
out_len = static_cast<spx_uint32_t>(outLen);
|
||||
spx_uint32_t in_len = static_cast<spx_uint32_t>(inLen), out_len = static_cast<spx_uint32_t>(outLen);
|
||||
|
||||
int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState *)mContext,
|
||||
(spx_int16_t*)src, &in_len,
|
||||
int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState*)mContext, (spx_int16_t*)src, &in_len,
|
||||
(spx_int16_t*)dest, &out_len);
|
||||
assert(speexCode == RESAMPLER_ERR_SUCCESS);
|
||||
|
||||
@@ -147,27 +143,27 @@ size_t SpeexResampler::getSize() const
|
||||
}
|
||||
|
||||
// -------------------------- 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);
|
||||
const short* input = (const short*)source;
|
||||
short* output = (short*)dest;
|
||||
for (int sampleIndex = 0; sampleIndex < destLength/2; sampleIndex++)
|
||||
short* output = (short*)dest;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
short* output = (short*)dest;
|
||||
short* output = (short*)dest;
|
||||
// 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;
|
||||
}
|
||||
@@ -184,13 +180,14 @@ Resampler48kTo16k::~Resampler48kTo16k()
|
||||
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;
|
||||
short* output = (short*)dest; //int outputCapacity = destLen / 2;
|
||||
const short* input = (const short*)source;
|
||||
int inputLen = sourceLen / 2;
|
||||
short* output = (short*)dest; // int outputCapacity = destLen / 2;
|
||||
assert(inputLen % 480 == 0);
|
||||
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);
|
||||
|
||||
return sourceLen / 3;
|
||||
@@ -207,13 +204,14 @@ Resampler16kto48k::~Resampler16kto48k()
|
||||
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;
|
||||
WebRtc_Word16* output = (WebRtc_Word16*)dest; //int outputCapacity = destLen / 2;
|
||||
const WebRtc_Word16* input = (const WebRtc_Word16*)source;
|
||||
int inputLen = sourceLen / 2;
|
||||
WebRtc_Word16* output = (WebRtc_Word16*)dest; // int outputCapacity = destLen / 2;
|
||||
assert(inputLen % 160 == 0);
|
||||
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);
|
||||
|
||||
return sourceLen * 3;
|
||||
@@ -222,18 +220,12 @@ int Resampler16kto48k::process(const void *source, int sourceLen, void *dest, in
|
||||
#endif
|
||||
|
||||
// ---------------- 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);
|
||||
size_t result;
|
||||
@@ -252,10 +244,7 @@ size_t UniversalResampler::resample(int sourceRate, const void *sourceBuffer, si
|
||||
return result;
|
||||
}
|
||||
|
||||
void UniversalResampler::preload()
|
||||
{
|
||||
|
||||
}
|
||||
void UniversalResampler::preload() {}
|
||||
|
||||
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);
|
||||
ResamplerMap::iterator resamplerIter = mResamplerMap.find(RatePair(sourceRate, destRate));
|
||||
PResampler r;
|
||||
PResampler r;
|
||||
if (resamplerIter == mResamplerMap.end())
|
||||
{
|
||||
r = std::make_shared<Resampler>();
|
||||
@@ -289,4 +278,4 @@ PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
|
||||
return r;
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
} // namespace Audio
|
||||
|
||||
@@ -7,100 +7,99 @@
|
||||
#define __AUDIO_RESAMPLER_H
|
||||
|
||||
#ifdef USE_WEBRTC_RESAMPLER
|
||||
# include "signal_processing_library/signal_processing_library.h"
|
||||
#include "signal_processing_library/signal_processing_library.h"
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace Audio
|
||||
namespace Audio
|
||||
{
|
||||
class SpeexResampler
|
||||
{
|
||||
public:
|
||||
SpeexResampler();
|
||||
~SpeexResampler();
|
||||
class SpeexResampler
|
||||
{
|
||||
public:
|
||||
SpeexResampler();
|
||||
~SpeexResampler();
|
||||
|
||||
void start(int channels, int sourceRate, int destRate);
|
||||
void stop();
|
||||
bool isOpened() const;
|
||||
void start(int channels, int sourceRate, int destRate);
|
||||
void stop();
|
||||
bool isOpened() const;
|
||||
|
||||
size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed,
|
||||
void* dest, size_t destCapacity);
|
||||
int sourceRate() const;
|
||||
int destRate() const;
|
||||
size_t getDestLength(size_t sourceLen) const;
|
||||
size_t getSourceLength(size_t destLen) const;
|
||||
size_t processBuffer(const void* source, size_t sourceLength, size_t& sourceProcessed, void* dest,
|
||||
size_t destCapacity);
|
||||
int sourceRate() const;
|
||||
int destRate() const;
|
||||
size_t getDestLength(size_t sourceLen) const;
|
||||
size_t getSourceLength(size_t destLen) const;
|
||||
|
||||
// Returns instance + speex encoder size in bytes
|
||||
size_t getSize() const;
|
||||
// Returns instance + speex encoder size in bytes
|
||||
size_t getSize() const;
|
||||
|
||||
protected:
|
||||
void* mContext = nullptr;
|
||||
int mErrorCode = 0;
|
||||
int mSourceRate = 0,
|
||||
mDestRate = 0,
|
||||
mChannels = 0;
|
||||
short mLastSample = 0;
|
||||
};
|
||||
protected:
|
||||
void* mContext = nullptr;
|
||||
int mErrorCode = 0;
|
||||
int mSourceRate = 0, mDestRate = 0, mChannels = 0;
|
||||
short mLastSample = 0;
|
||||
};
|
||||
|
||||
typedef SpeexResampler Resampler;
|
||||
typedef std::shared_ptr<Resampler> PResampler;
|
||||
typedef SpeexResampler Resampler;
|
||||
typedef std::shared_ptr<Resampler> PResampler;
|
||||
|
||||
class ChannelConverter
|
||||
{
|
||||
public:
|
||||
static int stereoToMono(const void* source, int sourceLength, void* dest, int destLength);
|
||||
static int monoToStereo(const void* source, int sourceLength, void* dest, int destLength);
|
||||
};
|
||||
class ChannelConverter
|
||||
{
|
||||
public:
|
||||
static int stereoToMono(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
|
||||
class UniversalResampler
|
||||
{
|
||||
public:
|
||||
UniversalResampler();
|
||||
~UniversalResampler();
|
||||
// Operates with AUDIO_CHANNELS number of channels
|
||||
class UniversalResampler
|
||||
{
|
||||
public:
|
||||
UniversalResampler();
|
||||
~UniversalResampler();
|
||||
|
||||
size_t resample(int sourceRate, const void* sourceBuffer, size_t sourceLength, size_t& sourceProcessed,
|
||||
int destRate, void* destBuffer, size_t destCapacity);
|
||||
size_t getDestLength(int sourceRate, int destRate, size_t sourceLength);
|
||||
size_t getSourceLength(int sourceRate, int destRate, size_t destLength);
|
||||
size_t resample(int sourceRate, const void* sourceBuffer, size_t sourceLength, size_t& sourceProcessed,
|
||||
int destRate, void* destBuffer, size_t destCapacity);
|
||||
size_t getDestLength(int sourceRate, int destRate, size_t sourceLength);
|
||||
size_t getSourceLength(int sourceRate, int destRate, size_t destLength);
|
||||
|
||||
protected:
|
||||
typedef std::pair<int, int> RatePair;
|
||||
typedef std::map<RatePair, PResampler> ResamplerMap;
|
||||
ResamplerMap mResamplerMap;
|
||||
PResampler findResampler(int sourceRate, int destRate);
|
||||
protected:
|
||||
typedef std::pair<int, int> RatePair;
|
||||
typedef std::map<RatePair, PResampler> ResamplerMap;
|
||||
ResamplerMap mResamplerMap;
|
||||
PResampler findResampler(int sourceRate, int destRate);
|
||||
|
||||
void preload();
|
||||
};
|
||||
void preload();
|
||||
};
|
||||
|
||||
#ifdef USE_WEBRTC_RESAMPLER
|
||||
// n*10 milliseconds buffers required!
|
||||
class Resampler48kTo16k
|
||||
{
|
||||
public:
|
||||
Resampler48kTo16k();
|
||||
~Resampler48kTo16k();
|
||||
int process(const void* source, int sourceLen, void* dest, int destLen);
|
||||
protected:
|
||||
WebRtc_Word32 mTemp[496];
|
||||
WebRtcSpl_State48khzTo16khz mContext;
|
||||
};
|
||||
#ifdef USE_WEBRTC_RESAMPLER
|
||||
// n*10 milliseconds buffers required!
|
||||
class Resampler48kTo16k
|
||||
{
|
||||
public:
|
||||
Resampler48kTo16k();
|
||||
~Resampler48kTo16k();
|
||||
int process(const void* source, int sourceLen, void* dest, int destLen);
|
||||
|
||||
class Resampler16kto48k
|
||||
{
|
||||
public:
|
||||
Resampler16kto48k();
|
||||
~Resampler16kto48k();
|
||||
int process(const void* source, int sourceLen, void* dest, int destLen);
|
||||
protected:
|
||||
WebRtc_Word32 mTemp[496];
|
||||
WebRtcSpl_State48khzTo16khz mContext;
|
||||
};
|
||||
|
||||
protected:
|
||||
WebRtc_Word32 mTemp[336];
|
||||
WebRtcSpl_State16khzTo48khz mContext;
|
||||
};
|
||||
#endif
|
||||
} // end of namespace
|
||||
class Resampler16kto48k
|
||||
{
|
||||
public:
|
||||
Resampler16kto48k();
|
||||
~Resampler16kto48k();
|
||||
int process(const void* source, int sourceLen, void* dest, int destLen);
|
||||
|
||||
protected:
|
||||
WebRtc_Word32 mTemp[336];
|
||||
WebRtcSpl_State16khzTo48khz mContext;
|
||||
};
|
||||
#endif
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,13 +13,14 @@
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef WORD
|
||||
# define WORD unsigned short
|
||||
#define WORD unsigned short
|
||||
#endif
|
||||
#ifndef DWORD
|
||||
# define DWORD unsigned int
|
||||
#define DWORD unsigned int
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
WORD wFormatTag;
|
||||
WORD nChannels;
|
||||
DWORD nSamplesPerSec;
|
||||
@@ -27,8 +28,7 @@ typedef struct {
|
||||
WORD nBlockAlign;
|
||||
WORD wBitsPerSample;
|
||||
WORD cbSize;
|
||||
}
|
||||
WaveFormatEx;
|
||||
} WaveFormatEx;
|
||||
|
||||
#define WAVE_FORMAT_PCM 1
|
||||
|
||||
@@ -39,17 +39,14 @@ WaveFormatEx;
|
||||
using namespace Audio;
|
||||
|
||||
// ---------------------- WavFileReader -------------------------
|
||||
WavFileReader::WavFileReader()
|
||||
:mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(0)
|
||||
WavFileReader::WavFileReader() : mSamplerate(0), mLastError(0), mChannels(0), mBits(0), mDataLength(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()
|
||||
{
|
||||
@@ -57,7 +54,7 @@ std::string WavFileReader::readChunk()
|
||||
readBuffer(name, 4);
|
||||
|
||||
std::string result = name;
|
||||
uint32_t size = 0;
|
||||
uint32_t size = 0;
|
||||
readBuffer(&size, 4);
|
||||
|
||||
if (result == "data")
|
||||
@@ -69,7 +66,7 @@ std::string WavFileReader::readChunk()
|
||||
return result;
|
||||
}
|
||||
|
||||
void WavFileReader::readBuffer(void* buffer, size_t sz)
|
||||
void WavFileReader::readBuffer(void* buffer, size_t sz)
|
||||
{
|
||||
auto p = mInput->tellg();
|
||||
mInput->read(reinterpret_cast<char*>(buffer), sz);
|
||||
@@ -122,12 +119,12 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
||||
uint32_t fmtSize = 0;
|
||||
readBuffer(&fmtSize, sizeof(fmtSize));
|
||||
|
||||
auto fmtStart = mInput->tellg();
|
||||
auto fmtStart = mInput->tellg();
|
||||
|
||||
uint16_t formattag = 0;
|
||||
uint16_t formattag = 0;
|
||||
readBuffer(&formattag, sizeof(formattag));
|
||||
|
||||
if (formattag != 1/*WAVE_FORMAT_PCM*/)
|
||||
if (formattag != 1 /*WAVE_FORMAT_PCM*/)
|
||||
THROW_READERROR;
|
||||
|
||||
mChannels = 0;
|
||||
@@ -168,7 +165,7 @@ bool WavFileReader::open(const std::filesystem::path& p)
|
||||
mDataOffset = mInput->tellg();
|
||||
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
mInput.reset();
|
||||
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
|
||||
size_t requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
|
||||
bool useHeap = requiredBytes > sizeof mTempBuffer;
|
||||
void* temp;
|
||||
bool useHeap = requiredBytes > sizeof mTempBuffer;
|
||||
void* temp;
|
||||
if (useHeap)
|
||||
temp = malloc(requiredBytes);
|
||||
else
|
||||
@@ -223,19 +220,18 @@ size_t WavFileReader::read(short* buffer, size_t samples)
|
||||
// Find required size of input buffer
|
||||
if (mDataLength)
|
||||
{
|
||||
auto filePosition = mInput->tellg();
|
||||
auto filePosition = mInput->tellg();
|
||||
|
||||
// Check how much data we can read
|
||||
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset;
|
||||
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
|
||||
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
|
||||
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
|
||||
}
|
||||
|
||||
size_t readBytes = tryReadBuffer(temp, requiredBytes);
|
||||
|
||||
size_t processedBytes = 0;
|
||||
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes,
|
||||
buffer, samples * 2 * AUDIO_CHANNELS);
|
||||
size_t result = mResampler.processBuffer(temp, readBytes, processedBytes, buffer, samples * 2 * AUDIO_CHANNELS);
|
||||
|
||||
if (useHeap)
|
||||
free(temp);
|
||||
@@ -256,11 +252,11 @@ size_t WavFileReader::readRaw(short* buffer, size_t samples)
|
||||
// Find required size of input buffer
|
||||
if (mDataLength)
|
||||
{
|
||||
auto filePosition = mInput->tellg();
|
||||
auto filePosition = mInput->tellg();
|
||||
|
||||
// Check how much data we can read
|
||||
std::streamoff dataEnd = std::streamoff(mDataLength) + mDataOffset;
|
||||
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
|
||||
size_t fileAvailable = filePosition < dataEnd ? size_t(dataEnd - filePosition) : 0;
|
||||
requiredBytes = fileAvailable < requiredBytes ? fileAvailable : requiredBytes;
|
||||
}
|
||||
|
||||
@@ -302,11 +298,9 @@ unsigned WavFileReader::lastError() const
|
||||
// ------------------------- WavFileWriter -------------------------
|
||||
#define LOG_SUBSYTEM "WavFileWriter"
|
||||
|
||||
#define BITS_PER_CHANNEL 16
|
||||
#define BITS_PER_CHANNEL 16
|
||||
|
||||
WavFileWriter::WavFileWriter()
|
||||
:mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
|
||||
{}
|
||||
WavFileWriter::WavFileWriter() : mLengthOffset(0), mSamplerate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0) {}
|
||||
|
||||
WavFileWriter::~WavFileWriter()
|
||||
{
|
||||
@@ -358,7 +352,8 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
|
||||
writeBuffer(wavefmt, 8);
|
||||
|
||||
// Set the format description
|
||||
uint32_t dwFmtSize = 16; /*= 16L*/;
|
||||
uint32_t dwFmtSize = 16; /*= 16L*/
|
||||
;
|
||||
writeBuffer(&dwFmtSize, sizeof(dwFmtSize));
|
||||
|
||||
WaveFormatEx format;
|
||||
@@ -384,7 +379,7 @@ bool WavFileWriter::open(const std::filesystem::path& p, int samplerate, int cha
|
||||
writeBuffer(data, 4);
|
||||
|
||||
mPath = p;
|
||||
mWritten = 0;
|
||||
mWritten = 0;
|
||||
|
||||
mLengthOffset = mOutput->tellp();
|
||||
writeBuffer(&mWritten, sizeof mWritten);
|
||||
@@ -435,4 +430,3 @@ std::filesystem::path WavFileWriter::path() const
|
||||
LOCK;
|
||||
return mPath;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,45 +22,45 @@ namespace Audio
|
||||
class WavFileReader
|
||||
{
|
||||
protected:
|
||||
uint16_t mChannels = 0;
|
||||
uint16_t mBits = 0;
|
||||
int mSamplerate = 0;
|
||||
std::filesystem::path mPath;
|
||||
mutable std::recursive_mutex mFileMtx;
|
||||
size_t mDataOffset = 0;
|
||||
size_t mDataLength = 0;
|
||||
Resampler mResampler;
|
||||
unsigned mLastError = 0;
|
||||
std::unique_ptr<std::ifstream> mInput;
|
||||
uint8_t mTempBuffer[16384];
|
||||
uint16_t mChannels = 0;
|
||||
uint16_t mBits = 0;
|
||||
int mSamplerate = 0;
|
||||
std::filesystem::path mPath;
|
||||
mutable std::recursive_mutex mFileMtx;
|
||||
size_t mDataOffset = 0;
|
||||
size_t mDataLength = 0;
|
||||
Resampler mResampler;
|
||||
unsigned mLastError = 0;
|
||||
std::unique_ptr<std::ifstream> mInput;
|
||||
uint8_t mTempBuffer[16384];
|
||||
|
||||
std::string readChunk();
|
||||
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
|
||||
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
|
||||
std::string readChunk();
|
||||
void readBuffer(void* buffer, size_t sz); // This raises an exception if sz bytes are not read
|
||||
size_t tryReadBuffer(void* buffer, size_t sz); // This doesn't raise an exception
|
||||
|
||||
public:
|
||||
WavFileReader();
|
||||
~WavFileReader();
|
||||
|
||||
bool open(const std::filesystem::path& p);
|
||||
void close();
|
||||
bool isOpened();
|
||||
void rewind();
|
||||
int samplerate() const;
|
||||
int channels() const;
|
||||
bool open(const std::filesystem::path& p);
|
||||
void close();
|
||||
bool isOpened();
|
||||
void rewind();
|
||||
int samplerate() const;
|
||||
int channels() const;
|
||||
|
||||
// This method returns number of read bytes
|
||||
size_t read(void* buffer, size_t bytes);
|
||||
size_t readRaw(void* buffer, size_t bytes);
|
||||
size_t read(void* buffer, size_t bytes);
|
||||
size_t readRaw(void* buffer, size_t bytes);
|
||||
|
||||
// This method returns number of read samples
|
||||
size_t read(short* buffer, size_t samples);
|
||||
size_t readRaw(short* buffer, size_t samples);
|
||||
size_t read(short* buffer, size_t samples);
|
||||
size_t readRaw(short* buffer, size_t samples);
|
||||
|
||||
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;
|
||||
@@ -68,29 +68,29 @@ typedef std::shared_ptr<WavFileReader> PWavFileReader;
|
||||
class WavFileWriter
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
|
||||
std::filesystem::path mPath; /// Path to requested audio file.
|
||||
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||
size_t mWritten = 0; /// Amount of written data (in bytes)
|
||||
size_t mLengthOffset = 0; /// Position of length field.
|
||||
int mSamplerate = 0,
|
||||
mChannels = 0;
|
||||
std::unique_ptr<std::ofstream> mOutput; /// Handle of audio file.
|
||||
std::filesystem::path mPath; /// Path to requested audio file.
|
||||
mutable std::recursive_mutex mFileMtx; /// Mutex to protect this instance.
|
||||
size_t mWritten = 0; /// Amount of written data (in bytes)
|
||||
size_t mLengthOffset = 0; /// Position of length field.
|
||||
int mSamplerate = 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:
|
||||
WavFileWriter();
|
||||
~WavFileWriter();
|
||||
|
||||
bool open(const std::filesystem::path& p, int samplerate, int channels);
|
||||
void close();
|
||||
bool isOpened() const;
|
||||
size_t write(const void* buffer, size_t bytes);
|
||||
bool open(const std::filesystem::path& p, int samplerate, int channels);
|
||||
void close();
|
||||
bool isOpened() const;
|
||||
size_t write(const void* buffer, size_t bytes);
|
||||
std::filesystem::path path() const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
|
||||
|
||||
}
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
+346
-347
@@ -15,541 +15,540 @@ using namespace Audio;
|
||||
|
||||
WmmeInputDevice::Buffer::Buffer()
|
||||
{
|
||||
// Do not use WAVEHDR allocated on stack!
|
||||
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof WAVEHDR);
|
||||
if (!mHeaderHandle)
|
||||
throw Exception(ERR_WMME_FAILED, GetLastError());
|
||||
mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle);
|
||||
// Do not use WAVEHDR allocated on stack!
|
||||
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof WAVEHDR);
|
||||
if (!mHeaderHandle)
|
||||
throw Exception(ERR_WMME_FAILED, GetLastError());
|
||||
mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle);
|
||||
|
||||
mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_MIC_BUFFER_SIZE);
|
||||
if (!mDataHandle)
|
||||
throw Exception(ERR_WMME_FAILED, GetLastError());
|
||||
mData = GlobalLock(mDataHandle);
|
||||
|
||||
memset(mHeader, 0, sizeof *mHeader);
|
||||
mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE;
|
||||
mHeader->dwFlags = 0;
|
||||
mHeader->lpData = (LPSTR)mData;
|
||||
mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_MIC_BUFFER_SIZE);
|
||||
if (!mDataHandle)
|
||||
throw Exception(ERR_WMME_FAILED, GetLastError());
|
||||
mData = GlobalLock(mDataHandle);
|
||||
|
||||
memset(mHeader, 0, sizeof *mHeader);
|
||||
mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE;
|
||||
mHeader->dwFlags = 0;
|
||||
mHeader->lpData = (LPSTR)mData;
|
||||
}
|
||||
|
||||
WmmeInputDevice::Buffer::~Buffer()
|
||||
{
|
||||
if (mDataHandle)
|
||||
{
|
||||
GlobalUnlock(mDataHandle);
|
||||
GlobalFree(mDataHandle);
|
||||
}
|
||||
if (mHeaderHandle)
|
||||
{
|
||||
GlobalUnlock(mHeaderHandle);
|
||||
GlobalFree(mHeaderHandle);
|
||||
}
|
||||
|
||||
if (mDataHandle)
|
||||
{
|
||||
GlobalUnlock(mDataHandle);
|
||||
GlobalFree(mDataHandle);
|
||||
}
|
||||
if (mHeaderHandle)
|
||||
{
|
||||
GlobalUnlock(mHeaderHandle);
|
||||
GlobalFree(mHeaderHandle);
|
||||
}
|
||||
}
|
||||
|
||||
bool WmmeInputDevice::Buffer::prepare(HWAVEIN device)
|
||||
{
|
||||
MMRESULT resCode = MMSYSERR_NOERROR;
|
||||
mHeader->dwFlags = 0;
|
||||
mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE;
|
||||
mHeader->lpData = (LPSTR)mData;
|
||||
MMRESULT resCode = MMSYSERR_NOERROR;
|
||||
mHeader->dwFlags = 0;
|
||||
mHeader->dwBufferLength = AUDIO_MIC_BUFFER_SIZE;
|
||||
mHeader->lpData = (LPSTR)mData;
|
||||
|
||||
resCode = waveInPrepareHeader(device, mHeader, sizeof *mHeader);
|
||||
//if (resCode != MMSYSERR_NOERROR)
|
||||
// LogCritical("Audio", << "Failed to prepare source header. Error code " << resCode << ".");
|
||||
resCode = waveInPrepareHeader(device, mHeader, sizeof *mHeader);
|
||||
// if (resCode != MMSYSERR_NOERROR)
|
||||
// LogCritical("Audio", << "Failed to prepare source header. Error code " << resCode << ".");
|
||||
|
||||
return resCode == MMSYSERR_NOERROR;
|
||||
return resCode == MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
bool WmmeInputDevice::Buffer::unprepare(HWAVEIN device)
|
||||
{
|
||||
if (mHeader->dwFlags & WHDR_PREPARED)
|
||||
{
|
||||
MMRESULT resCode = waveInUnprepareHeader(device, mHeader, sizeof *mHeader);
|
||||
//if (resCode != MMSYSERR_NOERROR)
|
||||
// LogCritical("Audio", << "Failed to unprepare source header. Error code " << resCode << ".");
|
||||
return resCode == MMSYSERR_NOERROR;
|
||||
}
|
||||
return true;
|
||||
if (mHeader->dwFlags & WHDR_PREPARED)
|
||||
{
|
||||
MMRESULT resCode = waveInUnprepareHeader(device, mHeader, sizeof *mHeader);
|
||||
// if (resCode != MMSYSERR_NOERROR)
|
||||
// LogCritical("Audio", << "Failed to unprepare source header. Error code " << resCode << ".");
|
||||
return resCode == MMSYSERR_NOERROR;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WmmeInputDevice::Buffer::isFinished()
|
||||
{
|
||||
return (mHeader->dwFlags & WHDR_DONE) != 0;
|
||||
return (mHeader->dwFlags & WHDR_DONE) != 0;
|
||||
}
|
||||
|
||||
bool WmmeInputDevice::Buffer::addToDevice(HWAVEIN device)
|
||||
{
|
||||
MMRESULT resCode = waveInAddBuffer(device, mHeader, sizeof(*mHeader));
|
||||
//if (resCode != MMSYSERR_NOERROR)
|
||||
// LogCritical("Audio", << "Failed to add buffer to source audio device. Error code is " << resCode << ".");
|
||||
return resCode == MMSYSERR_NOERROR;
|
||||
MMRESULT resCode = waveInAddBuffer(device, mHeader, sizeof(*mHeader));
|
||||
// if (resCode != MMSYSERR_NOERROR)
|
||||
// LogCritical("Audio", << "Failed to add buffer to source audio device. Error code is " << resCode << ".");
|
||||
return resCode == MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
void* WmmeInputDevice::Buffer::data()
|
||||
{
|
||||
return mData;
|
||||
return mData;
|
||||
}
|
||||
|
||||
|
||||
WmmeInputDevice::WmmeInputDevice(int deviceId)
|
||||
:mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false),
|
||||
mBufferIndex(0), mDeviceIndex(deviceId), mThreadHandle(0)
|
||||
: mDevHandle(NULL), mDoneSignal(INVALID_HANDLE_VALUE), mFakeMode(false), mBufferIndex(0), mDeviceIndex(deviceId),
|
||||
mThreadHandle(0)
|
||||
{
|
||||
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mRefCount = 0;
|
||||
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mRefCount = 0;
|
||||
}
|
||||
|
||||
WmmeInputDevice::~WmmeInputDevice()
|
||||
{
|
||||
close();
|
||||
::CloseHandle(mDoneSignal);
|
||||
::CloseHandle(mShutdownSignal);
|
||||
close();
|
||||
::CloseHandle(mDoneSignal);
|
||||
::CloseHandle(mShutdownSignal);
|
||||
}
|
||||
|
||||
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;
|
||||
switch(uMsg)
|
||||
{
|
||||
case WIM_DATA:
|
||||
impl = (WmmeInputDevice*)dwInstance;
|
||||
SetEvent(impl->mDoneSignal);
|
||||
break;
|
||||
WmmeInputDevice* impl;
|
||||
switch (uMsg)
|
||||
{
|
||||
case WIM_DATA:
|
||||
impl = (WmmeInputDevice*)dwInstance;
|
||||
SetEvent(impl->mDoneSignal);
|
||||
break;
|
||||
|
||||
case WIM_CLOSE:
|
||||
break;
|
||||
case WIM_CLOSE:
|
||||
break;
|
||||
|
||||
case WIM_OPEN:
|
||||
break;
|
||||
}
|
||||
case WIM_OPEN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WmmeInputDevice::openDevice()
|
||||
{
|
||||
// Build WAVEFORMATEX structure
|
||||
WAVEFORMATEX wfx;
|
||||
memset(&wfx, 0, sizeof(wfx));
|
||||
// Build WAVEFORMATEX structure
|
||||
WAVEFORMATEX wfx;
|
||||
memset(&wfx, 0, sizeof(wfx));
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = AUDIO_CHANNELS;
|
||||
wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.cbSize = 0;
|
||||
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
|
||||
|
||||
// Open wavein
|
||||
|
||||
MMRESULT mmres = waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
if (mmres != MMSYSERR_NOERROR)
|
||||
{
|
||||
mFakeMode = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
mFakeMode = false;
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = AUDIO_CHANNELS;
|
||||
wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.cbSize = 0;
|
||||
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
|
||||
|
||||
// Create the buffers for running
|
||||
mBufferIndex = 0;
|
||||
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
mBufferList[i].prepare(mDevHandle);
|
||||
|
||||
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
mBufferList[i].addToDevice(mDevHandle);
|
||||
// Open wavein
|
||||
|
||||
/*mmres = */waveInStart(mDevHandle);
|
||||
MMRESULT mmres =
|
||||
waveInOpen(&mDevHandle, mDeviceIndex, &wfx, (DWORD_PTR)callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
if (mmres != MMSYSERR_NOERROR)
|
||||
{
|
||||
mFakeMode = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
mFakeMode = false;
|
||||
|
||||
// Create the buffers for running
|
||||
mBufferIndex = 0;
|
||||
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
mBufferList[i].prepare(mDevHandle);
|
||||
|
||||
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
mBufferList[i].addToDevice(mDevHandle);
|
||||
|
||||
/*mmres = */ waveInStart(mDevHandle);
|
||||
}
|
||||
|
||||
bool WmmeInputDevice::open()
|
||||
{
|
||||
Lock lock(mGuard);
|
||||
|
||||
mRefCount++;
|
||||
if (mRefCount > 1)
|
||||
Lock lock(mGuard);
|
||||
|
||||
mRefCount++;
|
||||
if (mRefCount > 1)
|
||||
return true;
|
||||
|
||||
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
|
||||
return true;
|
||||
|
||||
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WmmeInputDevice::closeDevice()
|
||||
{
|
||||
// Stop device
|
||||
if (mDevHandle)
|
||||
{
|
||||
MMRESULT mmres = MMSYSERR_NOERROR;
|
||||
waveInReset(mDevHandle);
|
||||
waveInStop(mDevHandle);
|
||||
}
|
||||
|
||||
// Close buffers
|
||||
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
mBufferList[i].unprepare(mDevHandle);
|
||||
|
||||
// Close device
|
||||
if (mDevHandle)
|
||||
{
|
||||
waveInClose(mDevHandle);
|
||||
mDevHandle = NULL;
|
||||
}
|
||||
// Stop device
|
||||
if (mDevHandle)
|
||||
{
|
||||
MMRESULT mmres = MMSYSERR_NOERROR;
|
||||
waveInReset(mDevHandle);
|
||||
waveInStop(mDevHandle);
|
||||
}
|
||||
|
||||
// Close buffers
|
||||
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
|
||||
mBufferList[i].unprepare(mDevHandle);
|
||||
|
||||
// Close device
|
||||
if (mDevHandle)
|
||||
{
|
||||
waveInClose(mDevHandle);
|
||||
mDevHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void WmmeInputDevice::close()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
Lock l(mGuard);
|
||||
|
||||
mRefCount--;
|
||||
if (mRefCount != 0)
|
||||
return;
|
||||
|
||||
// Set shutdown signal
|
||||
if (!mThreadHandle)
|
||||
return;
|
||||
mRefCount--;
|
||||
if (mRefCount != 0)
|
||||
return;
|
||||
|
||||
::SetEvent(mShutdownSignal);
|
||||
::WaitForSingleObject(mThreadHandle, INFINITE);
|
||||
mThreadHandle = 0;
|
||||
|
||||
// Set shutdown signal
|
||||
if (!mThreadHandle)
|
||||
return;
|
||||
|
||||
::SetEvent(mShutdownSignal);
|
||||
::WaitForSingleObject(mThreadHandle, INFINITE);
|
||||
mThreadHandle = 0;
|
||||
}
|
||||
|
||||
bool WmmeInputDevice::tryReadBuffer(void* buffer)
|
||||
{
|
||||
Buffer& devBuffer = mBufferList[mBufferIndex];
|
||||
Buffer& devBuffer = mBufferList[mBufferIndex];
|
||||
|
||||
if (!devBuffer.isFinished())
|
||||
return false;
|
||||
memcpy(buffer, devBuffer.data(), AUDIO_MIC_BUFFER_SIZE);
|
||||
devBuffer.unprepare(mDevHandle);
|
||||
devBuffer.prepare(mDevHandle);
|
||||
if (!devBuffer.addToDevice(mDevHandle))
|
||||
setFakeMode(true);
|
||||
else
|
||||
{
|
||||
}
|
||||
mBufferIndex = (mBufferIndex + 1) % AUDIO_MIC_BUFFER_COUNT;
|
||||
return true;
|
||||
if (!devBuffer.isFinished())
|
||||
return false;
|
||||
memcpy(buffer, devBuffer.data(), AUDIO_MIC_BUFFER_SIZE);
|
||||
devBuffer.unprepare(mDevHandle);
|
||||
devBuffer.prepare(mDevHandle);
|
||||
if (!devBuffer.addToDevice(mDevHandle))
|
||||
setFakeMode(true);
|
||||
else
|
||||
{
|
||||
}
|
||||
mBufferIndex = (mBufferIndex + 1) % AUDIO_MIC_BUFFER_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WmmeInputDevice::setFakeMode(bool fakeMode)
|
||||
{
|
||||
mFakeMode = fakeMode;
|
||||
mFakeMode = fakeMode;
|
||||
}
|
||||
|
||||
int WmmeInputDevice::readBuffer(void* buffer)
|
||||
{
|
||||
//Lock lock(mGuard);
|
||||
// Lock lock(mGuard);
|
||||
|
||||
if (mRefCount <= 0 || mFakeMode)
|
||||
return 0;
|
||||
|
||||
// Check for finished buffer
|
||||
while (!tryReadBuffer(buffer))
|
||||
WaitForSingleObject(mDoneSignal, 50);
|
||||
|
||||
return AUDIO_MIC_BUFFER_SIZE;
|
||||
if (mRefCount <= 0 || mFakeMode)
|
||||
return 0;
|
||||
|
||||
// Check for finished buffer
|
||||
while (!tryReadBuffer(buffer))
|
||||
WaitForSingleObject(mDoneSignal, 50);
|
||||
|
||||
return AUDIO_MIC_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
HWAVEIN WmmeInputDevice::handle()
|
||||
{
|
||||
Lock lock(mGuard);
|
||||
return mDevHandle;
|
||||
Lock lock(mGuard);
|
||||
return mDevHandle;
|
||||
}
|
||||
|
||||
void WmmeInputDevice::threadProc(void* arg)
|
||||
{
|
||||
WmmeInputDevice* impl = (WmmeInputDevice*)arg;
|
||||
impl->openDevice();
|
||||
void* buffer = _alloca(AUDIO_MIC_BUFFER_SIZE);
|
||||
WmmeInputDevice* impl = (WmmeInputDevice*)arg;
|
||||
impl->openDevice();
|
||||
void* buffer = _alloca(AUDIO_MIC_BUFFER_SIZE);
|
||||
|
||||
DWORD waitResult = 0;
|
||||
HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal};
|
||||
DWORD wr;
|
||||
do
|
||||
{
|
||||
wr = ::WaitForMultipleObjects(2, waitArray, FALSE, INFINITE);
|
||||
|
||||
if (wr == WAIT_OBJECT_0)
|
||||
DWORD waitResult = 0;
|
||||
HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal};
|
||||
DWORD wr;
|
||||
do
|
||||
{
|
||||
impl->readBuffer(buffer);
|
||||
if (impl->connection())
|
||||
impl->connection()->onMicData(Format(), buffer, AUDIO_MIC_BUFFER_SIZE);
|
||||
}
|
||||
} while (wr == WAIT_OBJECT_0);
|
||||
wr = ::WaitForMultipleObjects(2, waitArray, FALSE, INFINITE);
|
||||
|
||||
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::Buffer::Buffer()
|
||||
:mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL)
|
||||
WmmeOutputDevice::Buffer::Buffer() : mHeaderHandle(NULL), mDataHandle(NULL), mData(NULL), mHeader(NULL)
|
||||
{
|
||||
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
|
||||
if (!mHeaderHandle)
|
||||
throw Exception(ERR_NOMEM);
|
||||
mHeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
|
||||
if (!mHeaderHandle)
|
||||
throw Exception(ERR_NOMEM);
|
||||
|
||||
mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
|
||||
if (!mDataHandle)
|
||||
throw Exception(ERR_NOMEM);
|
||||
mDataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, AUDIO_SPK_BUFFER_SIZE);
|
||||
if (!mDataHandle)
|
||||
throw Exception(ERR_NOMEM);
|
||||
|
||||
mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle);
|
||||
mData = GlobalLock(mDataHandle);
|
||||
memset(mHeader, 0, sizeof *mHeader);
|
||||
mHeader->dwBufferLength = AUDIO_SPK_BUFFER_SIZE;
|
||||
mHeader->lpData = (LPSTR)mData;
|
||||
mHeader = (WAVEHDR*)GlobalLock(mHeaderHandle);
|
||||
mData = GlobalLock(mDataHandle);
|
||||
memset(mHeader, 0, sizeof *mHeader);
|
||||
mHeader->dwBufferLength = AUDIO_SPK_BUFFER_SIZE;
|
||||
mHeader->lpData = (LPSTR)mData;
|
||||
}
|
||||
|
||||
WmmeOutputDevice::Buffer::~Buffer()
|
||||
{
|
||||
if (mHeaderHandle)
|
||||
{
|
||||
GlobalUnlock(mHeaderHandle);
|
||||
GlobalFree(mHeaderHandle);
|
||||
}
|
||||
if (mDataHandle)
|
||||
{
|
||||
GlobalUnlock(mDataHandle);
|
||||
GlobalFree(mDataHandle);
|
||||
}
|
||||
if (mHeaderHandle)
|
||||
{
|
||||
GlobalUnlock(mHeaderHandle);
|
||||
GlobalFree(mHeaderHandle);
|
||||
}
|
||||
if (mDataHandle)
|
||||
{
|
||||
GlobalUnlock(mDataHandle);
|
||||
GlobalFree(mDataHandle);
|
||||
}
|
||||
}
|
||||
|
||||
bool WmmeOutputDevice::Buffer::prepare(HWAVEOUT device)
|
||||
{
|
||||
MMRESULT result;
|
||||
result = ::waveOutPrepareHeader(device, mHeader, sizeof *mHeader);
|
||||
return result == MMSYSERR_NOERROR;
|
||||
MMRESULT result;
|
||||
result = ::waveOutPrepareHeader(device, mHeader, sizeof *mHeader);
|
||||
return result == MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
bool WmmeOutputDevice::Buffer::unprepare(HWAVEOUT device)
|
||||
{
|
||||
MMRESULT result;
|
||||
result = ::waveOutUnprepareHeader(device, mHeader, sizeof *mHeader);
|
||||
return result == MMSYSERR_NOERROR;
|
||||
MMRESULT result;
|
||||
result = ::waveOutUnprepareHeader(device, mHeader, sizeof *mHeader);
|
||||
return result == MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
bool WmmeOutputDevice::Buffer::write(HWAVEOUT device)
|
||||
{
|
||||
MMRESULT result;
|
||||
result = ::waveOutWrite(device, mHeader, sizeof *mHeader);
|
||||
return result == MMSYSERR_NOERROR;
|
||||
MMRESULT result;
|
||||
result = ::waveOutWrite(device, mHeader, sizeof *mHeader);
|
||||
return result == MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
WmmeOutputDevice::WmmeOutputDevice(int index)
|
||||
:mDevice(NULL), mDeviceIndex(index), mPlayedTime(0), mPlayedCount(0), mBufferIndex(0), mThreadHandle(NULL),
|
||||
mFailed(false), mShutdownMarker(false)
|
||||
: mDevice(NULL), mDeviceIndex(index), mPlayedTime(0), mPlayedCount(0), mBufferIndex(0), mThreadHandle(NULL),
|
||||
mFailed(false), mShutdownMarker(false)
|
||||
{
|
||||
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
}
|
||||
|
||||
|
||||
WmmeOutputDevice::~WmmeOutputDevice()
|
||||
{
|
||||
close();
|
||||
close();
|
||||
|
||||
// Destroy used signals
|
||||
CloseHandle(mDoneSignal); CloseHandle(mShutdownSignal);
|
||||
// Destroy used signals
|
||||
CloseHandle(mDoneSignal);
|
||||
CloseHandle(mShutdownSignal);
|
||||
}
|
||||
|
||||
bool WmmeOutputDevice::open()
|
||||
{
|
||||
// Start thread
|
||||
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
|
||||
return true;
|
||||
// Start thread
|
||||
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WmmeOutputDevice::close()
|
||||
{
|
||||
// Tell the thread to exit
|
||||
SetEvent(mShutdownSignal);
|
||||
mShutdownMarker = true;
|
||||
// Tell the thread to exit
|
||||
SetEvent(mShutdownSignal);
|
||||
mShutdownMarker = true;
|
||||
|
||||
// Wait for thread
|
||||
if (mThreadHandle)
|
||||
WaitForSingleObject(mThreadHandle, INFINITE);
|
||||
mThreadHandle = 0;
|
||||
// Wait for thread
|
||||
if (mThreadHandle)
|
||||
WaitForSingleObject(mThreadHandle, INFINITE);
|
||||
mThreadHandle = 0;
|
||||
}
|
||||
|
||||
void WmmeOutputDevice::openDevice()
|
||||
{
|
||||
mClosing = false;
|
||||
MMRESULT mmres = 0;
|
||||
WAVEFORMATEX wfx;
|
||||
memset(&wfx, 0, sizeof(wfx));
|
||||
wfx.wFormatTag = 0x0001;
|
||||
wfx.nChannels = AUDIO_CHANNELS;
|
||||
wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.cbSize = 0;
|
||||
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
|
||||
mClosing = false;
|
||||
MMRESULT mmres = 0;
|
||||
WAVEFORMATEX wfx;
|
||||
memset(&wfx, 0, sizeof(wfx));
|
||||
wfx.wFormatTag = 0x0001;
|
||||
wfx.nChannels = AUDIO_CHANNELS;
|
||||
wfx.nSamplesPerSec = AUDIO_SAMPLERATE;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.cbSize = 0;
|
||||
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
|
||||
|
||||
mmres = waveOutOpen(&mDevice, mDeviceIndex, &wfx, (DWORD_PTR)&callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
if (mmres != MMSYSERR_NOERROR)
|
||||
throw Exception(ERR_WMME_FAILED, mmres);
|
||||
mmres = waveOutOpen(&mDevice, mDeviceIndex, &wfx, (DWORD_PTR)&callbackProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
if (mmres != MMSYSERR_NOERROR)
|
||||
throw Exception(ERR_WMME_FAILED, mmres);
|
||||
|
||||
// Prebuffer silence
|
||||
for (unsigned i=0; i<AUDIO_SPK_BUFFER_COUNT; i++)
|
||||
{
|
||||
//bool dumb = false;
|
||||
//mCallback(mBufferList[i].mData, SPK_BUFFER_SIZE, dumb, dumb);
|
||||
memset(mBufferList[i].mData, 0, AUDIO_SPK_BUFFER_SIZE);
|
||||
mBufferList[i].prepare(mDevice);
|
||||
mBufferList[i].write(mDevice);
|
||||
}
|
||||
// Prebuffer silence
|
||||
for (unsigned i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
|
||||
{
|
||||
// bool dumb = false;
|
||||
// mCallback(mBufferList[i].mData, SPK_BUFFER_SIZE, dumb, dumb);
|
||||
memset(mBufferList[i].mData, 0, AUDIO_SPK_BUFFER_SIZE);
|
||||
mBufferList[i].prepare(mDevice);
|
||||
mBufferList[i].write(mDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WmmeOutputDevice::closeDevice()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
Lock l(mGuard);
|
||||
|
||||
mClosing = true;
|
||||
bool finished = false;
|
||||
while (!finished)
|
||||
{
|
||||
WaitForSingleObject(mDoneSignal, 10);
|
||||
finished = areBuffersFinished();
|
||||
}
|
||||
mClosing = true;
|
||||
bool finished = false;
|
||||
while (!finished)
|
||||
{
|
||||
WaitForSingleObject(mDoneSignal, 10);
|
||||
finished = areBuffersFinished();
|
||||
}
|
||||
|
||||
if (mDevice)
|
||||
{
|
||||
waveOutReset(mDevice);
|
||||
waveOutClose(mDevice);
|
||||
}
|
||||
|
||||
mDevice = NULL;
|
||||
if (mDevice)
|
||||
{
|
||||
waveOutReset(mDevice);
|
||||
waveOutClose(mDevice);
|
||||
}
|
||||
|
||||
mDevice = NULL;
|
||||
}
|
||||
|
||||
bool WmmeOutputDevice::areBuffersFinished()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
bool result = true;
|
||||
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)
|
||||
Lock l(mGuard);
|
||||
bool result = true;
|
||||
for (unsigned i = 0; i < AUDIO_SPK_BUFFER_COUNT && result; i++)
|
||||
{
|
||||
/* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED)
|
||||
mBufferList[i].Unprepare(mDevice); */
|
||||
bool finished = mBufferList[i].mHeader->dwFlags & WHDR_DONE || !mBufferList[i].mHeader->dwFlags;
|
||||
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)
|
||||
{
|
||||
WmmeOutputDevice* impl = (WmmeOutputDevice*)arg;
|
||||
impl->openDevice();
|
||||
WmmeOutputDevice* impl = (WmmeOutputDevice*)arg;
|
||||
impl->openDevice();
|
||||
|
||||
DWORD waitResult = 0;
|
||||
HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal};
|
||||
unsigned index, i;
|
||||
unsigned exitCount = 0;
|
||||
bool exitSignal = false;
|
||||
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++)
|
||||
DWORD waitResult = 0;
|
||||
HANDLE waitArray[2] = {impl->mDoneSignal, impl->mShutdownSignal};
|
||||
unsigned index, i;
|
||||
unsigned exitCount = 0;
|
||||
bool exitSignal = false;
|
||||
do
|
||||
{
|
||||
index = (impl->mBufferIndex + i) % AUDIO_SPK_BUFFER_COUNT;
|
||||
Buffer& buffer = impl->mBufferList[index];
|
||||
if (!(buffer.mHeader->dwFlags & WHDR_DONE))
|
||||
break;
|
||||
|
||||
buffer.unprepare(impl->mDevice);
|
||||
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();
|
||||
// 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;
|
||||
Buffer& buffer = impl->mBufferList[index];
|
||||
if (!(buffer.mHeader->dwFlags & WHDR_DONE))
|
||||
break;
|
||||
|
||||
buffer.unprepare(impl->mDevice);
|
||||
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()
|
||||
{
|
||||
return mDevice;
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
unsigned WmmeOutputDevice::playedTime()
|
||||
{
|
||||
if (!mDevice)
|
||||
return 0;
|
||||
unsigned result = 0;
|
||||
if (!mDevice)
|
||||
return 0;
|
||||
unsigned result = 0;
|
||||
|
||||
MMTIME mmt;
|
||||
memset(&mmt, 0, sizeof(mmt));
|
||||
mmt.wType = TIME_SAMPLES;
|
||||
MMRESULT rescode = waveOutGetPosition(mDevice, &mmt, sizeof(mmt));
|
||||
if (rescode != MMSYSERR_NOERROR || mmt.wType != TIME_SAMPLES)
|
||||
closeDevice();
|
||||
else
|
||||
{
|
||||
if (mmt.u.ms < mPlayedTime)
|
||||
result = 0;
|
||||
MMTIME mmt;
|
||||
memset(&mmt, 0, sizeof(mmt));
|
||||
mmt.wType = TIME_SAMPLES;
|
||||
MMRESULT rescode = waveOutGetPosition(mDevice, &mmt, sizeof(mmt));
|
||||
if (rescode != MMSYSERR_NOERROR || mmt.wType != TIME_SAMPLES)
|
||||
closeDevice();
|
||||
else
|
||||
{
|
||||
result = mmt.u.ms - mPlayedTime;
|
||||
mPlayedTime = mmt.u.ms - result % 8;
|
||||
if (mmt.u.ms < mPlayedTime)
|
||||
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)
|
||||
{
|
||||
closeDevice();
|
||||
closeDevice();
|
||||
}
|
||||
|
||||
bool WmmeOutputDevice::fakeMode()
|
||||
{
|
||||
return mFailed;
|
||||
return mFailed;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
impl = (WmmeOutputDevice*)dwInstance;
|
||||
InterlockedIncrement(&impl->mPlayedCount);
|
||||
SetEvent(impl->mDoneSignal);
|
||||
}
|
||||
if (msg == WOM_DONE)
|
||||
{
|
||||
impl = (WmmeOutputDevice*)dwInstance;
|
||||
InterlockedIncrement(&impl->mPlayedCount);
|
||||
SetEvent(impl->mDoneSignal);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,118 +30,118 @@
|
||||
namespace Audio
|
||||
{
|
||||
|
||||
class WmmeInputDevice: public InputDevice
|
||||
{
|
||||
public:
|
||||
class WmmeInputDevice : public InputDevice
|
||||
{
|
||||
public:
|
||||
WmmeInputDevice(int index);
|
||||
~WmmeInputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakeMode);
|
||||
|
||||
int readBuffer(void* buffer);
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool fakeMode();
|
||||
void setFakeMode(bool fakeMode);
|
||||
|
||||
int readBuffer(void* buffer);
|
||||
HWAVEIN handle();
|
||||
|
||||
protected:
|
||||
protected:
|
||||
class Buffer
|
||||
{
|
||||
public:
|
||||
Buffer();
|
||||
~Buffer();
|
||||
bool prepare(HWAVEIN device);
|
||||
bool unprepare(HWAVEIN device);
|
||||
bool isFinished();
|
||||
bool addToDevice(HWAVEIN device);
|
||||
void* data();
|
||||
Buffer();
|
||||
~Buffer();
|
||||
bool prepare(HWAVEIN device);
|
||||
bool unprepare(HWAVEIN device);
|
||||
bool isFinished();
|
||||
bool addToDevice(HWAVEIN device);
|
||||
void* data();
|
||||
|
||||
protected:
|
||||
HGLOBAL mDataHandle;
|
||||
void* mData;
|
||||
HGLOBAL mHeaderHandle;
|
||||
WAVEHDR* mHeader;
|
||||
HGLOBAL mDataHandle;
|
||||
void* mData;
|
||||
HGLOBAL mHeaderHandle;
|
||||
WAVEHDR* mHeader;
|
||||
};
|
||||
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
HWAVEIN mDevHandle; /// Handle of opened capture device.
|
||||
HANDLE mThreadHandle;
|
||||
HANDLE mShutdownSignal;
|
||||
HANDLE mDoneSignal; /// Event handle to signal about finished capture.
|
||||
Buffer mBufferList[AUDIO_MIC_BUFFER_COUNT];
|
||||
unsigned mBufferIndex;
|
||||
int mDeviceIndex; /// Index of capture device.
|
||||
volatile bool mFakeMode; /// Marks if fake mode is active.
|
||||
int mRefCount;
|
||||
Mutex mGuard; /// Mutex to protect this instance.
|
||||
HWAVEIN mDevHandle; /// Handle of opened capture device.
|
||||
HANDLE mThreadHandle;
|
||||
HANDLE mShutdownSignal;
|
||||
HANDLE mDoneSignal; /// Event handle to signal about finished capture.
|
||||
Buffer mBufferList[AUDIO_MIC_BUFFER_COUNT];
|
||||
unsigned mBufferIndex;
|
||||
int mDeviceIndex; /// Index of capture device.
|
||||
volatile bool mFakeMode; /// Marks if fake mode is active.
|
||||
int mRefCount;
|
||||
|
||||
bool tryReadBuffer(void* buffer);
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
bool tryReadBuffer(void* buffer);
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
|
||||
static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
|
||||
static void threadProc(void* arg);
|
||||
};
|
||||
static void CALLBACK callbackProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
|
||||
DWORD_PTR dwParam2);
|
||||
static void threadProc(void* arg);
|
||||
};
|
||||
|
||||
class WmmeOutputDevice: public OutputDevice
|
||||
{
|
||||
public:
|
||||
class WmmeOutputDevice : public OutputDevice
|
||||
{
|
||||
public:
|
||||
WmmeOutputDevice(int index);
|
||||
~WmmeOutputDevice();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
HWAVEOUT handle();
|
||||
unsigned playedTime();
|
||||
void setFakeMode(bool fakemode);
|
||||
bool fakeMode();
|
||||
bool closing();
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
protected:
|
||||
HWAVEOUT handle();
|
||||
unsigned playedTime();
|
||||
void setFakeMode(bool fakemode);
|
||||
bool fakeMode();
|
||||
bool closing();
|
||||
|
||||
protected:
|
||||
class Buffer
|
||||
{
|
||||
friend class WmmeOutputDevice;
|
||||
friend class WmmeOutputDevice;
|
||||
|
||||
public:
|
||||
Buffer();
|
||||
~Buffer();
|
||||
bool prepare(HWAVEOUT device);
|
||||
bool unprepare(HWAVEOUT device);
|
||||
bool write(HWAVEOUT device);
|
||||
Buffer();
|
||||
~Buffer();
|
||||
bool prepare(HWAVEOUT device);
|
||||
bool unprepare(HWAVEOUT device);
|
||||
bool write(HWAVEOUT device);
|
||||
|
||||
protected:
|
||||
WAVEHDR* mHeader;
|
||||
void* mData;
|
||||
HGLOBAL mHeaderHandle;
|
||||
HGLOBAL mDataHandle;
|
||||
WAVEHDR* mHeader;
|
||||
void* mData;
|
||||
HGLOBAL mHeaderHandle;
|
||||
HGLOBAL mDataHandle;
|
||||
};
|
||||
|
||||
Mutex mGuard; /// Mutex to protect this instance
|
||||
int mDeviceIndex;
|
||||
HWAVEOUT mDevice; /// Handle of opened audio device
|
||||
Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT];
|
||||
unsigned mPlayedTime; /// Amount of played time in milliseconds
|
||||
bool mClosing;
|
||||
HANDLE mDoneSignal,
|
||||
mShutdownSignal,
|
||||
mThreadHandle;
|
||||
volatile bool mShutdownMarker;
|
||||
|
||||
volatile LONG mPlayedCount;
|
||||
unsigned mBufferIndex;
|
||||
bool mFailed;
|
||||
Mutex mGuard; /// Mutex to protect this instance
|
||||
int mDeviceIndex;
|
||||
HWAVEOUT mDevice; /// Handle of opened audio device
|
||||
Buffer mBufferList[AUDIO_SPK_BUFFER_COUNT];
|
||||
unsigned mPlayedTime; /// Amount of played time in milliseconds
|
||||
bool mClosing;
|
||||
HANDLE mDoneSignal, mShutdownSignal, mThreadHandle;
|
||||
volatile bool mShutdownMarker;
|
||||
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
bool areBuffersFinished();
|
||||
volatile LONG mPlayedCount;
|
||||
unsigned mBufferIndex;
|
||||
bool mFailed;
|
||||
|
||||
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
|
||||
static void threadProc(void* arg);
|
||||
};
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
bool areBuffersFinished();
|
||||
|
||||
static void CALLBACK callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1,
|
||||
DWORD_PTR dwParam2);
|
||||
static void threadProc(void* arg);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
} // namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
#include "Audio_iOS.h"
|
||||
|
||||
|
||||
@@ -1,38 +1,36 @@
|
||||
#ifndef __AUDIO_IOS
|
||||
#define __AUDIO_IOS
|
||||
|
||||
class IosInputDevice: public InputDevice
|
||||
class IosInputDevice : public InputDevice
|
||||
{
|
||||
protected:
|
||||
|
||||
public:
|
||||
IosInputDevice();
|
||||
~IosInputDevice();
|
||||
|
||||
|
||||
|
||||
void open();
|
||||
void close();
|
||||
IosInputDevice();
|
||||
~IosInputDevice();
|
||||
|
||||
|
||||
void open();
|
||||
void close();
|
||||
};
|
||||
|
||||
class IosOutputDevice: public OutputDevice
|
||||
class IosOutputDevice : public OutputDevice
|
||||
{
|
||||
protected:
|
||||
public:
|
||||
IosOutputDevice();
|
||||
~IosOutputDevice();
|
||||
enum
|
||||
{
|
||||
Receiver,
|
||||
Speaker,
|
||||
Bluetooth
|
||||
};
|
||||
|
||||
int route();
|
||||
void setRoute(int route);
|
||||
|
||||
void open();
|
||||
void close();
|
||||
IosOutputDevice();
|
||||
~IosOutputDevice();
|
||||
enum
|
||||
{
|
||||
Receiver,
|
||||
Speaker,
|
||||
Bluetooth
|
||||
};
|
||||
|
||||
int route();
|
||||
void setRoute(int route);
|
||||
|
||||
void open();
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -15,49 +15,46 @@
|
||||
#define CONFIG(X) mConfig->at(X)
|
||||
#define CONFIG_EXISTS(X) mConfig->exists(X)
|
||||
|
||||
//#define MODIFY_VIA_BEHIND_NAT
|
||||
// #define MODIFY_VIA_BEHIND_NAT
|
||||
|
||||
// NAT decorator
|
||||
class NATDecorator: public resip::MessageDecorator
|
||||
class NATDecorator : public resip::MessageDecorator
|
||||
{
|
||||
protected:
|
||||
UserAgent& mUserAgent;
|
||||
UserAgent& mUserAgent;
|
||||
resip::SipMessage mMessage;
|
||||
|
||||
resip::Data mViaHost;
|
||||
unsigned short mViaPort;
|
||||
resip::Data mViaHost;
|
||||
unsigned short mViaPort;
|
||||
|
||||
resip::Data mContactsHost;
|
||||
resip::Data mContactsScheme;
|
||||
unsigned short mContactsPort;
|
||||
resip::Data mContactsHost;
|
||||
resip::Data mContactsScheme;
|
||||
unsigned short mContactsPort;
|
||||
|
||||
public:
|
||||
NATDecorator(UserAgent& endpoint);
|
||||
virtual ~NATDecorator();
|
||||
|
||||
virtual void decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, const resip::Data& sigcompId);
|
||||
virtual void rollbackMessage(resip::SipMessage& msg);
|
||||
virtual void decorateMessage(resip::SipMessage& msg, const resip::Tuple& source, const resip::Tuple& destination,
|
||||
const resip::Data& sigcompId);
|
||||
virtual void rollbackMessage(resip::SipMessage& msg);
|
||||
virtual MessageDecorator* clone() const;
|
||||
};
|
||||
|
||||
|
||||
NATDecorator::NATDecorator(UserAgent& ua)
|
||||
:mUserAgent(ua), mViaPort(0), mContactsPort(0)
|
||||
{
|
||||
}
|
||||
NATDecorator::NATDecorator(UserAgent& ua) : mUserAgent(ua), mViaPort(0), mContactsPort(0) {}
|
||||
|
||||
NATDecorator::~NATDecorator()
|
||||
{
|
||||
}
|
||||
NATDecorator::~NATDecorator() {}
|
||||
|
||||
void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &source, const resip::Tuple &destination, 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
|
||||
mMessage = msg;
|
||||
|
||||
std::stringstream dump;
|
||||
mMessage.encode(dump);
|
||||
//ICELogDebug(<< "Decorating message: \n" << dump.str());
|
||||
// ICELogDebug(<< "Decorating message: \n" << dump.str());
|
||||
|
||||
// Check From: header and find the account
|
||||
resip::NameAddr from;
|
||||
@@ -101,11 +98,11 @@ void NATDecorator::decorateMessage(resip::SipMessage &msg, const resip::Tuple &s
|
||||
uri.port() = account->mExternalAddress.port();
|
||||
if (account->mConfig->at(CONFIG_SIPS).asBool())
|
||||
{
|
||||
//uri.scheme() = "sips";
|
||||
//uri.param(resip::p_transport) = "tls";
|
||||
// uri.scheme() = "sips";
|
||||
// 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
|
||||
resip::NameAddr from = msg.header(resip::h_From);
|
||||
PAccount account = mUserAgent.getAccount(from);
|
||||
PAccount account = mUserAgent.getAccount(from);
|
||||
if (!account)
|
||||
return;
|
||||
|
||||
@@ -141,7 +138,7 @@ void NATDecorator::rollbackMessage(resip::SipMessage& msg)
|
||||
{
|
||||
uri.host() = mContactsHost;
|
||||
uri.port() = mContactsPort;
|
||||
//uri.scheme() = mContactsScheme;
|
||||
// uri.scheme() = mContactsScheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,19 +150,16 @@ resip::MessageDecorator* NATDecorator::clone() const
|
||||
}
|
||||
|
||||
Account::Account(PVariantMap config, UserAgent& agent)
|
||||
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
|
||||
mRegistration(NULL)
|
||||
: mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None), mRegistration(NULL)
|
||||
{
|
||||
mProfile = std::make_shared<resip::UserProfile>(agent.mProfile);
|
||||
mId = Account::generateId();
|
||||
setup(*config);
|
||||
}
|
||||
|
||||
Account::~Account()
|
||||
{
|
||||
}
|
||||
Account::~Account() {}
|
||||
|
||||
void Account::setup(VariantMap &config)
|
||||
void Account::setup(VariantMap& config)
|
||||
{
|
||||
// Credentials
|
||||
|
||||
@@ -176,8 +170,8 @@ void Account::setup(VariantMap &config)
|
||||
mProfile->setDigestCredential(resip::Data(config[CONFIG_DOMAIN].asStdString()),
|
||||
resip::Data(config[CONFIG_USERNAME].asStdString()),
|
||||
resip::Data(config[CONFIG_PASSWORD].asStdString()));
|
||||
ICELogInfo( << "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() <<
|
||||
", username to " << config[CONFIG_USERNAME].asStdString());
|
||||
ICELogInfo(<< "Credentials are set to domain " << config[CONFIG_DOMAIN].asStdString() << ", username to "
|
||||
<< config[CONFIG_USERNAME].asStdString());
|
||||
|
||||
// Proxy
|
||||
mProfile->unsetOutboundProxy();
|
||||
@@ -234,7 +228,7 @@ void Account::setup(VariantMap &config)
|
||||
if (config[CONFIG_DOMAINPORT].asInt() != 0)
|
||||
from.uri().port() = config[CONFIG_DOMAINPORT].asInt();
|
||||
else
|
||||
from.uri().port();// = 5060;
|
||||
from.uri().port(); // = 5060;
|
||||
|
||||
from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString());
|
||||
from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString());
|
||||
@@ -266,10 +260,12 @@ void Account::start()
|
||||
|
||||
// Create registration
|
||||
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++)
|
||||
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;
|
||||
|
||||
@@ -278,10 +274,10 @@ void Account::start()
|
||||
|
||||
// Check if STUN IP is required
|
||||
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)
|
||||
{
|
||||
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.isTimeToSend();
|
||||
queryStunServerIp();
|
||||
@@ -305,11 +301,10 @@ void Account::stop()
|
||||
mRegistrationHandle->removeAll();
|
||||
mRegistrationHandle = resip::ClientRegistrationHandle();
|
||||
}
|
||||
else
|
||||
if (mRegistration)
|
||||
{
|
||||
mRegistration->end();
|
||||
}
|
||||
else if (mRegistration)
|
||||
{
|
||||
mRegistration->end();
|
||||
}
|
||||
mRegistration = NULL;
|
||||
mRegistrationState = RegistrationState::None;
|
||||
}
|
||||
@@ -361,7 +356,8 @@ void Account::publishPresence(bool online, const std::string& content, int secon
|
||||
if (mPublication.isValid())
|
||||
mPublication->update(&p);
|
||||
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()
|
||||
@@ -381,14 +377,14 @@ PClientObserver Account::observe(const std::string& target, const std::string& p
|
||||
observer->mPeer = target;
|
||||
|
||||
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))
|
||||
expires = CONFIG(CONFIG_SUBSCRIPTION_TIME).asInt();
|
||||
if (mConfig->exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
|
||||
refresh = CONFIG(CONFIG_SUBSCRIPTION_REFRESHTIME).asInt();
|
||||
|
||||
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile,
|
||||
resip::Data(package), expires, refresh, observer->mSession);
|
||||
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(target)), mProfile, resip::Data(package), expires,
|
||||
refresh, observer->mSession);
|
||||
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
|
||||
|
||||
mAgent.mClientObserverMap[observer->mSessionId] = observer;
|
||||
@@ -406,15 +402,17 @@ int Account::sendMsg(const std::string& peer, const void* ptr, unsigned length,
|
||||
s->setRemoteAddress(peer);
|
||||
|
||||
// Find MIME type
|
||||
resip::Mime type;
|
||||
resip::Mime type;
|
||||
std::string::size_type p = mime.find('/');
|
||||
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
|
||||
type = resip::Mime(resip::Data(mime), resip::Data());
|
||||
|
||||
resip::ClientPagerMessageHandle msgHandle = 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));
|
||||
resip::ClientPagerMessageHandle msgHandle =
|
||||
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();
|
||||
msgHandle->page(std::move(contentPtr));
|
||||
|
||||
@@ -451,12 +449,13 @@ resip::NameAddr Account::contact(SecureScheme ss)
|
||||
|
||||
void Account::queryStunServerIp()
|
||||
{
|
||||
ICELogInfo(<<"Looking for STUN/TURN server IP");
|
||||
ICELogInfo(<< "Looking for STUN/TURN server IP");
|
||||
|
||||
if (!mConfig->exists(CONFIG_STUNSERVER_NAME))
|
||||
{
|
||||
// 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
|
||||
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;
|
||||
addr.setIp(mConfig->at(CONFIG_STUNSERVER_IP).asStdString());
|
||||
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.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool();
|
||||
//config.mDetectNetworkChange = true;
|
||||
//config.mNetworkCheckInterval = 5000;
|
||||
// config.mDetectNetworkChange = true;
|
||||
// config.mNetworkCheckInterval = 5000;
|
||||
|
||||
session->mIceStack = std::shared_ptr<ice::Stack>(ice::Stack::makeICEBox(config));
|
||||
session->mIceStack->setEventHandler(session, this);
|
||||
@@ -509,7 +508,7 @@ void Account::process()
|
||||
queryStunServerIp();
|
||||
}
|
||||
|
||||
void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
|
||||
void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
|
||||
{
|
||||
// Save registration handle
|
||||
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()));
|
||||
|
||||
// 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
|
||||
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
|
||||
int sentPort = via.sentPort();
|
||||
int sentPort = via.sentPort();
|
||||
|
||||
const resip::Data& sourceHost = response.getSource().toData(resip::UDP);
|
||||
int rport = 0;
|
||||
int rport = 0;
|
||||
if (via.exists(resip::p_rport))
|
||||
rport = via.param(resip::p_rport).port();
|
||||
|
||||
@@ -553,13 +552,14 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||
}
|
||||
|
||||
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
|
||||
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();
|
||||
// Unregister at first
|
||||
// mRegistrationHandle->requestRefresh();
|
||||
// Unregister at first
|
||||
mRegistrationHandle->removeAll();
|
||||
mRegistrationState = RegistrationState::Reregistering;
|
||||
return;
|
||||
@@ -570,12 +570,12 @@ void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||
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
|
||||
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());
|
||||
hostport.host() = resip::Data(mExternalAddress.ip());
|
||||
@@ -585,19 +585,24 @@ void Account::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessa
|
||||
const char* transportName = nullptr;
|
||||
switch (mUsedTransport)
|
||||
{
|
||||
case resip::TCP: transportName = "tcp"; break;
|
||||
case resip::TLS: transportName = "tls"; break;
|
||||
case resip::TCP:
|
||||
transportName = "tcp";
|
||||
break;
|
||||
case resip::TLS:
|
||||
transportName = "tls";
|
||||
break;
|
||||
}
|
||||
|
||||
hostport.param(resip::p_transport) = resip::Data(transportName);
|
||||
}
|
||||
mProfile->setOverrideHostAndPort(hostport);
|
||||
//mProfile->setDefaultFrom(from);
|
||||
// mProfile->setDefaultFrom(from);
|
||||
}
|
||||
mProfile->setRegId(mConfig->at(CONFIG_REGID).asInt());
|
||||
auto regmessage = mAgent.mDum->makeRegistration(mProfile->getDefaultFrom(), mProfile, UA_REGISTRATION_TIME);
|
||||
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);
|
||||
return;
|
||||
@@ -625,16 +630,17 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
|
||||
if (result.status == 0)
|
||||
{
|
||||
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());
|
||||
|
||||
// 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)
|
||||
mAgent.onAccountStart(mAgent.getAccount(this));
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -654,14 +657,14 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
||||
{
|
||||
// Find lowest priority
|
||||
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)
|
||||
priority = result.records[i].priority();
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -674,15 +677,15 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
||||
|
||||
const char* host = 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());
|
||||
ICELogInfo(<< "Success to find STUN/TURN server on " << result.records[index].target().c_str() << ":"
|
||||
<< (int)result.records[index].port());
|
||||
|
||||
|
||||
if (inet_addr(host) == INADDR_NONE)
|
||||
{
|
||||
// Try to resolve domain name now
|
||||
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
|
||||
{
|
||||
@@ -691,27 +694,20 @@ void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
|
||||
}
|
||||
else
|
||||
{
|
||||
ICELogError( << "Failed to find STUN or TURN service for specified domain.");
|
||||
//mAgent::shutdown();
|
||||
ICELogError(<< "Failed to find STUN or TURN service for specified domain.");
|
||||
// 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 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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
void Account::setUserInfo(const UserInfo &info)
|
||||
void Account::setUserInfo(const UserInfo& info)
|
||||
{
|
||||
mUserInfo = info;
|
||||
if (mRegistrationHandle.isValid())
|
||||
@@ -740,7 +736,7 @@ Account::UserInfo Account::getUserInfo() const
|
||||
}
|
||||
|
||||
std::atomic_int Account::IdGenerator;
|
||||
int Account::generateId()
|
||||
int Account::generateId()
|
||||
{
|
||||
return ++IdGenerator;
|
||||
}
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
class UserAgent;
|
||||
class Session;
|
||||
|
||||
class Account: public resip::DnsResultSink
|
||||
class Account : public resip::DnsResultSink
|
||||
{
|
||||
friend class UserAgent;
|
||||
friend class NATDecorator;
|
||||
|
||||
public:
|
||||
Account(PVariantMap config, UserAgent& agent);
|
||||
~Account();
|
||||
@@ -34,7 +35,7 @@ public:
|
||||
void stop();
|
||||
void refresh();
|
||||
bool active();
|
||||
int id() const;
|
||||
int id() const;
|
||||
|
||||
enum class RegistrationState
|
||||
{
|
||||
@@ -47,69 +48,69 @@ public:
|
||||
RegistrationState registrationState();
|
||||
|
||||
/* 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 */
|
||||
void stopPublish();
|
||||
void stopPublish();
|
||||
|
||||
/* 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. */
|
||||
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> */
|
||||
std::string name();
|
||||
|
||||
/* Updates account with configuration */
|
||||
void setup(VariantMap& config);
|
||||
void setup(VariantMap& config);
|
||||
|
||||
/* 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;
|
||||
void setUserInfo(const UserInfo& info);
|
||||
UserInfo getUserInfo() const;
|
||||
void setUserInfo(const UserInfo& info);
|
||||
UserInfo getUserInfo() const;
|
||||
|
||||
protected:
|
||||
PVariantMap mConfig;
|
||||
PVariantMap mConfig;
|
||||
|
||||
// Registration
|
||||
ResipSession* mRegistration;
|
||||
resip::ClientRegistrationHandle mRegistrationHandle;
|
||||
resip::ClientPublicationHandle mPublication;
|
||||
resip::TransportType mUsedTransport;
|
||||
ResipSession* mRegistration;
|
||||
resip::ClientRegistrationHandle mRegistrationHandle;
|
||||
resip::ClientPublicationHandle mPublication;
|
||||
resip::TransportType mUsedTransport;
|
||||
|
||||
RegistrationState mRegistrationState;
|
||||
RegistrationState mRegistrationState;
|
||||
|
||||
ice::NetworkAddress mExternalAddress;
|
||||
ice::NetworkAddress mExternalAddress;
|
||||
std::shared_ptr<resip::UserProfile> mProfile;
|
||||
UserAgent& mAgent;
|
||||
bool mPresenceOnline;
|
||||
std::string mPresenceContent;
|
||||
UserAgent& mAgent;
|
||||
bool mPresenceOnline;
|
||||
std::string mPresenceContent;
|
||||
|
||||
// Timer to refresh STUN server IP
|
||||
ice::ICEScheduleTimer mRefreshStunServerIpTimer;
|
||||
ice::ICEScheduleTimer mRefreshStunServerIpTimer;
|
||||
|
||||
// Cached auth
|
||||
resip::Auth mCachedAuth;
|
||||
resip::Auth mCachedAuth;
|
||||
|
||||
// Id of account
|
||||
int mId;
|
||||
int mId;
|
||||
|
||||
// User info about current state
|
||||
UserInfo mUserInfo;
|
||||
UserInfo mUserInfo;
|
||||
|
||||
// List of client subscriptions sent from this account
|
||||
typedef std::set<PClientObserver> ClientObserverSet;
|
||||
ClientObserverSet mClientObserverSet;
|
||||
typedef std::set<PClientObserver> ClientObserverSet;
|
||||
ClientObserverSet mClientObserverSet;
|
||||
|
||||
|
||||
void process();
|
||||
void process();
|
||||
// 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
|
||||
{
|
||||
SipsAndTls,
|
||||
@@ -121,10 +122,10 @@ protected:
|
||||
resip::NameAddr contact(SecureScheme ss = SecureScheme::SipsOnly);
|
||||
|
||||
// This method prepares configuration, creates ice stack and sets ownership to session
|
||||
void prepareIceStack(Session* session, ice::AgentRole role);
|
||||
void onSuccess(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 prepareIceStack(Session* session, ice::AgentRole role);
|
||||
void onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
|
||||
void onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response);
|
||||
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response);
|
||||
|
||||
#pragma region DnsResultSink implementation
|
||||
void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&);
|
||||
@@ -134,7 +135,7 @@ protected:
|
||||
void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&);
|
||||
#pragma endregion
|
||||
|
||||
static int generateId();
|
||||
static int generateId();
|
||||
static std::atomic_int IdGenerator;
|
||||
};
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#define LOG_SUBSYSTEM "engine"
|
||||
|
||||
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
|
||||
:mUserAgent(agent), mTerminal(terminal), mState(0),
|
||||
mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
|
||||
: mUserAgent(agent), mTerminal(terminal), mState(0), mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
|
||||
{
|
||||
mActive = mfActive;
|
||||
mRemoteState = msSendRecv;
|
||||
@@ -26,12 +25,11 @@ AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
|
||||
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
|
||||
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
|
||||
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()
|
||||
{
|
||||
@@ -47,7 +45,7 @@ std::string AudioProvider::streamProfile()
|
||||
}
|
||||
|
||||
// Sets destination IP address
|
||||
void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
|
||||
void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
|
||||
{
|
||||
if (!mActiveStream)
|
||||
return;
|
||||
@@ -55,7 +53,7 @@ void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
|
||||
mActiveStream->setDestination(addr);
|
||||
}
|
||||
|
||||
void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer, void* userTag)
|
||||
void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag)
|
||||
{
|
||||
mMediaObserver = observer;
|
||||
mMediaObserverTag = userTag;
|
||||
@@ -64,20 +62,21 @@ void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer,
|
||||
}
|
||||
|
||||
// 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)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -105,7 +104,7 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
|
||||
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);
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
|
||||
}
|
||||
@@ -122,22 +121,35 @@ void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, Sdp
|
||||
switch (mActive)
|
||||
{
|
||||
case mfActive:
|
||||
switch(mRemoteState)
|
||||
switch (mRemoteState)
|
||||
{
|
||||
case msSendonly: attr = "recvonly"; break;
|
||||
case msInactive: attr = "recvonly"; break;
|
||||
case msSendonly:
|
||||
attr = "recvonly";
|
||||
break;
|
||||
case msInactive:
|
||||
attr = "recvonly";
|
||||
break;
|
||||
case msRecvonly:
|
||||
case msSendRecv: break; // Do nothing here
|
||||
case msSendRecv:
|
||||
break; // Do nothing here
|
||||
}
|
||||
break;
|
||||
|
||||
case mfPaused:
|
||||
switch (mRemoteState)
|
||||
{
|
||||
case msRecvonly: attr = "sendonly"; break;
|
||||
case msSendonly: attr = "inactive"; break;
|
||||
case msInactive: attr = "inactive"; break;
|
||||
case msSendRecv: attr = "sendonly"; break;
|
||||
case msRecvonly:
|
||||
attr = "sendonly";
|
||||
break;
|
||||
case msSendonly:
|
||||
attr = "inactive";
|
||||
break;
|
||||
case msInactive:
|
||||
attr = "inactive";
|
||||
break;
|
||||
case msSendRecv:
|
||||
attr = "sendonly";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -227,7 +239,7 @@ bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& m
|
||||
|
||||
// Use CodecListPriority mCodecPriority to work with codec priorities
|
||||
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);
|
||||
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
|
||||
const std::list<resip::Data>& vl = media.getValues("crypto");
|
||||
SrtpSuite ss = SRTP_NONE;
|
||||
ByteBuffer key;
|
||||
SrtpSuite ss = SRTP_NONE;
|
||||
ByteBuffer key;
|
||||
for (std::list<resip::Data>::const_iterator attrIter = vl.begin(); attrIter != vl.end(); attrIter++)
|
||||
{
|
||||
const resip::Data& attr = *attrIter;
|
||||
ByteBuffer tempkey;
|
||||
int tag = 1;
|
||||
SrtpSuite suite = processCryptoAttribute(attr, tempkey, &tag);
|
||||
ByteBuffer tempkey;
|
||||
int tag = 1;
|
||||
SrtpSuite suite = processCryptoAttribute(attr, tempkey, &tag);
|
||||
if (srtpSuiteStrength(suite) > srtpSuiteStrength(ss))
|
||||
{
|
||||
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)
|
||||
{
|
||||
int srtpTag = 0;
|
||||
int srtpTag = 0;
|
||||
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)
|
||||
return SRTP_NONE;
|
||||
if (tag)
|
||||
@@ -343,7 +355,7 @@ void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::Codec
|
||||
for (codecIter = codecs.begin(); codecIter != codecs.end(); codecIter++)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -367,9 +379,9 @@ void AudioProvider::setupMirror(bool enable)
|
||||
mActiveStream->setupMirror(enable);
|
||||
}
|
||||
|
||||
void AudioProvider::setStateImpl(unsigned int state) {
|
||||
void AudioProvider::setStateImpl(unsigned int state)
|
||||
{
|
||||
mState = state;
|
||||
if (mActiveStream)
|
||||
mActiveStream->setState(state);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,104 +18,103 @@
|
||||
|
||||
class UserAgent;
|
||||
|
||||
class AudioProvider: public DataProvider
|
||||
class AudioProvider : public DataProvider
|
||||
{
|
||||
public:
|
||||
AudioProvider(UserAgent& agent, MT::Terminal& terminal);
|
||||
virtual ~AudioProvider();
|
||||
|
||||
AudioProvider(UserAgent& agent, MT::Terminal& terminal);
|
||||
virtual ~AudioProvider();
|
||||
|
||||
// Returns provider RTP name
|
||||
std::string streamName() override;
|
||||
|
||||
// Returns provider RTP profile name
|
||||
std::string streamProfile() override;
|
||||
|
||||
// Sets destination IP address
|
||||
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
||||
// Returns provider RTP name
|
||||
std::string streamName() override;
|
||||
|
||||
// Processes incoming data
|
||||
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
|
||||
// Returns provider RTP profile name
|
||||
std::string streamProfile() override;
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
|
||||
// Sets destination IP address
|
||||
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
|
||||
|
||||
// Updates SDP offer
|
||||
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
||||
// Processes incoming data
|
||||
void processData(const PDatagramSocket& s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
|
||||
|
||||
// Called by user agent when session is deleted.
|
||||
void sessionDeleted() override;
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
void sendData(const PDatagramSocket& s, InternetAddress& destination, const void* dataBuffer,
|
||||
unsigned int datasize) override;
|
||||
|
||||
// Called by user agent when session is terminated.
|
||||
void sessionTerminated() override;
|
||||
// Updates SDP offer
|
||||
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;
|
||||
|
||||
// Called by user agent when session is started.
|
||||
void sessionEstablished(int conntype) override;
|
||||
// Called by user agent when session is deleted.
|
||||
void sessionDeleted() override;
|
||||
|
||||
// Called by user agent to save media socket for this provider
|
||||
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override;
|
||||
|
||||
// Called by user agent to get media socket for this provider
|
||||
RtpPair<PDatagramSocket>& socket(int family) override;
|
||||
// Called by user agent when session is terminated.
|
||||
void sessionTerminated() override;
|
||||
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
// myAnswer sets if the answer will be sent after.
|
||||
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) override;
|
||||
// Called by user agent when session is started.
|
||||
void sessionEstablished(int conntype) override;
|
||||
|
||||
// Called by user agent to save media socket for this provider
|
||||
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6) override;
|
||||
|
||||
// Called by user agent to get media socket for this provider
|
||||
RtpPair<PDatagramSocket>& socket(int family) override;
|
||||
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
// myAnswer sets if the answer will be sent after.
|
||||
bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) override;
|
||||
|
||||
|
||||
void setState(unsigned state) override;
|
||||
unsigned state() override;
|
||||
MT::Statistics getStatistics() override;
|
||||
MT::PStream activeStream();
|
||||
void setState(unsigned state) override;
|
||||
unsigned state() override;
|
||||
MT::Statistics getStatistics() override;
|
||||
MT::PStream activeStream();
|
||||
|
||||
void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction);
|
||||
void writeFile(const Audio::PWavFileWriter& stream, MT::Stream::MediaDirection direction);
|
||||
void setupMirror(bool enable);
|
||||
void readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction);
|
||||
void writeFile(const Audio::PWavFileWriter& stream, MT::Stream::MediaDirection direction);
|
||||
void setupMirror(bool enable);
|
||||
|
||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag = nullptr);
|
||||
void configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
|
||||
static SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key, int* tag = nullptr);
|
||||
|
||||
protected:
|
||||
// SDP's stream name
|
||||
std::string mStreamName;
|
||||
|
||||
// Socket handles to operate
|
||||
RtpPair<PDatagramSocket> mSocket4, mSocket6;
|
||||
// SDP's stream name
|
||||
std::string mStreamName;
|
||||
|
||||
// Destination IP4/6 address
|
||||
RtpPair<InternetAddress> mDestination;
|
||||
|
||||
MT::PStream mActiveStream;
|
||||
UserAgent& mUserAgent;
|
||||
MT::Terminal& mTerminal;
|
||||
MT::Statistics mBackupStats;
|
||||
// Socket handles to operate
|
||||
RtpPair<PDatagramSocket> mSocket4, mSocket6;
|
||||
|
||||
unsigned mState;
|
||||
SrtpSuite mSrtpSuite;
|
||||
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
|
||||
struct RemoteCodec
|
||||
{
|
||||
RemoteCodec(MT::Codec::Factory* factory, int payloadType)
|
||||
:mFactory(factory), mRemotePayloadType(payloadType)
|
||||
{ }
|
||||
// Destination IP4/6 address
|
||||
RtpPair<InternetAddress> mDestination;
|
||||
|
||||
MT::Codec::Factory* mFactory;
|
||||
int mRemotePayloadType;
|
||||
};
|
||||
std::vector<RemoteCodec> mAvailableCodecs;
|
||||
int mRemoteTelephoneCodec; // Payload type of remote rfc2833 codec
|
||||
bool mRemoteNoSdp; // Marks if we got no-sdp offer
|
||||
MT::CodecListPriority mCodecPriority;
|
||||
MT::Stream::MediaObserver* mMediaObserver = nullptr;
|
||||
void* mMediaObserverTag = nullptr;
|
||||
MT::PStream mActiveStream;
|
||||
UserAgent& mUserAgent;
|
||||
MT::Terminal& mTerminal;
|
||||
MT::Statistics mBackupStats;
|
||||
|
||||
std::string createCryptoAttribute(SrtpSuite suite, int tag);
|
||||
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
||||
unsigned mState;
|
||||
SrtpSuite mSrtpSuite;
|
||||
int mSrtpTag = 1; // RFC 4568 tag of the negotiated crypto attribute
|
||||
struct RemoteCodec
|
||||
{
|
||||
RemoteCodec(MT::Codec::Factory* factory, int payloadType) : mFactory(factory), mRemotePayloadType(payloadType)
|
||||
{
|
||||
}
|
||||
|
||||
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
||||
void setStateImpl(unsigned state);
|
||||
MT::Codec::Factory* mFactory;
|
||||
int mRemotePayloadType;
|
||||
};
|
||||
std::vector<RemoteCodec> mAvailableCodecs;
|
||||
int mRemoteTelephoneCodec; // Payload type of remote rfc2833 codec
|
||||
bool mRemoteNoSdp; // Marks if we got no-sdp offer
|
||||
MT::CodecListPriority mCodecPriority;
|
||||
MT::Stream::MediaObserver* mMediaObserver = nullptr;
|
||||
void* mMediaObserverTag = nullptr;
|
||||
|
||||
std::string createCryptoAttribute(SrtpSuite suite, int tag);
|
||||
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
|
||||
|
||||
// Implements setState() logic. This allows to be called from constructor (it is not virtual function)
|
||||
void setStateImpl(unsigned state);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,7 +10,7 @@ bool DataProvider::isSupported(const char* name)
|
||||
{
|
||||
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()
|
||||
@@ -20,7 +20,7 @@ void DataProvider::pause()
|
||||
|
||||
// Stop receive RTP stream
|
||||
if (state() & (int)StreamState::Receiving)
|
||||
setState( state() & ~(int)StreamState::Receiving );
|
||||
setState(state() & ~(int)StreamState::Receiving);
|
||||
|
||||
mActive = mfPaused;
|
||||
}
|
||||
@@ -28,10 +28,10 @@ void DataProvider::pause()
|
||||
void DataProvider::resume()
|
||||
{
|
||||
// Tell remote peer about resumed receiving in SDP
|
||||
//setState( state() | STATE_SIPRECV );
|
||||
// setState( state() | STATE_SIPRECV );
|
||||
|
||||
// Start receive RTP stream
|
||||
setState( state() | (int)StreamState::Receiving );
|
||||
setState(state() | (int)StreamState::Receiving);
|
||||
|
||||
mActive = mfActive;
|
||||
}
|
||||
@@ -44,31 +44,29 @@ bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& me
|
||||
mRemoteState = msSendonly;
|
||||
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
|
||||
if (media.exists("recvonly"))
|
||||
{
|
||||
mRemoteState = msSendRecv;
|
||||
switch (mActive)
|
||||
{
|
||||
mRemoteState = msRecvonly;
|
||||
setState(state() & ~(int)StreamState::Receiving);
|
||||
}
|
||||
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 mfActive:
|
||||
setState(state() | (int)StreamState::Sending | (int)StreamState::Receiving);
|
||||
break;
|
||||
|
||||
case mfPaused:
|
||||
setState(state() | (int)StreamState::Sending );
|
||||
break;
|
||||
}
|
||||
}
|
||||
case mfPaused:
|
||||
setState(state() | (int)StreamState::Sending);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,59 +33,61 @@ public:
|
||||
msInactive
|
||||
};
|
||||
|
||||
static bool isSupported(const char* name);
|
||||
static bool isSupported(const char* name);
|
||||
|
||||
// Returns provider RTP name
|
||||
virtual std::string streamName() = 0;
|
||||
virtual std::string streamName() = 0;
|
||||
|
||||
// Returns provider RTP profile name
|
||||
virtual std::string streamProfile() = 0;
|
||||
virtual std::string streamProfile() = 0;
|
||||
|
||||
// Sets destination IP address
|
||||
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
||||
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
|
||||
|
||||
// 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
|
||||
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
|
||||
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().
|
||||
virtual void sessionDeleted() = 0;
|
||||
virtual void sessionDeleted() = 0;
|
||||
|
||||
// Called by user agent when session is terminated.
|
||||
virtual void sessionTerminated() = 0;
|
||||
virtual void sessionTerminated() = 0;
|
||||
|
||||
// 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
|
||||
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
|
||||
virtual RtpPair<PDatagramSocket>& socket(int family) = 0;
|
||||
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) = 0;
|
||||
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection) = 0;
|
||||
|
||||
virtual unsigned state() = 0;
|
||||
virtual void setState(unsigned state) = 0;
|
||||
virtual unsigned state() = 0;
|
||||
virtual void setState(unsigned state) = 0;
|
||||
|
||||
virtual void pause();
|
||||
virtual void resume();
|
||||
virtual void pause();
|
||||
virtual void resume();
|
||||
|
||||
virtual MT::Statistics getStatistics() = 0;
|
||||
virtual MT::Statistics getStatistics() = 0;
|
||||
|
||||
protected:
|
||||
MediaFlow mActive;
|
||||
MediaState mRemoteState;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<DataProvider> PDataProvider;
|
||||
typedef std::shared_ptr<DataProvider> PDataProvider;
|
||||
typedef std::vector<PDataProvider> DataProviderVector;
|
||||
|
||||
#endif
|
||||
|
||||
+290
-317
File diff suppressed because it is too large
Load Diff
+142
-133
@@ -70,45 +70,47 @@ enum
|
||||
|
||||
enum
|
||||
{
|
||||
CONFIG_IPV4 = 0, // Use IP4
|
||||
CONFIG_IPV6, // Use IP6.
|
||||
CONFIG_USERNAME, // Username. String value.
|
||||
CONFIG_DOMAIN, // Domain. String value.
|
||||
CONFIG_PASSWORD, // Password. String 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_DISPLAYNAME, // Optional user display name. String value.
|
||||
CONFIG_DOMAINPORT, // Optional domain port number. Integer 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_KEEPALIVETIME, // Interval between UDP keep-alive messages. 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_ICEUSERNAME, // Optional username 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_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use CONFIG_STUNSERVER_NAME.
|
||||
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
|
||||
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
|
||||
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
|
||||
CONFIG_ICEREQUIRED, // ICE MUST be present in remote peer offers and answers. Boolean value.
|
||||
CONFIG_TRANSPORT, // 0 - all transports, 1 - UDP, 2 - TCP, 3 - TLS,
|
||||
CONFIG_SUBSCRIPTION_TIME, // Subscription time (in seconds)
|
||||
CONFIG_IPV4 = 0, // Use IP4
|
||||
CONFIG_IPV6, // Use IP6.
|
||||
CONFIG_USERNAME, // Username. String value.
|
||||
CONFIG_DOMAIN, // Domain. String value.
|
||||
CONFIG_PASSWORD, // Password. String 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_DISPLAYNAME, // Optional user display name. String value.
|
||||
CONFIG_DOMAINPORT, // Optional domain port number. Integer 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_KEEPALIVETIME, // Interval between UDP keep-alive messages. 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_ICEUSERNAME, // Optional username 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_STUNSERVER_IP, // Optional IP address of STUN/TURN server. String value. It is better to use
|
||||
// CONFIG_STUNSERVER_NAME.
|
||||
CONFIG_STUNSERVER_NAME, // Host name of STUN/TURN server. stun.xten.com for example. String value.
|
||||
CONFIG_STUNSERVER_PORT, // Port number of STUN/TURN server. Integer value.
|
||||
CONFIG_USERAGENT, // Name of user agent in SIP headers. String value.
|
||||
CONFIG_ICEREQUIRED, // ICE MUST be present in remote peer offers and answers. Boolean value.
|
||||
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_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
|
||||
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
|
||||
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_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
|
||||
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
|
||||
CONFIG_DEFERRELAYED, // Defer relayed media path
|
||||
CONFIG_PROXY, // Proxy host name or IP address
|
||||
CONFIG_PROXYPORT, // Proxy port number
|
||||
CONFIG_CODEC_PRIORITY, // Another VariantMap with codec priorities,
|
||||
CONFIG_ACCOUNT, // VariantMap with account configuration
|
||||
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
||||
CONFIG_OWN_DNS, // Use predefined DNS servers
|
||||
CONFIG_REGID // reg-id value from RFC5626,
|
||||
CONFIG_DNS_CACHE_TIME, // DNS cache time; default is 86400 seconds
|
||||
CONFIG_PRESENCE_ID, // Tuple ID used in presence publishing; determines source device
|
||||
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_RTCP_ATTR, // Use "rtcp" attribute in sdp. Default value is true.
|
||||
CONFIG_MULTIPLEXING, // Do rtp/rtcp multiplexing
|
||||
CONFIG_DEFERRELAYED, // Defer relayed media path
|
||||
CONFIG_PROXY, // Proxy host name or IP address
|
||||
CONFIG_PROXYPORT, // Proxy port number
|
||||
CONFIG_CODEC_PRIORITY, // Another VariantMap with codec priorities,
|
||||
CONFIG_ACCOUNT, // VariantMap with account configuration
|
||||
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
||||
CONFIG_OWN_DNS, // Use predefined DNS servers
|
||||
CONFIG_REGID // reg-id value from RFC5626,
|
||||
};
|
||||
|
||||
// Conntype parameter for OnSessionEstablished event
|
||||
@@ -130,7 +132,7 @@ public:
|
||||
typedef std::vector<SIPAction*> SIPActionVector;
|
||||
|
||||
// Session termination reason
|
||||
enum
|
||||
enum
|
||||
{
|
||||
Error,
|
||||
Timeout,
|
||||
@@ -139,35 +141,37 @@ enum
|
||||
RemoteBye,
|
||||
LocalCancel,
|
||||
RemoteCancel,
|
||||
Rejected, //Only as UAS, UAC has distinct onFailure callback
|
||||
Rejected, // Only as UAS, UAC has distinct onFailure callback
|
||||
Referred
|
||||
};
|
||||
|
||||
class UserAgent: public resip::ClientRegistrationHandler,
|
||||
public resip::InviteSessionHandler,
|
||||
public resip::DumShutdownHandler,
|
||||
public resip::ExternalLogger,
|
||||
class UserAgent : public resip::ClientRegistrationHandler,
|
||||
public resip::InviteSessionHandler,
|
||||
public resip::DumShutdownHandler,
|
||||
public resip::ExternalLogger,
|
||||
public resip::DnsResultSink,
|
||||
public resip::ClientSubscriptionHandler,
|
||||
public resip::ClientSubscriptionHandler,
|
||||
public resip::ServerSubscriptionHandler,
|
||||
public resip::ClientPagerMessageHandler,
|
||||
public resip::ClientPagerMessageHandler,
|
||||
public resip::ServerPagerMessageHandler,
|
||||
public resip::ClientPublicationHandler
|
||||
//public resip::InternalTransport::TransportLogger
|
||||
// public resip::InternalTransport::TransportLogger
|
||||
{
|
||||
friend class Account;
|
||||
friend class Session;
|
||||
friend class ResipSession;
|
||||
friend class NATDecorator;
|
||||
friend class WatcherQueue;
|
||||
|
||||
public:
|
||||
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same. Otherwise returns false. */
|
||||
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
|
||||
/* Compares two sip addresses. Returns true if they represent the same entity - user and domain are the same.
|
||||
* Otherwise returns false. */
|
||||
static bool compareSipAddresses(const std::string& sip1, const std::string& sip2);
|
||||
static std::string formatSipAddress(const std::string& sip);
|
||||
static bool isSipAddressValid(const std::string& sip);
|
||||
static bool isSipAddressValid(const std::string& sip);
|
||||
struct SipAddress
|
||||
{
|
||||
bool mValid;
|
||||
bool mValid;
|
||||
std::string mScheme;
|
||||
std::string mUsername;
|
||||
std::string mDomain;
|
||||
@@ -181,113 +185,118 @@ public:
|
||||
|
||||
/* Brings user agent online. Basically it creates a signalling socket(s).
|
||||
This is asynchronous method. */
|
||||
void start();
|
||||
void start();
|
||||
|
||||
/* 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 */
|
||||
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. */
|
||||
void stop();
|
||||
/* Emergency stop. Please always call shutdown() before this. Kills registration, sessions & presence - everything.
|
||||
* onStop() is called in context of this method. */
|
||||
void stop();
|
||||
|
||||
/* Checks if user agent is active (started). */
|
||||
bool active();
|
||||
bool active();
|
||||
|
||||
/* Used to refresh existing registration(s), publication, subscriptions. */
|
||||
void refresh();
|
||||
void refresh();
|
||||
|
||||
/* 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. */
|
||||
void addRootCert(const ByteBuffer& data);
|
||||
void addRootCert(const ByteBuffer& data);
|
||||
|
||||
PAccount createAccount(PVariantMap config);
|
||||
void deleteAccount(PAccount account);
|
||||
PAccount createAccount(PVariantMap config);
|
||||
void deleteAccount(PAccount account);
|
||||
|
||||
/* Creates session. Returns session ID. */
|
||||
PSession createSession(PAccount account);
|
||||
PSession createSession(PAccount account);
|
||||
|
||||
// Must be called when IP interface list is changed
|
||||
void updateInterfaceList();
|
||||
void updateInterfaceList();
|
||||
|
||||
// Called on new incoming session; providers shoukld
|
||||
virtual PDataProvider onProviderNeeded(const std::string& name) = 0;
|
||||
|
||||
// Called on new session offer
|
||||
virtual void onNewSession(PSession s) = 0;
|
||||
virtual void onNewSession(PSession s) = 0;
|
||||
|
||||
// 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
|
||||
// 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
|
||||
virtual void onSessionProvisional(PSession s, int code) = 0;
|
||||
virtual void onSessionProvisional(PSession s, int code) = 0;
|
||||
|
||||
// Called when user agent started
|
||||
virtual void onStart(int errorcode) = 0;
|
||||
virtual void onStart(int errorcode) = 0;
|
||||
|
||||
// Called when user agent stopped
|
||||
virtual void onStop() = 0;
|
||||
virtual void onStop() = 0;
|
||||
|
||||
// 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)
|
||||
virtual void onAccountStop(PAccount account, int error) = 0;
|
||||
virtual void onAccountStop(PAccount account, int error) = 0;
|
||||
|
||||
// Called when connectivity checks failed.
|
||||
virtual void onConnectivityFailed(PSession s) = 0;
|
||||
virtual void onConnectivityFailed(PSession s) = 0;
|
||||
|
||||
// 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
|
||||
virtual void onNetworkChange(PSession s) = 0;
|
||||
virtual void onNetworkChange(PSession s) = 0;
|
||||
|
||||
// Called when all candidates are gathered
|
||||
virtual void onGathered(PSession s);
|
||||
virtual void onGathered(PSession s);
|
||||
|
||||
// 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
|
||||
virtual void onLog(const char* msg);
|
||||
virtual void onLog(const char* msg);
|
||||
|
||||
// Called when problem with SIP connection(s) detected
|
||||
virtual void onSipConnectionFailed() = 0;
|
||||
virtual void onSipConnectionFailed() = 0;
|
||||
|
||||
// Subscribe/publish presence methods
|
||||
virtual void onPublicationSuccess(PAccount acc);
|
||||
virtual void onPublicationTerminated(PAccount acc, int code);
|
||||
virtual void onClientObserverStart(PClientObserver observer);
|
||||
virtual void onServerObserverStart(PServerObserver observer);
|
||||
virtual void onClientObserverStop(PClientObserver observer, int code);
|
||||
virtual void onServerObserverStop(PServerObserver observer, int code);
|
||||
virtual void onPublicationSuccess(PAccount acc);
|
||||
virtual void onPublicationTerminated(PAccount acc, int code);
|
||||
virtual void onClientObserverStart(PClientObserver observer);
|
||||
virtual void onServerObserverStart(PServerObserver observer);
|
||||
virtual void onClientObserverStop(PClientObserver 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 onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
|
||||
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
|
||||
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
|
||||
virtual void onPresenceUpdate(PClientObserver observer, const std::string& peer, bool online,
|
||||
const std::string& content);
|
||||
virtual void onMessageArrived(PAccount account, const std::string& peer, const void* ptr, unsigned length);
|
||||
virtual void onMessageFailed(PAccount account, int id, const std::string& peer, int code, void* tag);
|
||||
virtual void onMessageSent(PAccount account, int id, const std::string& peer, void* tag);
|
||||
|
||||
// Configuration methods
|
||||
VariantMap& config();
|
||||
VariantMap& config();
|
||||
|
||||
public:
|
||||
// InviteSessionHandler implementation
|
||||
// InviteSessionHandler implementation
|
||||
#pragma region InviteSessionHandler implementation
|
||||
/// 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::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage& msg) override;
|
||||
/// 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::ServerInviteSessionHandle, resip::InviteSession::OfferAnswerType oat,
|
||||
const resip::SipMessage& msg) override;
|
||||
|
||||
/// Received a failure response from UAS
|
||||
virtual void onFailure(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// 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
|
||||
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
|
||||
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
|
||||
/// 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,
|
||||
/// DialogUsageManager::makeInviteSessionFromRefer can be used to create an
|
||||
/// 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;
|
||||
|
||||
@@ -353,7 +364,8 @@ public:
|
||||
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
|
||||
|
||||
/// 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
|
||||
|
||||
// ClientRegistrationHandler implementation
|
||||
@@ -367,7 +379,7 @@ public:
|
||||
|
||||
/// call on Retry-After failure.
|
||||
/// 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
|
||||
/// Registration retry interval is enabled in the Profile)
|
||||
@@ -377,14 +389,9 @@ public:
|
||||
|
||||
#pragma region ExternalLogger implementation
|
||||
/** return true to also do default logging, false to suppress default logging. */
|
||||
virtual bool operator()(resip::Log::Level level,
|
||||
const resip::Subsystem& subsystem,
|
||||
const resip::Data& appName,
|
||||
const char* file,
|
||||
int line,
|
||||
const resip::Data& message,
|
||||
const resip::Data& messageWithHeaders,
|
||||
const resip::Data& instanceName) override;
|
||||
virtual bool operator()(resip::Log::Level level, const resip::Subsystem& subsystem, const resip::Data& appName,
|
||||
const char* file, int line, const resip::Data& message,
|
||||
const resip::Data& messageWithHeaders, const resip::Data& instanceName) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DnsResultSink implementation
|
||||
@@ -399,13 +406,13 @@ public:
|
||||
|
||||
#pragma region TransportLogger implementation
|
||||
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen);
|
||||
#pragma endregion
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ClientPublicationHandler
|
||||
void onSuccess(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;
|
||||
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 region SubscriptionHandler
|
||||
@@ -413,13 +420,13 @@ public:
|
||||
void onUpdatePending(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;
|
||||
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;
|
||||
//not sure if this has any value.
|
||||
// not sure if this has any value.
|
||||
void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override;
|
||||
|
||||
/// called to allow app to adorn a message.
|
||||
@@ -438,56 +445,58 @@ public:
|
||||
|
||||
#pragma region PagerHandler
|
||||
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;
|
||||
#pragma endregion
|
||||
|
||||
void onDumCanBeDeleted() override;
|
||||
|
||||
protected:
|
||||
// Mutex to protect this instance
|
||||
Mutex mGuard;
|
||||
Mutex mGuard;
|
||||
|
||||
// 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
|
||||
resip::SipStack* mStack;
|
||||
resip::SipStack* mStack;
|
||||
|
||||
// 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.
|
||||
std::vector<resip::InternalTransport*> mTransportList;
|
||||
|
||||
typedef std::map<int, PSession> SessionMap;
|
||||
typedef std::map<int, PSession> SessionMap;
|
||||
|
||||
// Session's map
|
||||
SessionMap mSessionMap;
|
||||
SessionMap mSessionMap;
|
||||
|
||||
// Used configuration
|
||||
VariantMap mConfig;
|
||||
VariantMap mConfig;
|
||||
|
||||
// Action vector
|
||||
SIPActionVector mActionVector;
|
||||
SIPActionVector mActionVector;
|
||||
|
||||
typedef std::map<int, PClientObserver> ClientObserverMap;
|
||||
ClientObserverMap mClientObserverMap;
|
||||
ClientObserverMap mClientObserverMap;
|
||||
|
||||
typedef std::map<int, PServerObserver> ServerObserverMap;
|
||||
ServerObserverMap mServerObserverMap;
|
||||
ServerObserverMap mServerObserverMap;
|
||||
|
||||
typedef std::set<PAccount> AccountSet;
|
||||
AccountSet mAccountSet;
|
||||
typedef std::set<PAccount> AccountSet;
|
||||
AccountSet mAccountSet;
|
||||
|
||||
// Constructs and sends INVITE to remote peer. Remote peer address is stored inside session object.
|
||||
void sendOffer(Session* session);
|
||||
void internalStopSession(Session& session);
|
||||
void processWatchingList();
|
||||
bool handleMultipartRelatedNotify(const resip::SipMessage& notify);
|
||||
void sendOffer(Session* session);
|
||||
void internalStopSession(Session& session);
|
||||
void processWatchingList();
|
||||
bool handleMultipartRelatedNotify(const resip::SipMessage& notify);
|
||||
|
||||
PSession getUserSession(int sessionId);
|
||||
PAccount getAccount(const resip::NameAddr& myAddr);
|
||||
PAccount getAccount(Account* account);
|
||||
PAccount getAccount(int sessionId);
|
||||
PSession getUserSession(int sessionId);
|
||||
PAccount getAccount(const resip::NameAddr& myAddr);
|
||||
PAccount getAccount(Account* account);
|
||||
PAccount getAccount(int sessionId);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
#include "EP_NetworkQueue.h"
|
||||
#include "EP_Engine.h"
|
||||
|
||||
WatcherQueue::WatcherQueue(UserAgent& ua)
|
||||
:mActiveId(0), mAgent(ua)
|
||||
{}
|
||||
WatcherQueue::WatcherQueue(UserAgent& ua) : mActiveId(0), mAgent(ua) {}
|
||||
|
||||
WatcherQueue::~WatcherQueue()
|
||||
{}
|
||||
WatcherQueue::~WatcherQueue() {}
|
||||
|
||||
int WatcherQueue::add(const std::string& peer, const std::string& package, void* tag)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
|
||||
// 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];
|
||||
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);
|
||||
|
||||
// Check if queue has similar item
|
||||
for (auto& item: mItemList)
|
||||
for (auto& item : mItemList)
|
||||
{
|
||||
if (item.mId == id && id)
|
||||
{
|
||||
@@ -60,7 +57,7 @@ void WatcherQueue::refresh(int id)
|
||||
ice::Lock l(mGuard);
|
||||
|
||||
// Check if queue has similar item
|
||||
for (auto& item: mItemList)
|
||||
for (auto& item : mItemList)
|
||||
{
|
||||
if (item.mId == id && id)
|
||||
{
|
||||
@@ -77,7 +74,7 @@ void WatcherQueue::process()
|
||||
{
|
||||
// Find next item to process
|
||||
ItemList::iterator i = mItemList.begin();
|
||||
for (;i != mItemList.end() && !i->scheduled(); i++)
|
||||
for (; i != mItemList.end() && !i->scheduled(); i++)
|
||||
;
|
||||
if (i == mItemList.end())
|
||||
return;
|
||||
@@ -133,7 +130,7 @@ void WatcherQueue::process()
|
||||
|
||||
void WatcherQueue::onTerminated(int id, int code)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
ice::Lock l(mGuard);
|
||||
ItemList::iterator i = findById(id);
|
||||
if (i != mItemList.end())
|
||||
{
|
||||
@@ -148,7 +145,7 @@ void WatcherQueue::onTerminated(int id, int code)
|
||||
|
||||
void WatcherQueue::onEstablished(int id, int code)
|
||||
{
|
||||
ice::Lock l(mGuard);
|
||||
ice::Lock l(mGuard);
|
||||
ItemList::iterator i = findById(id);
|
||||
if (i != mItemList.end())
|
||||
{
|
||||
@@ -161,7 +158,7 @@ void WatcherQueue::onEstablished(int id, int code)
|
||||
|
||||
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)
|
||||
return i;
|
||||
return mItemList.end();
|
||||
@@ -170,7 +167,7 @@ WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
|
||||
void WatcherQueue::clear()
|
||||
{
|
||||
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())
|
||||
i->mHandle->end();
|
||||
|
||||
@@ -27,26 +27,26 @@ public:
|
||||
State_Deleting
|
||||
};
|
||||
|
||||
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
||||
ResipSession* mSession = nullptr;
|
||||
State mState = State::State_None;
|
||||
std::string mTarget; // Target's address
|
||||
std::string mPackage; // Event package
|
||||
void* mTag = nullptr; // User tag
|
||||
int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only)
|
||||
resip::ClientSubscriptionHandle mHandle; // Subscription handle
|
||||
ResipSession* mSession = nullptr;
|
||||
State mState = State::State_None;
|
||||
std::string mTarget; // Target's address
|
||||
std::string mPackage; // Event package
|
||||
void* mTag = nullptr; // User tag
|
||||
int mId = 0; // Related session ID - it is always non-zero (zero is here for initialization only)
|
||||
|
||||
Item()
|
||||
{}
|
||||
Item() {}
|
||||
|
||||
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();
|
||||
|
||||
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 refresh(int id);
|
||||
void clear();
|
||||
@@ -56,13 +56,13 @@ public:
|
||||
|
||||
protected:
|
||||
typedef std::vector<Item> ItemList;
|
||||
ItemList mItemList;
|
||||
ice::Mutex mGuard;
|
||||
UserAgent& mAgent;
|
||||
int mActiveId;
|
||||
ItemList mItemList;
|
||||
ice::Mutex mGuard;
|
||||
UserAgent& mAgent;
|
||||
int mActiveId;
|
||||
|
||||
void process();
|
||||
ItemList::iterator findById(int id);
|
||||
void process();
|
||||
ItemList::iterator findById(int id);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,13 +9,9 @@
|
||||
#include <resip/stack/Pidf.hxx>
|
||||
#include <resip/dum/ClientSubscription.hxx>
|
||||
|
||||
ClientObserver::ClientObserver()
|
||||
{
|
||||
}
|
||||
ClientObserver::ClientObserver() {}
|
||||
|
||||
ClientObserver::~ClientObserver()
|
||||
{
|
||||
}
|
||||
ClientObserver::~ClientObserver() {}
|
||||
|
||||
void ClientObserver::refresh()
|
||||
{
|
||||
@@ -27,13 +23,12 @@ void ClientObserver::stop()
|
||||
{
|
||||
if (mHandle.isValid())
|
||||
mHandle->end();
|
||||
else
|
||||
else if (mSession)
|
||||
{
|
||||
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
|
||||
if (mSession)
|
||||
{
|
||||
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
|
||||
if (mSession)
|
||||
mSession->end();
|
||||
}
|
||||
mSession->end();
|
||||
}
|
||||
mSession = NULL;
|
||||
}
|
||||
|
||||
@@ -42,11 +37,7 @@ std::string ClientObserver::peer()
|
||||
return mPeer;
|
||||
}
|
||||
|
||||
ServerObserver::ServerObserver()
|
||||
:mState(State_Incoming)
|
||||
{
|
||||
|
||||
}
|
||||
ServerObserver::ServerObserver() : mState(State_Incoming) {}
|
||||
|
||||
ServerObserver::~ServerObserver()
|
||||
{
|
||||
|
||||
@@ -23,19 +23,20 @@ class ClientObserver
|
||||
{
|
||||
friend class Account;
|
||||
friend class UserAgent;
|
||||
|
||||
public:
|
||||
ClientObserver();
|
||||
~ClientObserver();
|
||||
|
||||
void refresh();
|
||||
void stop();
|
||||
void refresh();
|
||||
void stop();
|
||||
std::string peer();
|
||||
|
||||
protected:
|
||||
resip::ClientSubscriptionHandle mHandle;
|
||||
ResipSession* mSession;
|
||||
int mSessionId;
|
||||
std::string mPeer;
|
||||
ResipSession* mSession;
|
||||
int mSessionId;
|
||||
std::string mPeer;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ClientObserver> PClientObserver;
|
||||
@@ -43,6 +44,7 @@ typedef std::shared_ptr<ClientObserver> PClientObserver;
|
||||
class ServerObserver
|
||||
{
|
||||
friend class UserAgent;
|
||||
|
||||
public:
|
||||
ServerObserver();
|
||||
~ServerObserver();
|
||||
@@ -50,9 +52,9 @@ public:
|
||||
std::string peer() const;
|
||||
std::string package() const;
|
||||
|
||||
void accept();
|
||||
void update(std::string simpleId, bool online, std::string msg);
|
||||
void stop();
|
||||
void accept();
|
||||
void update(std::string simpleId, bool online, std::string msg);
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
enum State
|
||||
@@ -61,11 +63,11 @@ protected:
|
||||
State_Active,
|
||||
State_Closed
|
||||
};
|
||||
State mState;
|
||||
State mState;
|
||||
resip::ServerSubscriptionHandle mHandle;
|
||||
std::string mPeer, mPackage;
|
||||
resip::Uri mContact;
|
||||
int mSessionId;
|
||||
std::string mPeer, mPackage;
|
||||
resip::Uri mContact;
|
||||
int mSessionId;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ServerObserver> PServerObserver;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Copyright (C) 2007-2012 Dmytro Bogovych <dmytro.bogovych@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,11 +13,11 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
@@ -30,8 +30,8 @@
|
||||
|
||||
enum
|
||||
{
|
||||
CONFIRMATION_PT = 1,
|
||||
DATA_PT = 2
|
||||
CONFIRMATION_PT = 1,
|
||||
DATA_PT = 2
|
||||
};
|
||||
|
||||
#define CONFIRMATION_TIMEOUT 500
|
||||
@@ -39,266 +39,267 @@ enum
|
||||
|
||||
ReliableTunnel::ReliableTunnel(const char* streamname)
|
||||
{
|
||||
mStack.setEncryption(this);
|
||||
mStreamName = streamname;
|
||||
mBandwidth = 0;
|
||||
mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
mStack.setEncryption(this);
|
||||
mStreamName = streamname;
|
||||
mBandwidth = 0;
|
||||
mExitSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mDataSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
}
|
||||
|
||||
ReliableTunnel::~ReliableTunnel()
|
||||
{
|
||||
::CloseHandle(mDataSignal);
|
||||
::CloseHandle(mExitSignal);
|
||||
::CloseHandle(mDataSignal);
|
||||
::CloseHandle(mExitSignal);
|
||||
}
|
||||
|
||||
std::string ReliableTunnel::streamName()
|
||||
{
|
||||
return mStreamName;
|
||||
return mStreamName;
|
||||
}
|
||||
|
||||
std::string ReliableTunnel::streamProfile()
|
||||
{
|
||||
return "RTP/DP";
|
||||
return "RTP/DP";
|
||||
}
|
||||
|
||||
void ReliableTunnel::setDestinationAddress(InternetAddress& addr)
|
||||
{
|
||||
mDestination = addr;
|
||||
mDestination = addr;
|
||||
}
|
||||
|
||||
|
||||
void ReliableTunnel::queueData(const void* bufferptr, int buffersize)
|
||||
{
|
||||
assert(bufferptr != NULL);
|
||||
assert(buffersize != 0);
|
||||
assert(bufferptr != NULL);
|
||||
assert(buffersize != 0);
|
||||
|
||||
resip::Lock l(mNewQueuedGuard);
|
||||
mNewQueued.push_back(std::string((const char*)bufferptr, buffersize));
|
||||
|
||||
::SetEvent(mDataSignal);
|
||||
resip::Lock l(mNewQueuedGuard);
|
||||
mNewQueued.push_back(std::string((const char*)bufferptr, buffersize));
|
||||
|
||||
::SetEvent(mDataSignal);
|
||||
}
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
void ReliableTunnel::sendData(InternetAddress& addr, const void* dataBuffer, unsigned int datasize)
|
||||
{
|
||||
switch (addr.type())
|
||||
{
|
||||
case AF_INET:
|
||||
mSocket4.sendDatagram(addr, dataBuffer, datasize);
|
||||
return;
|
||||
switch (addr.type())
|
||||
{
|
||||
case AF_INET:
|
||||
mSocket4.sendDatagram(addr, dataBuffer, datasize);
|
||||
return;
|
||||
|
||||
case AF_INET6:
|
||||
mSocket4.sendDatagram(addr, dataBuffer, datasize);
|
||||
return;
|
||||
}
|
||||
case AF_INET6:
|
||||
mSocket4.sendDatagram(addr, dataBuffer, datasize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ReliableTunnel::sessionEstablished(int conntype)
|
||||
{
|
||||
// Start worker thread
|
||||
if (conntype == EV_ICE)
|
||||
run();
|
||||
// Start worker thread
|
||||
if (conntype == EV_ICE)
|
||||
run();
|
||||
}
|
||||
|
||||
void ReliableTunnel::sessionTerminated()
|
||||
{
|
||||
// Stop worker thread
|
||||
::SetEvent(mExitSignal);
|
||||
shutdown();
|
||||
join();
|
||||
// Stop worker thread
|
||||
::SetEvent(mExitSignal);
|
||||
shutdown();
|
||||
join();
|
||||
}
|
||||
|
||||
void ReliableTunnel::updateSdpOffer(resip::SdpContents::Session::Medium& sdp)
|
||||
{
|
||||
// Get new destination port
|
||||
mDestination.setPort((unsigned short)sdp.port());
|
||||
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec("rt", 104));
|
||||
// Get new destination port
|
||||
mDestination.setPort((unsigned short)sdp.port());
|
||||
|
||||
sdp.addCodec(resip::SdpContents::Session::Codec("rt", 104));
|
||||
}
|
||||
|
||||
void ReliableTunnel::setSocket(DatagramSocket& socket4, DatagramSocket& socket6)
|
||||
{
|
||||
mSocket4 = socket4;
|
||||
mSocket6 = socket6;
|
||||
mSocket4 = socket4;
|
||||
mSocket6 = socket6;
|
||||
}
|
||||
|
||||
|
||||
DatagramSocket& ReliableTunnel::socket(int family)
|
||||
{
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
return mSocket4;
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
return mSocket4;
|
||||
|
||||
case AF_INET6:
|
||||
return mSocket4;
|
||||
case AF_INET6:
|
||||
return mSocket4;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReliableTunnel::processSdpOffer(const resip::SdpContents::Session::Medium& media)
|
||||
{
|
||||
//check for default port number
|
||||
mDestination.setPort(media.port());
|
||||
|
||||
return true;
|
||||
// check for default port number
|
||||
mDestination.setPort(media.port());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReliableTunnel::thread()
|
||||
{
|
||||
// Construct event array
|
||||
while (true)
|
||||
{
|
||||
HANDLE eventarray[2] = { mDataSignal, mExitSignal };
|
||||
|
||||
DWORD rescode = ::WaitForMultipleObjects(2, eventarray, FALSE, INFINITE);
|
||||
if (rescode == WAIT_OBJECT_0)
|
||||
// Construct event array
|
||||
while (true)
|
||||
{
|
||||
resip::Lock l(mNewQueuedGuard);
|
||||
for (unsigned i = 0; i<mNewQueued.size(); i++)
|
||||
mStack.queueOutgoing(mNewQueued[i].c_str(), mNewQueued[i].size());
|
||||
mNewQueued.clear();
|
||||
|
||||
sendOutgoing();
|
||||
HANDLE eventarray[2] = {mDataSignal, mExitSignal};
|
||||
|
||||
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)
|
||||
{
|
||||
mBandwidth = bytesPerSecond;
|
||||
mBandwidth = bytesPerSecond;
|
||||
}
|
||||
|
||||
unsigned int ReliableTunnel::bandwidth()
|
||||
{
|
||||
return mBandwidth;
|
||||
return mBandwidth;
|
||||
}
|
||||
|
||||
void ReliableTunnel::processData(const void* dataptr, int datasize)
|
||||
{
|
||||
resip::Lock l(mStackGuard);
|
||||
mStack.processIncoming(dataptr, datasize);
|
||||
resip::Lock l(mStackGuard);
|
||||
mStack.processIncoming(dataptr, datasize);
|
||||
}
|
||||
|
||||
bool ReliableTunnel::hasData()
|
||||
{
|
||||
resip::Lock l(mStackGuard);
|
||||
|
||||
return mIncomingData.size() || mStack.hasAppData();
|
||||
resip::Lock l(mStackGuard);
|
||||
|
||||
return mIncomingData.size() || mStack.hasAppData();
|
||||
}
|
||||
|
||||
unsigned ReliableTunnel::getData(void* ptr, unsigned capacity)
|
||||
{
|
||||
resip::Lock l(mStackGuard);
|
||||
|
||||
char* dataOut = (char*)ptr;
|
||||
|
||||
while (capacity && hasData())
|
||||
{
|
||||
// Check if mIncomingData is empty
|
||||
if (!mIncomingData.size())
|
||||
{
|
||||
unsigned available = mStack.appData(NULL);
|
||||
if (!available)
|
||||
return 0;
|
||||
resip::Lock l(mStackGuard);
|
||||
|
||||
mIncomingData.resize(available);
|
||||
mIncomingData.rewind();
|
||||
mStack.appData(mIncomingData.mutableData());
|
||||
char* dataOut = (char*)ptr;
|
||||
|
||||
while (capacity && hasData())
|
||||
{
|
||||
// Check if mIncomingData is empty
|
||||
if (!mIncomingData.size())
|
||||
{
|
||||
unsigned available = mStack.appData(NULL);
|
||||
if (!available)
|
||||
return 0;
|
||||
|
||||
mIncomingData.resize(available);
|
||||
mIncomingData.rewind();
|
||||
mStack.appData(mIncomingData.mutableData());
|
||||
}
|
||||
|
||||
if (mIncomingData.size())
|
||||
{
|
||||
unsigned toCopy = min(capacity, mIncomingData.size());
|
||||
mIncomingData.dequeueBuffer(dataOut, toCopy);
|
||||
dataOut += toCopy;
|
||||
capacity -= toCopy;
|
||||
}
|
||||
}
|
||||
|
||||
if (mIncomingData.size())
|
||||
{
|
||||
unsigned toCopy = min(capacity, mIncomingData.size());
|
||||
mIncomingData.dequeueBuffer(dataOut, toCopy);
|
||||
dataOut += toCopy;
|
||||
capacity -= toCopy;
|
||||
}
|
||||
}
|
||||
|
||||
return dataOut - (char*)ptr;
|
||||
return dataOut - (char*)ptr;
|
||||
}
|
||||
|
||||
// Returns block size for encryption algorythm
|
||||
int ReliableTunnel::blockSize()
|
||||
{
|
||||
return 8;
|
||||
return 8;
|
||||
}
|
||||
|
||||
|
||||
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void ReliableTunnel::encrypt(void* dataPtr, int dataSize)
|
||||
{
|
||||
if (mEncryptionKey.empty())
|
||||
return;
|
||||
if (mEncryptionKey.empty())
|
||||
return;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_ENCRYPT);
|
||||
for (unsigned i = 0; i < dataSize / blockSize(); i++)
|
||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher,
|
||||
BF_ENCRYPT);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
mEncryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
|
||||
for (unsigned i = 0; i < dataSize / blockSize(); i++)
|
||||
mEncryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void ReliableTunnel::decrypt(void* dataPtr, int dataSize)
|
||||
{
|
||||
if (mEncryptionKey.empty())
|
||||
return;
|
||||
if (mEncryptionKey.empty())
|
||||
return;
|
||||
#ifdef USE_OPENSSL
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher, BF_DECRYPT);
|
||||
for (unsigned i = 0; i < dataSize / blockSize(); i++)
|
||||
BF_ecb_encrypt((unsigned char*)dataPtr + i * blockSize(), (unsigned char*)dataPtr + i * blockSize(), &mCipher,
|
||||
BF_DECRYPT);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
for (unsigned i=0; i<dataSize / blockSize(); i++)
|
||||
mDecryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
|
||||
for (unsigned i = 0; i < dataSize / blockSize(); i++)
|
||||
mDecryptor.ProcessBlock((unsigned char*)dataPtr + i * blockSize());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Calculates CRC
|
||||
unsigned ReliableTunnel::crc(const void* dataptr, int datasize)
|
||||
{
|
||||
unsigned long result;
|
||||
ICEImpl::CRC32 crc;
|
||||
crc.fullCrc((const unsigned char*)dataptr, datasize, &result);
|
||||
|
||||
return result;
|
||||
unsigned long result;
|
||||
ICEImpl::CRC32 crc;
|
||||
crc.fullCrc((const unsigned char*)dataptr, datasize, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReliableTunnel::sendOutgoing()
|
||||
{
|
||||
// Check if stack has to send smth
|
||||
if (mStack.hasPacketToSend())
|
||||
{
|
||||
// Get data to send
|
||||
char buffer[2048];
|
||||
int length = sizeof(buffer);
|
||||
mStack.getPacketToSend(buffer, length);
|
||||
|
||||
// Send it over UDP
|
||||
sendData(this->mDestination, buffer, length);
|
||||
}
|
||||
// Check if stack has to send smth
|
||||
if (mStack.hasPacketToSend())
|
||||
{
|
||||
// Get data to send
|
||||
char buffer[2048];
|
||||
int length = sizeof(buffer);
|
||||
mStack.getPacketToSend(buffer, length);
|
||||
|
||||
// Send it over UDP
|
||||
sendData(this->mDestination, buffer, length);
|
||||
}
|
||||
}
|
||||
|
||||
void ReliableTunnel::setEncryptionKey(void* ptr, unsigned length)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
BF_set_key(&mCipher, length, (const unsigned char*)ptr);
|
||||
BF_set_key(&mCipher, length, (const unsigned char*)ptr);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
mEncryptor.SetKey((unsigned char*)ptr, length);
|
||||
mDecryptor.SetKey((unsigned char*)ptr, length);
|
||||
mEncryptor.SetKey((unsigned char*)ptr, length);
|
||||
mDecryptor.SetKey((unsigned char*)ptr, length);
|
||||
#endif
|
||||
|
||||
// Save key
|
||||
mEncryptionKey = std::string((const char*)ptr, length);
|
||||
// Save key
|
||||
mEncryptionKey = std::string((const char*)ptr, length);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Copyright (C) 2007-2010 Dmytro Bogovych <dmytro.bogovych@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,7 +13,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef __RELIABLE_TUNNEL_H
|
||||
#define __RELIABLE_TUNNEL_H
|
||||
@@ -26,122 +26,121 @@
|
||||
|
||||
#include "../ICE/ICEReliableTransport.h"
|
||||
#ifdef USE_CRYPTOPP
|
||||
# include "../Libs/CryptoPP/blowfish.h"
|
||||
#include "../Libs/CryptoPP/blowfish.h"
|
||||
#endif
|
||||
#ifdef USE_OPENSSL
|
||||
# include "../Libs/openssl/include/openssl/blowfish.h"
|
||||
#include "../Libs/openssl/include/openssl/blowfish.h"
|
||||
#endif
|
||||
|
||||
class ReliableTunnel: public DataProvider, public resip::ThreadIf, public ICEImpl::ReliableTransport::Encryption
|
||||
class ReliableTunnel : public DataProvider, public resip::ThreadIf, public ICEImpl::ReliableTransport::Encryption
|
||||
{
|
||||
public:
|
||||
ReliableTunnel(const char* streamname);
|
||||
virtual ~ReliableTunnel();
|
||||
|
||||
// Returns provider RTP name
|
||||
virtual std::string streamName();
|
||||
|
||||
// Returns provider RTP profile name
|
||||
virtual std::string streamProfile();
|
||||
|
||||
// Sets destination IP address
|
||||
virtual void setDestinationAddress(InternetAddress& addr);
|
||||
ReliableTunnel(const char* streamname);
|
||||
virtual ~ReliableTunnel();
|
||||
|
||||
// Processes incoming data
|
||||
virtual void processData(const void* dataBuffer, int dataSize);
|
||||
// Returns provider RTP name
|
||||
virtual std::string streamName();
|
||||
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
virtual void sendData(InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
|
||||
// Returns provider RTP profile name
|
||||
virtual std::string streamProfile();
|
||||
|
||||
// Updates SDP offer
|
||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp);
|
||||
// Sets destination IP address
|
||||
virtual void setDestinationAddress(InternetAddress& addr);
|
||||
|
||||
// Called by user agent when session is terminated.
|
||||
virtual void sessionTerminated();
|
||||
// Processes incoming data
|
||||
virtual void processData(const void* dataBuffer, int dataSize);
|
||||
|
||||
// Called by user agent when session is started.
|
||||
virtual void sessionEstablished(int conntype);
|
||||
// This method is called by user agent to send ICE packet from mediasocket
|
||||
virtual void sendData(InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
|
||||
|
||||
// Updates SDP offer
|
||||
virtual void updateSdpOffer(resip::SdpContents::Session::Medium& sdp);
|
||||
|
||||
// Called by user agent when session is terminated.
|
||||
virtual void sessionTerminated();
|
||||
|
||||
// Called by user agent when session is started.
|
||||
virtual void sessionEstablished(int conntype);
|
||||
|
||||
// Called by user agent to save media socket for this provider
|
||||
virtual void setSocket(DatagramSocket& socket4, DatagramSocket& socket6);
|
||||
|
||||
// Called by user agent to get media socket for this provider
|
||||
virtual DatagramSocket& socket(int family);
|
||||
virtual void setSocket(DatagramSocket& socket4, DatagramSocket& socket6);
|
||||
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media);
|
||||
// Called by user agent to get media socket for this provider
|
||||
virtual DatagramSocket& socket(int family);
|
||||
|
||||
virtual void thread();
|
||||
// Called by user agent to process media stream description from remote peer.
|
||||
// Returns true if description is processed succesfully. Otherwise method returns false.
|
||||
virtual bool processSdpOffer(const resip::SdpContents::Session::Medium& media);
|
||||
|
||||
// Enqueues outgoing packet to sending queue
|
||||
void queueData(const void* bufferPtr, int bufferSize);
|
||||
|
||||
void setBandwidth(unsigned int bytesPerSecond);
|
||||
unsigned int bandwidth();
|
||||
|
||||
// Checks if there is any received application data
|
||||
bool hasData();
|
||||
|
||||
// Reads received data. If ptr is NULL - the length of available data is returned.
|
||||
unsigned getData(void* ptr, unsigned capacity);
|
||||
virtual void thread();
|
||||
|
||||
void setEncryptionKey(void* ptr, unsigned length);
|
||||
// Enqueues outgoing packet to sending queue
|
||||
void queueData(const void* bufferPtr, int bufferSize);
|
||||
|
||||
void setBandwidth(unsigned int bytesPerSecond);
|
||||
unsigned int bandwidth();
|
||||
|
||||
// Checks if there is any received application data
|
||||
bool hasData();
|
||||
|
||||
// Reads received data. If ptr is NULL - the length of available data is returned.
|
||||
unsigned getData(void* ptr, unsigned capacity);
|
||||
|
||||
void setEncryptionKey(void* ptr, unsigned length);
|
||||
|
||||
protected:
|
||||
// SDP's stream name
|
||||
std::string mStreamName;
|
||||
// SDP's stream name
|
||||
std::string mStreamName;
|
||||
|
||||
// Transport stack
|
||||
ICEImpl::ReliableTransport mStack;
|
||||
|
||||
// Socket handles to operate
|
||||
DatagramSocket mSocket4;
|
||||
DatagramSocket mSocket6;
|
||||
// Transport stack
|
||||
ICEImpl::ReliableTransport mStack;
|
||||
|
||||
// Destination IP4/6 address
|
||||
InternetAddress mDestination;
|
||||
|
||||
// Win32 exit signal
|
||||
HANDLE mExitSignal;
|
||||
|
||||
// Win32 "new outgoing data" signal
|
||||
HANDLE mDataSignal;
|
||||
|
||||
// Mutex to protect queuing/sending outgoing data
|
||||
resip::Mutex mOutgoingMtx;
|
||||
|
||||
std::vector<std::string>
|
||||
mNewQueued;
|
||||
resip::Mutex mNewQueuedGuard;
|
||||
resip::Mutex mStackGuard;
|
||||
// Socket handles to operate
|
||||
DatagramSocket mSocket4;
|
||||
DatagramSocket mSocket6;
|
||||
|
||||
// Destination IP4/6 address
|
||||
InternetAddress mDestination;
|
||||
|
||||
// Win32 exit signal
|
||||
HANDLE mExitSignal;
|
||||
|
||||
// Win32 "new outgoing data" signal
|
||||
HANDLE mDataSignal;
|
||||
|
||||
// Mutex to protect queuing/sending outgoing data
|
||||
resip::Mutex mOutgoingMtx;
|
||||
|
||||
std::vector<std::string> mNewQueued;
|
||||
resip::Mutex mNewQueuedGuard;
|
||||
resip::Mutex mStackGuard;
|
||||
|
||||
unsigned int mBandwidth;
|
||||
std::string mEncryptionKey;
|
||||
|
||||
unsigned int mBandwidth;
|
||||
std::string mEncryptionKey;
|
||||
|
||||
#ifdef USE_CRYPTOPP
|
||||
CryptoPP::BlowfishEncryption mEncryptor;
|
||||
CryptoPP::BlowfishDecryption mDecryptor;
|
||||
CryptoPP::BlowfishEncryption mEncryptor;
|
||||
CryptoPP::BlowfishDecryption mDecryptor;
|
||||
#endif
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
BF_KEY mCipher;
|
||||
BF_KEY mCipher;
|
||||
#endif
|
||||
|
||||
ICEImpl::ICEByteBuffer mIncomingData;
|
||||
ICEImpl::ICEByteBuffer mIncomingData;
|
||||
|
||||
// Returns block size for encryption algorythm
|
||||
int blockSize();
|
||||
|
||||
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void encrypt(void* dataPtr, int dataSize);
|
||||
// Returns block size for encryption algorythm
|
||||
int blockSize();
|
||||
|
||||
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void decrypt(void* dataPtr, int dataSize);
|
||||
// Encrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void encrypt(void* dataPtr, int dataSize);
|
||||
|
||||
// Calculates CRC
|
||||
unsigned crc(const void* dataptr, int datasize);
|
||||
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
|
||||
void decrypt(void* dataPtr, int dataSize);
|
||||
|
||||
void sendOutgoing();
|
||||
// Calculates CRC
|
||||
unsigned crc(const void* dataptr, int datasize);
|
||||
|
||||
void sendOutgoing();
|
||||
};
|
||||
#endif
|
||||
+115
-109
@@ -13,21 +13,18 @@
|
||||
|
||||
#define LOG_SUBSYSTEM "engine"
|
||||
|
||||
typedef resip::SdpContents::Session::Medium Medium;
|
||||
typedef resip::SdpContents::Session::Medium Medium;
|
||||
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 ------------
|
||||
#pragma region ResipSessionAppDialog
|
||||
ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham)
|
||||
{
|
||||
}
|
||||
ResipSessionAppDialog::ResipSessionAppDialog(resip::HandleManager& ham) : AppDialog(ham) {}
|
||||
|
||||
ResipSessionAppDialog::~ResipSessionAppDialog()
|
||||
{
|
||||
}
|
||||
ResipSessionAppDialog::~ResipSessionAppDialog() {}
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -35,7 +32,7 @@ ResipSessionAppDialog::~ResipSessionAppDialog()
|
||||
|
||||
std::atomic_int ResipSession::InstanceCounter;
|
||||
|
||||
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
||||
ResipSession::ResipSession(resip::DialogUsageManager& dum)
|
||||
: resip::AppDialogSet(dum), mUserAgent(nullptr), mType(Type_None), mSessionId(0), mSession(0)
|
||||
{
|
||||
ResipSession::InstanceCounter++;
|
||||
@@ -54,7 +51,7 @@ ResipSession::~ResipSession()
|
||||
mSession->mResipSession = nullptr;
|
||||
runTerminatedEvent(Type_Auto, 0, 0);
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -94,7 +91,8 @@ void ResipSession::runTerminatedEvent(Type type, int code, int reason)
|
||||
case Type_Subscription:
|
||||
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())
|
||||
mUserAgent->onClientObserverStop(observerIter->second, code);
|
||||
}
|
||||
@@ -186,14 +184,9 @@ std::shared_ptr<resip::UserProfile> ResipSession::selectUASUserProfile(const res
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Session::Stream
|
||||
Session::Stream::Stream()
|
||||
:mRtcpAttr(false), mRtcpMuxAttr(false)
|
||||
{
|
||||
}
|
||||
Session::Stream::Stream() : mRtcpAttr(false), mRtcpMuxAttr(false) {}
|
||||
|
||||
Session::Stream::~Stream()
|
||||
{
|
||||
}
|
||||
Session::Stream::~Stream() {}
|
||||
|
||||
void Session::Stream::setProvider(PDataProvider provider)
|
||||
{
|
||||
@@ -261,7 +254,7 @@ void Session::Stream::setRtcpMuxAttr(bool value)
|
||||
std::atomic_int Session::InstanceCounter;
|
||||
|
||||
Session::Session(PAccount account)
|
||||
{
|
||||
{
|
||||
InstanceCounter++;
|
||||
mAccount = account;
|
||||
mSessionId = Session::generateId();
|
||||
@@ -290,14 +283,15 @@ Session::~Session()
|
||||
mResipSession->setSession(NULL);
|
||||
clearProvidersAndSockets();
|
||||
}
|
||||
catch(...)
|
||||
{}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
InstanceCounter--;
|
||||
}
|
||||
|
||||
void Session::start(const std::string& peer)
|
||||
{
|
||||
ICELogInfo( << "Attempt to start session to " << peer);
|
||||
ICELogInfo(<< "Attempt to start session to " << peer);
|
||||
Lock l(mGuard);
|
||||
|
||||
if (mResipSession)
|
||||
@@ -320,7 +314,7 @@ void Session::start(const std::string& peer)
|
||||
// Mark session as Initiator
|
||||
mRole = Session::Initiator;
|
||||
|
||||
resip::Data addrData(peer);
|
||||
resip::Data addrData(peer);
|
||||
resip::NameAddr addr(addrData);
|
||||
|
||||
// Save target address
|
||||
@@ -334,7 +328,7 @@ void Session::stop()
|
||||
{
|
||||
ICELogInfo(<< "Stopping session " << mSessionId);
|
||||
Lock l(mGuard);
|
||||
for (unsigned i=0; i<mStreamList.size(); i++)
|
||||
for (unsigned i = 0; i < mStreamList.size(); i++)
|
||||
{
|
||||
Session::Stream& dataStream = mStreamList[i];
|
||||
|
||||
@@ -344,8 +338,8 @@ void Session::stop()
|
||||
dataStream.provider()->sessionTerminated();
|
||||
|
||||
// Free socket
|
||||
SocketHeap::instance().freeSocketPair( dataStream.socket4() );
|
||||
SocketHeap::instance().freeSocketPair( dataStream.socket6() );
|
||||
SocketHeap::instance().freeSocketPair(dataStream.socket4());
|
||||
SocketHeap::instance().freeSocketPair(dataStream.socket6());
|
||||
|
||||
// Drop the references so the destructor's cleanup does not free them again
|
||||
dataStream.setSocket4(RtpPair<PDatagramSocket>());
|
||||
@@ -357,7 +351,7 @@ void Session::stop()
|
||||
mResipSession->runTerminatedEvent(ResipSession::Type_Call, 0, LocalBye);
|
||||
|
||||
if (mResipSession)
|
||||
mResipSession->end(); // Stop SIP session
|
||||
mResipSession->end(); // Stop SIP session
|
||||
}
|
||||
|
||||
void Session::accept()
|
||||
@@ -366,7 +360,8 @@ void Session::accept()
|
||||
|
||||
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;
|
||||
|
||||
if (mGatheredCandidates || mIceStack->state() == ice::IceNone)
|
||||
@@ -405,7 +400,7 @@ void Session::accept()
|
||||
|
||||
void Session::reject(int code)
|
||||
{
|
||||
ICELogInfo( << "Attempt to reject session " << mSessionId);
|
||||
ICELogInfo(<< "Attempt to reject session " << mSessionId);
|
||||
|
||||
Lock l(mGuard);
|
||||
|
||||
@@ -449,8 +444,8 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||
MT::Statistics stat;
|
||||
|
||||
// Iterate all session providers
|
||||
Stream* media = nullptr;
|
||||
for (Stream& stream: mStreamList)
|
||||
Stream* media = nullptr;
|
||||
for (Stream& stream : mStreamList)
|
||||
{
|
||||
if (!stream.provider())
|
||||
continue;
|
||||
@@ -473,7 +468,9 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
||||
info[SessionInfo_SentRtp] = static_cast<int>(stat.mSentRtp);
|
||||
info[SessionInfo_SentRtcp] = static_cast<int>(stat.mSentRtcp);
|
||||
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
|
||||
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);
|
||||
|
||||
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;
|
||||
if (stat.mRttDelay.is_initialized())
|
||||
@@ -505,7 +503,8 @@ PAccount Session::account()
|
||||
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);
|
||||
|
||||
@@ -513,7 +512,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
|
||||
return;
|
||||
|
||||
// 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);
|
||||
received.setRemoteAddress(src);
|
||||
|
||||
@@ -531,7 +530,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
|
||||
if (received.size() >= 4)
|
||||
{
|
||||
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]);
|
||||
if (turnPrefix)
|
||||
received.erase(0, 4);
|
||||
@@ -546,7 +545,7 @@ void Session::onReceivedData(PDatagramSocket socket, InternetAddress& src, const
|
||||
{
|
||||
ice::ByteBuffer buffer(receivedPtr, receivedSize);
|
||||
buffer.setRemoteAddress(src);
|
||||
/*bool processed = */mIceStack->processIncomingData(stream, component, buffer);
|
||||
/*bool processed = */ mIceStack->processIncomingData(stream, component, buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -594,20 +593,19 @@ void Session::onGathered(ice::Stack* stack, void* tag)
|
||||
|
||||
if (mRole == Initiator)
|
||||
mUserAgent->sendOffer(this);
|
||||
else
|
||||
if (mRole == Acceptor)
|
||||
{
|
||||
// Mark session as gathered ICE candidates
|
||||
mGatheredCandidates = true;
|
||||
else if (mRole == Acceptor)
|
||||
{
|
||||
// Mark session as gathered ICE candidates
|
||||
mGatheredCandidates = true;
|
||||
|
||||
// if AcceptSession was already called() on session - recall it again to make real work
|
||||
if (mAcceptedByUser)
|
||||
{
|
||||
// Check if session is needed here - because session can be terminated already
|
||||
if (mResipSession && mInviteHandle.isValid())
|
||||
accept();
|
||||
}
|
||||
// if AcceptSession was already called() on session - recall it again to make real work
|
||||
if (mAcceptedByUser)
|
||||
{
|
||||
// Check if session is needed here - because session can be terminated already
|
||||
if (mResipSession && mInviteHandle.isValid())
|
||||
accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::onSuccess(ice::Stack* stack, void* tag)
|
||||
@@ -616,7 +614,7 @@ void Session::onSuccess(ice::Stack* stack, void* tag)
|
||||
|
||||
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();
|
||||
if (p)
|
||||
@@ -638,8 +636,8 @@ void Session::onSuccess(ice::Stack* stack, void* tag)
|
||||
|
||||
mUserAgent->onSessionEstablished(mUserAgent->getUserSession(mSessionId), EV_ICE, t);
|
||||
|
||||
//time to resend updated media info over SIP
|
||||
//TODO:
|
||||
// time to resend updated media info over SIP
|
||||
// TODO:
|
||||
}
|
||||
|
||||
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);
|
||||
mUserAgent->onConnectivityFailed(mUserAgent->getUserSession(mSessionId));
|
||||
|
||||
//if (mInviteHandle.isValid())
|
||||
// mInviteHandle->end();
|
||||
// if (mInviteHandle.isValid())
|
||||
// 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);
|
||||
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().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);
|
||||
|
||||
// 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().connection().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
|
||||
? 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
|
||||
if (mIceStack->state() > ice::IceNone)
|
||||
@@ -686,30 +688,30 @@ void Session::buildSdp(resip::SdpContents &sdp, SdpDirection sdpDirection)
|
||||
}
|
||||
|
||||
// Iterate media streams
|
||||
for (unsigned i=0; i<mStreamList.size(); i++)
|
||||
for (unsigned i = 0; i < mStreamList.size(); i++)
|
||||
{
|
||||
Stream& stream = mStreamList[i];
|
||||
if (!stream.provider())
|
||||
continue;
|
||||
DataProvider& provider = *mStreamList[i].provider();
|
||||
DataProvider& provider = *mStreamList[i].provider();
|
||||
|
||||
// Get default stream port
|
||||
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
|
||||
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
|
||||
if (mUserAgent->mConfig[CONFIG_RTCP_ATTR].asBool())
|
||||
{
|
||||
if (mUserAgent->mConfig[CONFIG_MULTIPLEXING].asBool())
|
||||
rtcpPort = rtpPort;
|
||||
else
|
||||
if (rtcpPort.isEmpty())
|
||||
else if (rtcpPort.isEmpty())
|
||||
{
|
||||
rtcpPort = rtpPort;
|
||||
rtcpPort.setPort( rtpPort.port() + 1);
|
||||
rtcpPort.setPort(rtpPort.port() + 1);
|
||||
}
|
||||
|
||||
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))
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -766,7 +768,7 @@ void Session::addProvider(PDataProvider provider)
|
||||
return;
|
||||
|
||||
// Avoid duplicating providers
|
||||
for (unsigned i=0; i<mStreamList.size(); i++)
|
||||
for (unsigned i = 0; i < mStreamList.size(); i++)
|
||||
if (mStreamList[i].provider() == provider)
|
||||
return;
|
||||
|
||||
@@ -777,18 +779,18 @@ void Session::addProvider(PDataProvider provider)
|
||||
{
|
||||
if (!streamIter->provider() && (streamIter->socket4().mRtp->isValid() || streamIter->socket6().mRtp->isValid()))
|
||||
{
|
||||
streamIter->setProvider( provider );
|
||||
streamIter->setProvider(provider);
|
||||
provider->setSocket(streamIter->socket4(), streamIter->socket6());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Stream s;
|
||||
s.setProvider( provider );
|
||||
s.setProvider(provider);
|
||||
|
||||
// Allocate socket for provider
|
||||
s.setSocket4( SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()) );
|
||||
s.setSocket6( SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()) );
|
||||
s.setSocket4(SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()));
|
||||
s.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
|
||||
s.provider()->setSocket(s.socket4(), s.socket6());
|
||||
|
||||
// Create ICE stream/component
|
||||
@@ -797,10 +799,11 @@ void Session::addProvider(PDataProvider provider)
|
||||
ii.mPort4 = s.socket4().mRtp->localport();
|
||||
ii.mPort6 = s.socket6().mRtp->localport();
|
||||
|
||||
ii.mComponentId.mRtp = mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(),
|
||||
s.socket6().mRtp->localport());
|
||||
ii.mComponentId.mRtp =
|
||||
mIceStack->addComponent(ii.mStreamId, NULL, s.socket4().mRtp->localport(), s.socket6().mRtp->localport());
|
||||
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);
|
||||
|
||||
@@ -825,7 +828,7 @@ int Session::sessionId()
|
||||
}
|
||||
|
||||
std::atomic_int Session::IdGenerator;
|
||||
int Session::generateId()
|
||||
int Session::generateId()
|
||||
{
|
||||
return ++IdGenerator;
|
||||
}
|
||||
@@ -857,7 +860,7 @@ void Session::setTag(void* tag)
|
||||
|
||||
void Session::pause()
|
||||
{
|
||||
for (unsigned i=0; i<mStreamList.size(); i++)
|
||||
for (unsigned i = 0; i < mStreamList.size(); i++)
|
||||
{
|
||||
Stream& s = mStreamList[i];
|
||||
if (s.provider())
|
||||
@@ -868,7 +871,7 @@ void Session::pause()
|
||||
|
||||
void Session::resume()
|
||||
{
|
||||
for (unsigned i=0; i<mStreamList.size(); i++)
|
||||
for (unsigned i = 0; i < mStreamList.size(); i++)
|
||||
{
|
||||
Stream& s = mStreamList[i];
|
||||
if (s.provider())
|
||||
@@ -880,9 +883,9 @@ void Session::resume()
|
||||
void Session::refreshMediaPath()
|
||||
{
|
||||
// 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();
|
||||
if (!p)
|
||||
continue;
|
||||
@@ -891,7 +894,7 @@ void Session::refreshMediaPath()
|
||||
SocketHeap::instance().freeSocketPair(p->socket(AF_INET));
|
||||
|
||||
// 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());
|
||||
|
||||
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,
|
||||
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;
|
||||
unsigned streamIndex = 0;
|
||||
unsigned streamIndex = 0;
|
||||
for (mediaIter = media.begin(); mediaIter != media.end(); ++mediaIter, ++streamIndex)
|
||||
{
|
||||
// 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
|
||||
if (!stream.provider())
|
||||
stream.setProvider( mUserAgent->onProviderNeeded(remoteStream.name().c_str()) );
|
||||
stream.setProvider(mUserAgent->onProviderNeeded(remoteStream.name().c_str()));
|
||||
|
||||
// Check the stream validity
|
||||
if (!stream.provider())
|
||||
@@ -938,8 +941,8 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
||||
continue;
|
||||
|
||||
// See for rtcp & rtcp-mux attribute
|
||||
stream.setRtcpAttr( remoteStream.exists("rtcp") );
|
||||
stream.setRtcpMuxAttr( remoteStream.exists("rtcp-mux") );
|
||||
stream.setRtcpAttr(remoteStream.exists("rtcp"));
|
||||
stream.setRtcpMuxAttr(remoteStream.exists("rtcp-mux"));
|
||||
|
||||
// Set destination address
|
||||
if (!remoteStream.getConnections().empty())
|
||||
@@ -951,12 +954,12 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
||||
|
||||
targetAddr.mRtcp.setIp(remoteIp);
|
||||
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
|
||||
if (stream.rtcpAttr())
|
||||
targetAddr.mRtcp.setPort( strx::toInt(remoteStream.getValues("rtcp").front().c_str(), remoteStream.port() + 1 ) );
|
||||
else
|
||||
targetAddr.mRtcp.setPort( remoteStream.port() + 1);
|
||||
targetAddr.mRtcp.setPort(remoteStream.port() + 1);
|
||||
|
||||
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.setSocket6(SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()));
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
ICELogError( << "Cannot create media socket.");
|
||||
ICELogError(<< "Cannot create media socket.");
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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
|
||||
const std::list<resip::Data> candidateList = remoteStream.getValues("candidate");
|
||||
const std::list<resip::Data> candidateList = remoteStream.getValues("candidate");
|
||||
|
||||
// Repackage information about remote candidates
|
||||
std::vector<std::string> candidateVector;
|
||||
std::vector<std::string> candidateVector;
|
||||
std::list<resip::Data>::const_iterator cit = candidateList.begin();
|
||||
|
||||
for (; cit != candidateList.end(); ++cit)
|
||||
@@ -1032,9 +1037,11 @@ int Session::processSdp(uint64_t version, bool iceAvailable, std::string icePwd,
|
||||
if (candidateVector.empty())
|
||||
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)
|
||||
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
|
||||
@@ -1081,22 +1088,22 @@ int Session::release()
|
||||
|
||||
void Session::clearProvidersAndSockets()
|
||||
{
|
||||
for (unsigned i=0; i<mStreamList.size(); i++)
|
||||
for (unsigned i = 0; i < mStreamList.size(); i++)
|
||||
{
|
||||
Session::Stream& ds = mStreamList[i];
|
||||
|
||||
if (ds.provider())
|
||||
{
|
||||
ds.provider()->sessionDeleted();
|
||||
SocketHeap::instance().freeSocketPair( ds.socket4() );
|
||||
SocketHeap::instance().freeSocketPair( ds.socket6() );
|
||||
SocketHeap::instance().freeSocketPair(ds.socket4());
|
||||
SocketHeap::instance().freeSocketPair(ds.socket6());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::clearProviders()
|
||||
{
|
||||
for (unsigned i=0; i<mStreamList.size(); i++)
|
||||
for (unsigned i = 0; i < mStreamList.size(); i++)
|
||||
{
|
||||
Session::Stream& ds = mStreamList[i];
|
||||
|
||||
@@ -1132,14 +1139,13 @@ void Session::processQueuedOffer()
|
||||
//-------------- ResipSessionFactory ---------
|
||||
#pragma region ResipSessionFactory
|
||||
|
||||
ResipSessionFactory::ResipSessionFactory(UserAgent* agent)
|
||||
:mAgent(agent)
|
||||
{}
|
||||
ResipSessionFactory::ResipSessionFactory(UserAgent* 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);
|
||||
s->setUa( mAgent );
|
||||
s->setUa(mAgent);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+257
-259
@@ -49,365 +49,363 @@ class ResipSession;
|
||||
|
||||
enum SessionInfo
|
||||
{
|
||||
SessionInfo_RemoteSipAddress, // remote sip address
|
||||
SessionInfo_ReceivedTraffic, // amount of received 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_AudioPeer, // remote peer rtp address in text
|
||||
SessionInfo_AudioCodec, // selected audio codec as text
|
||||
SessionInfo_DtmfInterface, // Pointer to DtmfQueue class; returned as void*
|
||||
SessionInfo_IceState,
|
||||
SessionInfo_NetworkMos,
|
||||
SessionInfo_PvqaMos,
|
||||
SessionInfo_PvqaReport,
|
||||
SessionInfo_SentRtp,
|
||||
SessionInfo_SentRtcp,
|
||||
SessionInfo_ReceivedRtp,
|
||||
SessionInfo_ReceivedRtcp,
|
||||
SessionInfo_LostRtp,
|
||||
SessionInfo_DroppedRtp,
|
||||
SessionInfo_Duration,
|
||||
SessionInfo_Jitter,
|
||||
SessionInfo_Rtt,
|
||||
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
|
||||
SessionInfo_RemotePeer,
|
||||
SessionInfo_SSRC,
|
||||
SessionInfo_CngCounter, // For AMR codecs only
|
||||
SessionInfo_ReceivedRtpTraffic // amount of received RTP traffic in bytes, RTCP excluded
|
||||
SessionInfo_RemoteSipAddress, // remote sip address
|
||||
SessionInfo_ReceivedTraffic, // amount of received 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_AudioPeer, // remote peer rtp address in text
|
||||
SessionInfo_AudioCodec, // selected audio codec as text
|
||||
SessionInfo_DtmfInterface, // Pointer to DtmfQueue class; returned as void*
|
||||
SessionInfo_IceState,
|
||||
SessionInfo_NetworkMos,
|
||||
SessionInfo_PvqaMos,
|
||||
SessionInfo_PvqaReport,
|
||||
SessionInfo_SentRtp,
|
||||
SessionInfo_SentRtcp,
|
||||
SessionInfo_ReceivedRtp,
|
||||
SessionInfo_ReceivedRtcp,
|
||||
SessionInfo_LostRtp,
|
||||
SessionInfo_DroppedRtp,
|
||||
SessionInfo_Duration,
|
||||
SessionInfo_Jitter,
|
||||
SessionInfo_Rtt,
|
||||
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
|
||||
SessionInfo_RemotePeer,
|
||||
SessionInfo_SSRC,
|
||||
SessionInfo_CngCounter, // For AMR codecs only
|
||||
SessionInfo_ReceivedRtpTraffic // amount of received RTP traffic in bytes, RTCP excluded
|
||||
};
|
||||
|
||||
|
||||
class Session :
|
||||
public SocketSink,
|
||||
public ice::StageHandler
|
||||
class Session : public SocketSink, public ice::StageHandler
|
||||
{
|
||||
public:
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
virtual void run(Session& s) = 0;
|
||||
};
|
||||
|
||||
// Describes ice stream/component
|
||||
struct IceInfo
|
||||
{
|
||||
IceInfo()
|
||||
:mStreamId(-1)
|
||||
class Command
|
||||
{
|
||||
mPort4 = mPort6 = 0;
|
||||
mComponentId.mRtp = mComponentId.mRtcp = -1;
|
||||
}
|
||||
public:
|
||||
virtual void run(Session& s) = 0;
|
||||
};
|
||||
|
||||
RtpPair<int> mComponentId;
|
||||
int mStreamId;
|
||||
unsigned short mPort4;
|
||||
unsigned short mPort6;
|
||||
};
|
||||
// Describes ice stream/component
|
||||
struct IceInfo
|
||||
{
|
||||
IceInfo() : mStreamId(-1)
|
||||
{
|
||||
mPort4 = mPort6 = 0;
|
||||
mComponentId.mRtp = mComponentId.mRtcp = -1;
|
||||
}
|
||||
|
||||
// Describes media stream (audio/video) in session
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
Stream();
|
||||
~Stream();
|
||||
RtpPair<int> mComponentId;
|
||||
int mStreamId;
|
||||
unsigned short mPort4;
|
||||
unsigned short mPort6;
|
||||
};
|
||||
|
||||
void setProvider(PDataProvider provider);
|
||||
PDataProvider provider();
|
||||
// Describes media stream (audio/video) in session
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
Stream();
|
||||
~Stream();
|
||||
|
||||
void setSocket4(const RtpPair<PDatagramSocket>& socket);
|
||||
RtpPair<PDatagramSocket>& socket4();
|
||||
void setProvider(PDataProvider provider);
|
||||
PDataProvider provider();
|
||||
|
||||
void setSocket6(const RtpPair<PDatagramSocket>& socket);
|
||||
RtpPair<PDatagramSocket>& socket6();
|
||||
void setSocket4(const RtpPair<PDatagramSocket>& socket);
|
||||
RtpPair<PDatagramSocket>& socket4();
|
||||
|
||||
void setIceInfo(const IceInfo& info);
|
||||
IceInfo iceInfo() const;
|
||||
void setSocket6(const RtpPair<PDatagramSocket>& socket);
|
||||
RtpPair<PDatagramSocket>& socket6();
|
||||
|
||||
// rtcpAttr/rtcpMuxAttr signals about corresponding sip attribute in offer/answer from remote peer
|
||||
bool rtcpAttr() const;
|
||||
void setRtcpAttr(bool value);
|
||||
void setIceInfo(const IceInfo& info);
|
||||
IceInfo iceInfo() const;
|
||||
|
||||
bool rtcpMuxAttr() const;
|
||||
void setRtcpMuxAttr(bool value);
|
||||
// rtcpAttr/rtcpMuxAttr signals about corresponding sip attribute in offer/answer from remote peer
|
||||
bool rtcpAttr() const;
|
||||
void setRtcpAttr(bool value);
|
||||
|
||||
protected:
|
||||
// Provider for corresponding stream
|
||||
PDataProvider mProvider;
|
||||
bool rtcpMuxAttr() const;
|
||||
void setRtcpMuxAttr(bool value);
|
||||
|
||||
// Socket for stream
|
||||
RtpPair<PDatagramSocket> mSocket4, mSocket6;
|
||||
protected:
|
||||
// Provider for corresponding stream
|
||||
PDataProvider mProvider;
|
||||
|
||||
bool mRtcpAttr;
|
||||
bool mRtcpMuxAttr;
|
||||
IceInfo mIceInfo;
|
||||
};
|
||||
// Socket for stream
|
||||
RtpPair<PDatagramSocket> mSocket4, mSocket6;
|
||||
|
||||
Session(PAccount account);
|
||||
virtual ~Session();
|
||||
bool mRtcpAttr;
|
||||
bool mRtcpMuxAttr;
|
||||
IceInfo mIceInfo;
|
||||
};
|
||||
|
||||
// Starts call to specified peer
|
||||
void start(const std::string& peer);
|
||||
Session(PAccount account);
|
||||
virtual ~Session();
|
||||
|
||||
// Stops call
|
||||
void stop();
|
||||
// Starts call to specified peer
|
||||
void start(const std::string& peer);
|
||||
|
||||
// Accepts call
|
||||
void accept();
|
||||
// Stops call
|
||||
void stop();
|
||||
|
||||
// Rejects call
|
||||
void reject(int code);
|
||||
// Accepts call
|
||||
void accept();
|
||||
|
||||
enum class InfoOptions
|
||||
{
|
||||
Standard = 0,
|
||||
Detailed = 1,
|
||||
};
|
||||
// Rejects call
|
||||
void reject(int code);
|
||||
|
||||
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
|
||||
int id() const;
|
||||
void getSessionInfo(InfoOptions options, VariantMap& result);
|
||||
|
||||
// Returns owning account
|
||||
PAccount account();
|
||||
// Returns integer identifier of the session; it is unique amongst all session in application
|
||||
int id() const;
|
||||
|
||||
typedef std::map<std::string, std::string> UserHeaders;
|
||||
void setUserHeaders(const UserHeaders& headers);
|
||||
// Returns owning account
|
||||
PAccount account();
|
||||
|
||||
// Called when new media data are available for this session
|
||||
void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize);
|
||||
typedef std::map<std::string, std::string> UserHeaders;
|
||||
void setUserHeaders(const UserHeaders& headers);
|
||||
|
||||
// Called when new candidate is gathered
|
||||
void onCandidateGathered(ice::Stack* stack, void* tag, const char* address);
|
||||
// Called when new media data are available for this session
|
||||
void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize);
|
||||
|
||||
// Called when connectivity check is finished
|
||||
void onCheckFinished(ice::Stack* stack, void* tag, const char* checkDescription);
|
||||
// Called when new candidate is gathered
|
||||
void onCandidateGathered(ice::Stack* stack, void* tag, const char* address);
|
||||
|
||||
// Called when ICE candidates are gathered - with success or timeout.
|
||||
void onGathered(ice::Stack* stack, void* tag);
|
||||
// Called when connectivity check is finished
|
||||
void onCheckFinished(ice::Stack* stack, void* tag, const char* checkDescription);
|
||||
|
||||
// Called when ICE connectivity check is good at least for one of required streams
|
||||
void onSuccess(ice::Stack* stack, void* tag);
|
||||
// Called when ICE candidates are gathered - with success or timeout.
|
||||
void onGathered(ice::Stack* stack, void* tag);
|
||||
|
||||
// Called when ICE connectivity check is failed for all of required streams
|
||||
void onFailed(ice::Stack* stack, void* tag);
|
||||
// Called when ICE connectivity check is good at least for one of required streams
|
||||
void onSuccess(ice::Stack* stack, void* tag);
|
||||
|
||||
// Called when ICE stack detects network change during the call
|
||||
void onNetworkChange(ice::Stack* stack, void* tag);
|
||||
// Called when ICE connectivity check is failed for all of required streams
|
||||
void onFailed(ice::Stack* stack, void* tag);
|
||||
|
||||
// Fills SDP according to ICE and provider's data
|
||||
void buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection);
|
||||
// Called when ICE stack detects network change during the call
|
||||
void onNetworkChange(ice::Stack* stack, void* tag);
|
||||
|
||||
// Searches provider by its local port number
|
||||
PDataProvider findProviderByPort(int family, unsigned short port);
|
||||
// Fills SDP according to ICE and provider's data
|
||||
void buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection);
|
||||
|
||||
// Add provider to internal list
|
||||
void addProvider(PDataProvider provider);
|
||||
PDataProvider providerAt(int index);
|
||||
int getProviderCount();
|
||||
// Searches provider by its local port number
|
||||
PDataProvider findProviderByPort(int family, unsigned short port);
|
||||
|
||||
void setUserAgent(UserAgent* agent);
|
||||
UserAgent* userAgent();
|
||||
// Add provider to internal list
|
||||
void addProvider(PDataProvider provider);
|
||||
PDataProvider providerAt(int index);
|
||||
int getProviderCount();
|
||||
|
||||
// Pauses and resumes all providers; updates states
|
||||
void pause();
|
||||
void resume();
|
||||
void refreshMediaPath();
|
||||
void setUserAgent(UserAgent* agent);
|
||||
UserAgent* userAgent();
|
||||
|
||||
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
||||
// There are passing string objects by value; this is correct; this values will modified on the stack.
|
||||
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
|
||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
||||
// Pauses and resumes all providers; updates states
|
||||
void pause();
|
||||
void resume();
|
||||
void refreshMediaPath();
|
||||
|
||||
// Session ID
|
||||
int mSessionId;
|
||||
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
|
||||
// There are passing string objects by value; this is correct; this values will modified on the stack.
|
||||
int processSdp(uint64_t version, bool iceAvailable, std::string icePwd, const std::string iceUfrag,
|
||||
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
|
||||
|
||||
// Media streams collection
|
||||
std::vector<Stream> mStreamList;
|
||||
// Session ID
|
||||
int mSessionId;
|
||||
|
||||
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
||||
std::shared_ptr<ice::Stack> mIceStack;
|
||||
// Media streams collection
|
||||
std::vector<Stream> mStreamList;
|
||||
|
||||
// Pointer to owner user agent instance
|
||||
UserAgent* mUserAgent;
|
||||
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
|
||||
std::shared_ptr<ice::Stack> mIceStack;
|
||||
|
||||
// Remote peer SIP address
|
||||
resip::NameAddr mRemotePeer;
|
||||
// Pointer to owner user agent instance
|
||||
UserAgent* mUserAgent;
|
||||
|
||||
// Mutex to protect this instance
|
||||
Mutex mGuard;
|
||||
// Remote peer SIP address
|
||||
resip::NameAddr mRemotePeer;
|
||||
|
||||
// SDP's origin version for sending
|
||||
int mOriginVersion;
|
||||
uint64_t mRemoteOriginVersion;
|
||||
// Mutex to protect this instance
|
||||
Mutex mGuard;
|
||||
|
||||
// SDP's session version
|
||||
int mSessionVersion;
|
||||
// SDP's origin version for sending
|
||||
int mOriginVersion;
|
||||
uint64_t mRemoteOriginVersion;
|
||||
|
||||
// Marks if this session does not need OnNewSession event
|
||||
bool mAcceptedByEngine;
|
||||
bool mAcceptedByUser;
|
||||
// SDP's session version
|
||||
int mSessionVersion;
|
||||
|
||||
// Invite session handle
|
||||
resip::InviteSessionHandle mInviteHandle;
|
||||
// Marks if this session does not need OnNewSession event
|
||||
bool mAcceptedByEngine;
|
||||
bool mAcceptedByUser;
|
||||
|
||||
// Dialog set object pointer
|
||||
ResipSession* mResipSession;
|
||||
// Invite session handle
|
||||
resip::InviteSessionHandle mInviteHandle;
|
||||
|
||||
// Reference counter
|
||||
int mRefCount;
|
||||
// Dialog set object pointer
|
||||
ResipSession* mResipSession;
|
||||
|
||||
enum
|
||||
{
|
||||
Initiator = 1,
|
||||
Acceptor = 2
|
||||
};
|
||||
// Reference counter
|
||||
int mRefCount;
|
||||
|
||||
// Specifies session role - caller (Initiator) or callee (Acceptor)
|
||||
volatile int mRole;
|
||||
enum
|
||||
{
|
||||
Initiator = 1,
|
||||
Acceptor = 2
|
||||
};
|
||||
|
||||
// Marks if candidates are gather already
|
||||
volatile bool mGatheredCandidates;
|
||||
// Specifies session role - caller (Initiator) or callee (Acceptor)
|
||||
volatile int mRole;
|
||||
|
||||
// Marks if OnTerminated event was called already on session
|
||||
volatile bool mTerminated;
|
||||
// Marks if candidates are gather already
|
||||
volatile bool mGatheredCandidates;
|
||||
|
||||
// User friend remote peer's sip address
|
||||
std::string mRemoteAddress;
|
||||
// Marks if OnTerminated event was called already on session
|
||||
volatile bool mTerminated;
|
||||
|
||||
// Application specific data
|
||||
void* mTag;
|
||||
|
||||
// Used to count number of transistions to Connected state and avoid multiple onEstablished events.
|
||||
int mOfferAnswerCounter;
|
||||
// User friend remote peer's sip address
|
||||
std::string mRemoteAddress;
|
||||
|
||||
// List of turn prefixes related to sessioj
|
||||
std::vector<int> mTurnPrefixList;
|
||||
// Application specific data
|
||||
void* mTag;
|
||||
|
||||
// True if user agent has to send offer
|
||||
bool mHasToSendOffer;
|
||||
// Used to count number of transistions to Connected state and avoid multiple onEstablished events.
|
||||
int mOfferAnswerCounter;
|
||||
|
||||
// True if user agent has to enqueue offer after ice gather finished
|
||||
bool mSendOfferUpdateAfterIceGather;
|
||||
// List of turn prefixes related to sessioj
|
||||
std::vector<int> mTurnPrefixList;
|
||||
|
||||
// Related sip account
|
||||
PAccount mAccount;
|
||||
// True if user agent has to send offer
|
||||
bool mHasToSendOffer;
|
||||
|
||||
// User headers for INVITE transaction
|
||||
UserHeaders mUserHeaders;
|
||||
// True if user agent has to enqueue offer after ice gather finished
|
||||
bool mSendOfferUpdateAfterIceGather;
|
||||
|
||||
std::string remoteAddress() const;
|
||||
void setRemoteAddress(const std::string& address);
|
||||
// Related sip account
|
||||
PAccount mAccount;
|
||||
|
||||
void* tag();
|
||||
void setTag(void* tag);
|
||||
int sessionId();
|
||||
int increaseSdpVersion();
|
||||
int addRef();
|
||||
int release();
|
||||
// User headers for INVITE transaction
|
||||
UserHeaders mUserHeaders;
|
||||
|
||||
// Deletes providers and media sockets
|
||||
void clearProvidersAndSockets();
|
||||
std::string remoteAddress() const;
|
||||
void setRemoteAddress(const std::string& address);
|
||||
|
||||
// Deletes providers
|
||||
void clearProviders();
|
||||
void* tag();
|
||||
void setTag(void* tag);
|
||||
int sessionId();
|
||||
int increaseSdpVersion();
|
||||
int addRef();
|
||||
int release();
|
||||
|
||||
// Helper method to find audio provider for active sip stream
|
||||
AudioProvider* findProviderForActiveAudio();
|
||||
// Deletes providers and media sockets
|
||||
void clearProvidersAndSockets();
|
||||
|
||||
void processCommandList();
|
||||
void addCommand(Command* cmd);
|
||||
void enqueueOffer();
|
||||
void processQueuedOffer();
|
||||
static int generateId();
|
||||
static std::atomic_int IdGenerator;
|
||||
static std::atomic_int InstanceCounter;
|
||||
// Deletes providers
|
||||
void clearProviders();
|
||||
|
||||
// Helper method to find audio provider for active sip stream
|
||||
AudioProvider* findProviderForActiveAudio();
|
||||
|
||||
void processCommandList();
|
||||
void addCommand(Command* cmd);
|
||||
void enqueueOffer();
|
||||
void processQueuedOffer();
|
||||
static int generateId();
|
||||
static std::atomic_int IdGenerator;
|
||||
static std::atomic_int InstanceCounter;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Session> PSession;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Classes that provide the mapping between Application Data and DUM
|
||||
// Classes that provide the mapping between Application Data and DUM
|
||||
// dialogs/dialogsets
|
||||
//
|
||||
//
|
||||
// The DUM layer creates an AppDialog/AppDialogSet object for inbound/outbound
|
||||
// SIP Request that results in Dialog creation.
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
class ResipSessionAppDialog : public resip::AppDialog
|
||||
{
|
||||
public:
|
||||
ResipSessionAppDialog(resip::HandleManager& ham);
|
||||
virtual ~ResipSessionAppDialog();
|
||||
ResipSessionAppDialog(resip::HandleManager& ham);
|
||||
virtual ~ResipSessionAppDialog();
|
||||
};
|
||||
|
||||
class ResipSession: public resip::AppDialogSet
|
||||
class ResipSession : public resip::AppDialogSet
|
||||
{
|
||||
friend class UserAgent;
|
||||
friend class Account;
|
||||
friend class UserAgent;
|
||||
friend class Account;
|
||||
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Type_None,
|
||||
Type_Registration,
|
||||
Type_Subscription,
|
||||
Type_Call,
|
||||
Type_Auto
|
||||
};
|
||||
static std::atomic_int InstanceCounter;
|
||||
enum Type
|
||||
{
|
||||
Type_None,
|
||||
Type_Registration,
|
||||
Type_Subscription,
|
||||
Type_Call,
|
||||
Type_Auto
|
||||
};
|
||||
static std::atomic_int InstanceCounter;
|
||||
|
||||
|
||||
ResipSession(resip::DialogUsageManager& dum);
|
||||
virtual ~ResipSession();
|
||||
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
||||
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||
|
||||
void setType(Type type);
|
||||
Type type();
|
||||
|
||||
Session* session();
|
||||
void setSession(Session* session);
|
||||
|
||||
UserAgent* ua();
|
||||
void setUa(UserAgent* ua);
|
||||
|
||||
// Used for subscriptions/messages
|
||||
int sessionId();
|
||||
|
||||
// Used for subscriptions/messages
|
||||
void* tag() const;
|
||||
void setTag(void* tag);
|
||||
ResipSession(resip::DialogUsageManager& dum);
|
||||
virtual ~ResipSession();
|
||||
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
|
||||
virtual std::shared_ptr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
|
||||
|
||||
// Used for subscriptions/messages
|
||||
std::string remoteAddress() const;
|
||||
void setRemoteAddress(std::string address);
|
||||
void setType(Type type);
|
||||
Type type();
|
||||
|
||||
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
||||
Session* session();
|
||||
void setSession(Session* session);
|
||||
|
||||
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
|
||||
UserAgent* ua();
|
||||
void setUa(UserAgent* ua);
|
||||
|
||||
// Used for subscriptions/messages
|
||||
int sessionId();
|
||||
|
||||
// Used for subscriptions/messages
|
||||
void* tag() const;
|
||||
void setTag(void* tag);
|
||||
|
||||
// Used for subscriptions/messages
|
||||
std::string remoteAddress() const;
|
||||
void setRemoteAddress(std::string address);
|
||||
|
||||
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
|
||||
|
||||
void setUASProfile(const std::shared_ptr<resip::UserProfile>& profile);
|
||||
|
||||
protected:
|
||||
bool mTerminated;
|
||||
UserAgent* mUserAgent;
|
||||
Type mType;
|
||||
Session* mSession;
|
||||
int mSessionId;
|
||||
std::string mRemoteAddress;
|
||||
void* mTag;
|
||||
bool mOnWatchingStartSent;
|
||||
std::shared_ptr<resip::UserProfile> mUASProfile;
|
||||
bool mTerminated;
|
||||
UserAgent* mUserAgent;
|
||||
Type mType;
|
||||
Session* mSession;
|
||||
int mSessionId;
|
||||
std::string mRemoteAddress;
|
||||
void* mTag;
|
||||
bool mOnWatchingStartSent;
|
||||
std::shared_ptr<resip::UserProfile> mUASProfile;
|
||||
};
|
||||
|
||||
|
||||
class ResipSessionFactory : public resip::AppDialogSetFactory
|
||||
{
|
||||
public:
|
||||
ResipSessionFactory(UserAgent* agent);
|
||||
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
|
||||
ResipSessionFactory(UserAgent* agent);
|
||||
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
|
||||
|
||||
protected:
|
||||
UserAgent* mAgent;
|
||||
};
|
||||
UserAgent* mAgent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+16
-16
@@ -9,7 +9,7 @@
|
||||
#define USE_SPEEX_AEC
|
||||
|
||||
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
|
||||
//#define USE_WEBRTC_AEC
|
||||
// #define USE_WEBRTC_AEC
|
||||
#define USER
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#define AUDIO_SAMPLERATE 48000
|
||||
#define AUDIO_MIC_BUFFER_COUNT 16
|
||||
#define AUDIO_MIC_BUFFER_LENGTH 10
|
||||
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
#define AUDIO_SPK_BUFFER_COUNT 16
|
||||
#define AUDIO_SPK_BUFFER_LENGTH 10
|
||||
#define AUDIO_SPK_BUFFER_SIZE (AUDIO_SPK_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
|
||||
@@ -36,25 +36,25 @@
|
||||
|
||||
|
||||
// Defined these two lines to get dumping of audio input/output
|
||||
//#define AUDIO_DUMPINPUT
|
||||
//#define AUDIO_DUMPOUTPUT
|
||||
// #define AUDIO_DUMPINPUT
|
||||
// #define AUDIO_DUMPOUTPUT
|
||||
|
||||
|
||||
#define UA_REGISTRATION_TIME 3600
|
||||
#define UA_MEDIA_PORT_START 20000
|
||||
#define UA_MEDIA_PORT_FINISH 30000
|
||||
#define UA_MAX_UDP_PACKET_SIZE 576
|
||||
#define UA_PUBLICATION_ID "314"
|
||||
#define UA_REGISTRATION_TIME 3600
|
||||
#define UA_MEDIA_PORT_START 20000
|
||||
#define UA_MEDIA_PORT_FINISH 30000
|
||||
#define UA_MAX_UDP_PACKET_SIZE 576
|
||||
#define UA_PUBLICATION_ID "314"
|
||||
|
||||
#define MT_SAMPLERATE AUDIO_SAMPLERATE
|
||||
|
||||
#define MT_MAXAUDIOFRAME 1440
|
||||
#define MT_MAXRTPPACKET 1500
|
||||
#define MT_DTMF_END_PACKETS 3
|
||||
#define MT_MAXAUDIOFRAME 1440
|
||||
#define MT_MAXRTPPACKET 1500
|
||||
#define MT_DTMF_END_PACKETS 3
|
||||
|
||||
// Milliseconds before
|
||||
#define RTP_BUFFER_HIGH (2000)
|
||||
#define RTP_BUFFER_LOW (0)
|
||||
#define RTP_BUFFER_HIGH (2000)
|
||||
#define RTP_BUFFER_LOW (0)
|
||||
#define RTP_BUFFER_PREBUFFER (100)
|
||||
|
||||
#define RTP_DECODED_CAPACITY 2048
|
||||
@@ -107,13 +107,13 @@
|
||||
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
# define TEXT(X) X
|
||||
#define TEXT(X) X
|
||||
#endif
|
||||
|
||||
// In milliseconds
|
||||
#define MT_SEVANA_FRAME_TIME 680
|
||||
|
||||
// Number of samples
|
||||
#define MT_MAX_DECODEBUFFER 32768
|
||||
#define MT_MAX_DECODEBUFFER 32768
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
|
||||
#include "HL_AsyncCommand.h"
|
||||
|
||||
AsyncCommand::AsyncCommand()
|
||||
{
|
||||
AsyncCommand::AsyncCommand() {}
|
||||
|
||||
}
|
||||
|
||||
AsyncCommand::~AsyncCommand()
|
||||
{
|
||||
|
||||
}
|
||||
AsyncCommand::~AsyncCommand() {}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
class AsyncCommand
|
||||
{
|
||||
public:
|
||||
AsyncCommand();
|
||||
virtual ~AsyncCommand();
|
||||
AsyncCommand();
|
||||
virtual ~AsyncCommand();
|
||||
|
||||
virtual void run(void* environment) = 0;
|
||||
virtual bool finished() = 0;
|
||||
virtual void run(void* environment) = 0;
|
||||
virtual bool finished() = 0;
|
||||
};
|
||||
|
||||
#endif // HL_ASYNCCOMMAND_H
|
||||
|
||||
+239
-191
@@ -4,253 +4,301 @@
|
||||
#include <string>
|
||||
|
||||
const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
class Base64 {
|
||||
public:
|
||||
static bool Encode(const std::string &in, std::string *out) {
|
||||
int i = 0, j = 0;
|
||||
size_t enc_len = 0;
|
||||
unsigned char a3[3];
|
||||
unsigned char a4[4];
|
||||
class Base64
|
||||
{
|
||||
public:
|
||||
static bool Encode(const std::string& in, std::string* out)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
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();
|
||||
std::string::const_iterator input = in.begin();
|
||||
int input_len = in.size();
|
||||
std::string::const_iterator input = in.begin();
|
||||
|
||||
while (input_len--) {
|
||||
a3[i++] = *(input++);
|
||||
if (i == 3) {
|
||||
a3_to_a4(a4, a3);
|
||||
while (input_len--)
|
||||
{
|
||||
a3[i++] = *(input++);
|
||||
if (i == 3)
|
||||
{
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
if (i)
|
||||
{
|
||||
for (j = i; j < 3; j++)
|
||||
{
|
||||
a3[j] = '\0';
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++) {
|
||||
a3[j] = '\0';
|
||||
}
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
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++) {
|
||||
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
|
||||
}
|
||||
|
||||
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]];
|
||||
while ((i++ < 3))
|
||||
{
|
||||
(*out)[enc_len++] = '=';
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
return (enc_len == out->size());
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++) {
|
||||
a3[j] = '\0';
|
||||
}
|
||||
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];
|
||||
|
||||
a3_to_a4(a4, a3);
|
||||
size_t encoded_length = EncodedLength(input_length);
|
||||
|
||||
for (j = 0; j < i + 1; j++) {
|
||||
*out++ = kBase64Alphabet[a4[j]];
|
||||
}
|
||||
if (out_length < encoded_length)
|
||||
return false;
|
||||
|
||||
while ((i++ < 3)) {
|
||||
*out++ = '=';
|
||||
}
|
||||
}
|
||||
while (input_length--)
|
||||
{
|
||||
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) {
|
||||
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]);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
if (i)
|
||||
{
|
||||
for (j = i; j < 3; j++)
|
||||
{
|
||||
a3[j] = '\0';
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
(*out)[dec_len++] = a3[i];
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
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) {
|
||||
for (j = i; j < 4; j++) {
|
||||
a4[j] = '\0';
|
||||
}
|
||||
static bool Decode(const std::string& in, std::string* out)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
size_t dec_len = 0;
|
||||
unsigned char a3[3];
|
||||
unsigned char a4[4];
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
a4[j] = b64_lookup(a4[j]);
|
||||
}
|
||||
int input_len = in.size();
|
||||
std::string::const_iterator input = in.begin();
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
out->resize(DecodedLength(in));
|
||||
|
||||
for (j = 0; j < i - 1; j++) {
|
||||
(*out)[dec_len++] = a3[j];
|
||||
}
|
||||
}
|
||||
while (input_len--)
|
||||
{
|
||||
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) {
|
||||
int i = 0, j = 0;
|
||||
char *out_begin = out;
|
||||
unsigned char a3[3];
|
||||
unsigned char a4[4];
|
||||
a4_to_a3(a3, a4);
|
||||
|
||||
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;
|
||||
|
||||
while (input_length--) {
|
||||
if (*input == '=') {
|
||||
break;
|
||||
}
|
||||
|
||||
a4[i++] = *(input++);
|
||||
if (i == 4) {
|
||||
for (i = 0; i <4; i++) {
|
||||
a4[i] = b64_lookup(a4[i]);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
if (i)
|
||||
{
|
||||
for (j = i; j < 4; j++)
|
||||
{
|
||||
a4[j] = '\0';
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
*out++ = a3[i];
|
||||
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)[dec_len++] = a3[j];
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
return (dec_len == out->size());
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 4; j++) {
|
||||
a4[j] = '\0';
|
||||
}
|
||||
static bool Decode(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];
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
a4[j] = b64_lookup(a4[j]);
|
||||
}
|
||||
size_t decoded_length = DecodedLength(input, input_length);
|
||||
|
||||
a4_to_a3(a3,a4);
|
||||
if (out_length < decoded_length)
|
||||
return false;
|
||||
|
||||
for (j = 0; j < i - 1; j++) {
|
||||
*out++ = a3[j];
|
||||
}
|
||||
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);
|
||||
|
||||
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) {
|
||||
int numEq = 0;
|
||||
const char* in_end = in + in_length;
|
||||
while (*--in_end == '=')
|
||||
++numEq;
|
||||
|
||||
const char *in_end = in + in_length;
|
||||
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 * in_length) / 8) - 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) {
|
||||
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
|
||||
}
|
||||
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it)
|
||||
{
|
||||
++numEq;
|
||||
}
|
||||
|
||||
inline static int EncodedLength(const std::string &in) {
|
||||
return EncodedLength(in.length());
|
||||
}
|
||||
return ((6 * n) / 8) - numEq;
|
||||
}
|
||||
|
||||
inline static void StripPadding(std::string *in) {
|
||||
while (!in->empty() && *(in->rbegin()) == '=') in->resize(in->size() - 1);
|
||||
}
|
||||
inline static int EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; }
|
||||
|
||||
private:
|
||||
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);
|
||||
}
|
||||
inline static int EncodedLength(const std::string& in) { return EncodedLength(in.length()); }
|
||||
|
||||
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];
|
||||
}
|
||||
inline static void StripPadding(std::string* in)
|
||||
{
|
||||
while (!in->empty() && *(in->rbegin()) == '=')
|
||||
in->resize(in->size() - 1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
private:
|
||||
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)
|
||||
{
|
||||
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
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
#include "ice/ICEByteBuffer.h"
|
||||
|
||||
typedef ice::ByteBuffer ByteBuffer;
|
||||
typedef ice::PByteBuffer PByteBuffer;
|
||||
typedef ice::BitReader BitReader;
|
||||
typedef ice::BitWriter BitWriter;
|
||||
typedef ice::ByteBuffer ByteBuffer;
|
||||
typedef ice::PByteBuffer PByteBuffer;
|
||||
typedef ice::BitReader BitReader;
|
||||
typedef ice::BitWriter BitWriter;
|
||||
typedef ice::BufferReader BufferReader;
|
||||
typedef ice::BufferWriter BufferWriter;
|
||||
|
||||
|
||||
+480
-467
File diff suppressed because it is too large
Load Diff
@@ -14,64 +14,65 @@
|
||||
|
||||
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;
|
||||
|
||||
typedef int(__stdcall *CrUninstallProc)();
|
||||
typedef int(__stdcall* CrUninstallProc)();
|
||||
static CrUninstallProc CrUninstall = nullptr;
|
||||
|
||||
typedef int(__stdcall *CrInstallIntoCurrentThreadProc)(DWORD dwFlags);
|
||||
typedef int(__stdcall* CrInstallIntoCurrentThreadProc)(DWORD dwFlags);
|
||||
static CrInstallIntoCurrentThreadProc CrInstallIntoCurrentThread = nullptr;
|
||||
|
||||
typedef int(__stdcall *CrUninstallFromCurrentThreadProc)();
|
||||
typedef int(__stdcall* CrUninstallFromCurrentThreadProc)();
|
||||
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 HMODULE CrLibraryHandle = NULL;
|
||||
static HMODULE CrLibraryHandle = NULL;
|
||||
#endif
|
||||
|
||||
void CrashReporter::init(const std::string& appname, const std::string& version, const std::string& url)
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
#if defined(CRASHRPT_DYNAMIC)
|
||||
// Check if DLL functions are here
|
||||
if (CrLibraryHandle)
|
||||
return; // Library is loaded already - so initialized already
|
||||
// Check if DLL functions are here
|
||||
if (CrLibraryHandle)
|
||||
return; // Library is loaded already - so initialized already
|
||||
|
||||
CrLibraryHandle = ::LoadLibrary(TEXT("crashrpt.dll"));
|
||||
if (!CrLibraryHandle)
|
||||
return; // No logging here - initialization happens on very first stages, no chance to log anything
|
||||
|
||||
CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW");
|
||||
CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall");
|
||||
CrInstallIntoCurrentThread = (CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2");
|
||||
CrUninstallFromCurrentThread = (CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread");
|
||||
CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW");
|
||||
CrLibraryHandle = ::LoadLibrary(TEXT("crashrpt.dll"));
|
||||
if (!CrLibraryHandle)
|
||||
return; // No logging here - initialization happens on very first stages, no chance to log anything
|
||||
|
||||
CrInstall = (CrInstallProc)::GetProcAddress(CrLibraryHandle, "crInstallW");
|
||||
CrUninstall = (CrUninstallProc)::GetProcAddress(CrLibraryHandle, "crUninstall");
|
||||
CrInstallIntoCurrentThread =
|
||||
(CrInstallIntoCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crInstallToCurrentThread2");
|
||||
CrUninstallFromCurrentThread =
|
||||
(CrUninstallFromCurrentThreadProc)::GetProcAddress(CrLibraryHandle, "crUninstallFromCurrentThread");
|
||||
CrGetLastErrorMsg = (CrGetLastErrorMsgProc)::GetProcAddress(CrLibraryHandle, "crGetLastErrorMsgW");
|
||||
#else
|
||||
CrInstall = &crInstallW;
|
||||
CrUninstall = &crUninstall;
|
||||
CrInstallIntoCurrentThread = &crInstallToCurrentThread2;
|
||||
CrUninstallFromCurrentThread = &crUninstallFromCurrentThread;
|
||||
CrGetLastErrorMsg = &crGetLastErrorMsgW;
|
||||
CrInstall = &crInstallW;
|
||||
CrUninstall = &crUninstall;
|
||||
CrInstallIntoCurrentThread = &crInstallToCurrentThread2;
|
||||
CrUninstallFromCurrentThread = &crUninstallFromCurrentThread;
|
||||
CrGetLastErrorMsg = &crGetLastErrorMsgW;
|
||||
#endif
|
||||
|
||||
if (!isLoaded())
|
||||
return;
|
||||
if (!isLoaded())
|
||||
return;
|
||||
|
||||
CR_INSTALL_INFO info;
|
||||
memset(&info, 0, sizeof(CR_INSTALL_INFO));
|
||||
info.cb = sizeof(CR_INSTALL_INFO);
|
||||
CR_INSTALL_INFO info;
|
||||
memset(&info, 0, sizeof(CR_INSTALL_INFO));
|
||||
info.cb = sizeof(CR_INSTALL_INFO);
|
||||
struct
|
||||
{
|
||||
std::wstring appname, version, url;
|
||||
} unicode;
|
||||
unicode.appname = StringHelper::makeTstring(appname),
|
||||
unicode.version = StringHelper::makeTstring(version),
|
||||
unicode.appname = StringHelper::makeTstring(appname), unicode.version = StringHelper::makeTstring(version),
|
||||
unicode.url = StringHelper::makeTstring(url);
|
||||
|
||||
if (unicode.appname.empty())
|
||||
@@ -83,111 +84,105 @@ void CrashReporter::init(const std::string& appname, const std::string& version,
|
||||
if (unicode.version.empty())
|
||||
unicode.version = L"General version";
|
||||
|
||||
info.pszAppName = unicode.appname.c_str();
|
||||
info.pszAppVersion = unicode.version.c_str();
|
||||
info.pszEmailSubject = TEXT("Crash report");
|
||||
//info.pszEmailTo = L"amegyeri@minerva-soft.com";
|
||||
//info.pszUrl = L"http://ftp.minerva-soft.com/crashlog/crashrpt.php";
|
||||
//info.pszUrl = L"http://sip.crypttalk.com/crashlog/crashrpt.php";
|
||||
info.pszUrl = unicode.url.c_str();
|
||||
info.pfnCrashCallback = Callback;
|
||||
info.uPriorities[CR_HTTP] = 1;
|
||||
info.uPriorities[CR_SMTP] = CR_NEGATIVE_PRIORITY;
|
||||
info.uPriorities[CR_SMAPI] = CR_NEGATIVE_PRIORITY;
|
||||
info.dwFlags = 0;
|
||||
info.pszCrashSenderPath = TEXT(".");
|
||||
info.pszAppName = unicode.appname.c_str();
|
||||
info.pszAppVersion = unicode.version.c_str();
|
||||
info.pszEmailSubject = TEXT("Crash report");
|
||||
// info.pszEmailTo = L"amegyeri@minerva-soft.com";
|
||||
// info.pszUrl = L"http://ftp.minerva-soft.com/crashlog/crashrpt.php";
|
||||
// info.pszUrl = L"http://sip.crypttalk.com/crashlog/crashrpt.php";
|
||||
info.pszUrl = unicode.url.c_str();
|
||||
info.pfnCrashCallback = Callback;
|
||||
info.uPriorities[CR_HTTP] = 1;
|
||||
info.uPriorities[CR_SMTP] = CR_NEGATIVE_PRIORITY;
|
||||
info.uPriorities[CR_SMAPI] = CR_NEGATIVE_PRIORITY;
|
||||
info.dwFlags = 0;
|
||||
info.pszCrashSenderPath = TEXT(".");
|
||||
|
||||
int nResult = CrInstall(&info);
|
||||
if (nResult)
|
||||
{
|
||||
wchar_t errorMsg[512] = L"";
|
||||
CrGetLastErrorMsg(errorMsg, 512);
|
||||
OutputDebugStringW(errorMsg);
|
||||
//LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " << errorMsg);
|
||||
}
|
||||
int nResult = CrInstall(&info);
|
||||
if (nResult)
|
||||
{
|
||||
wchar_t errorMsg[512] = L"";
|
||||
CrGetLastErrorMsg(errorMsg, 512);
|
||||
OutputDebugStringW(errorMsg);
|
||||
// LogCritical("Core", << "Failed to install CrashReporter with code " << nResult << " and message " <<
|
||||
// errorMsg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CrashReporter::free()
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
if (isLoaded())
|
||||
{
|
||||
CrUninstall();
|
||||
CrInstall = nullptr;
|
||||
CrUninstall = nullptr;
|
||||
CrInstallIntoCurrentThread = nullptr;
|
||||
CrUninstallFromCurrentThread = nullptr;
|
||||
CrGetLastErrorMsg = nullptr;
|
||||
if (isLoaded())
|
||||
{
|
||||
CrUninstall();
|
||||
CrInstall = nullptr;
|
||||
CrUninstall = nullptr;
|
||||
CrInstallIntoCurrentThread = nullptr;
|
||||
CrUninstallFromCurrentThread = nullptr;
|
||||
CrGetLastErrorMsg = nullptr;
|
||||
#if defined(CRASHRPT_DYNAMIC)
|
||||
::FreeLibrary(CrLibraryHandle); CrLibraryHandle = NULL;
|
||||
::FreeLibrary(CrLibraryHandle);
|
||||
CrLibraryHandle = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CrashReporter::isLoaded()
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg ||
|
||||
!CrInstallIntoCurrentThread || !CrUninstallFromCurrentThread);
|
||||
return !(!CrInstall || !CrUninstall || !CrGetLastErrorMsg || !CrInstallIntoCurrentThread ||
|
||||
!CrUninstallFromCurrentThread);
|
||||
#else
|
||||
return false;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CrashReporter::initThread()
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
if (isLoaded())
|
||||
CrInstallIntoCurrentThread(0);
|
||||
if (isLoaded())
|
||||
CrInstallIntoCurrentThread(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CrashReporter::freeThread()
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
if (isLoaded())
|
||||
CrUninstallFromCurrentThread();
|
||||
if (isLoaded())
|
||||
CrUninstallFromCurrentThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
CrashReporterThreadPoint::CrashReporterThreadPoint()
|
||||
{
|
||||
CrashReporter::initThread();
|
||||
CrashReporter::initThread();
|
||||
}
|
||||
|
||||
CrashReporterThreadPoint::~CrashReporterThreadPoint()
|
||||
{
|
||||
CrashReporter::freeThread();
|
||||
CrashReporter::freeThread();
|
||||
}
|
||||
|
||||
CrashReporterGuard::CrashReporterGuard()
|
||||
{
|
||||
CrashReporter::init("generic");
|
||||
CrashReporter::init("generic");
|
||||
}
|
||||
|
||||
CrashReporterGuard::~CrashReporterGuard()
|
||||
{
|
||||
CrashReporter::free();
|
||||
CrashReporter::free();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
CrashReporterThreadPoint::CrashReporterThreadPoint()
|
||||
{
|
||||
}
|
||||
CrashReporterThreadPoint::CrashReporterThreadPoint() {}
|
||||
|
||||
CrashReporterThreadPoint::~CrashReporterThreadPoint()
|
||||
{
|
||||
}
|
||||
CrashReporterThreadPoint::~CrashReporterThreadPoint() {}
|
||||
|
||||
CrashReporterGuard::CrashReporterGuard()
|
||||
{
|
||||
}
|
||||
CrashReporterGuard::CrashReporterGuard() {}
|
||||
|
||||
CrashReporterGuard::~CrashReporterGuard()
|
||||
{
|
||||
}
|
||||
CrashReporterGuard::~CrashReporterGuard() {}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <string>
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
// Helper class to translate SEH exceptions to C++ - sometimes it is needed
|
||||
@@ -14,35 +14,36 @@
|
||||
class SE_Exception
|
||||
{
|
||||
private:
|
||||
unsigned int nSE;
|
||||
unsigned int nSE;
|
||||
|
||||
public:
|
||||
SE_Exception() {}
|
||||
SE_Exception(unsigned int n) : nSE(n) {}
|
||||
~SE_Exception() {}
|
||||
unsigned int getSeNumber() { return nSE; }
|
||||
SE_Exception() {}
|
||||
SE_Exception(unsigned int n) : nSE(n) {}
|
||||
~SE_Exception() {}
|
||||
unsigned int getSeNumber() { return nSE; }
|
||||
};
|
||||
|
||||
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.
|
||||
// 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)
|
||||
#else
|
||||
# define SET_SEH_TO_CPP
|
||||
#define SET_SEH_TO_CPP
|
||||
#endif
|
||||
|
||||
class CrashReporter
|
||||
{
|
||||
public:
|
||||
static void init(const std::string& appname, const std::string& version = "", const std::string& url = "");
|
||||
static void free();
|
||||
static void initThread();
|
||||
static void freeThread();
|
||||
static bool isLoaded();
|
||||
static void init(const std::string& appname, const std::string& version = "", const std::string& url = "");
|
||||
static void free();
|
||||
static void initThread();
|
||||
static void freeThread();
|
||||
static bool isLoaded();
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
static BOOL WINAPI Callback(LPVOID /*lpvState*/);
|
||||
static BOOL WINAPI Callback(LPVOID /*lpvState*/);
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -50,15 +51,15 @@ public:
|
||||
class CrashReporterThreadPoint
|
||||
{
|
||||
public:
|
||||
CrashReporterThreadPoint();
|
||||
~CrashReporterThreadPoint();
|
||||
CrashReporterThreadPoint();
|
||||
~CrashReporterThreadPoint();
|
||||
};
|
||||
|
||||
class CrashReporterGuard
|
||||
{
|
||||
public:
|
||||
CrashReporterGuard();
|
||||
~CrashReporterGuard();
|
||||
CrashReporterGuard();
|
||||
~CrashReporterGuard();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,28 +2,25 @@
|
||||
#include "HL_String.h"
|
||||
|
||||
// --------- CsvFile ----------------
|
||||
CsvReader::CsvReader(std::istream& stream)
|
||||
:mInputStream(stream)
|
||||
{}
|
||||
CsvReader::CsvReader(std::istream& stream) : mInputStream(stream) {}
|
||||
|
||||
CsvReader::~CsvReader()
|
||||
{}
|
||||
CsvReader::~CsvReader() {}
|
||||
|
||||
std::istream& CsvReader::stream() const
|
||||
{
|
||||
return mInputStream;
|
||||
return mInputStream;
|
||||
}
|
||||
|
||||
bool CsvReader::readLine(std::vector<std::string>& cells)
|
||||
{
|
||||
cells.clear();
|
||||
std::string line;
|
||||
if (!std::getline(mInputStream, line))
|
||||
return false;
|
||||
strx::trim(line);
|
||||
if (line.empty())
|
||||
return false;
|
||||
cells.clear();
|
||||
std::string line;
|
||||
if (!std::getline(mInputStream, line))
|
||||
return false;
|
||||
strx::trim(line);
|
||||
if (line.empty())
|
||||
return false;
|
||||
|
||||
strx::split(line, cells, ",;");
|
||||
return true;
|
||||
strx::split(line, cells, ",;");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
class CsvReader
|
||||
{
|
||||
public:
|
||||
CsvReader(std::istream& stream);
|
||||
~CsvReader();
|
||||
CsvReader(std::istream& stream);
|
||||
~CsvReader();
|
||||
|
||||
void setStream(std::istream& input);
|
||||
std::istream& stream() const;
|
||||
void setStream(std::istream& input);
|
||||
std::istream& stream() const;
|
||||
|
||||
bool readLine(std::vector<std::string>& cells);
|
||||
bool readLine(std::vector<std::string>& cells);
|
||||
|
||||
protected:
|
||||
std::istream& mInputStream;
|
||||
std::istream& mInputStream;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,36 +13,35 @@
|
||||
#include <cstdio>
|
||||
enum
|
||||
{
|
||||
ERR_MEDIA_SOCKET_FAILED = 1, // Failed to create media socket
|
||||
ERR_CANNOT_FIND_SESSION = 2, // Cannot find session
|
||||
ERR_NO_CREDENTIALS = 3, // No credentials to configure instance
|
||||
ERR_BAD_VARIANT_TYPE = 4, // Bad variant type conversion
|
||||
ERR_RINSTANCE = 5,
|
||||
ERR_SRTP = 6, // libsrtp error
|
||||
ERR_WEBRTC = 7, // webrtc error
|
||||
ERR_NOMEM = 8, // no more memory
|
||||
ERR_WMME_FAILED = 9, // WMME error
|
||||
ERR_QPC = 10, // QueryPerformanceCounter failed
|
||||
ERR_BAD_PARAM = 11, // Bad parameter
|
||||
ERR_NET_FAILED = 12, // Call to OS network subsystem failed
|
||||
ERR_NOT_IMPLEMENTED = 13, // Not implemented in this build
|
||||
ERR_MIXER_OVERFLOW = 14, // No more available channels in audio mixer
|
||||
ERR_WAVFILE_FAILED = 15, // Error with .wav file
|
||||
ERR_DSOUND = 16, // DSound error
|
||||
ERR_COREAUDIO = 17, // CoreAudio error
|
||||
ERR_CREATEWINDOW = 18, // CreateWindow failed
|
||||
ERR_MEDIA_SOCKET_FAILED = 1, // Failed to create media socket
|
||||
ERR_CANNOT_FIND_SESSION = 2, // Cannot find session
|
||||
ERR_NO_CREDENTIALS = 3, // No credentials to configure instance
|
||||
ERR_BAD_VARIANT_TYPE = 4, // Bad variant type conversion
|
||||
ERR_RINSTANCE = 5,
|
||||
ERR_SRTP = 6, // libsrtp error
|
||||
ERR_WEBRTC = 7, // webrtc error
|
||||
ERR_NOMEM = 8, // no more memory
|
||||
ERR_WMME_FAILED = 9, // WMME error
|
||||
ERR_QPC = 10, // QueryPerformanceCounter failed
|
||||
ERR_BAD_PARAM = 11, // Bad parameter
|
||||
ERR_NET_FAILED = 12, // Call to OS network subsystem failed
|
||||
ERR_NOT_IMPLEMENTED = 13, // Not implemented in this build
|
||||
ERR_MIXER_OVERFLOW = 14, // No more available channels in audio mixer
|
||||
ERR_WAVFILE_FAILED = 15, // Error with .wav file
|
||||
ERR_DSOUND = 16, // DSound error
|
||||
ERR_COREAUDIO = 17, // CoreAudio error
|
||||
ERR_CREATEWINDOW = 18, // CreateWindow failed
|
||||
ERR_REGISTERNOTIFICATION = 19, // RegisterDeviceNotification failed
|
||||
ERR_PCAP = 20, // Smth bad with libpcap
|
||||
ERR_CACHE_FAILED = 21, // Failed to open cache directory
|
||||
ERR_FILENOTOPEN = 22, // Cannot open the file
|
||||
ERR_OPENSLES = 23 // OpenSL ES failed. Subcode has actual error code.
|
||||
ERR_PCAP = 20, // Smth bad with libpcap
|
||||
ERR_CACHE_FAILED = 21, // Failed to open cache directory
|
||||
ERR_FILENOTOPEN = 22, // Cannot open the file
|
||||
ERR_OPENSLES = 23 // OpenSL ES failed. Subcode has actual error code.
|
||||
};
|
||||
|
||||
class Exception: public std::exception
|
||||
class Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(int code, int subcode = 0)
|
||||
:mCode(code), mSubcode(subcode)
|
||||
Exception(int code, int subcode = 0) : mCode(code), mSubcode(subcode)
|
||||
{
|
||||
std::snprintf(mMessage, sizeof(mMessage), "%d-%d", code, subcode);
|
||||
}
|
||||
@@ -50,35 +49,24 @@ public:
|
||||
Exception(int code, const char* message)
|
||||
{
|
||||
if (message)
|
||||
strncpy(mMessage, message, (sizeof mMessage) - 1 );
|
||||
strncpy(mMessage, message, (sizeof mMessage) - 1);
|
||||
}
|
||||
|
||||
Exception(const Exception& src)
|
||||
:mCode(src.mCode), mSubcode(src.mSubcode)
|
||||
Exception(const Exception& src) : mCode(src.mCode), mSubcode(src.mSubcode)
|
||||
{
|
||||
memcpy(mMessage, src.mMessage, sizeof mMessage);
|
||||
}
|
||||
|
||||
~Exception()
|
||||
{ }
|
||||
~Exception() {}
|
||||
|
||||
int code() const
|
||||
{
|
||||
return mCode;
|
||||
}
|
||||
int code() const { return mCode; }
|
||||
|
||||
int subcode() const
|
||||
{
|
||||
return mSubcode;
|
||||
}
|
||||
int subcode() const { return mSubcode; }
|
||||
|
||||
const char* what() const noexcept
|
||||
{
|
||||
return mMessage;
|
||||
}
|
||||
const char* what() const noexcept { return mMessage; }
|
||||
|
||||
protected:
|
||||
int mCode = 0, mSubcode = 0;
|
||||
int mCode = 0, mSubcode = 0;
|
||||
char mMessage[256] = {0};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
#include <fstream>
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
||||
# include <unistd.h>
|
||||
# include <sys/statvfs.h>
|
||||
# include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <memory.h>
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
#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);
|
||||
if (retcode == 0)
|
||||
@@ -111,11 +112,11 @@ size_t FileHelper::getFreespace(const std::string& path)
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string FileHelper::expandUserHome(const std::string &path)
|
||||
std::string FileHelper::expandUserHome(const std::string& path)
|
||||
{
|
||||
if (path.empty() || path[0] != '~')
|
||||
return path; // No expansion needed
|
||||
|
||||
return path; // No expansion needed
|
||||
|
||||
const char* home_dir = nullptr;
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
@@ -124,7 +125,8 @@ std::string FileHelper::expandUserHome(const std::string &path)
|
||||
{
|
||||
home_dir = std::getenv("HOMEDRIVE");
|
||||
const char* homepath = std::getenv("HOMEPATH");
|
||||
if (home_dir && homepath) {
|
||||
if (home_dir && homepath)
|
||||
{
|
||||
std::string fullpath(home_dir);
|
||||
fullpath += homepath;
|
||||
return fullpath + path.substr(1);
|
||||
@@ -133,9 +135,9 @@ std::string FileHelper::expandUserHome(const std::string &path)
|
||||
#else
|
||||
home_dir = std::getenv("HOME");
|
||||
#endif
|
||||
|
||||
|
||||
if (!home_dir)
|
||||
throw std::runtime_error("Unable to determine the home directory");
|
||||
|
||||
|
||||
return std::string(home_dir) + path.substr(1);
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
class FileHelper
|
||||
{
|
||||
public:
|
||||
static bool exists(const std::string& s);
|
||||
static bool exists(const char* s);
|
||||
static bool exists(const std::string& s);
|
||||
static bool exists(const char* s);
|
||||
|
||||
static void remove(const std::string& s);
|
||||
static void remove(const char* s);
|
||||
static void remove(const std::string& s);
|
||||
static void remove(const char* s);
|
||||
|
||||
// static std::string gettempname();
|
||||
static bool isAbsolute(const std::string& s);
|
||||
static bool isAbsolute(const std::string& s);
|
||||
|
||||
static std::string getCurrentDir();
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
|
||||
// Returns free space on volume for path
|
||||
// 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);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ static const uint32_t HEPID2 = 0x021002;
|
||||
static const uint32_t HEPID3 = 0x48455033;
|
||||
|
||||
|
||||
bool Packet::parseV3(const ByteBuffer& packet)
|
||||
bool Packet::parseV3(const ByteBuffer& packet)
|
||||
{
|
||||
if (packet.size() < 30)
|
||||
return false;
|
||||
|
||||
BufferReader r(packet);
|
||||
char signature[4];
|
||||
char signature[4];
|
||||
r.readBuffer(signature, 4);
|
||||
|
||||
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;
|
||||
|
||||
InternetAddress sourceAddr4, destAddr4, sourceAddr6, destAddr6;
|
||||
uint16_t sourcePort = 0, destPort = 0;
|
||||
uint16_t sourcePort = 0, destPort = 0;
|
||||
while (r.count() < packet.size())
|
||||
{
|
||||
mVendorId = (VendorId)r.readUShort();
|
||||
ChunkType chunkType = (ChunkType)r.readUShort();
|
||||
int chunkLength = r.readUShort();
|
||||
int chunkLength = r.readUShort();
|
||||
|
||||
switch (chunkType)
|
||||
{
|
||||
@@ -102,8 +102,7 @@ bool Packet::parseV3(const ByteBuffer& packet)
|
||||
|
||||
if (!sourceAddr4.isEmpty())
|
||||
mSourceAddress = sourceAddr4;
|
||||
else
|
||||
if (!sourceAddr6.isEmpty())
|
||||
else if (!sourceAddr6.isEmpty())
|
||||
mSourceAddress = sourceAddr6;
|
||||
|
||||
if (!mSourceAddress.isEmpty())
|
||||
@@ -111,8 +110,7 @@ bool Packet::parseV3(const ByteBuffer& packet)
|
||||
|
||||
if (!destAddr4.isEmpty())
|
||||
mDestinationAddress = destAddr4;
|
||||
else
|
||||
if (!destAddr6.isEmpty())
|
||||
else if (!destAddr6.isEmpty())
|
||||
mDestinationAddress = destAddr6;
|
||||
|
||||
if (!mDestinationAddress.isEmpty())
|
||||
@@ -121,7 +119,7 @@ bool Packet::parseV3(const ByteBuffer& packet)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Packet::parseV2(const ByteBuffer &packet)
|
||||
bool Packet::parseV2(const ByteBuffer& packet)
|
||||
{
|
||||
if (packet.size() < 31)
|
||||
return false;
|
||||
@@ -148,16 +146,53 @@ bool Packet::parseV2(const ByteBuffer &packet)
|
||||
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_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());}
|
||||
#define WRITE_CHUNK_UCHAR(T, V) \
|
||||
{ \
|
||||
w.writeUShort((uint16_t)mVendorId); \
|
||||
w.writeUShort((uint16_t)T); \
|
||||
w.writeUShort(1); \
|
||||
w.writeUChar((uint8_t)V); \
|
||||
}
|
||||
#define WRITE_CHUNK_USHORT(T, V) \
|
||||
{ \
|
||||
w.writeUShort((uint16_t)mVendorId); \
|
||||
w.writeUShort((uint16_t)T); \
|
||||
w.writeUShort(2); \
|
||||
w.writeUShort((uint16_t)V); \
|
||||
}
|
||||
#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 r; r.resize(mBody.size() + 512);
|
||||
ByteBuffer r;
|
||||
r.resize(mBody.size() + 512);
|
||||
BufferWriter w(r);
|
||||
|
||||
// Signature
|
||||
@@ -174,9 +209,8 @@ ByteBuffer Packet::buildV3()
|
||||
{
|
||||
if (mSourceAddress.isV4())
|
||||
WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress)
|
||||
else
|
||||
if (mSourceAddress.isV6())
|
||||
WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
|
||||
else if (mSourceAddress.isV6())
|
||||
WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
|
||||
|
||||
WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port());
|
||||
}
|
||||
@@ -186,9 +220,8 @@ ByteBuffer Packet::buildV3()
|
||||
{
|
||||
if (mDestinationAddress.isV4())
|
||||
WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress)
|
||||
else
|
||||
if (mDestinationAddress.isV6())
|
||||
WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
|
||||
else if (mDestinationAddress.isV6())
|
||||
WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
|
||||
|
||||
WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port());
|
||||
}
|
||||
@@ -216,7 +249,9 @@ ByteBuffer Packet::buildV3()
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
namespace HEP
|
||||
{
|
||||
enum class ChunkType
|
||||
{
|
||||
enum class ChunkType
|
||||
{
|
||||
None = 0,
|
||||
IPProtocolFamily,
|
||||
IPProtocolID,
|
||||
@@ -19,17 +19,17 @@ namespace HEP
|
||||
DestinationPort,
|
||||
Timestamp,
|
||||
TimestampMicro,
|
||||
ProtocolType, // Maps to Protocol Types below
|
||||
ProtocolType, // Maps to Protocol Types below
|
||||
CaptureAgentID,
|
||||
KeepAliveTimer,
|
||||
AuthenticationKey,
|
||||
PacketPayload,
|
||||
CompressedPayload,
|
||||
InternalC
|
||||
};
|
||||
};
|
||||
|
||||
enum class VendorId
|
||||
{
|
||||
enum class VendorId
|
||||
{
|
||||
None,
|
||||
FreeSwitch,
|
||||
Kamailio,
|
||||
@@ -37,10 +37,10 @@ namespace HEP
|
||||
Asterisk,
|
||||
Homer,
|
||||
SipXecs
|
||||
};
|
||||
};
|
||||
|
||||
enum class ProtocolId
|
||||
{
|
||||
enum class ProtocolId
|
||||
{
|
||||
Reserved = 0,
|
||||
SIP,
|
||||
XMPP,
|
||||
@@ -54,32 +54,28 @@ namespace HEP
|
||||
IAX,
|
||||
H322,
|
||||
H321
|
||||
};
|
||||
};
|
||||
|
||||
struct Packet
|
||||
{
|
||||
bool parseV3(const ByteBuffer& packet);
|
||||
bool parseV2(const ByteBuffer& packet);
|
||||
ByteBuffer buildV3();
|
||||
struct Packet
|
||||
{
|
||||
bool parseV3(const ByteBuffer& packet);
|
||||
bool parseV2(const ByteBuffer& packet);
|
||||
ByteBuffer buildV3();
|
||||
|
||||
uint8_t
|
||||
mIpProtocolFamily,
|
||||
mIpProtocolId;
|
||||
uint8_t mIpProtocolFamily, mIpProtocolId;
|
||||
|
||||
InternetAddress
|
||||
mSourceAddress,
|
||||
mDestinationAddress;
|
||||
InternetAddress mSourceAddress, mDestinationAddress;
|
||||
|
||||
timeval mTimestamp;
|
||||
ProtocolId mProtocolType;
|
||||
uint16_t mCaptureAgentId;
|
||||
uint16_t mKeepAliveTimer;
|
||||
ByteBuffer mAuthenticateKey;
|
||||
ByteBuffer mBody;
|
||||
VendorId mVendorId;
|
||||
uint32_t mBodyOffset = 0;
|
||||
};
|
||||
timeval mTimestamp;
|
||||
ProtocolId mProtocolType;
|
||||
uint16_t mCaptureAgentId;
|
||||
uint16_t mKeepAliveTimer;
|
||||
ByteBuffer mAuthenticateKey;
|
||||
ByteBuffer mBody;
|
||||
VendorId mVendorId;
|
||||
uint32_t mBodyOffset = 0;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace HEP
|
||||
|
||||
#endif
|
||||
|
||||
+897
-1078
File diff suppressed because it is too large
Load Diff
+23
-24
@@ -6,34 +6,33 @@
|
||||
class IuUP
|
||||
{
|
||||
public:
|
||||
enum class PduType
|
||||
{
|
||||
DataWithCrc = 0,
|
||||
DataNoCrc = 1,
|
||||
ControlProc = 14
|
||||
};
|
||||
enum class PduType
|
||||
{
|
||||
DataWithCrc = 0,
|
||||
DataNoCrc = 1,
|
||||
ControlProc = 14
|
||||
};
|
||||
|
||||
struct Frame
|
||||
{
|
||||
PduType mPduType;
|
||||
uint8_t mFrameNumber;
|
||||
uint8_t mFqc;
|
||||
uint8_t mRfci;
|
||||
uint8_t mHeaderCrc;
|
||||
bool mHeaderCrcOk;
|
||||
uint16_t mPayloadCrc;
|
||||
bool mPayloadCrcOk;
|
||||
const uint8_t* mPayload;
|
||||
uint16_t mPayloadSize;
|
||||
};
|
||||
struct Frame
|
||||
{
|
||||
PduType mPduType;
|
||||
uint8_t mFrameNumber;
|
||||
uint8_t mFqc;
|
||||
uint8_t mRfci;
|
||||
uint8_t mHeaderCrc;
|
||||
bool mHeaderCrcOk;
|
||||
uint16_t mPayloadCrc;
|
||||
bool mPayloadCrcOk;
|
||||
const uint8_t* mPayload;
|
||||
uint16_t mPayloadSize;
|
||||
};
|
||||
|
||||
/* Default value is false */
|
||||
static bool TwoBytePseudoheader;
|
||||
/* Default value is false */
|
||||
static bool TwoBytePseudoheader;
|
||||
|
||||
static bool parse(const uint8_t* packet, int size, Frame& result);
|
||||
static bool parse2(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);
|
||||
};
|
||||
|
||||
|
||||
#endif // HL_IUUP_H
|
||||
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
using ice::GLogger;
|
||||
using ice::LogLock;
|
||||
|
||||
using ice::LL_MEDIA;
|
||||
using ice::LL_DEBUG;
|
||||
using ice::LL_INFO;
|
||||
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_SPECIAL;
|
||||
using ice::LL_ERROR;
|
||||
using ice::LogLevelHelper;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
#include "HL_NetworkFrame.h"
|
||||
#include "HL_InternetAddress.h"
|
||||
|
||||
#define ETHERTYPE_MPLS_UC (0x8847)
|
||||
#define ETHERTYPE_MPLS_MC (0x8848)
|
||||
#define ETHERTYPE_IPV6 (0x86dd)
|
||||
#define ETHERTYPE_IP (0x0800)
|
||||
#define ETHERTYPE_MPLS_UC (0x8847)
|
||||
#define ETHERTYPE_MPLS_MC (0x8848)
|
||||
#define ETHERTYPE_IPV6 (0x86dd)
|
||||
#define ETHERTYPE_IP (0x0800)
|
||||
|
||||
#define MPLS_STACK_MASK (0x00000100)
|
||||
#define MPLS_STACK_SHIFT (8)
|
||||
@@ -34,7 +34,7 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForRaw(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);
|
||||
|
||||
@@ -53,8 +53,7 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
||||
result.mData += sizeof(VlanHeader);
|
||||
result.mLength -= sizeof(VlanHeader);
|
||||
proto = ntohs(vlan->mData);
|
||||
}
|
||||
while (proto == 0x8100);
|
||||
} while (proto == 0x8100);
|
||||
}
|
||||
|
||||
// Skip MPLS headers
|
||||
@@ -63,11 +62,11 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForEthernet(const Packet& data)
|
||||
case ETHERTYPE_MPLS_UC:
|
||||
case ETHERTYPE_MPLS_MC:
|
||||
// Parse MPLS here until marker "bottom of mpls stack"
|
||||
for(bool bottomOfStack = false; !bottomOfStack;
|
||||
bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
||||
for (bool bottomOfStack = false; !bottomOfStack;
|
||||
bottomOfStack = ((ntohl(*(uint32_t*)(result.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
|
||||
{
|
||||
result.mData += 4;
|
||||
result.mLength -=4;
|
||||
result.mLength -= 4;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -156,7 +155,7 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForLoopback(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);
|
||||
if (ip4->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||
return Payload();
|
||||
@@ -187,40 +186,36 @@ NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp4(const Packet& data)
|
||||
struct Ip6Header
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
uint8_t traffic_class_hi:4,
|
||||
version:4;
|
||||
uint8_t flow_label_hi:4,
|
||||
traffic_class_lo:4;
|
||||
uint16_t flow_label_lo;
|
||||
uint8_t traffic_class_hi : 4, version : 4;
|
||||
uint8_t flow_label_hi : 4, traffic_class_lo : 4;
|
||||
uint16_t flow_label_lo;
|
||||
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
uint8_t version:4,
|
||||
traffic_class_hi:4;
|
||||
uint8_t traffic_class_lo:4,
|
||||
flow_label_hi:4;
|
||||
uint16_t flow_label_lo;
|
||||
uint8_t version : 4, traffic_class_hi : 4;
|
||||
uint8_t traffic_class_lo : 4, flow_label_hi : 4;
|
||||
uint16_t flow_label_lo;
|
||||
#else
|
||||
# error "Please fix endianness defines"
|
||||
#error "Please fix endianness defines"
|
||||
#endif
|
||||
|
||||
uint16_t payload_len;
|
||||
uint8_t next_header;
|
||||
uint8_t hop_limit;
|
||||
uint16_t payload_len;
|
||||
uint8_t next_header;
|
||||
uint8_t hop_limit;
|
||||
|
||||
struct in6_addr src_ip;
|
||||
struct in6_addr dst_ip;
|
||||
struct in6_addr src_ip;
|
||||
struct in6_addr dst_ip;
|
||||
};
|
||||
|
||||
NetworkFrame::Payload NetworkFrame::GetUdpPayloadForIp6(const Packet& data)
|
||||
{
|
||||
Packet result(data);
|
||||
Packet result(data);
|
||||
const Ip6Header* ip6 = reinterpret_cast<const Ip6Header*>(result.mData);
|
||||
/*if (ip6->mProtocol != IPPROTO_UDP && ip4->mProtocol != 0)
|
||||
return PacketData(nullptr, 0);
|
||||
*/
|
||||
result.mData += 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);
|
||||
result.mData += sizeof(UdpHeader);
|
||||
|
||||
@@ -10,25 +10,18 @@ public:
|
||||
struct Packet
|
||||
{
|
||||
const uint8_t* mData;
|
||||
size_t mLength;
|
||||
size_t mLength;
|
||||
|
||||
Packet(const uint8_t* data, size_t length)
|
||||
:mData(data), mLength(length)
|
||||
{}
|
||||
Packet(const uint8_t* data, size_t length) : mData(data), mLength(length) {}
|
||||
|
||||
Packet()
|
||||
:mData(nullptr), mLength(0)
|
||||
{}
|
||||
Packet() : mData(nullptr), mLength(0) {}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return mData == nullptr || mLength == 0;
|
||||
}
|
||||
bool is_empty() const { return mData == nullptr || mLength == 0; }
|
||||
};
|
||||
|
||||
struct Payload
|
||||
{
|
||||
Packet data;
|
||||
Packet data;
|
||||
InternetAddress source;
|
||||
InternetAddress dest;
|
||||
};
|
||||
@@ -44,14 +37,14 @@ public:
|
||||
{
|
||||
/* Ethernet addresses are 6 bytes */
|
||||
static const int AddressLength = 6;
|
||||
uint8_t mEtherDHost[AddressLength]; /* Destination host address */
|
||||
uint8_t mEtherSHost[AddressLength]; /* Source host address */
|
||||
uint16_t mEtherType; /* IP? ARP? RARP? etc */
|
||||
uint8_t mEtherDHost[AddressLength]; /* Destination host address */
|
||||
uint8_t mEtherSHost[AddressLength]; /* Source host address */
|
||||
uint16_t mEtherType; /* IP? ARP? RARP? etc */
|
||||
};
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
struct /*__attribute__((packed))*/ LinuxSllHeader
|
||||
#else
|
||||
#else
|
||||
struct __attribute__((packed)) LinuxSllHeader
|
||||
#endif
|
||||
{
|
||||
@@ -70,55 +63,48 @@ public:
|
||||
|
||||
struct Ip4Header
|
||||
{
|
||||
uint8_t mVhl; /* version << 4 | header length >> 2 */
|
||||
uint8_t mTos; /* type of service */
|
||||
uint16_t mLen; /* total length */
|
||||
uint16_t mId; /* identification */
|
||||
uint16_t mOffset; /* fragment offset field */
|
||||
#define IP_RF 0x8000 /* reserved fragment flag */
|
||||
#define IP_DF 0x4000 /* dont fragment flag */
|
||||
#define IP_MF 0x2000 /* more fragments flag */
|
||||
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
|
||||
uint8_t mTtl; /* time to live */
|
||||
uint8_t mProtocol; /* protocol */
|
||||
uint16_t mChecksum; /* checksum */
|
||||
in_addr mSource,
|
||||
mDestination; /* source and dest address */
|
||||
uint8_t mVhl; /* version << 4 | header length >> 2 */
|
||||
uint8_t mTos; /* type of service */
|
||||
uint16_t mLen; /* total length */
|
||||
uint16_t mId; /* identification */
|
||||
uint16_t mOffset; /* fragment offset field */
|
||||
#define IP_RF 0x8000 /* reserved fragment flag */
|
||||
#define IP_DF 0x4000 /* dont fragment flag */
|
||||
#define IP_MF 0x2000 /* more fragments flag */
|
||||
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
|
||||
uint8_t mTtl; /* time to live */
|
||||
uint8_t mProtocol; /* protocol */
|
||||
uint16_t mChecksum; /* checksum */
|
||||
in_addr mSource, mDestination; /* source and dest address */
|
||||
|
||||
int headerLength() const
|
||||
{
|
||||
return (mVhl & 0x0f) * 4;
|
||||
}
|
||||
int headerLength() const { return (mVhl & 0x0f) * 4; }
|
||||
|
||||
int version() const
|
||||
{
|
||||
return mVhl >> 4;
|
||||
}
|
||||
int version() const { return mVhl >> 4; }
|
||||
|
||||
const in_addr& source4() const { return mSource; }
|
||||
const in_addr& dest4() const { return mDestination; }
|
||||
const in_addr& source4() const { return mSource; }
|
||||
const in_addr& dest4() const { return mDestination; }
|
||||
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
|
||||
{
|
||||
uint16_t mSourcePort; /* source port */
|
||||
uint16_t mDestinationPort;
|
||||
uint16_t mDatagramLength; /* datagram length */
|
||||
uint16_t mDatagramChecksum; /* datagram checksum */
|
||||
uint16_t mSourcePort; /* source port */
|
||||
uint16_t mDestinationPort;
|
||||
uint16_t mDatagramLength; /* datagram length */
|
||||
uint16_t mDatagramChecksum; /* datagram checksum */
|
||||
};
|
||||
|
||||
|
||||
struct TcpHeader
|
||||
{
|
||||
uint16_t mSourcePort; /* source port */
|
||||
uint16_t mDestinationPort; /* destination port */
|
||||
uint32_t mSeqNo; /* sequence number */
|
||||
uint32_t mAckNo; /* acknowledgement number */
|
||||
uint32_t mDataOffset; /* data offset, rsvd */
|
||||
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
|
||||
uint8_t mFlags;
|
||||
uint16_t mSourcePort; /* source port */
|
||||
uint16_t mDestinationPort; /* destination port */
|
||||
uint32_t mSeqNo; /* sequence number */
|
||||
uint32_t mAckNo; /* acknowledgement number */
|
||||
uint32_t mDataOffset; /* data offset, rsvd */
|
||||
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
|
||||
uint8_t mFlags;
|
||||
#define TH_FIN 0x01
|
||||
#define TH_SYN 0x02
|
||||
#define TH_RST 0x04
|
||||
@@ -127,11 +113,10 @@ public:
|
||||
#define TH_URG 0x20
|
||||
#define TH_ECE 0x40
|
||||
#define TH_CWR 0x80
|
||||
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
|
||||
uint16_t mWindow; /* window */
|
||||
uint16_t mChecksum; /* checksum */
|
||||
uint16_t mUrgentPointer; /* urgent pointer */
|
||||
#define TH_FLAGS (TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR)
|
||||
uint16_t mWindow; /* window */
|
||||
uint16_t mChecksum; /* checksum */
|
||||
uint16_t mUrgentPointer; /* urgent pointer */
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
|
||||
# include <asm/ioctls.h>
|
||||
#include <asm/ioctls.h>
|
||||
#endif
|
||||
|
||||
#include "../engine_config.h"
|
||||
@@ -12,19 +12,17 @@
|
||||
#include "HL_Log.h"
|
||||
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
# include <fcntl.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#if !defined(TARGET_WIN)
|
||||
# include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#define LOG_SUBSYSTEM "network"
|
||||
|
||||
DatagramSocket::DatagramSocket()
|
||||
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
|
||||
{}
|
||||
DatagramSocket::DatagramSocket() : mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0) {}
|
||||
|
||||
DatagramSocket::~DatagramSocket()
|
||||
{
|
||||
@@ -41,10 +39,12 @@ void DatagramSocket::open(int family)
|
||||
mHandle = ::socket(mFamily, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (mHandle != INVALID_SOCKET)
|
||||
{
|
||||
sockaddr_in addr4; sockaddr_in6 addr6;
|
||||
memset(&addr4, 0, sizeof(addr4)); memset(&addr6, 0, sizeof(addr6));
|
||||
sockaddr_in addr4;
|
||||
sockaddr_in6 addr6;
|
||||
memset(&addr4, 0, sizeof(addr4));
|
||||
memset(&addr6, 0, sizeof(addr6));
|
||||
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
|
||||
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
|
||||
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
|
||||
if (!retcode)
|
||||
{
|
||||
mLocalPort = ntohs(mFamily == AF_INET ? addr4.sin_port : addr6.sin6_port);
|
||||
@@ -57,27 +57,34 @@ int DatagramSocket::localport()
|
||||
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)
|
||||
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)
|
||||
return 0;
|
||||
|
||||
sockaddr* addr = nullptr;
|
||||
socklen_t addrLen = 0;
|
||||
sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}};
|
||||
sockaddr* addr = nullptr;
|
||||
socklen_t addrLen = 0;
|
||||
sockaddr_in addr_4 = {AF_INET, 0, {0}, {0}};
|
||||
sockaddr_in6 addr_6 = {AF_INET6, 0, 0, {0}, 0};
|
||||
switch (mFamily)
|
||||
{
|
||||
case AF_INET: addr = (sockaddr*)&addr_4; addrLen = sizeof(addr_4); break;
|
||||
case AF_INET6: addr = (sockaddr*)&addr_6; addrLen = sizeof(addr_6); break;
|
||||
case AF_INET:
|
||||
addr = (sockaddr*)&addr_4;
|
||||
addrLen = sizeof(addr_4);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr = (sockaddr*)&addr_6;
|
||||
addrLen = sizeof(addr_6);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
@@ -131,7 +138,7 @@ bool DatagramSocket::setBlocking(bool blocking)
|
||||
int flags = fcntl(mHandle, F_GETFL, 0);
|
||||
if (flags < 0)
|
||||
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;
|
||||
#endif
|
||||
#if defined(TARGET_ANDROID)
|
||||
@@ -152,9 +159,7 @@ DatagramAgreggator::DatagramAgreggator()
|
||||
mMaxHandle = 0;
|
||||
}
|
||||
|
||||
DatagramAgreggator::~DatagramAgreggator()
|
||||
{
|
||||
}
|
||||
DatagramAgreggator::~DatagramAgreggator() {}
|
||||
|
||||
void DatagramAgreggator::addSocket(PDatagramSocket socket)
|
||||
{
|
||||
@@ -196,6 +201,6 @@ bool DatagramAgreggator::waitForData(std::chrono::milliseconds timeout)
|
||||
tv.tv_sec = timeout.count() / 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;
|
||||
}
|
||||
|
||||
@@ -15,34 +15,34 @@ class NetworkSocket
|
||||
{
|
||||
public:
|
||||
virtual int localport() = 0;
|
||||
|
||||
};
|
||||
|
||||
class DatagramSocket
|
||||
{
|
||||
friend class SocketHeap;
|
||||
friend class DatagramAgreggator;
|
||||
|
||||
public:
|
||||
DatagramSocket();
|
||||
virtual ~DatagramSocket();
|
||||
|
||||
virtual int localport();
|
||||
virtual int localport();
|
||||
|
||||
virtual void sendDatagram(InternetAddress& dest, const void* packetData, unsigned packetSize);
|
||||
virtual unsigned recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity);
|
||||
virtual void closeSocket();
|
||||
virtual bool isValid() const;
|
||||
virtual int family() const;
|
||||
virtual bool setBlocking(bool blocking);
|
||||
virtual SOCKET socket() const;
|
||||
virtual void sendDatagram(InternetAddress& dest, const void* packetData, unsigned packetSize);
|
||||
virtual unsigned recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity);
|
||||
virtual void closeSocket();
|
||||
virtual bool isValid() const;
|
||||
virtual int family() const;
|
||||
virtual bool setBlocking(bool blocking);
|
||||
virtual SOCKET socket() const;
|
||||
|
||||
virtual void open(int family);
|
||||
virtual void open(int family);
|
||||
|
||||
protected:
|
||||
int mFamily;
|
||||
int mFamily;
|
||||
SOCKET mHandle;
|
||||
int mLocalPort;
|
||||
void internalClose();
|
||||
int mLocalPort;
|
||||
void internalClose();
|
||||
};
|
||||
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
|
||||
|
||||
@@ -52,18 +52,18 @@ public:
|
||||
DatagramAgreggator();
|
||||
~DatagramAgreggator();
|
||||
|
||||
void addSocket(PDatagramSocket socket);
|
||||
unsigned count();
|
||||
bool hasDataAtIndex(unsigned index);
|
||||
void addSocket(PDatagramSocket socket);
|
||||
unsigned count();
|
||||
bool hasDataAtIndex(unsigned index);
|
||||
PDatagramSocket socketAt(unsigned index);
|
||||
|
||||
bool waitForData(std::chrono::milliseconds timeout);
|
||||
bool waitForData(std::chrono::milliseconds timeout);
|
||||
|
||||
protected:
|
||||
typedef std::vector<PDatagramSocket> SocketList;
|
||||
SocketList mSocketVector;
|
||||
fd_set mReadSet;
|
||||
SOCKET mMaxHandle;
|
||||
SocketList mSocketVector;
|
||||
fd_set mReadSet;
|
||||
SOCKET mMaxHandle;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+747
-635
File diff suppressed because it is too large
Load Diff
@@ -10,79 +10,79 @@
|
||||
#include <windows.h>
|
||||
|
||||
#if defined(USE_MINIDUMP)
|
||||
# include <DbgHelp.h>
|
||||
#include <DbgHelp.h>
|
||||
#endif
|
||||
|
||||
int winVersion()
|
||||
{
|
||||
DWORD dwVersion = 0;
|
||||
DWORD dwMajorVersion = 0;
|
||||
DWORD dwMinorVersion = 0;
|
||||
DWORD dwBuild = 0;
|
||||
DWORD dwVersion = 0;
|
||||
DWORD dwMajorVersion = 0;
|
||||
DWORD dwMinorVersion = 0;
|
||||
DWORD dwBuild = 0;
|
||||
|
||||
dwVersion = GetVersion();
|
||||
dwVersion = GetVersion();
|
||||
|
||||
// Get the Windows version.
|
||||
// Get the Windows version.
|
||||
|
||||
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
|
||||
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
|
||||
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
|
||||
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
|
||||
|
||||
// Get the build number.
|
||||
// Get the build number.
|
||||
|
||||
if (dwVersion < 0x80000000)
|
||||
dwBuild = (DWORD)(HIWORD(dwVersion));
|
||||
|
||||
if (dwMajorVersion == 5)
|
||||
return Win_Xp;
|
||||
if (dwVersion < 0x80000000)
|
||||
dwBuild = (DWORD)(HIWORD(dwVersion));
|
||||
|
||||
if (dwMinorVersion == 1)
|
||||
return Win_Seven;
|
||||
else
|
||||
return Win_Vista;
|
||||
}
|
||||
if (dwMajorVersion == 5)
|
||||
return Win_Xp;
|
||||
|
||||
if (dwMinorVersion == 1)
|
||||
return Win_Seven;
|
||||
else
|
||||
return Win_Vista;
|
||||
}
|
||||
|
||||
// ----------------- CrashMiniDump -----------------
|
||||
#if defined(USE_MINIDUMP)
|
||||
static LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
|
||||
{
|
||||
// Open the file
|
||||
HANDLE hFile = CreateFile( L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE,
|
||||
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
// Open the file
|
||||
HANDLE hFile =
|
||||
CreateFile(L"MiniDump.dmp", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )
|
||||
{
|
||||
// Create the minidump
|
||||
MINIDUMP_EXCEPTION_INFORMATION mdei;
|
||||
if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
|
||||
{
|
||||
// Create the minidump
|
||||
MINIDUMP_EXCEPTION_INFORMATION mdei;
|
||||
|
||||
mdei.ThreadId = GetCurrentThreadId();
|
||||
mdei.ExceptionPointers = ExceptionInfo;
|
||||
mdei.ClientPointers = FALSE;
|
||||
mdei.ThreadId = GetCurrentThreadId();
|
||||
mdei.ExceptionPointers = ExceptionInfo;
|
||||
mdei.ClientPointers = FALSE;
|
||||
|
||||
MINIDUMP_TYPE mdt = MiniDumpWithFullMemory;
|
||||
MINIDUMP_TYPE mdt = MiniDumpWithFullMemory;
|
||||
|
||||
BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(),
|
||||
hFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, 0 );
|
||||
BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt,
|
||||
(ExceptionInfo != 0) ? &mdei : 0, 0, 0);
|
||||
|
||||
// Close the file
|
||||
CloseHandle( hFile );
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
// Close the file
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER OldExceptionHandler = nullptr;
|
||||
|
||||
void CrashMiniDump::registerHandler()
|
||||
void CrashMiniDump::registerHandler()
|
||||
{
|
||||
OldExceptionHandler = ::SetUnhandledExceptionFilter(&MyExceptionHandler);
|
||||
OldExceptionHandler = ::SetUnhandledExceptionFilter(&MyExceptionHandler);
|
||||
}
|
||||
|
||||
void CrashMiniDump::unregisterHandler()
|
||||
{
|
||||
::SetUnhandledExceptionFilter(nullptr);
|
||||
::SetUnhandledExceptionFilter(nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -124,29 +124,29 @@ int _kbhit()
|
||||
ioctl(STDIN, FIONREAD, &bytesWaiting);
|
||||
return bytesWaiting;*/
|
||||
static const int STDIN_FILENO = 0;
|
||||
struct termios oldt, newt;
|
||||
int ch;
|
||||
int oldf;
|
||||
struct termios oldt, newt;
|
||||
int ch;
|
||||
int oldf;
|
||||
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
|
||||
|
||||
ch = getchar();
|
||||
ch = getchar();
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf);
|
||||
|
||||
if(ch != EOF)
|
||||
{
|
||||
ungetc(ch, stdin);
|
||||
return 1;
|
||||
}
|
||||
if (ch != EOF)
|
||||
{
|
||||
ungetc(ch, stdin);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
enum
|
||||
{
|
||||
Win_Xp = 0,
|
||||
Win_Vista = 1,
|
||||
Win_Seven = 2,
|
||||
Win_Eight = 3,
|
||||
Win_Ten = 4
|
||||
Win_Xp = 0,
|
||||
Win_Vista = 1,
|
||||
Win_Seven = 2,
|
||||
Win_Eight = 3,
|
||||
Win_Ten = 4
|
||||
};
|
||||
|
||||
extern int winVersion();
|
||||
@@ -22,8 +22,8 @@ extern int winVersion();
|
||||
class CrashMiniDump
|
||||
{
|
||||
public:
|
||||
static void registerHandler();
|
||||
static void unregisterHandler();
|
||||
static void registerHandler();
|
||||
static void unregisterHandler();
|
||||
};
|
||||
|
||||
extern void writeMiniDump();
|
||||
@@ -41,7 +41,7 @@ int iosVersion();
|
||||
#include <sys/select.h>
|
||||
#include <termios.h>
|
||||
#if defined(TARGET_LINUX)
|
||||
//# include <stropts.h>
|
||||
// # include <stropts.h>
|
||||
#endif
|
||||
|
||||
extern int _kbhit();
|
||||
@@ -49,6 +49,6 @@ extern int _kbhit();
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# include <conio.h>
|
||||
#include <conio.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -5,51 +5,49 @@
|
||||
|
||||
#include "HL_Pointer.h"
|
||||
|
||||
UsageCounter::UsageCounter()
|
||||
{}
|
||||
UsageCounter::UsageCounter() {}
|
||||
|
||||
UsageCounter::~UsageCounter()
|
||||
{}
|
||||
UsageCounter::~UsageCounter() {}
|
||||
|
||||
int UsageCounter::obtain(int usageId)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
UsageMap::iterator usageIter = mUsage.find(usageId);
|
||||
if (usageIter != mUsage.end())
|
||||
usageIter->second = usageIter->second + 1;
|
||||
else
|
||||
mUsage[usageId] = 1;
|
||||
|
||||
return usageCount();
|
||||
Lock l(mGuard);
|
||||
UsageMap::iterator usageIter = mUsage.find(usageId);
|
||||
if (usageIter != mUsage.end())
|
||||
usageIter->second = usageIter->second + 1;
|
||||
else
|
||||
mUsage[usageId] = 1;
|
||||
|
||||
return usageCount();
|
||||
}
|
||||
|
||||
int UsageCounter::release(int usageId)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
UsageMap::iterator usageIter = mUsage.find(usageId);
|
||||
if (usageIter == mUsage.end())
|
||||
return usageCount();
|
||||
Lock l(mGuard);
|
||||
UsageMap::iterator usageIter = mUsage.find(usageId);
|
||||
if (usageIter == mUsage.end())
|
||||
return usageCount();
|
||||
|
||||
usageIter->second = usageIter->second - 1;
|
||||
if (!usageIter->second)
|
||||
mUsage.erase(usageIter);
|
||||
|
||||
return usageCount();
|
||||
usageIter->second = usageIter->second - 1;
|
||||
if (!usageIter->second)
|
||||
mUsage.erase(usageIter);
|
||||
|
||||
return usageCount();
|
||||
}
|
||||
|
||||
int UsageCounter::usageCount()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
UsageMap::const_iterator usageIter;
|
||||
int result = 0;
|
||||
for (usageIter = mUsage.begin(); usageIter != mUsage.end(); usageIter++)
|
||||
result += usageIter->second;
|
||||
Lock l(mGuard);
|
||||
UsageMap::const_iterator usageIter;
|
||||
int result = 0;
|
||||
for (usageIter = mUsage.begin(); usageIter != mUsage.end(); usageIter++)
|
||||
result += usageIter->second;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void UsageCounter::clear()
|
||||
{
|
||||
Lock l(mGuard);
|
||||
mUsage.clear();
|
||||
Lock l(mGuard);
|
||||
mUsage.clear();
|
||||
}
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
class UsageCounter
|
||||
{
|
||||
public:
|
||||
UsageCounter();
|
||||
~UsageCounter();
|
||||
int obtain(int usageId);
|
||||
int release(int usageId);
|
||||
int usageCount();
|
||||
void clear();
|
||||
UsageCounter();
|
||||
~UsageCounter();
|
||||
int obtain(int usageId);
|
||||
int release(int usageId);
|
||||
int usageCount();
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
typedef std::map<int, int> UsageMap;
|
||||
UsageMap mUsage;
|
||||
Mutex mGuard;
|
||||
typedef std::map<int, int> UsageMap;
|
||||
UsageMap mUsage;
|
||||
Mutex mGuard;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,354 +19,381 @@
|
||||
#include <vector>
|
||||
|
||||
#ifndef HL_RTP_POOL
|
||||
# define HL_RTP_POOL 1
|
||||
#define HL_RTP_POOL 1
|
||||
#endif
|
||||
|
||||
namespace hl
|
||||
{
|
||||
#if HL_RTP_POOL
|
||||
/// @class FixedBlockPool
|
||||
/// A process-wide, fixed-block pool with a lock-free thread-local fast path: uniform 256-byte
|
||||
/// blocks carved from 64 KB chunks, an intrusive thread-local free list, and a per-block header
|
||||
/// tag so deallocate() is O(1) and lock-free for any block (and can tell pooled blocks from the
|
||||
/// global-allocator fallback used for oversized requests) regardless of the freeing thread.
|
||||
///
|
||||
/// CROSS-THREAD RECLAIM (2026-07-26) — fixes an unbounded capacity ratchet. A uniform block size
|
||||
/// makes a block allocated on one thread safe to free on another, but "safe" is not "reclaimed".
|
||||
/// With a purely thread-local free list the block joins the FREEING thread's list, so a
|
||||
/// producer/consumer split strands it: vq-core allocates RTPPacket / jitter-buffer nodes on the
|
||||
/// capture threads and frees them on the interval/reap side, so the capture threads' lists were
|
||||
/// perpetually empty and refill() carved a fresh chunk every BlocksPerChunk allocations, forever.
|
||||
/// Observed on vqmonitor (the only box with >1 capture thread): capacityBlocks() 256 -> 11.8M
|
||||
/// = 2.7 GiB over 4 days at ~25 MiB/h, while activeBlocks() drained to 0 every idle interval.
|
||||
///
|
||||
/// The fix is a process-wide depot of free-block batches. A thread whose free list grows past
|
||||
/// ThreadCacheHighWater hands a batch down to the depot; a thread that runs dry takes a batch
|
||||
/// from the depot before carving a new chunk; a thread flushes its remainder to the depot on
|
||||
/// exit. The depot is touched once per BlocksPerChunk operations and the hysteresis between
|
||||
/// "empty" and the high-water keeps it off the per-packet path, so the fast path stays lock-free.
|
||||
/// capacityBlocks() now plateaus at the true concurrent high-water instead of ratcheting.
|
||||
class FixedBlockPool
|
||||
{
|
||||
public:
|
||||
/// Usable bytes handed back to the caller from a pooled block. Comfortably covers the
|
||||
/// shared_ptr nodes we pool (control block + RTPPacket / RtpBuffer::Packet, ~90-130 bytes).
|
||||
static constexpr std::size_t PayloadSize = 240;
|
||||
static constexpr std::size_t BlocksPerChunk = 256;
|
||||
/// Free blocks a thread keeps to itself before handing a BlocksPerChunk batch to the depot.
|
||||
/// Must exceed BlocksPerChunk so a thread that churns around the boundary does not bounce
|
||||
/// on the depot mutex; 2x leaves a full batch in hand after every release.
|
||||
static constexpr std::size_t ThreadCacheHighWater = 2 * BlocksPerChunk;
|
||||
/// @class FixedBlockPool
|
||||
/// A process-wide, fixed-block pool with a lock-free thread-local fast path: uniform 256-byte
|
||||
/// blocks carved from 64 KB chunks, an intrusive thread-local free list, and a per-block header
|
||||
/// tag so deallocate() is O(1) and lock-free for any block (and can tell pooled blocks from the
|
||||
/// global-allocator fallback used for oversized requests) regardless of the freeing thread.
|
||||
///
|
||||
/// CROSS-THREAD RECLAIM (2026-07-26) — fixes an unbounded capacity ratchet. A uniform block size
|
||||
/// makes a block allocated on one thread safe to free on another, but "safe" is not "reclaimed".
|
||||
/// With a purely thread-local free list the block joins the FREEING thread's list, so a
|
||||
/// producer/consumer split strands it: vq-core allocates RTPPacket / jitter-buffer nodes on the
|
||||
/// capture threads and frees them on the interval/reap side, so the capture threads' lists were
|
||||
/// perpetually empty and refill() carved a fresh chunk every BlocksPerChunk allocations, forever.
|
||||
/// Observed on vqmonitor (the only box with >1 capture thread): capacityBlocks() 256 -> 11.8M
|
||||
/// = 2.7 GiB over 4 days at ~25 MiB/h, while activeBlocks() drained to 0 every idle interval.
|
||||
///
|
||||
/// The fix is a process-wide depot of free-block batches. A thread whose free list grows past
|
||||
/// ThreadCacheHighWater hands a batch down to the depot; a thread that runs dry takes a batch
|
||||
/// from the depot before carving a new chunk; a thread flushes its remainder to the depot on
|
||||
/// exit. The depot is touched once per BlocksPerChunk operations and the hysteresis between
|
||||
/// "empty" and the high-water keeps it off the per-packet path, so the fast path stays lock-free.
|
||||
/// capacityBlocks() now plateaus at the true concurrent high-water instead of ratcheting.
|
||||
class FixedBlockPool
|
||||
{
|
||||
public:
|
||||
/// Usable bytes handed back to the caller from a pooled block. Comfortably covers the
|
||||
/// shared_ptr nodes we pool (control block + RTPPacket / RtpBuffer::Packet, ~90-130 bytes).
|
||||
static constexpr std::size_t PayloadSize = 240;
|
||||
static constexpr std::size_t BlocksPerChunk = 256;
|
||||
/// Free blocks a thread keeps to itself before handing a BlocksPerChunk batch to the depot.
|
||||
/// Must exceed BlocksPerChunk so a thread that churns around the boundary does not bounce
|
||||
/// on the depot mutex; 2x leaves a full batch in hand after every release.
|
||||
static constexpr std::size_t ThreadCacheHighWater = 2 * BlocksPerChunk;
|
||||
|
||||
static void* allocate(std::size_t size)
|
||||
{
|
||||
if (size > PayloadSize)
|
||||
{
|
||||
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
||||
tagOf(raw) = TagGlobal;
|
||||
s_activeGlobal.fetch_add(1, std::memory_order_relaxed);
|
||||
return raw + HeaderSize;
|
||||
}
|
||||
static void* allocate(std::size_t size)
|
||||
{
|
||||
if (size > PayloadSize)
|
||||
{
|
||||
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
||||
tagOf(raw) = TagGlobal;
|
||||
s_activeGlobal.fetch_add(1, std::memory_order_relaxed);
|
||||
return raw + HeaderSize;
|
||||
}
|
||||
|
||||
ThreadCache& tc = cache();
|
||||
if (tc.head == nullptr)
|
||||
{
|
||||
// Reclaim from the depot before asking the OS for more memory. Only when the
|
||||
// depot is dry as well has the pool genuinely outgrown its current capacity.
|
||||
std::size_t taken = 0;
|
||||
tc.head = depot().takeBatch(taken);
|
||||
if (tc.head == nullptr)
|
||||
{
|
||||
tc.head = registry().refill();
|
||||
taken = BlocksPerChunk;
|
||||
}
|
||||
tc.count = taken;
|
||||
}
|
||||
ThreadCache& tc = cache();
|
||||
if (tc.head == nullptr)
|
||||
{
|
||||
// Reclaim from the depot before asking the OS for more memory. Only when the
|
||||
// depot is dry as well has the pool genuinely outgrown its current capacity.
|
||||
std::size_t taken = 0;
|
||||
tc.head = depot().takeBatch(taken);
|
||||
if (tc.head == nullptr)
|
||||
{
|
||||
tc.head = registry().refill();
|
||||
taken = BlocksPerChunk;
|
||||
}
|
||||
tc.count = taken;
|
||||
}
|
||||
|
||||
uint8_t* block = static_cast<uint8_t*>(tc.head);
|
||||
tc.head = nextOf(block);
|
||||
--tc.count;
|
||||
s_activeBlocks.fetch_add(1, std::memory_order_relaxed);
|
||||
return block + HeaderSize;
|
||||
}
|
||||
uint8_t* block = static_cast<uint8_t*>(tc.head);
|
||||
tc.head = nextOf(block);
|
||||
--tc.count;
|
||||
s_activeBlocks.fetch_add(1, std::memory_order_relaxed);
|
||||
return block + HeaderSize;
|
||||
}
|
||||
|
||||
static void deallocate(void* ptr) noexcept
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
return;
|
||||
static void deallocate(void* ptr) noexcept
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
return;
|
||||
|
||||
uint8_t* block = static_cast<uint8_t*>(ptr) - HeaderSize;
|
||||
if (tagOf(block) == TagPool)
|
||||
{
|
||||
ThreadCache& tc = cache();
|
||||
nextOf(block) = tc.head;
|
||||
tc.head = block;
|
||||
++tc.count;
|
||||
s_activeBlocks.fetch_sub(1, std::memory_order_relaxed);
|
||||
uint8_t* block = static_cast<uint8_t*>(ptr) - HeaderSize;
|
||||
if (tagOf(block) == TagPool)
|
||||
{
|
||||
ThreadCache& tc = cache();
|
||||
nextOf(block) = tc.head;
|
||||
tc.head = block;
|
||||
++tc.count;
|
||||
s_activeBlocks.fetch_sub(1, std::memory_order_relaxed);
|
||||
|
||||
// The one line that closes the ratchet: surplus goes back to the depot, where
|
||||
// the thread that actually allocates can reach it.
|
||||
if (tc.count >= ThreadCacheHighWater)
|
||||
releaseBatch(tc, BlocksPerChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(static_cast<void*>(block));
|
||||
s_activeGlobal.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
// The one line that closes the ratchet: surplus goes back to the depot, where
|
||||
// the thread that actually allocates can reach it.
|
||||
if (tc.count >= ThreadCacheHighWater)
|
||||
releaseBatch(tc, BlocksPerChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(static_cast<void*>(block));
|
||||
s_activeGlobal.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// @name Diagnostics (relaxed gauges — not synchronization)
|
||||
/// @{
|
||||
/// Blocks currently handed out from the pool = pooled allocate() minus
|
||||
/// pooled deallocate(). Unlike capacityBlocks() (the chunk high-water,
|
||||
/// which only ever grows) this DROPS when objects are freed, so a rising
|
||||
/// activeBlocks() is a genuine leak of referenced objects rather than a
|
||||
/// traffic peak that merely carved extra chunks. Covers every pooled
|
||||
/// shared_ptr node (allocate_shared<RTPPacket> + jitter-buffer packets).
|
||||
static std::int64_t activeBlocks() noexcept
|
||||
{
|
||||
return s_activeBlocks.load(std::memory_order_relaxed);
|
||||
}
|
||||
/// Live oversized allocations that overflowed to ::operator new.
|
||||
static std::int64_t activeGlobalAllocations() noexcept
|
||||
{
|
||||
return s_activeGlobal.load(std::memory_order_relaxed);
|
||||
}
|
||||
/// Total blocks ever carved (chunks x BlocksPerChunk) = high-water capacity.
|
||||
/// With cross-thread reclaim this PLATEAUS once the pool has covered the
|
||||
/// concurrent high-water; sustained linear growth means reclaim is not working.
|
||||
static std::int64_t capacityBlocks() noexcept
|
||||
{
|
||||
return s_chunks.load(std::memory_order_relaxed) * static_cast<std::int64_t>(BlocksPerChunk);
|
||||
}
|
||||
/// Free blocks parked in the process-wide depot, i.e. reclaimed from a freeing
|
||||
/// thread and available to any allocating thread. Healthy steady state is a
|
||||
/// non-zero, bounded value: it is the surplus that used to be stranded.
|
||||
static std::int64_t depotBlocks() noexcept
|
||||
{
|
||||
return s_depotBlocks.load(std::memory_order_relaxed);
|
||||
}
|
||||
/// Approximate live pooled payload bytes (excludes per-block header).
|
||||
static std::int64_t activeBytes() noexcept
|
||||
{
|
||||
return activeBlocks() * static_cast<std::int64_t>(PayloadSize);
|
||||
}
|
||||
/// @}
|
||||
/// @name Diagnostics (relaxed gauges — not synchronization)
|
||||
/// @{
|
||||
/// Blocks currently handed out from the pool = pooled allocate() minus
|
||||
/// pooled deallocate(). Unlike capacityBlocks() (the chunk high-water,
|
||||
/// which only ever grows) this DROPS when objects are freed, so a rising
|
||||
/// activeBlocks() is a genuine leak of referenced objects rather than a
|
||||
/// traffic peak that merely carved extra chunks. Covers every pooled
|
||||
/// shared_ptr node (allocate_shared<RTPPacket> + jitter-buffer packets).
|
||||
static std::int64_t activeBlocks() noexcept { return s_activeBlocks.load(std::memory_order_relaxed); }
|
||||
/// Live oversized allocations that overflowed to ::operator new.
|
||||
static std::int64_t activeGlobalAllocations() noexcept { return s_activeGlobal.load(std::memory_order_relaxed); }
|
||||
/// Total blocks ever carved (chunks x BlocksPerChunk) = high-water capacity.
|
||||
/// With cross-thread reclaim this PLATEAUS once the pool has covered the
|
||||
/// concurrent high-water; sustained linear growth means reclaim is not working.
|
||||
static std::int64_t capacityBlocks() noexcept
|
||||
{
|
||||
return s_chunks.load(std::memory_order_relaxed) * static_cast<std::int64_t>(BlocksPerChunk);
|
||||
}
|
||||
/// Free blocks parked in the process-wide depot, i.e. reclaimed from a freeing
|
||||
/// thread and available to any allocating thread. Healthy steady state is a
|
||||
/// non-zero, bounded value: it is the surplus that used to be stranded.
|
||||
static std::int64_t depotBlocks() noexcept { return s_depotBlocks.load(std::memory_order_relaxed); }
|
||||
/// Approximate live pooled payload bytes (excludes per-block header).
|
||||
static std::int64_t activeBytes() noexcept { return activeBlocks() * static_cast<std::int64_t>(PayloadSize); }
|
||||
/// @}
|
||||
|
||||
private:
|
||||
inline static std::atomic<std::int64_t> s_activeBlocks{0};
|
||||
inline static std::atomic<std::int64_t> s_activeGlobal{0};
|
||||
inline static std::atomic<std::int64_t> s_chunks{0};
|
||||
inline static std::atomic<std::int64_t> s_depotBlocks{0};
|
||||
private:
|
||||
inline static std::atomic<std::int64_t> s_activeBlocks{0};
|
||||
inline static std::atomic<std::int64_t> s_activeGlobal{0};
|
||||
inline static std::atomic<std::int64_t> s_chunks{0};
|
||||
inline static std::atomic<std::int64_t> s_depotBlocks{0};
|
||||
|
||||
static constexpr std::size_t HeaderSize =
|
||||
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
|
||||
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
|
||||
static constexpr std::size_t HeaderSize =
|
||||
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
|
||||
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
|
||||
|
||||
static constexpr uint64_t TagPool = 0x504F4F4C52545008ULL; // "POOLRTP\b"
|
||||
static constexpr uint64_t TagGlobal = 0x474C4F42524C0808ULL; // "GLOBRL\b\b"
|
||||
static constexpr uint64_t TagPool = 0x504F4F4C52545008ULL; // "POOLRTP\b"
|
||||
static constexpr uint64_t TagGlobal = 0x474C4F42524C0808ULL; // "GLOBRL\b\b"
|
||||
|
||||
/// A free block's payload is dead space, so the list links live there. Slot 0 chains
|
||||
/// blocks within a list/batch; slots 1-2 are used only on a batch's head block, to chain
|
||||
/// batches inside the depot and to carry the batch length (batches from a thread-exit
|
||||
/// flush are shorter than BlocksPerChunk).
|
||||
static_assert(PayloadSize >= 2 * sizeof(void*) + sizeof(std::size_t),
|
||||
"pooled block payload must hold the free-list, depot and length slots");
|
||||
/// A free block's payload is dead space, so the list links live there. Slot 0 chains
|
||||
/// blocks within a list/batch; slots 1-2 are used only on a batch's head block, to chain
|
||||
/// batches inside the depot and to carry the batch length (batches from a thread-exit
|
||||
/// flush are shorter than BlocksPerChunk).
|
||||
static_assert(PayloadSize >= 2 * sizeof(void*) + sizeof(std::size_t),
|
||||
"pooled block payload must hold the free-list, depot and length slots");
|
||||
|
||||
static uint64_t& tagOf(void* block) noexcept
|
||||
{
|
||||
return *reinterpret_cast<uint64_t*>(block);
|
||||
}
|
||||
static uint64_t& tagOf(void* block) noexcept { return *reinterpret_cast<uint64_t*>(block); }
|
||||
|
||||
static void*& nextOf(void* block) noexcept
|
||||
{
|
||||
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize);
|
||||
}
|
||||
static void*& nextOf(void* block) noexcept
|
||||
{
|
||||
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize);
|
||||
}
|
||||
|
||||
static void*& batchNextOf(void* block) noexcept
|
||||
{
|
||||
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize + sizeof(void*));
|
||||
}
|
||||
static void*& batchNextOf(void* block) noexcept
|
||||
{
|
||||
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize + sizeof(void*));
|
||||
}
|
||||
|
||||
static std::size_t& batchLenOf(void* block) noexcept
|
||||
{
|
||||
return *reinterpret_cast<std::size_t*>(static_cast<uint8_t*>(block) + HeaderSize + 2 * sizeof(void*));
|
||||
}
|
||||
static std::size_t& batchLenOf(void* block) noexcept
|
||||
{
|
||||
return *reinterpret_cast<std::size_t*>(static_cast<uint8_t*>(block) + HeaderSize + 2 * sizeof(void*));
|
||||
}
|
||||
|
||||
/// Per-thread free list. The destructor hands the remainder to the depot so a thread that
|
||||
/// exits does not strand its blocks — the same ratchet as the cross-thread case, just
|
||||
/// triggered once per thread instead of continuously.
|
||||
struct ThreadCache
|
||||
{
|
||||
void* head = nullptr;
|
||||
std::size_t count = 0;
|
||||
/// Per-thread free list. The destructor hands the remainder to the depot so a thread that
|
||||
/// exits does not strand its blocks — the same ratchet as the cross-thread case, just
|
||||
/// triggered once per thread instead of continuously.
|
||||
struct ThreadCache
|
||||
{
|
||||
void* head = nullptr;
|
||||
std::size_t count = 0;
|
||||
|
||||
~ThreadCache()
|
||||
{
|
||||
while (count > 0 && head != nullptr)
|
||||
releaseBatch(*this, count);
|
||||
}
|
||||
};
|
||||
~ThreadCache()
|
||||
{
|
||||
while (count > 0 && head != nullptr)
|
||||
releaseBatch(*this, count);
|
||||
}
|
||||
};
|
||||
|
||||
static ThreadCache& cache() noexcept
|
||||
{
|
||||
static thread_local ThreadCache tc;
|
||||
return tc;
|
||||
}
|
||||
static ThreadCache& cache() noexcept
|
||||
{
|
||||
static thread_local ThreadCache tc;
|
||||
return tc;
|
||||
}
|
||||
|
||||
/// Detach the first `blocks` entries of the thread list and park them in the depot.
|
||||
static void releaseBatch(ThreadCache& tc, std::size_t blocks) noexcept
|
||||
{
|
||||
if (blocks == 0 || tc.head == nullptr)
|
||||
return;
|
||||
/// Detach the first `blocks` entries of the thread list and park them in the depot.
|
||||
static void releaseBatch(ThreadCache& tc, std::size_t blocks) noexcept
|
||||
{
|
||||
if (blocks == 0 || tc.head == nullptr)
|
||||
return;
|
||||
|
||||
void* batchHead = tc.head;
|
||||
void* batchTail = batchHead;
|
||||
std::size_t length = 1;
|
||||
for (; length < blocks && nextOf(batchTail) != nullptr; ++length)
|
||||
batchTail = nextOf(batchTail);
|
||||
void* batchHead = tc.head;
|
||||
void* batchTail = batchHead;
|
||||
std::size_t length = 1;
|
||||
for (; length < blocks && nextOf(batchTail) != nullptr; ++length)
|
||||
batchTail = nextOf(batchTail);
|
||||
|
||||
tc.head = nextOf(batchTail);
|
||||
tc.count -= length;
|
||||
nextOf(batchTail) = nullptr;
|
||||
tc.head = nextOf(batchTail);
|
||||
tc.count -= length;
|
||||
nextOf(batchTail) = nullptr;
|
||||
|
||||
batchLenOf(batchHead) = length;
|
||||
depot().giveBatch(batchHead, length);
|
||||
}
|
||||
batchLenOf(batchHead) = length;
|
||||
depot().giveBatch(batchHead, length);
|
||||
}
|
||||
|
||||
/// Process-wide store of reclaimed batches. Intrusive (the links live in the free blocks
|
||||
/// themselves) so neither path can allocate or throw — deallocate() is noexcept. The mutex
|
||||
/// is taken once per BlocksPerChunk operations, not per packet.
|
||||
class Depot
|
||||
{
|
||||
public:
|
||||
void* takeBatch(std::size_t& lengthOut) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
void* batch = m_Head;
|
||||
if (batch == nullptr)
|
||||
{
|
||||
lengthOut = 0;
|
||||
return nullptr;
|
||||
}
|
||||
m_Head = batchNextOf(batch);
|
||||
lengthOut = batchLenOf(batch);
|
||||
batchNextOf(batch) = nullptr;
|
||||
s_depotBlocks.fetch_sub(static_cast<std::int64_t>(lengthOut), std::memory_order_relaxed);
|
||||
return batch;
|
||||
}
|
||||
/// Process-wide store of reclaimed batches. Intrusive (the links live in the free blocks
|
||||
/// themselves) so neither path can allocate or throw — deallocate() is noexcept. The mutex
|
||||
/// is taken once per BlocksPerChunk operations, not per packet.
|
||||
class Depot
|
||||
{
|
||||
public:
|
||||
void* takeBatch(std::size_t& lengthOut) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
void* batch = m_Head;
|
||||
if (batch == nullptr)
|
||||
{
|
||||
lengthOut = 0;
|
||||
return nullptr;
|
||||
}
|
||||
m_Head = batchNextOf(batch);
|
||||
lengthOut = batchLenOf(batch);
|
||||
batchNextOf(batch) = nullptr;
|
||||
s_depotBlocks.fetch_sub(static_cast<std::int64_t>(lengthOut), std::memory_order_relaxed);
|
||||
return batch;
|
||||
}
|
||||
|
||||
void giveBatch(void* batch, std::size_t length) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
batchNextOf(batch) = m_Head;
|
||||
m_Head = batch;
|
||||
s_depotBlocks.fetch_add(static_cast<std::int64_t>(length), std::memory_order_relaxed);
|
||||
}
|
||||
void giveBatch(void* batch, std::size_t length) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
batchNextOf(batch) = m_Head;
|
||||
m_Head = batch;
|
||||
s_depotBlocks.fetch_add(static_cast<std::int64_t>(length), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_Mutex;
|
||||
void* m_Head = nullptr;
|
||||
};
|
||||
private:
|
||||
std::mutex m_Mutex;
|
||||
void* m_Head = nullptr;
|
||||
};
|
||||
|
||||
class ChunkRegistry
|
||||
{
|
||||
public:
|
||||
void* refill()
|
||||
{
|
||||
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
||||
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
|
||||
s_chunks.fetch_add(1, std::memory_order_relaxed);
|
||||
class ChunkRegistry
|
||||
{
|
||||
public:
|
||||
void* refill()
|
||||
{
|
||||
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
||||
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
|
||||
s_chunks.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Chunks.push_back(chunk);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void* list = nullptr;
|
||||
for (std::size_t i = 0; i < BlocksPerChunk; ++i)
|
||||
{
|
||||
uint8_t* block = chunk + i * BlockSize;
|
||||
tagOf(block) = TagPool;
|
||||
nextOf(block) = list;
|
||||
list = block;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
void* list = nullptr;
|
||||
for (std::size_t i = 0; i < BlocksPerChunk; ++i)
|
||||
{
|
||||
uint8_t* block = chunk + i * BlockSize;
|
||||
tagOf(block) = TagPool;
|
||||
nextOf(block) = list;
|
||||
list = block;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_Mutex;
|
||||
std::vector<uint8_t*> m_Chunks;
|
||||
};
|
||||
private:
|
||||
std::mutex m_Mutex;
|
||||
std::vector<uint8_t*> m_Chunks;
|
||||
};
|
||||
|
||||
/// Both singletons are deliberately immortal (leaked at exit, reclaimed by the OS).
|
||||
/// A thread_local ThreadCache is destroyed at thread exit — for the main thread that is
|
||||
/// during static destruction — and its destructor touches the depot, so a depot with a
|
||||
/// destructor could be used after being destroyed. The same applies to the chunk memory:
|
||||
/// any pooled object outliving the registry would free into deleted chunks. Never
|
||||
/// destroying them removes both hazards; the process is exiting either way.
|
||||
static Depot& depot() noexcept
|
||||
{
|
||||
static Depot* instance = new Depot();
|
||||
return *instance;
|
||||
}
|
||||
/// Both singletons are deliberately immortal (leaked at exit, reclaimed by the OS).
|
||||
/// A thread_local ThreadCache is destroyed at thread exit — for the main thread that is
|
||||
/// during static destruction — and its destructor touches the depot, so a depot with a
|
||||
/// destructor could be used after being destroyed. The same applies to the chunk memory:
|
||||
/// any pooled object outliving the registry would free into deleted chunks. Never
|
||||
/// destroying them removes both hazards; the process is exiting either way.
|
||||
static Depot& depot() noexcept
|
||||
{
|
||||
static Depot* instance = new Depot();
|
||||
return *instance;
|
||||
}
|
||||
|
||||
static ChunkRegistry& registry()
|
||||
{
|
||||
static ChunkRegistry* instance = new ChunkRegistry();
|
||||
return *instance;
|
||||
}
|
||||
};
|
||||
#endif // HL_RTP_POOL
|
||||
static ChunkRegistry& registry()
|
||||
{
|
||||
static ChunkRegistry* instance = new ChunkRegistry();
|
||||
return *instance;
|
||||
}
|
||||
};
|
||||
#endif // HL_RTP_POOL
|
||||
|
||||
/// Mode-independent accessors for the pool diagnostics, so callers need not know
|
||||
/// whether HL_RTP_POOL is compiled in. When pooling is disabled they return -1
|
||||
/// ("not applicable") since allocate_shared then behaves like make_shared.
|
||||
/// Mode-independent accessors for the pool diagnostics, so callers need not know
|
||||
/// whether HL_RTP_POOL is compiled in. When pooling is disabled they return -1
|
||||
/// ("not applicable") since allocate_shared then behaves like make_shared.
|
||||
#if HL_RTP_POOL
|
||||
inline std::int64_t poolActiveBlocks() noexcept { return FixedBlockPool::activeBlocks(); }
|
||||
inline std::int64_t poolCapacityBlocks() noexcept { return FixedBlockPool::capacityBlocks(); }
|
||||
inline std::int64_t poolDepotBlocks() noexcept { return FixedBlockPool::depotBlocks(); }
|
||||
inline std::int64_t poolActiveGlobal() noexcept { return FixedBlockPool::activeGlobalAllocations(); }
|
||||
inline std::int64_t poolActiveBytes() noexcept { return FixedBlockPool::activeBytes(); }
|
||||
inline std::int64_t poolActiveBlocks() noexcept
|
||||
{
|
||||
return FixedBlockPool::activeBlocks();
|
||||
}
|
||||
inline std::int64_t poolCapacityBlocks() noexcept
|
||||
{
|
||||
return FixedBlockPool::capacityBlocks();
|
||||
}
|
||||
inline std::int64_t poolDepotBlocks() noexcept
|
||||
{
|
||||
return FixedBlockPool::depotBlocks();
|
||||
}
|
||||
inline std::int64_t poolActiveGlobal() noexcept
|
||||
{
|
||||
return FixedBlockPool::activeGlobalAllocations();
|
||||
}
|
||||
inline std::int64_t poolActiveBytes() noexcept
|
||||
{
|
||||
return FixedBlockPool::activeBytes();
|
||||
}
|
||||
#else
|
||||
inline std::int64_t poolActiveBlocks() noexcept { return -1; }
|
||||
inline std::int64_t poolCapacityBlocks() noexcept { return -1; }
|
||||
inline std::int64_t poolDepotBlocks() noexcept { return -1; }
|
||||
inline std::int64_t poolActiveGlobal() noexcept { return -1; }
|
||||
inline std::int64_t poolActiveBytes() noexcept { return -1; }
|
||||
inline std::int64_t poolActiveBlocks() noexcept
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
inline std::int64_t poolCapacityBlocks() noexcept
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
inline std::int64_t poolDepotBlocks() noexcept
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
inline std::int64_t poolActiveGlobal() noexcept
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
inline std::int64_t poolActiveBytes() noexcept
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @class PoolAllocator
|
||||
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
|
||||
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
|
||||
/// that does not fit a pooled block) it delegates to the global allocator, matching make_shared.
|
||||
template <class T> struct PoolAllocator
|
||||
{
|
||||
using value_type = T;
|
||||
/// @class PoolAllocator
|
||||
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
|
||||
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
|
||||
/// that does not fit a pooled block) it delegates to the global allocator, matching make_shared.
|
||||
template<class T>
|
||||
struct PoolAllocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
PoolAllocator() noexcept = default;
|
||||
template <class U> PoolAllocator(const PoolAllocator<U>&) noexcept {}
|
||||
PoolAllocator() noexcept = default;
|
||||
template<class U>
|
||||
PoolAllocator(const PoolAllocator<U>&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
#if HL_RTP_POOL
|
||||
return static_cast<T*>(FixedBlockPool::allocate(n * sizeof(T)));
|
||||
return static_cast<T*>(FixedBlockPool::allocate(n * sizeof(T)));
|
||||
#else
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t /*n*/) noexcept
|
||||
{
|
||||
void deallocate(T* p, std::size_t /*n*/) noexcept
|
||||
{
|
||||
#if HL_RTP_POOL
|
||||
FixedBlockPool::deallocate(p);
|
||||
FixedBlockPool::deallocate(p);
|
||||
#else
|
||||
::operator delete(static_cast<void*>(p));
|
||||
::operator delete(static_cast<void*>(p));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <class U> bool operator==(const PoolAllocator<U>&) const noexcept { return true; }
|
||||
template <class U> bool operator!=(const PoolAllocator<U>&) const noexcept { return false; }
|
||||
};
|
||||
} // namespace hl
|
||||
template<class U>
|
||||
bool operator==(const PoolAllocator<U>&) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
template<class U>
|
||||
bool operator!=(const PoolAllocator<U>&) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace hl
|
||||
|
||||
+172
-170
@@ -4,8 +4,8 @@
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# define popen _popen
|
||||
# define pclose _pclose
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#endif
|
||||
|
||||
#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 output;
|
||||
HANDLE hPipeRead, hPipeWrite;
|
||||
std::string output;
|
||||
HANDLE hPipeRead, hPipeWrite;
|
||||
|
||||
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
|
||||
saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process.
|
||||
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
|
||||
saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
|
||||
// Create a pipe to get results from child's stdout.
|
||||
if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) )
|
||||
if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
|
||||
return output;
|
||||
|
||||
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
|
||||
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = hPipeWrite;
|
||||
si.hStdError = hPipeWrite;
|
||||
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
|
||||
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
|
||||
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = hPipeWrite;
|
||||
si.hStdError = hPipeWrite;
|
||||
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());
|
||||
|
||||
BOOL fSuccess = CreateProcessA( nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
||||
if (! fSuccess)
|
||||
BOOL fSuccess = CreateProcessA(nullptr, cmdline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
|
||||
if (!fSuccess)
|
||||
{
|
||||
CloseHandle( hPipeWrite );
|
||||
CloseHandle( hPipeRead );
|
||||
CloseHandle(hPipeWrite);
|
||||
CloseHandle(hPipeRead);
|
||||
return output;
|
||||
}
|
||||
|
||||
bool bProcessEnded = false;
|
||||
for (; !bProcessEnded ;)
|
||||
for (; !bProcessEnded;)
|
||||
{
|
||||
// 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.
|
||||
for (;;)
|
||||
{
|
||||
char buf[1024];
|
||||
char buf[1024];
|
||||
DWORD dwRead = 0;
|
||||
DWORD dwAvail = 0;
|
||||
|
||||
@@ -75,119 +75,122 @@ std::string OsProcess::execCommand(const std::string& cmd)
|
||||
buf[dwRead] = 0;
|
||||
output += buf;
|
||||
}
|
||||
} //for
|
||||
} // for
|
||||
|
||||
CloseHandle( hPipeWrite );
|
||||
CloseHandle( hPipeRead );
|
||||
CloseHandle( pi.hProcess );
|
||||
CloseHandle( pi.hThread );
|
||||
CloseHandle(hPipeWrite);
|
||||
CloseHandle(hPipeRead);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline,
|
||||
std::function<void(const std::string& line)> callback,
|
||||
std::function<void(const std::string& reason)> finished_callback,
|
||||
bool& finish_flag)
|
||||
std::shared_ptr<std::thread>
|
||||
OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> callback,
|
||||
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag)
|
||||
{
|
||||
// std::cout << cmdline << std::endl;
|
||||
|
||||
std::string output;
|
||||
HANDLE hPipeRead, hPipeWrite;
|
||||
std::string output;
|
||||
HANDLE hPipeRead, hPipeWrite;
|
||||
|
||||
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE };
|
||||
saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process.
|
||||
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE};
|
||||
saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
|
||||
saAttr.lpSecurityDescriptor = nullptr;
|
||||
|
||||
|
||||
// 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>();
|
||||
|
||||
STARTUPINFOA si; memset(&si, 0, sizeof si);
|
||||
STARTUPINFOA si;
|
||||
memset(&si, 0, sizeof si);
|
||||
si.cb = sizeof(STARTUPINFOA);
|
||||
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = hPipeWrite;
|
||||
si.hStdError = hPipeWrite;
|
||||
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
|
||||
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = hPipeWrite;
|
||||
si.hStdError = hPipeWrite;
|
||||
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
|
||||
|
||||
PROCESS_INFORMATION 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());
|
||||
|
||||
|
||||
BOOL fSuccess = CreateProcessA( nullptr, cmdbuffer, nullptr, nullptr, TRUE,
|
||||
CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi);
|
||||
if (! fSuccess)
|
||||
BOOL fSuccess =
|
||||
CreateProcessA(nullptr, cmdbuffer, nullptr, nullptr, TRUE, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi);
|
||||
if (!fSuccess)
|
||||
{
|
||||
CloseHandle( hPipeWrite );
|
||||
CloseHandle( hPipeRead );
|
||||
CloseHandle(hPipeWrite);
|
||||
CloseHandle(hPipeRead);
|
||||
return std::shared_ptr<std::thread>();
|
||||
}
|
||||
|
||||
std::shared_ptr<std::thread> r = std::make_shared<std::thread>(
|
||||
[&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]()
|
||||
{
|
||||
char buf[4096]; memset(buf, 0, sizeof buf);
|
||||
for (; !finish_flag ;)
|
||||
[&finish_flag, pi, callback, finished_callback, hPipeRead, hPipeWrite]()
|
||||
{
|
||||
// Give some timeslice (50ms), so we won't waste 100% cpu.
|
||||
bool timeouted = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0;
|
||||
|
||||
// Even if process exited - we continue reading, if there is some data available over pipe.
|
||||
for (;;)
|
||||
char buf[4096];
|
||||
memset(buf, 0, sizeof buf);
|
||||
for (; !finish_flag;)
|
||||
{
|
||||
DWORD dwRead = 0;
|
||||
DWORD dwAvail = 0;
|
||||
// Give some timeslice (50ms), so we won't waste 100% cpu.
|
||||
bool timeouted = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
|
||||
|
||||
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)
|
||||
// Even if process exited - we continue reading, if there is some data available over pipe.
|
||||
for (;;)
|
||||
{
|
||||
std::string line(buf, cr - buf -1);
|
||||
if (callback)
|
||||
callback(strx::trim(line));
|
||||
memmove(buf, cr + 1, strlen(cr+1) + 1);
|
||||
DWORD dwRead = 0;
|
||||
DWORD dwAvail = 0;
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
CloseHandle( pi.hProcess );
|
||||
CloseHandle( pi.hThread );
|
||||
if (finished_callback)
|
||||
finished_callback(std::string());
|
||||
});
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
if (finished_callback)
|
||||
finished_callback(std::string());
|
||||
});
|
||||
|
||||
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 cp = cmd;
|
||||
std::string cp = cmd;
|
||||
std::shared_ptr<FILE> pipe(popen(cp.c_str(), "r"), pclose);
|
||||
if (!pipe)
|
||||
throw std::runtime_error("Failed to run.");
|
||||
|
||||
char buffer[1024];
|
||||
char buffer[1024];
|
||||
std::string result = "";
|
||||
while (!feof(pipe.get()))
|
||||
{
|
||||
@@ -229,84 +232,83 @@ int OsProcess::execSystem(const std::string& cmd)
|
||||
#include "helper/HL_String.h"
|
||||
#include "helper/HL_Sync.h"
|
||||
|
||||
std::shared_ptr<std::thread> OsProcess::asyncExecCommand(const std::string& cmdline,
|
||||
std::function<void(const std::string& line)> line_callback,
|
||||
std::function<void(const std::string& reason)> finished_callback,
|
||||
bool& finish_flag)
|
||||
std::shared_ptr<std::thread>
|
||||
OsProcess::asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback,
|
||||
std::function<void(const std::string& reason)> finished_callback, bool& 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)
|
||||
std::shared_ptr<std::thread> t = std::make_shared<std::thread>(
|
||||
[cmdline, line_callback, finished_callback, &finish_flag]()
|
||||
{
|
||||
if (finished_callback)
|
||||
finished_callback("Failed to open pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
ThreadHelper::setName("OsProcess::asyncExecCommand");
|
||||
std::string cp = cmdline;
|
||||
FILE* pipe = popen(cp.c_str(), "r");
|
||||
if (!pipe)
|
||||
{
|
||||
r = static_cast<int>(read(fno, buffer, sizeof(buffer) - 1));
|
||||
if (r > 0)
|
||||
{
|
||||
buffer[r] = 0;
|
||||
lines += std::string(buffer);
|
||||
}
|
||||
if (finished_callback)
|
||||
finished_callback("Failed to open pipe");
|
||||
return;
|
||||
}
|
||||
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;
|
||||
while (p < lines.size())
|
||||
// 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
|
||||
{
|
||||
std::string::size_type d = lines.find('\n', p);
|
||||
if (d != std::string::npos)
|
||||
r = static_cast<int>(read(fno, buffer, sizeof(buffer) - 1));
|
||||
if (r > 0)
|
||||
{
|
||||
if (line_callback)
|
||||
line_callback(strx::trim(lines.substr(p, d-p)));
|
||||
p = d + 1;
|
||||
buffer[r] = 0;
|
||||
lines += std::string(buffer);
|
||||
}
|
||||
} 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)
|
||||
{
|
||||
// Send SIGINT to process
|
||||
}
|
||||
if (pipe)
|
||||
pclose(pipe);
|
||||
if (finish_flag)
|
||||
{
|
||||
// Send SIGINT to process
|
||||
}
|
||||
if (pipe)
|
||||
pclose(pipe);
|
||||
|
||||
finish_flag = true;
|
||||
if (finished_callback)
|
||||
finished_callback(std::string());
|
||||
});
|
||||
finish_flag = true;
|
||||
if (finished_callback)
|
||||
finished_callback(std::string());
|
||||
});
|
||||
|
||||
return t;
|
||||
}
|
||||
@@ -322,7 +324,7 @@ pid_t OsProcess::findPid(const std::string& cmdline)
|
||||
std::string output = execCommand(oss.str());
|
||||
return std::atoi(output.c_str());
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,16 +10,14 @@ class OsProcess
|
||||
{
|
||||
public:
|
||||
static std::string execCommand(const std::string& cmdline);
|
||||
static int execSystem(const std::string& cmdline);
|
||||
static std::shared_ptr<std::thread> asyncExecCommand(const std::string& cmdline,
|
||||
std::function<void(const std::string& line)> line_callback,
|
||||
std::function<void(const std::string& reason)> finished_callback,
|
||||
bool& finish_flag);
|
||||
static int execSystem(const std::string& cmdline);
|
||||
static std::shared_ptr<std::thread>
|
||||
asyncExecCommand(const std::string& cmdline, std::function<void(const std::string& line)> line_callback,
|
||||
std::function<void(const std::string& reason)> finished_callback, bool& finish_flag);
|
||||
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
|
||||
static pid_t findPid(const std::string& cmdline);
|
||||
static void killByPid(pid_t pid);
|
||||
static void killByPid(pid_t pid);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX)
|
||||
# include <arpa/inet.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "HL_Rtp.h"
|
||||
@@ -28,30 +28,30 @@
|
||||
#define LOG_SUBSYSTEM "network"
|
||||
|
||||
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)
|
||||
struct RtpHeader
|
||||
{
|
||||
unsigned char cc:4; /* CSRC count */
|
||||
unsigned char x:1; /* header extension flag */
|
||||
unsigned char p:1; /* padding flag */
|
||||
unsigned char version:2; /* protocol version */
|
||||
unsigned char pt:7; /* payload type */
|
||||
unsigned char m:1; /* marker bit */
|
||||
unsigned char cc : 4; /* CSRC count */
|
||||
unsigned char x : 1; /* header extension flag */
|
||||
unsigned char p : 1; /* padding flag */
|
||||
unsigned char version : 2; /* protocol version */
|
||||
unsigned char pt : 7; /* payload type */
|
||||
unsigned char m : 1; /* marker bit */
|
||||
unsigned short seq; /* sequence number */
|
||||
unsigned int ts; /* timestamp */
|
||||
unsigned int ssrc; /* synchronization source */
|
||||
unsigned int ts; /* timestamp */
|
||||
unsigned int ssrc; /* synchronization source */
|
||||
};
|
||||
|
||||
struct RtcpHeader
|
||||
{
|
||||
unsigned char rc:5; /* reception report count */
|
||||
unsigned char p:1; /* padding flag */
|
||||
unsigned char version:2; /* protocol version */
|
||||
unsigned char pt; /* payload type */
|
||||
uint16_t len; /* length */
|
||||
uint32_t ssrc; /* synchronization source */
|
||||
unsigned char rc : 5; /* reception report count */
|
||||
unsigned char p : 1; /* padding flag */
|
||||
unsigned char version : 2; /* protocol version */
|
||||
unsigned char pt; /* payload type */
|
||||
uint16_t len; /* length */
|
||||
uint32_t ssrc; /* synchronization source */
|
||||
};
|
||||
|
||||
// --- IPv4 address helpers ---
|
||||
@@ -59,10 +59,8 @@ struct RtcpHeader
|
||||
static std::string ipToString(uint32_t ip)
|
||||
{
|
||||
// ip in host byte order → dotted-decimal
|
||||
return std::to_string((ip >> 24) & 0xFF) + "." +
|
||||
std::to_string((ip >> 16) & 0xFF) + "." +
|
||||
std::to_string((ip >> 8) & 0xFF) + "." +
|
||||
std::to_string( ip & 0xFF);
|
||||
return std::to_string((ip >> 24) & 0xFF) + "." + std::to_string((ip >> 16) & 0xFF) + "." +
|
||||
std::to_string((ip >> 8) & 0xFF) + "." + std::to_string(ip & 0xFF);
|
||||
}
|
||||
|
||||
static uint32_t stringToIp(const std::string& s)
|
||||
@@ -87,7 +85,7 @@ bool RtpHelper::isRtp(const void* buffer, size_t length)
|
||||
return false;
|
||||
|
||||
unsigned char pt = h->pt;
|
||||
bool rtp = (pt >= 96 && pt <= 127) || (pt < 35);
|
||||
bool rtp = (pt >= 96 && pt <= 127) || (pt < 35);
|
||||
return rtp;
|
||||
}
|
||||
|
||||
@@ -130,7 +128,7 @@ int RtpHelper::findPtype(const void* buffer, size_t length)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int RtpHelper::findPacketNo(const void *buffer, size_t length)
|
||||
int RtpHelper::findPacketNo(const void* buffer, size_t length)
|
||||
{
|
||||
if (isRtp(buffer, length))
|
||||
return ntohs(reinterpret_cast<const RtpHeader*>(buffer)->seq);
|
||||
@@ -144,15 +142,16 @@ int RtpHelper::findPayloadLength(const void* buffer, size_t length)
|
||||
return -1;
|
||||
|
||||
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)
|
||||
size_t offset = 12 + 4u * h->cc;
|
||||
size_t offset = 12 + 4u * h->cc;
|
||||
if (offset > length)
|
||||
return -1;
|
||||
|
||||
// Header extension
|
||||
if (h->x) {
|
||||
if (h->x)
|
||||
{
|
||||
if (offset + 4 > length)
|
||||
return -1;
|
||||
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;
|
||||
|
||||
// Padding
|
||||
if (h->p && payloadLen > 0) {
|
||||
if (h->p && payloadLen > 0)
|
||||
{
|
||||
uint8_t padBytes = p[length - 1];
|
||||
if (padBytes > payloadLen)
|
||||
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))
|
||||
return nullptr;
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
// Both are heap-allocated; RTPRawPacket takes ownership and deletes them
|
||||
jrtplib::RTPIPAddress senderAddress = {jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0))};
|
||||
uint8_t* dataCopy = new uint8_t[len];
|
||||
uint8_t* dataCopy = new uint8_t[len];
|
||||
std::memcpy(dataCopy, data, len);
|
||||
|
||||
jrtplib::RTPRawPacket raw(dataCopy, len, senderAddress, jrtplib::RTPTime(0), true);
|
||||
auto packet = std::make_shared<jrtplib::RTPPacket>(raw);
|
||||
auto packet = std::make_shared<jrtplib::RTPPacket>(raw);
|
||||
|
||||
if (packet->GetCreationError() != 0)
|
||||
return nullptr;
|
||||
|
||||
return packet;
|
||||
} catch (const std::exception& e) {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
ICELogInfo(<< "Failed to parse RTP packet: " << e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RtpDump::RtpDump(const char* filename)
|
||||
: mFilename(filename ? filename : "")
|
||||
{
|
||||
}
|
||||
RtpDump::RtpDump(const char* filename) : mFilename(filename ? filename : "") {}
|
||||
|
||||
RtpDump::~RtpDump() = default;
|
||||
|
||||
@@ -237,14 +237,19 @@ void RtpDump::load()
|
||||
|
||||
// Parse source address from the text line
|
||||
size_t spacePos = textLine.find(' ');
|
||||
if (spacePos != std::string::npos) {
|
||||
if (spacePos != std::string::npos)
|
||||
{
|
||||
std::string addrPart = textLine.substr(spacePos + 1);
|
||||
size_t slashPos = addrPart.find('/');
|
||||
if (slashPos != std::string::npos) {
|
||||
size_t slashPos = addrPart.find('/');
|
||||
if (slashPos != std::string::npos)
|
||||
{
|
||||
mSourceIp = stringToIp(addrPart.substr(0, slashPos));
|
||||
try {
|
||||
try
|
||||
{
|
||||
mSourcePort = static_cast<uint16_t>(std::stoi(addrPart.substr(slashPos + 1)));
|
||||
} catch (...) {
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
mSourcePort = 0;
|
||||
}
|
||||
}
|
||||
@@ -260,14 +265,14 @@ void RtpDump::load()
|
||||
input.read(reinterpret_cast<char*>(&buf32), 4);
|
||||
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
|
||||
mSourceIp = ntohl(buf32);
|
||||
|
||||
input.read(reinterpret_cast<char*>(&buf16), 2);
|
||||
mSourcePort = ntohs(buf16);
|
||||
|
||||
input.read(reinterpret_cast<char*>(&buf16), 2); // padding — discard
|
||||
input.read(reinterpret_cast<char*>(&buf16), 2); // padding — discard
|
||||
|
||||
if (!input.good())
|
||||
throw std::runtime_error("Failed to read rtpdump binary header");
|
||||
@@ -275,21 +280,25 @@ void RtpDump::load()
|
||||
// --- 3. Packet records ---
|
||||
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
|
||||
uint16_t recLength, plen;
|
||||
uint32_t offsetMs;
|
||||
|
||||
input.read(reinterpret_cast<char*>(&recLength), 2);
|
||||
if (input.gcount() != 2) break;
|
||||
if (input.gcount() != 2)
|
||||
break;
|
||||
recLength = ntohs(recLength);
|
||||
|
||||
input.read(reinterpret_cast<char*>(&plen), 2);
|
||||
if (input.gcount() != 2) break;
|
||||
if (input.gcount() != 2)
|
||||
break;
|
||||
plen = ntohs(plen);
|
||||
|
||||
input.read(reinterpret_cast<char*>(&offsetMs), 4);
|
||||
if (input.gcount() != 4) break;
|
||||
if (input.gcount() != 4)
|
||||
break;
|
||||
offsetMs = ntohl(offsetMs);
|
||||
|
||||
// All-zeros record signals end of file in some implementations
|
||||
@@ -365,9 +374,10 @@ void RtpDump::add(const void* buffer, size_t len)
|
||||
return;
|
||||
|
||||
uint32_t offsetMs = 0;
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (!mRecording) {
|
||||
if (!mRecording)
|
||||
{
|
||||
mRecording = true;
|
||||
mRecordStart = now;
|
||||
|
||||
@@ -376,9 +386,11 @@ void RtpDump::add(const void* buffer, size_t len)
|
||||
auto epoch = wallNow.time_since_epoch();
|
||||
auto sec = std::chrono::duration_cast<std::chrono::seconds>(epoch);
|
||||
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(epoch - sec);
|
||||
mStartSec = static_cast<uint32_t>(sec.count());
|
||||
mStartSec = static_cast<uint32_t>(sec.count());
|
||||
mStartUsec = static_cast<uint32_t>(usec.count());
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - mRecordStart);
|
||||
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));
|
||||
|
||||
RtpData entry;
|
||||
entry.mRawData.assign(static_cast<const uint8_t*>(buffer),
|
||||
static_cast<const uint8_t*>(buffer) + len);
|
||||
entry.mRawData.assign(static_cast<const uint8_t*>(buffer), static_cast<const uint8_t*>(buffer) + len);
|
||||
entry.mOffsetMs = offsetMs;
|
||||
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
|
||||
|
||||
@@ -414,9 +425,8 @@ void RtpDump::flush()
|
||||
throw std::runtime_error("Failed to open file for writing: " + mFilename);
|
||||
|
||||
// --- 1. Text header ---
|
||||
std::string textLine = std::string(RTPDUMP_SHEBANG) + " " +
|
||||
ipToString(mSourceIp) + "/" +
|
||||
std::to_string(mSourcePort) + "\n";
|
||||
std::string textLine =
|
||||
std::string(RTPDUMP_SHEBANG) + " " + ipToString(mSourceIp) + "/" + std::to_string(mSourcePort) + "\n";
|
||||
output.write(textLine.data(), static_cast<std::streamsize>(textLine.size()));
|
||||
|
||||
// --- 2. Binary file header (16 bytes) ---
|
||||
@@ -435,13 +445,14 @@ void RtpDump::flush()
|
||||
buf16 = htons(mSourcePort);
|
||||
output.write(reinterpret_cast<const char*>(&buf16), 2);
|
||||
|
||||
buf16 = 0; // padding
|
||||
buf16 = 0; // padding
|
||||
output.write(reinterpret_cast<const char*>(&buf16), 2);
|
||||
|
||||
// --- 3. Packet records ---
|
||||
size_t written = 0;
|
||||
|
||||
for (const auto& pkt : mPacketList) {
|
||||
for (const auto& pkt : mPacketList)
|
||||
{
|
||||
if (pkt.mRawData.empty())
|
||||
continue;
|
||||
|
||||
|
||||
+35
-38
@@ -22,12 +22,9 @@ struct RtpPair
|
||||
T mRtp;
|
||||
T mRtcp;
|
||||
|
||||
RtpPair()
|
||||
{}
|
||||
RtpPair() {}
|
||||
|
||||
RtpPair(const T& rtp, const T& rtcp)
|
||||
:mRtp(rtp), mRtcp(rtcp)
|
||||
{}
|
||||
RtpPair(const T& rtp, const T& rtcp) : mRtp(rtp), mRtcp(rtcp) {}
|
||||
|
||||
bool multiplexed() const { return mRtp == mRtcp; }
|
||||
};
|
||||
@@ -35,14 +32,14 @@ struct RtpPair
|
||||
class RtpHelper
|
||||
{
|
||||
public:
|
||||
static bool isRtp(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 bool isRtpOrRtcp(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 void setSsrc(void* buffer, size_t length, uint32_t ssrc);
|
||||
static int findPayloadLength(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 findPacketNo(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 unsigned findSsrc(const void* buffer, size_t length);
|
||||
static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
|
||||
static int findPayloadLength(const void* buffer, size_t length);
|
||||
|
||||
static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t);
|
||||
};
|
||||
@@ -79,72 +76,72 @@ protected:
|
||||
struct RtpData
|
||||
{
|
||||
std::shared_ptr<jrtplib::RTPPacket> mPacket;
|
||||
std::vector<uint8_t> mRawData;
|
||||
uint32_t mOffsetMs = 0;
|
||||
std::vector<uint8_t> mRawData;
|
||||
uint32_t mOffsetMs = 0;
|
||||
};
|
||||
|
||||
typedef std::vector<RtpData> PacketList;
|
||||
PacketList mPacketList;
|
||||
std::string mFilename;
|
||||
bool mLoaded = false;
|
||||
typedef std::vector<RtpData> PacketList;
|
||||
PacketList mPacketList;
|
||||
std::string mFilename;
|
||||
bool mLoaded = false;
|
||||
|
||||
// File header fields
|
||||
uint32_t mSourceIp = 0;
|
||||
uint16_t mSourcePort = 0;
|
||||
uint32_t mStartSec = 0;
|
||||
uint32_t mStartUsec = 0;
|
||||
uint32_t mSourceIp = 0;
|
||||
uint16_t mSourcePort = 0;
|
||||
uint32_t mStartSec = 0;
|
||||
uint32_t mStartUsec = 0;
|
||||
|
||||
// Auto-compute packet offsets during recording
|
||||
bool mRecording = false;
|
||||
bool mRecording = false;
|
||||
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:
|
||||
explicit RtpDump(const char* filename);
|
||||
~RtpDump();
|
||||
|
||||
/** Set source address for the file header (host byte order). */
|
||||
void setSource(uint32_t ip, uint16_t port);
|
||||
uint32_t sourceIp() const { return mSourceIp; }
|
||||
uint16_t sourcePort() const { return mSourcePort; }
|
||||
void setSource(uint32_t ip, uint16_t port);
|
||||
uint32_t sourceIp() const { return mSourceIp; }
|
||||
uint16_t sourcePort() const { return mSourcePort; }
|
||||
|
||||
/**
|
||||
* @brief Load packets from an rtpdump file
|
||||
* @throws std::runtime_error on file/format error
|
||||
*/
|
||||
void load();
|
||||
bool isLoaded() const { return mLoaded; }
|
||||
void load();
|
||||
bool isLoaded() const { return mLoaded; }
|
||||
|
||||
size_t count() const;
|
||||
size_t count() const;
|
||||
|
||||
/**
|
||||
* @brief Get parsed RTP packet at index
|
||||
* @throws std::out_of_range if index is invalid
|
||||
* @throws std::runtime_error if packet could not be parsed as RTP
|
||||
*/
|
||||
jrtplib::RTPPacket& packetAt(size_t index);
|
||||
jrtplib::RTPPacket& packetAt(size_t index);
|
||||
|
||||
/** @brief Get raw packet bytes at index */
|
||||
const std::vector<uint8_t>& rawDataAt(size_t index) const;
|
||||
|
||||
/** @brief Get packet time offset in milliseconds */
|
||||
uint32_t offsetAt(size_t index) const;
|
||||
uint32_t offsetAt(size_t index) const;
|
||||
|
||||
/** @brief Add a packet; time offset is auto-computed from first add() call */
|
||||
void add(const void* data, size_t len);
|
||||
void add(const void* data, size_t len);
|
||||
|
||||
/** @brief Add a packet with an explicit millisecond offset */
|
||||
void add(const void* data, size_t len, uint32_t offsetMs);
|
||||
void add(const void* data, size_t len, uint32_t offsetMs);
|
||||
|
||||
/**
|
||||
* @brief Write all packets to file in rtpdump format
|
||||
* @throws std::runtime_error on file error
|
||||
*/
|
||||
void flush();
|
||||
void flush();
|
||||
|
||||
void clear();
|
||||
const std::string& filename() const { return mFilename; }
|
||||
void clear();
|
||||
const std::string& filename() const { return mFilename; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
class SafeSingleton
|
||||
{
|
||||
protected:
|
||||
static std::atomic<T*> SharedInstance;
|
||||
static std::mutex mMutex;
|
||||
static std::mutex mMutex;
|
||||
|
||||
public:
|
||||
static T& instance()
|
||||
{
|
||||
@@ -50,9 +51,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
std::atomic<T*> SafeSingleton<T>::SharedInstance;
|
||||
template <class T>
|
||||
template<class T>
|
||||
std::mutex SafeSingleton<T>::mMutex;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <assert.h>
|
||||
#include <chrono>
|
||||
#if !defined(TARGET_WIN)
|
||||
# include <unistd.h> // Responsible for close() call on Linux
|
||||
#include <unistd.h> // Responsible for close() call on Linux
|
||||
#endif
|
||||
|
||||
#include "HL_SocketHeap.h"
|
||||
@@ -31,14 +31,13 @@
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// ----------------------------- SocketSink -------------------------
|
||||
SocketSink::~SocketSink()
|
||||
{}
|
||||
SocketSink::~SocketSink() {}
|
||||
|
||||
// ----------------------------- SocketHeap -------------------------
|
||||
|
||||
SocketHeap::SocketHeap(unsigned short start, unsigned short finish)
|
||||
{
|
||||
mStart = start;
|
||||
mStart = start;
|
||||
mFinish = finish;
|
||||
}
|
||||
|
||||
@@ -74,7 +73,7 @@ void SocketHeap::setRange(unsigned short start, unsigned short finish)
|
||||
mFinish = finish;
|
||||
}
|
||||
|
||||
void SocketHeap::range(unsigned short &start, unsigned short &finish)
|
||||
void SocketHeap::range(unsigned short& start, unsigned short& finish)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
|
||||
@@ -82,10 +81,10 @@ void SocketHeap::range(unsigned short &start, unsigned short &finish)
|
||||
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;
|
||||
for (int attempt=0; (!rtp || !rtcp) && attempt < (mFinish - mStart)/2; attempt++)
|
||||
for (int attempt = 0; (!rtp || !rtcp) && attempt < (mFinish - mStart) / 2; attempt++)
|
||||
{
|
||||
// Allocate RTP
|
||||
try
|
||||
@@ -96,7 +95,7 @@ RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink *sin
|
||||
else
|
||||
rtcp = allocSocket(family, sink, rtp->localport() + 1);
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
// Release a partially allocated pair before retrying - otherwise
|
||||
// the RTP socket from this attempt leaks into the socket map.
|
||||
@@ -121,14 +120,13 @@ RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink *sin
|
||||
freeSocket(rtcp);
|
||||
throw Exception(ERR_NET_FAILED);
|
||||
}
|
||||
ICELogInfo(<< "Allocated socket pair " << (family == AF_INET ? "AF_INET" : "AF_INET6") << " "
|
||||
<< rtp->socket() << ":" << rtcp->socket()
|
||||
<< " at ports " << rtp->localport() << ":"<< rtcp->localport());
|
||||
ICELogInfo(<< "Allocated socket pair " << (family == AF_INET ? "AF_INET" : "AF_INET6") << " " << rtp->socket()
|
||||
<< ":" << rtcp->socket() << " at ports " << rtp->localport() << ":" << rtcp->localport());
|
||||
|
||||
return RtpPair<PDatagramSocket>(rtp, rtcp);
|
||||
}
|
||||
|
||||
void SocketHeap::freeSocketPair(const RtpPair<PDatagramSocket> &p)
|
||||
void SocketHeap::freeSocketPair(const RtpPair<PDatagramSocket>& p)
|
||||
{
|
||||
freeSocket(p.mRtp);
|
||||
freeSocket(p.mRtcp);
|
||||
@@ -136,7 +134,7 @@ void SocketHeap::freeSocketPair(const RtpPair<PDatagramSocket> &p)
|
||||
|
||||
PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||
{
|
||||
Lock l(mGuard);
|
||||
Lock l(mGuard);
|
||||
SOCKET sock = ::socket(family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET)
|
||||
{
|
||||
@@ -148,13 +146,13 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||
}
|
||||
|
||||
// Obtain port number
|
||||
sockaddr_in addr;
|
||||
sockaddr_in addr;
|
||||
sockaddr_in6 addr6;
|
||||
int result = 0;
|
||||
int testport;
|
||||
int result = 0;
|
||||
int testport;
|
||||
// A fixed port cannot be retried (it would loop forever if the port is
|
||||
// owned by another process); random ports get a bounded number of attempts.
|
||||
int attemptsLeft = port ? 1 : 100;
|
||||
int attemptsLeft = port ? 1 : 100;
|
||||
do
|
||||
{
|
||||
testport = port ? port : rand() % ((mFinish - mStart) / 2) * 2 + mStart;
|
||||
@@ -215,7 +213,7 @@ void SocketHeap::freeSocket(PDatagramSocket socket)
|
||||
|
||||
void SocketHeap::processDeleted()
|
||||
{
|
||||
Lock l(mDeleteGuard);
|
||||
Lock l(mDeleteGuard);
|
||||
|
||||
SocketVector::iterator socketIter = mDeleteVector.begin();
|
||||
while (socketIter != mDeleteVector.end())
|
||||
@@ -257,10 +255,11 @@ void SocketHeap::thread()
|
||||
}
|
||||
|
||||
// Update socket set
|
||||
for (auto& socketIter: mSocketMap)
|
||||
for (auto& socketIter : mSocketMap)
|
||||
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
|
||||
agreggator.addSocket(socketIter->second.mSocket);
|
||||
@@ -278,12 +277,12 @@ void SocketHeap::thread()
|
||||
// Remove deleted sockets to avoid call non-existant sinks
|
||||
processDeleted();
|
||||
|
||||
for (unsigned i=0; i<agreggator.count(); i++)
|
||||
for (unsigned i = 0; i < agreggator.count(); i++)
|
||||
{
|
||||
if (agreggator.hasDataAtIndex(i))
|
||||
{
|
||||
//ICELogInfo(<<"Got incoming UDP packet at index " << (const int)i);
|
||||
PDatagramSocket sock = agreggator.socketAt(i);
|
||||
// ICELogInfo(<<"Got incoming UDP packet at index " << (const int)i);
|
||||
PDatagramSocket sock = agreggator.socketAt(i);
|
||||
|
||||
// Find corresponding data sink
|
||||
SocketMap::iterator socketItemIter = mSocketMap.find(sock->mHandle);
|
||||
@@ -291,16 +290,16 @@ void SocketHeap::thread()
|
||||
if (socketItemIter != mSocketMap.end())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// There is a call to ProcessDeleted() as OnReceivedData() could delete sockets
|
||||
processDeleted();
|
||||
}
|
||||
} //of for
|
||||
} // of for
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -308,12 +307,12 @@ void SocketHeap::thread()
|
||||
}
|
||||
|
||||
mShutdown = false;
|
||||
//#endif
|
||||
// #endif
|
||||
}
|
||||
|
||||
|
||||
static SocketHeap GRTPSocketHeap(20002, 25100);
|
||||
SocketHeap& SocketHeap::instance()
|
||||
SocketHeap& SocketHeap::instance()
|
||||
{
|
||||
return GRTPSocketHeap;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ class SocketSink
|
||||
{
|
||||
public:
|
||||
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
|
||||
@@ -38,76 +39,68 @@ public:
|
||||
SocketHeap(unsigned short start, unsigned short finish);
|
||||
virtual ~SocketHeap();
|
||||
|
||||
static SocketHeap& instance();
|
||||
static SocketHeap& instance();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
// 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
|
||||
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
|
||||
PDatagramSocket allocSocket(int family, SocketSink* sink, int port = 0);
|
||||
RtpPair<PDatagramSocket> allocSocketPair(int family, SocketSink* sink, Multiplex m);
|
||||
// Attempts to allocate and return socket + allocated port number. REQUIRES pointer to data sink - it will be used
|
||||
// to process incoming datagrams
|
||||
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.
|
||||
void freeSocket(PDatagramSocket socket);
|
||||
void freeSocketPair(const RtpPair<PDatagramSocket>& p);
|
||||
void freeSocket(PDatagramSocket socket);
|
||||
void freeSocketPair(const RtpPair<PDatagramSocket>& p);
|
||||
|
||||
// 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:
|
||||
struct SocketItem
|
||||
{
|
||||
// Local port number for socket
|
||||
PDatagramSocket mSocket;
|
||||
PDatagramSocket mSocket;
|
||||
|
||||
// Data sink pointer
|
||||
SocketSink* mSink;
|
||||
SocketSink* mSink;
|
||||
|
||||
SocketItem()
|
||||
:mSink(nullptr)
|
||||
{ }
|
||||
SocketItem() : mSink(nullptr) {}
|
||||
|
||||
SocketItem(unsigned short portnumber, SocketSink* sink)
|
||||
:mSink(sink)
|
||||
{
|
||||
mSocket->mLocalPort = portnumber;
|
||||
}
|
||||
SocketItem(unsigned short portnumber, SocketSink* sink) : mSink(sink) { mSocket->mLocalPort = portnumber; }
|
||||
|
||||
~SocketItem()
|
||||
{ }
|
||||
~SocketItem() {}
|
||||
};
|
||||
|
||||
typedef std::map<SOCKET, SocketItem> SocketMap;
|
||||
typedef std::vector<unsigned short> PortVector;
|
||||
typedef std::vector<unsigned short> PortVector;
|
||||
typedef std::vector<PDatagramSocket> SocketVector;
|
||||
|
||||
Mutex mGuard;
|
||||
SocketMap mSocketMap;
|
||||
PortVector mPortVector;
|
||||
unsigned short mStart,
|
||||
mFinish;
|
||||
SocketVector mDeleteVector;
|
||||
Mutex mDeleteGuard;
|
||||
Mutex mGuard;
|
||||
SocketMap mSocketMap;
|
||||
PortVector mPortVector;
|
||||
unsigned short mStart, mFinish;
|
||||
SocketVector mDeleteVector;
|
||||
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;
|
||||
bool mShutdown = false;
|
||||
bool isShutdown() const { return mShutdown; }
|
||||
std::thread::id mThreadId;
|
||||
bool mShutdown = false;
|
||||
bool isShutdown() const { return mShutdown; }
|
||||
|
||||
void thread();
|
||||
void thread();
|
||||
|
||||
// Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector
|
||||
void processDeleted();
|
||||
|
||||
void processDeleted();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,18 +5,15 @@ template<typename T>
|
||||
struct Average
|
||||
{
|
||||
int mCount = 0;
|
||||
T mSum = 0;
|
||||
T average() const
|
||||
T mSum = 0;
|
||||
T average() const
|
||||
{
|
||||
if (!mCount)
|
||||
return 0;
|
||||
return mSum / mCount;
|
||||
}
|
||||
|
||||
T value() const
|
||||
{
|
||||
return average();
|
||||
}
|
||||
T value() const { return average(); }
|
||||
|
||||
void process(T value)
|
||||
{
|
||||
@@ -28,12 +25,12 @@ struct Average
|
||||
template<typename T, int minimum = 100000, int maximum = 0, int default_value = 0>
|
||||
struct TestResult
|
||||
{
|
||||
T mMin = minimum;
|
||||
T mMax = maximum;
|
||||
T mMin = minimum;
|
||||
T mMax = maximum;
|
||||
Average<T> mAverage;
|
||||
T mCurrent = default_value;
|
||||
T mCurrent = default_value;
|
||||
|
||||
void process(T value)
|
||||
void process(T value)
|
||||
{
|
||||
if (mMin > value)
|
||||
mMin = value;
|
||||
@@ -43,12 +40,9 @@ struct TestResult
|
||||
mAverage.process(value);
|
||||
}
|
||||
|
||||
bool is_initialized() const
|
||||
{
|
||||
return mAverage.mCount > 0;
|
||||
}
|
||||
bool is_initialized() const { return mAverage.mCount > 0; }
|
||||
|
||||
T current() const
|
||||
T current() const
|
||||
{
|
||||
if (is_initialized())
|
||||
return mCurrent;
|
||||
@@ -56,26 +50,17 @@ struct TestResult
|
||||
return 0;
|
||||
}
|
||||
|
||||
T value() const
|
||||
{
|
||||
return current();
|
||||
}
|
||||
T value() const { return current(); }
|
||||
|
||||
T average() const
|
||||
{
|
||||
return mAverage.average();
|
||||
}
|
||||
T average() const { return mAverage.average(); }
|
||||
|
||||
TestResult<T>& operator = (T value)
|
||||
TestResult<T>& operator=(T value)
|
||||
{
|
||||
process(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T()
|
||||
{
|
||||
return mCurrent;
|
||||
}
|
||||
operator T() { return mCurrent; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -10,15 +10,14 @@
|
||||
|
||||
enum class StreamState
|
||||
{
|
||||
Sending = 1, // Transmitting RTP. Set this flag to allow outgoing 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.
|
||||
Grabbing = 8, // Capture audio. Unmutes the audio to specified stream.
|
||||
Srtp = 16, // Use SRTP. Make attempt
|
||||
SipSend = 32, // Declare send capability in SDP
|
||||
SipRecv = 64 // Declare recv capability in SDP
|
||||
Sending = 1, // Transmitting RTP. Set this flag to allow outgoing 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.
|
||||
Grabbing = 8, // Capture audio. Unmutes the audio to specified stream.
|
||||
Srtp = 16, // Use SRTP. Make attempt
|
||||
SipSend = 32, // Declare send capability in SDP
|
||||
SipRecv = 64 // Declare recv capability in SDP
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
# include <cctype>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <cctype>
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string strx::makeUtf8(const std::tstring &arg)
|
||||
std::string strx::makeUtf8(const std::tstring& arg)
|
||||
{
|
||||
#if defined(TARGET_WIN)
|
||||
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
|
||||
}
|
||||
|
||||
std::string strx::toUtf8(const std::tstring &arg)
|
||||
std::string strx::toUtf8(const std::tstring& arg)
|
||||
{
|
||||
return makeUtf8(arg);
|
||||
}
|
||||
@@ -80,7 +80,7 @@ std::tstring strx::makeTstring(const std::string& arg)
|
||||
#endif
|
||||
}
|
||||
|
||||
int strx::toInt(const char *s, int defaultValue, bool* isOk)
|
||||
int strx::toInt(const char* s, int defaultValue, bool* isOk)
|
||||
{
|
||||
int result;
|
||||
if (sscanf(s, "%d", &result) != 1)
|
||||
@@ -89,14 +89,13 @@ int strx::toInt(const char *s, int defaultValue, bool* isOk)
|
||||
*isOk = false;
|
||||
result = defaultValue;
|
||||
}
|
||||
else
|
||||
if (isOk)
|
||||
*isOk = true;
|
||||
else if (isOk)
|
||||
*isOk = true;
|
||||
|
||||
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;
|
||||
if (sscanf(s, "%" SCNu64, &result) != 1)
|
||||
@@ -105,9 +104,8 @@ uint64_t strx::toUint64(const char* s, uint64_t def, bool *isOk)
|
||||
*isOk = false;
|
||||
result = def;
|
||||
}
|
||||
else
|
||||
if (isOk)
|
||||
*isOk = true;
|
||||
else if (isOk)
|
||||
*isOk = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -119,28 +117,29 @@ std::string strx::toHex(unsigned int value)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string strx::toHex(const void *ptr)
|
||||
std::string strx::toHex(const void* ptr)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::fixed << std::setw(8) << std::setfill('0') << std::hex << ptr;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
//must be lowercase for MD5
|
||||
// must be lowercase for MD5
|
||||
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;
|
||||
char* r = &result[0];
|
||||
for (size_t i=0; i < inputLength; ++i)
|
||||
char* r = &result[0];
|
||||
for (size_t i = 0; i < inputLength; ++i)
|
||||
{
|
||||
unsigned char temp = *p++;
|
||||
|
||||
int hi = (temp & 0xf0)>>4;
|
||||
int low = (temp & 0xf);
|
||||
int hi = (temp & 0xf0) >> 4;
|
||||
int low = (temp & 0xf);
|
||||
|
||||
*r++ = hexmap[hi];
|
||||
*r++ = hexmap[low];
|
||||
@@ -149,13 +148,13 @@ std::string strx::toHex(const uint8_t* input, size_t inputLength)
|
||||
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
|
||||
std::istringstream iss(source);
|
||||
std::ostringstream oss;
|
||||
std::string line;
|
||||
while (std::getline(iss,line))
|
||||
std::string line;
|
||||
while (std::getline(iss, line))
|
||||
{
|
||||
oss << prefix << line << std::endl;
|
||||
}
|
||||
@@ -194,7 +193,7 @@ void strx::split(const std::string& src, std::vector<std::string>& dst, const st
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string t = src.substr(p, f-p);
|
||||
std::string t = src.substr(p, f - p);
|
||||
if (!t.empty())
|
||||
dst.push_back(t);
|
||||
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> result;
|
||||
std::size_t p = host.find(':');
|
||||
std::size_t p = host.find(':');
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
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::string::size_type p = s.find('=');
|
||||
std::string::size_type p = s.find('=');
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
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 ((result.second[0] == '"' && result.second[result.second.size()-1] == '"') ||
|
||||
(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 = result.second.substr(1, result.second.size() - 2);
|
||||
}
|
||||
}
|
||||
@@ -255,10 +254,10 @@ std::string strx::intToString(int value)
|
||||
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;
|
||||
int code = sscanf(s.c_str(), "%f", &result);
|
||||
int code = sscanf(s.c_str(), "%f", &result);
|
||||
if (code == 1)
|
||||
{
|
||||
if (isOk)
|
||||
@@ -274,16 +273,18 @@ float strx::toFloat(const std::string &s, float v, bool* isOk)
|
||||
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 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));
|
||||
auto wsfront =
|
||||
std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c) || c == '\r' || c == '\n'; });
|
||||
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)
|
||||
{
|
||||
char buffer[128] = "";
|
||||
char buffer[128] = "";
|
||||
struct tm lt;
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
|
||||
if (localtime_r(&t, <) == nullptr)
|
||||
@@ -291,16 +292,16 @@ std::string strx::timeToString(time_t t)
|
||||
#else
|
||||
lt = *localtime(&t);
|
||||
#endif
|
||||
strftime(buffer, sizeof(buffer)-1, "%Y-%m-%d %H:%M:%S", <);
|
||||
strftime(buffer, sizeof(buffer) - 1, "%Y-%m-%d %H:%M:%S", <);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
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 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 result; result.resize(s.size() / 2);
|
||||
std::string result;
|
||||
result.resize(s.size() / 2);
|
||||
const char* t = s.c_str();
|
||||
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;
|
||||
}
|
||||
@@ -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 result(s);
|
||||
std::string result(s);
|
||||
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);
|
||||
p += n.size();
|
||||
@@ -366,12 +368,12 @@ std::string strx::decodeUri(const std::string& s)
|
||||
|
||||
char ch;
|
||||
|
||||
int i, ii = 0;
|
||||
for (i=0; i<(int)s.length(); i++)
|
||||
int i, ii = 0;
|
||||
for (i = 0; i < (int)s.length(); i++)
|
||||
{
|
||||
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);
|
||||
ret += ch;
|
||||
@@ -403,13 +405,13 @@ bool strx::endsWith(const std::string& s, const std::string& suffix)
|
||||
int strx::stringToDuration(const std::string& s)
|
||||
{
|
||||
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"))
|
||||
return std::stoi(s.substr(0, s.size()-1)) * 1000;
|
||||
return std::stoi(s.substr(0, s.size() - 1)) * 1000;
|
||||
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"))
|
||||
return std::stoi(s.substr(0, s.size()-1)) * 3600 * 1000;
|
||||
return std::stoi(s.substr(0, s.size() - 1)) * 3600 * 1000;
|
||||
else
|
||||
return std::stoi(s) * 1000;
|
||||
}
|
||||
@@ -438,7 +440,7 @@ std::string strx::removeQuotes(const std::string& s)
|
||||
r = r.substr(1);
|
||||
|
||||
if (r.back() == '"')
|
||||
r = r.substr(0, r.size()-1);
|
||||
r = r.substr(0, r.size() - 1);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -446,20 +448,18 @@ std::string strx::removeQuotes(const std::string& s)
|
||||
#if defined(TARGET_WIN)
|
||||
|
||||
// MSVC++ lacks memmem support
|
||||
const void *memmem(const void *haystack, size_t haystack_len,
|
||||
const void * const needle, const size_t needle_len)
|
||||
const void* memmem(const void* haystack, size_t haystack_len, const void* const needle, const size_t needle_len)
|
||||
{
|
||||
if (!haystack || !haystack_len || !needle || !needle_len)
|
||||
return nullptr;
|
||||
|
||||
for (const char *h = (const char*)haystack;
|
||||
haystack_len >= needle_len;
|
||||
++h, --haystack_len) {
|
||||
if (!memcmp(h, needle, needle_len)) {
|
||||
for (const char* h = (const char*)haystack; haystack_len >= needle_len; ++h, --haystack_len)
|
||||
{
|
||||
if (!memcmp(h, needle, needle_len))
|
||||
{
|
||||
return h;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -20,32 +20,32 @@
|
||||
class strx
|
||||
{
|
||||
public:
|
||||
static std::string extractFilename(const std::string& path);
|
||||
static std::string appendPath(const std::string& s1, const std::string& s2);
|
||||
static std::string extractFilename(const std::string& path);
|
||||
static std::string appendPath(const std::string& s1, const std::string& s2);
|
||||
|
||||
static std::string makeUtf8(const std::tstring& arg);
|
||||
static std::string toUtf8(const std::tstring& arg);
|
||||
static std::string makeUtf8(const std::tstring& arg);
|
||||
static std::string toUtf8(const std::tstring& arg);
|
||||
static std::tstring makeTstring(const std::string& arg);
|
||||
|
||||
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 std::string toHex(unsigned int value);
|
||||
static std::string toHex(const void* ptr);
|
||||
static std::string toHex(const uint8_t* input, size_t inputLength);
|
||||
static std::string intToString(int value);
|
||||
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
||||
static std::string doubleToString(double value, int precision);
|
||||
static int fromHex2Int(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 int toInt(const char* s, int defaultValue, 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(const void* ptr);
|
||||
static std::string toHex(const uint8_t* input, size_t inputLength);
|
||||
static std::string intToString(int value);
|
||||
static std::string prefixLines(const std::string& source, const std::string& prefix);
|
||||
static std::string doubleToString(double value, int precision);
|
||||
static int fromHex2Int(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 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");
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
static std::string join(const std::vector<T>& v, const std::string& delimiter)
|
||||
{
|
||||
std::ostringstream s;
|
||||
@@ -58,21 +58,21 @@ public:
|
||||
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::string trim(const std::string& s);
|
||||
static std::string timeToString(time_t t);
|
||||
static std::string millisecondsToString(uint64_t t);
|
||||
static std::string replace(const std::string& s, char f, char r);
|
||||
static std::string trim(const std::string& s);
|
||||
static std::string timeToString(time_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, const std::string& tmpl, const std::string& n);
|
||||
static std::string decodeUri(const std::string& s);
|
||||
static bool startsWith(const std::string& s, const std::string& prefix);
|
||||
static bool endsWith(const std::string& s, const std::string& suffix);
|
||||
static int stringToDuration(const std::string& s);
|
||||
|
||||
static std::string uppercase(const std::string& s);
|
||||
static std::string lowercase(const std::string& s);
|
||||
static std::string removeQuotes(const std::string& s);
|
||||
static bool startsWith(const std::string& s, const std::string& prefix);
|
||||
static bool endsWith(const std::string& s, const std::string& suffix);
|
||||
static int stringToDuration(const std::string& s);
|
||||
|
||||
static std::string uppercase(const std::string& s);
|
||||
static std::string lowercase(const std::string& s);
|
||||
static std::string removeQuotes(const std::string& s);
|
||||
};
|
||||
|
||||
class XcapHelper
|
||||
@@ -88,8 +88,7 @@ public:
|
||||
#if defined(TARGET_WIN)
|
||||
|
||||
// MSVC++ lacks memmem support
|
||||
extern const void *memmem(const void *haystack, size_t haystack_len,
|
||||
const void * const needle, const size_t needle_len);
|
||||
extern const void* memmem(const void* haystack, size_t haystack_len, const void* const needle, const size_t needle_len);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
#include <iostream>
|
||||
|
||||
#ifdef TARGET_OSX
|
||||
# include <libkern/OSAtomic.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_WIN
|
||||
# include <Windows.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
void SyncHelper::delay(unsigned int microseconds)
|
||||
@@ -35,7 +35,7 @@ void SyncHelper::delay(unsigned int microseconds)
|
||||
|
||||
|
||||
// ------------------- ThreadHelper -------------------
|
||||
void ThreadHelper::setName(const std::string &name)
|
||||
void ThreadHelper::setName(const std::string& name)
|
||||
{
|
||||
#if defined(TARGET_LINUX)
|
||||
// The name will be truncated to 8 or 16 characters
|
||||
@@ -53,7 +53,7 @@ uint64_t ThreadHelper::getCurrentId()
|
||||
return static_cast<uint64_t>(GetCurrentThreadId());
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_LINUX)||defined(TARGET_OSX)
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
// RPi builds want this!
|
||||
return (uint64_t)(pthread_self());
|
||||
#endif
|
||||
@@ -70,13 +70,13 @@ using namespace std::chrono;
|
||||
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
||||
|
||||
// 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
|
||||
std::chrono::milliseconds chronox::getTimestamp()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ std::chrono::milliseconds chronox::getUptime()
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -140,21 +140,15 @@ std::chrono::milliseconds chronox::ExecutionTime::getSpentTime() const
|
||||
}
|
||||
|
||||
// --------------- BufferQueue -----------------
|
||||
BufferQueue::BufferQueue()
|
||||
{
|
||||
BufferQueue::BufferQueue() {}
|
||||
|
||||
}
|
||||
|
||||
BufferQueue::~BufferQueue()
|
||||
{
|
||||
|
||||
}
|
||||
BufferQueue::~BufferQueue() {}
|
||||
|
||||
void BufferQueue::push(const void* data, int bytes)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
|
||||
PBlock b = std::make_shared<Block>();
|
||||
PBlock b = std::make_shared<Block>();
|
||||
b->resize(bytes);
|
||||
memcpy(b->data(), data, bytes);
|
||||
mBlockList.push_back(b);
|
||||
@@ -164,8 +158,7 @@ void BufferQueue::push(const void* data, int bytes)
|
||||
BufferQueue::PBlock BufferQueue::pull(int milliseconds)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
mSignal.wait_for(l, std::chrono::milliseconds(milliseconds),
|
||||
[this]() { return !mBlockList.empty(); });
|
||||
mSignal.wait_for(l, std::chrono::milliseconds(milliseconds), [this]() { return !mBlockList.empty(); });
|
||||
|
||||
PBlock r;
|
||||
if (!mBlockList.empty())
|
||||
@@ -178,9 +171,7 @@ BufferQueue::PBlock BufferQueue::pull(int milliseconds)
|
||||
}
|
||||
|
||||
// ----------------- Semaphore ---------------------
|
||||
Semaphore::Semaphore(unsigned int count)
|
||||
: m_count(count)
|
||||
{}
|
||||
Semaphore::Semaphore(unsigned int count) : m_count(count) {}
|
||||
|
||||
void Semaphore::notify()
|
||||
{
|
||||
@@ -196,7 +187,8 @@ void Semaphore::wait()
|
||||
m_count--;
|
||||
}
|
||||
|
||||
bool Semaphore::waitFor(std::chrono::milliseconds timeout) {
|
||||
bool Semaphore::waitFor(std::chrono::milliseconds timeout)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
|
||||
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);
|
||||
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
uint64_t id = ++m_idcounter;
|
||||
uint64_t id = ++m_idcounter;
|
||||
item.id = id;
|
||||
m_items.push(std::move(item));
|
||||
lk.unlock();
|
||||
@@ -246,19 +238,22 @@ uint64_t TimerQueue::add(std::chrono::milliseconds milliseconds, std::function<v
|
||||
// 1 if the timer was cancelled.
|
||||
// 0 if you were too late to cancel (or the timer ID was never valid to
|
||||
// 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
|
||||
// heap integrity), we set the item as having no handler, and put
|
||||
// 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
|
||||
// handler.
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
for (auto&& item : m_items.getContainer()) {
|
||||
if (item.id == id && item.handler) {
|
||||
for (auto&& item : m_items.getContainer())
|
||||
{
|
||||
if (item.id == id && item.handler)
|
||||
{
|
||||
WorkItem newItem;
|
||||
// Zero time, so it stays at the top for immediate execution
|
||||
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.
|
||||
// Also, we need to manually set the handler to nullptr, since
|
||||
// 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,
|
||||
// since it maintains the heap integrity
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
for (auto&& item : m_items.getContainer()) {
|
||||
if (item.id) {
|
||||
for (auto&& item : m_items.getContainer())
|
||||
{
|
||||
if (item.id)
|
||||
{
|
||||
item.end = Clock::time_point();
|
||||
item.id = 0;
|
||||
}
|
||||
@@ -307,9 +304,12 @@ void TimerQueue::run()
|
||||
if (end.first)
|
||||
{
|
||||
// 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);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// No timers exist, so wait forever until something changes
|
||||
m_checkWork.wait();
|
||||
}
|
||||
@@ -327,11 +327,15 @@ void TimerQueue::run()
|
||||
std::pair<bool, TimerQueue::Clock::time_point> TimerQueue::calcWaitTime()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mtx);
|
||||
while (m_items.size()) {
|
||||
if (m_items.top().handler) {
|
||||
while (m_items.size())
|
||||
{
|
||||
if (m_items.top().handler)
|
||||
{
|
||||
// Item present, so return the new wait time
|
||||
return std::make_pair(true, m_items.top().end);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Discard empty handlers (they were cancelled)
|
||||
m_items.pop();
|
||||
}
|
||||
@@ -342,9 +346,11 @@ std::pair<bool, TimerQueue::Clock::time_point> TimerQueue::calcWaitTime()
|
||||
return std::make_pair(false, Clock::time_point());
|
||||
}
|
||||
|
||||
void TimerQueue::checkWork() {
|
||||
void TimerQueue::checkWork()
|
||||
{
|
||||
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()));
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<TimerQueue::WorkItem>& TimerQueue::Queue::getContainer() {
|
||||
std::vector<TimerQueue::WorkItem>& TimerQueue::Queue::getContainer()
|
||||
{
|
||||
return this->c;
|
||||
}
|
||||
|
||||
+31
-31
@@ -15,11 +15,11 @@
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# include <WinSock2.h>
|
||||
# include <Windows.h>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
typedef std::recursive_mutex Mutex;
|
||||
typedef std::recursive_mutex Mutex;
|
||||
typedef std::unique_lock<std::recursive_mutex> Lock;
|
||||
|
||||
class SyncHelper
|
||||
@@ -38,15 +38,15 @@ public:
|
||||
bool waitFor(std::chrono::milliseconds timeout);
|
||||
|
||||
private:
|
||||
std::mutex m_mtx;
|
||||
std::mutex m_mtx;
|
||||
std::condition_variable m_cv;
|
||||
unsigned int m_count;
|
||||
unsigned int m_count;
|
||||
};
|
||||
|
||||
class ThreadHelper
|
||||
{
|
||||
public:
|
||||
static void setName(const std::string& name);
|
||||
static void setName(const std::string& name);
|
||||
static uint64_t getCurrentId();
|
||||
};
|
||||
|
||||
@@ -61,21 +61,22 @@ public:
|
||||
|
||||
// Finds time delta between 'later' and 'earlier' time points.
|
||||
// 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.
|
||||
static timespec toTimespec(uint64_t milliseconds);
|
||||
static uint64_t toTimestamp(const timeval& ts);
|
||||
static timespec toTimespec(uint64_t milliseconds);
|
||||
static uint64_t toTimestamp(const timeval& ts);
|
||||
|
||||
// Returns difference between timestamps in milliseconds
|
||||
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 timespec& a, const timespec& b);
|
||||
static int64_t getDelta(const timeval& a, const timeval& b);
|
||||
|
||||
class ExecutionTime
|
||||
{
|
||||
public:
|
||||
ExecutionTime();
|
||||
std::chrono::milliseconds getSpentTime() const;
|
||||
|
||||
protected:
|
||||
std::chrono::milliseconds mStart;
|
||||
};
|
||||
@@ -88,16 +89,16 @@ public:
|
||||
BufferQueue();
|
||||
~BufferQueue();
|
||||
|
||||
typedef std::vector<uint8_t> Block;
|
||||
typedef std::vector<uint8_t> Block;
|
||||
typedef std::shared_ptr<Block> PBlock;
|
||||
|
||||
void push(const void* data, int bytes);
|
||||
PBlock pull(int milliseconds);
|
||||
void push(const void* data, int bytes);
|
||||
PBlock pull(int milliseconds);
|
||||
|
||||
protected:
|
||||
std::mutex mMutex;
|
||||
std::mutex mMutex;
|
||||
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
|
||||
// Guarantees:
|
||||
// - 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.
|
||||
// - Handlers are ALWAYS executed in the Timer Queue worker thread.
|
||||
// - Handlers execution order is NOT guaranteed
|
||||
@@ -128,38 +129,37 @@ public:
|
||||
// 1 if the timer was cancelled.
|
||||
// 0 if you were too late to cancel (or the timer ID was never valid to
|
||||
// start with)
|
||||
size_t cancel(uint64_t id);
|
||||
size_t cancel(uint64_t id);
|
||||
|
||||
//! Cancels all timers
|
||||
// \return
|
||||
// The number of timers cancelled
|
||||
size_t cancelAll();
|
||||
size_t cancelAll();
|
||||
|
||||
private:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
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();
|
||||
void checkWork();
|
||||
Semaphore m_checkWork;
|
||||
std::thread m_th;
|
||||
bool m_finish = false;
|
||||
uint64_t m_idcounter = 0;
|
||||
void checkWork();
|
||||
Semaphore m_checkWork;
|
||||
std::thread m_th;
|
||||
bool m_finish = false;
|
||||
uint64_t m_idcounter = 0;
|
||||
|
||||
struct WorkItem
|
||||
{
|
||||
Clock::time_point end;
|
||||
uint64_t id; // id==0 means it was cancelled
|
||||
Clock::time_point end;
|
||||
uint64_t id; // id==0 means it was cancelled
|
||||
std::function<void(bool)> handler;
|
||||
bool operator > (const WorkItem& other) const;
|
||||
bool operator>(const WorkItem& other) const;
|
||||
};
|
||||
|
||||
std::mutex m_mtx;
|
||||
// Inheriting from priority_queue, so we can access the internal container
|
||||
class Queue : public std::priority_queue<WorkItem, std::vector<WorkItem>,
|
||||
std::greater<WorkItem>>
|
||||
class Queue : public std::priority_queue<WorkItem, std::vector<WorkItem>, std::greater<WorkItem>>
|
||||
{
|
||||
public:
|
||||
std::vector<WorkItem>& getContainer();
|
||||
|
||||
@@ -6,7 +6,7 @@ thread_pool::thread_pool(size_t num_of_threads, const std::string& name)
|
||||
if (!num_of_threads)
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ thread_pool::~thread_pool()
|
||||
stop = true;
|
||||
}
|
||||
this->condition.notify_all();
|
||||
for(std::thread &worker: workers)
|
||||
for (std::thread& worker : workers)
|
||||
worker.join();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ void thread_pool::run_worker()
|
||||
{
|
||||
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())
|
||||
{
|
||||
t = tasks.front();
|
||||
|
||||
@@ -21,26 +21,26 @@ public:
|
||||
thread_pool(size_t num_of_threads, const std::string& thread_name);
|
||||
~thread_pool();
|
||||
|
||||
void enqueue(const task& task);
|
||||
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
|
||||
void enqueue(const task& task);
|
||||
void wait(std::chrono::milliseconds interval = std::chrono::milliseconds(50));
|
||||
size_t size();
|
||||
size_t threads();
|
||||
|
||||
private:
|
||||
// need to keep track of threads so we can join them
|
||||
std::vector< std::thread > workers;
|
||||
std::vector<std::thread> workers;
|
||||
|
||||
// the task queue
|
||||
std::queue< task > tasks;
|
||||
|
||||
std::queue<task> tasks;
|
||||
|
||||
// synchronization
|
||||
std::mutex queue_mutex;
|
||||
std::condition_variable condition;
|
||||
std::atomic_bool stop = false;
|
||||
std::mutex queue_mutex;
|
||||
std::condition_variable condition;
|
||||
std::atomic_bool stop = false;
|
||||
|
||||
// thread name prefix for worker threads
|
||||
std::string name;
|
||||
std::string name;
|
||||
|
||||
void run_worker();
|
||||
void run_worker();
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -12,7 +12,7 @@ double now_ms(void)
|
||||
#else
|
||||
struct timespec res;
|
||||
clock_gettime(CLOCK_MONOTONIC, &res);
|
||||
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
|
||||
return 1000.0 * res.tv_sec + (double)res.tv_nsec / 1e6;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -31,17 +31,17 @@ int compare_timespec(const timespec& lhs, const timespec& rhs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator < (const timespec& lhs, const timespec& rhs)
|
||||
bool operator<(const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
bool operator == (const timespec& lhs, const timespec& rhs)
|
||||
bool operator==(const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) == 0;
|
||||
}
|
||||
|
||||
bool operator > (const timespec& lhs, const timespec& rhs)
|
||||
bool operator>(const timespec& lhs, const timespec& rhs)
|
||||
{
|
||||
return compare_timespec(lhs, rhs) > 0;
|
||||
}
|
||||
@@ -50,4 +50,3 @@ bool is_zero(const timespec& ts)
|
||||
{
|
||||
return !ts.tv_sec && !ts.tv_nsec;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ extern double now_ms();
|
||||
|
||||
// Compare the timespec.
|
||||
// Returns -1 if lhs < rhs, 1 if lhs > rhs, 0 if equal
|
||||
extern int compare_timespec(const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator < (const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator == (const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator > (const timespec& lhs, const timespec& rhs);
|
||||
extern bool is_zero(const timespec& ts);
|
||||
extern int compare_timespec(const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator<(const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator==(const timespec& lhs, const timespec& rhs);
|
||||
extern bool operator>(const timespec& lhs, const timespec& rhs);
|
||||
extern bool is_zero(const timespec& ts);
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user