Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97c4c3aef0 | |||
| 7cb3b4334f |
@@ -26,16 +26,6 @@ AudioManager::~AudioManager()
|
|||||||
// stop();
|
// stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioManager& AudioManager::instance()
|
|
||||||
{
|
|
||||||
static std::shared_ptr<AudioManager> GAudioManager;
|
|
||||||
|
|
||||||
if (!GAudioManager)
|
|
||||||
GAudioManager = std::make_shared<AudioManager>();
|
|
||||||
|
|
||||||
return *GAudioManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioManager::setTerminal(MT::Terminal* terminal)
|
void AudioManager::setTerminal(MT::Terminal* terminal)
|
||||||
{
|
{
|
||||||
mTerminal = terminal;
|
mTerminal = terminal;
|
||||||
@@ -67,6 +57,7 @@ void AudioManager::start(int usageId)
|
|||||||
if (mUsage.obtain(usageId) > 1)
|
if (mUsage.obtain(usageId) > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Maybe it is time to initialize global audio support
|
||||||
if (Audio::OsEngine::instance())
|
if (Audio::OsEngine::instance())
|
||||||
Audio::OsEngine::instance()->open();
|
Audio::OsEngine::instance()->open();
|
||||||
|
|
||||||
@@ -89,14 +80,15 @@ void AudioManager::start(int usageId)
|
|||||||
enumerator->open(Audio::myMicrophone);
|
enumerator->open(Audio::myMicrophone);
|
||||||
int inputIndex = enumerator->indexOfDefaultDevice();
|
int inputIndex = enumerator->indexOfDefaultDevice();
|
||||||
|
|
||||||
// Construct and set to terminal's audio pair input device
|
// Construct default platform input device
|
||||||
if (usageId != atNull)
|
if (usageId != atNull)
|
||||||
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
|
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
|
||||||
else
|
else
|
||||||
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
|
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
|
||||||
|
|
||||||
mTerminal->audio()->setInput(mAudioInput);
|
|
||||||
}
|
}
|
||||||
|
// Bind input to the terminal's device pair regardless of whether it was
|
||||||
|
// just constructed or externally injected via setAudioInput().
|
||||||
|
mTerminal->audio()->setInput(mAudioInput);
|
||||||
|
|
||||||
if (!mAudioOutput)
|
if (!mAudioOutput)
|
||||||
{
|
{
|
||||||
@@ -104,7 +96,7 @@ void AudioManager::start(int usageId)
|
|||||||
enumerator->open(Audio::mySpeaker);
|
enumerator->open(Audio::mySpeaker);
|
||||||
int outputIndex = enumerator->indexOfDefaultDevice();
|
int outputIndex = enumerator->indexOfDefaultDevice();
|
||||||
|
|
||||||
// Construct and set terminal's audio pair output device
|
// Construct default platform output device
|
||||||
if (usageId != atNull)
|
if (usageId != atNull)
|
||||||
{
|
{
|
||||||
if (outputIndex >= enumerator->count())
|
if (outputIndex >= enumerator->count())
|
||||||
@@ -115,9 +107,8 @@ void AudioManager::start(int usageId)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
|
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
|
||||||
|
|
||||||
mTerminal->audio()->setOutput(mAudioOutput);
|
|
||||||
}
|
}
|
||||||
|
mTerminal->audio()->setOutput(mAudioOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open audio
|
// Open audio
|
||||||
@@ -167,6 +158,18 @@ void AudioManager::stop(int usageId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioManager::setAudioInput(Audio::PInputDevice input)
|
||||||
|
{
|
||||||
|
LOCK_MANAGER;
|
||||||
|
mAudioInput = std::move(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioManager::setAudioOutput(Audio::POutputDevice output)
|
||||||
|
{
|
||||||
|
LOCK_MANAGER;
|
||||||
|
mAudioOutput = std::move(output);
|
||||||
|
}
|
||||||
|
|
||||||
void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit)
|
void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit)
|
||||||
{
|
{
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
@@ -202,6 +205,6 @@ void AudioManager::process()
|
|||||||
mPlayer.releasePlayed();
|
mPlayer.releasePlayed();
|
||||||
std::vector<int> ids;
|
std::vector<int> ids;
|
||||||
mTerminal->audio()->player().retrieveUsageIds(ids);
|
mTerminal->audio()->player().retrieveUsageIds(ids);
|
||||||
for (unsigned i=0; i<ids.size(); i++)
|
for (int id : ids)
|
||||||
stop(ids[i]);
|
stop(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
AudioManager();
|
AudioManager();
|
||||||
virtual ~AudioManager();
|
virtual ~AudioManager();
|
||||||
|
|
||||||
static AudioManager& instance();
|
// static AudioManager& instance();
|
||||||
|
|
||||||
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
|
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
|
||||||
void close();
|
void close();
|
||||||
@@ -53,6 +53,12 @@ public:
|
|||||||
void start(int usageId);
|
void start(int usageId);
|
||||||
void stop(int usageId);
|
void stop(int usageId);
|
||||||
|
|
||||||
|
// Inject a custom input device. Must be called before start(): when set,
|
||||||
|
// 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);
|
||||||
|
|
||||||
enum AudioTarget
|
enum AudioTarget
|
||||||
{
|
{
|
||||||
atNull,
|
atNull,
|
||||||
|
|||||||
+114
-110
@@ -3,10 +3,11 @@
|
|||||||
#include "helper/HL_String.h"
|
#include "helper/HL_String.h"
|
||||||
#include "helper/HL_StreamState.h"
|
#include "helper/HL_StreamState.h"
|
||||||
#include "helper/HL_VariantMap.h"
|
#include "helper/HL_VariantMap.h"
|
||||||
#include "helper/HL_CsvReader.h"
|
// #include "helper/HL_CsvReader.h"
|
||||||
#include "helper/HL_Base64.h"
|
// #include "helper/HL_Base64.h"
|
||||||
#include "media/MT_CodecList.h"
|
#include "media/MT_CodecList.h"
|
||||||
#include <fstream>
|
#include "audio/Audio_Null.h"
|
||||||
|
// #include <fstream>
|
||||||
|
|
||||||
|
|
||||||
const std::string Status_Ok = "ok";
|
const std::string Status_Ok = "ok";
|
||||||
@@ -37,19 +38,19 @@ AgentImpl::~AgentImpl()
|
|||||||
// Get access to internal audio manager. Value can be nullptr.
|
// Get access to internal audio manager. Value can be nullptr.
|
||||||
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
|
const std::shared_ptr<AudioManager>& AgentImpl::audioManager() const
|
||||||
{
|
{
|
||||||
return mAudioManager;
|
return mAudioManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
|
void AgentImpl::setAudioMonitoring(Audio::DataConnection* monitoring)
|
||||||
{
|
{
|
||||||
mAudioMonitoring = monitoring;
|
mAudioMonitoring = monitoring;
|
||||||
if (mAudioManager)
|
if (mAudioManager)
|
||||||
mAudioManager->setAudioMonitoring(monitoring);
|
mAudioManager->setAudioMonitoring(monitoring);
|
||||||
}
|
}
|
||||||
|
|
||||||
Audio::DataConnection* AgentImpl::monitoring() const
|
Audio::DataConnection* AgentImpl::monitoring() const
|
||||||
{
|
{
|
||||||
return mAudioMonitoring;
|
return mAudioMonitoring;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentImpl::run()
|
void AgentImpl::run()
|
||||||
@@ -84,73 +85,73 @@ std::string AgentImpl::command(const std::string& command)
|
|||||||
if (cmd == "config")
|
if (cmd == "config")
|
||||||
processConfig(d, answer);
|
processConfig(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "start")
|
if (cmd == "start")
|
||||||
processStart(d, answer);
|
processStart(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "stop")
|
if (cmd == "stop")
|
||||||
processStop(d, answer);
|
processStop(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "account_create")
|
if (cmd == "account_create")
|
||||||
processCreateAccount(d, answer);
|
processCreateAccount(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "account_start")
|
if (cmd == "account_start")
|
||||||
processStartAccount(d, answer);
|
processStartAccount(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "account_setuserinfo")
|
if (cmd == "account_setuserinfo")
|
||||||
processSetUserInfoToAccount(d, answer);
|
processSetUserInfoToAccount(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "session_create") {
|
if (cmd == "session_create") {
|
||||||
// For Bugsnag test
|
// For Bugsnag test
|
||||||
// int* v = nullptr;
|
// int* v = nullptr;
|
||||||
// *v = 0;
|
// *v = 0;
|
||||||
processCreateSession(d, answer);
|
processCreateSession(d, answer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (cmd == "session_start")
|
if (cmd == "session_start")
|
||||||
processStartSession(d, answer);
|
processStartSession(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "session_stop")
|
if (cmd == "session_stop")
|
||||||
processStopSession(d, answer);
|
processStopSession(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "session_accept")
|
if (cmd == "session_accept")
|
||||||
processAcceptSession(d, answer);
|
processAcceptSession(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "session_destroy")
|
if (cmd == "session_destroy")
|
||||||
processDestroySession(d, answer);
|
processDestroySession(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "session_use_stream")
|
if (cmd == "session_use_stream")
|
||||||
processUseStreamForSession(d, answer);
|
processUseStreamForSession(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "wait_for_event")
|
if (cmd == "wait_for_event")
|
||||||
processWaitForEvent(d, answer);
|
processWaitForEvent(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "session_get_media_stats")
|
if (cmd == "session_get_media_stats")
|
||||||
processGetMediaStats(d, answer);
|
processGetMediaStats(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "agent_network_changed")
|
if (cmd == "agent_network_changed")
|
||||||
processNetworkChanged(d, answer);
|
processNetworkChanged(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "agent_add_root_cert")
|
if (cmd == "agent_add_root_cert")
|
||||||
processAddRootCert(d, answer);
|
processAddRootCert(d, answer);
|
||||||
else
|
else
|
||||||
if (cmd == "detach_log")
|
if (cmd == "detach_log")
|
||||||
{
|
{
|
||||||
GLogger.closeFile();
|
GLogger.closeFile();
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (cmd == "attach_log")
|
if (cmd == "attach_log")
|
||||||
{
|
{
|
||||||
GLogger.openFile();
|
GLogger.openFile();
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (cmd == "log_message")
|
if (cmd == "log_message")
|
||||||
processLogMessage(d, answer);
|
processLogMessage(d, answer);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
answer["status"] = Status_NoCommand;
|
answer["status"] = Status_NoCommand;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(std::exception& e)
|
catch(std::exception& e)
|
||||||
{
|
{
|
||||||
@@ -226,19 +227,13 @@ void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
|
|||||||
for (int i=0; i<cl.count(); i++)
|
for (int i=0; i<cl.count(); i++)
|
||||||
priorityConfig->at(i) = i;
|
priorityConfig->at(i) = i;
|
||||||
|
|
||||||
// Disable dynamic payload codec types - commented for now
|
|
||||||
// if (cl.codecAt(i).payloadType() < 96)
|
|
||||||
// priorityConfig->at(i) = i;
|
|
||||||
// else
|
|
||||||
// priorityConfig->at(i) = -1;
|
|
||||||
|
|
||||||
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
|
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
|
||||||
|
|
||||||
// Enable audio
|
// Enable audio
|
||||||
mAudioManager = std::make_shared<AudioManager>();
|
mAudioManager = std::make_shared<AudioManager>();
|
||||||
mAudioManager->setTerminal(mTerminal.get());
|
mAudioManager->setTerminal(mTerminal.get());
|
||||||
if (mAudioMonitoring)
|
if (mAudioMonitoring)
|
||||||
mAudioManager->setAudioMonitoring(mAudioMonitoring);
|
mAudioManager->setAudioMonitoring(mAudioMonitoring);
|
||||||
|
|
||||||
// Do not start audio manager here. Start right before call.
|
// Do not start audio manager here. Start right before call.
|
||||||
|
|
||||||
@@ -341,6 +336,11 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request["use_null_mic"].asBool())
|
||||||
|
mAudioManager->setAudioInput(std::make_shared<Audio::NullInputDevice>());
|
||||||
|
if (request["use_null_spk"].asBool())
|
||||||
|
mAudioManager->setAudioOutput(std::make_shared<Audio::NullOutputDevice>());
|
||||||
|
|
||||||
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
||||||
|
|
||||||
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
|
||||||
@@ -351,7 +351,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
|
|||||||
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
|
||||||
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
||||||
|
|
||||||
/*#if defined(USE_AQUA_LIBRARY)
|
/*#if defined(USE_AQUA_LIBRARY)
|
||||||
std::string path_faults = request["path_faults"].asString();
|
std::string path_faults = request["path_faults"].asString();
|
||||||
|
|
||||||
sevana::aqua::config config = {
|
sevana::aqua::config config = {
|
||||||
@@ -440,6 +440,10 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Ensure audio manager is here
|
// Ensure audio manager is here
|
||||||
|
if (request["use_null_mic"].asBool())
|
||||||
|
mAudioManager->setAudioInput(std::make_shared<Audio::NullInputDevice>());
|
||||||
|
if (request["use_null_spk"].asBool())
|
||||||
|
mAudioManager->setAudioOutput(std::make_shared<Audio::NullOutputDevice>());
|
||||||
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
|
||||||
|
|
||||||
// Accept session on SIP level
|
// Accept session on SIP level
|
||||||
@@ -471,9 +475,9 @@ void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& a
|
|||||||
auto sessionIter = mSessionMap.find(sessionId);
|
auto sessionIter = mSessionMap.find(sessionId);
|
||||||
if (sessionIter != mSessionMap.end())
|
if (sessionIter != mSessionMap.end())
|
||||||
mSessionMap.erase(sessionIter);
|
mSessionMap.erase(sessionIter);
|
||||||
//#if defined(USE_AQUA_LIBRARY)
|
//#if defined(USE_AQUA_LIBRARY)
|
||||||
// closeAqua(sessionId);
|
// closeAqua(sessionId);
|
||||||
//#endif
|
//#endif
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,7 +641,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
|||||||
|
|
||||||
// Parse command
|
// Parse command
|
||||||
std::string actionText = request["media_action"].asString(),
|
std::string actionText = request["media_action"].asString(),
|
||||||
directionText = request["media_direction"].asString();
|
directionText = request["media_direction"].asString();
|
||||||
|
|
||||||
MT::Stream::MediaDirection direction = directionText == "incoming" ? MT::Stream::MediaDirection::Incoming
|
MT::Stream::MediaDirection direction = directionText == "incoming" ? MT::Stream::MediaDirection::Incoming
|
||||||
: MT::Stream::MediaDirection::Outgoing;
|
: MT::Stream::MediaDirection::Outgoing;
|
||||||
@@ -668,34 +672,34 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (actionText == "write")
|
if (actionText == "write")
|
||||||
{
|
|
||||||
if (path.empty())
|
|
||||||
{
|
{
|
||||||
// Turn off recording from the stream
|
if (path.empty())
|
||||||
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);
|
// Turn off recording from the stream
|
||||||
|
prov->writeFile(Audio::PWavFileWriter(), direction);
|
||||||
answer["status"] = Status_Ok;
|
answer["status"] = Status_Ok;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Audio::PWavFileWriter writer = std::make_shared<Audio::WavFileWriter>();
|
||||||
|
if (!writer->open(strx::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
|
||||||
|
answer["status"] = Status_FailedToOpenFile;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prov->writeFile(writer, direction);
|
||||||
|
answer["status"] = Status_Ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
if (actionText == "mirror")
|
||||||
if (actionText == "mirror")
|
{
|
||||||
{
|
prov->setupMirror(request["enable"].asBool());
|
||||||
prov->setupMirror(request["enable"].asBool());
|
answer["status"] = Status_Ok;
|
||||||
answer["status"] = Status_Ok;
|
}
|
||||||
}
|
else
|
||||||
else
|
answer["status"] = Status_NoCommand;
|
||||||
answer["status"] = Status_NoCommand;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
answer["status"] = Status_NoMediaAction;
|
answer["status"] = Status_NoMediaAction;
|
||||||
|
|||||||
@@ -18,111 +18,111 @@
|
|||||||
class AgentImpl: public UserAgent, public MT::Stream::MediaObserver
|
class AgentImpl: public UserAgent, public MT::Stream::MediaObserver
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::recursive_mutex mAgentMutex;
|
std::recursive_mutex mAgentMutex;
|
||||||
std::mutex mEventListMutex;
|
std::mutex mEventListMutex;
|
||||||
std::condition_variable mEventListChangeCondVar;
|
std::condition_variable mEventListChangeCondVar;
|
||||||
std::vector<JsonCpp::Value> mEventList;
|
std::vector<JsonCpp::Value> mEventList;
|
||||||
bool mUseNativeAudio = false;
|
bool mUseNativeAudio = false;
|
||||||
|
|
||||||
typedef std::map<int, PAccount> AccountMap;
|
typedef std::map<int, PAccount> AccountMap;
|
||||||
AccountMap mAccountMap;
|
AccountMap mAccountMap;
|
||||||
|
|
||||||
typedef std::map<int, PSession> SessionMap;
|
typedef std::map<int, PSession> SessionMap;
|
||||||
SessionMap mSessionMap;
|
SessionMap mSessionMap;
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<std::thread> mThread;
|
std::shared_ptr<std::thread> mThread;
|
||||||
volatile bool mShutdown;
|
volatile bool mShutdown;
|
||||||
std::shared_ptr<MT::Terminal> mTerminal;
|
std::shared_ptr<MT::Terminal> mTerminal;
|
||||||
std::shared_ptr<AudioManager> mAudioManager;
|
std::shared_ptr<AudioManager> mAudioManager;
|
||||||
Audio::DataConnection* mAudioMonitoring = nullptr;
|
Audio::DataConnection* mAudioMonitoring = nullptr;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void addEvent(const JsonCpp::Value& v);
|
void addEvent(const JsonCpp::Value& v);
|
||||||
void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processConfig(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processStart(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processStart(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processStop(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processStop(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processCreateAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processStartAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processSetUserInfoToAccount(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processCreateSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processStartSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processStopSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processWaitForEvent(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processNetworkChanged(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processAddRootCert(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer);
|
void processLogMessage(JsonCpp::Value& request, JsonCpp::Value& answer);
|
||||||
void stopAgentAndThread();
|
void stopAgentAndThread();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AgentImpl();
|
AgentImpl();
|
||||||
~AgentImpl();
|
~AgentImpl();
|
||||||
|
|
||||||
std::string command(const std::string& command);
|
std::string command(const std::string& command);
|
||||||
bool waitForData(int milliseconds);
|
bool waitForData(int milliseconds);
|
||||||
std::string read();
|
std::string read();
|
||||||
|
|
||||||
// Get access to internal audio manager. Value can be nullptr.
|
// Get access to internal audio manager. Value can be nullptr.
|
||||||
const std::shared_ptr<AudioManager>& audioManager() const;
|
const std::shared_ptr<AudioManager>& audioManager() const;
|
||||||
|
|
||||||
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
void setAudioMonitoring(Audio::DataConnection* monitoring);
|
||||||
Audio::DataConnection* monitoring() const;
|
Audio::DataConnection* monitoring() const;
|
||||||
|
|
||||||
// UserAgent overrides
|
// UserAgent overrides
|
||||||
// Called on new incoming session; providers shoukld
|
// Called on new incoming session; providers shoukld
|
||||||
PDataProvider onProviderNeeded(const std::string& name) override;
|
PDataProvider onProviderNeeded(const std::string& name) override;
|
||||||
|
|
||||||
// Called on new session offer
|
// Called on new session offer
|
||||||
void onNewSession(PSession s) override;
|
void onNewSession(PSession s) override;
|
||||||
|
|
||||||
// Called when session is terminated
|
// Called when session is terminated
|
||||||
void onSessionTerminated(PSession s, int responsecode, int reason) override;
|
void onSessionTerminated(PSession s, int responsecode, int reason) override;
|
||||||
|
|
||||||
// Called when session is established ok i.e. after all ICE signalling is finished
|
// Called when session is established ok i.e. after all ICE signalling is finished
|
||||||
// Conntype is type of establish event - EV_SIP or EV_ICE
|
// Conntype is type of establish event - EV_SIP or EV_ICE
|
||||||
void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) override;
|
void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) override;
|
||||||
|
|
||||||
void onSessionProvisional(PSession s, int code) override;
|
void onSessionProvisional(PSession s, int code) override;
|
||||||
|
|
||||||
// Called when user agent started
|
// Called when user agent started
|
||||||
void onStart(int errorcode) override;
|
void onStart(int errorcode) override;
|
||||||
|
|
||||||
// Called when user agent stopped
|
// Called when user agent stopped
|
||||||
void onStop() override;
|
void onStop() override;
|
||||||
|
|
||||||
// Called when account registered
|
// Called when account registered
|
||||||
void onAccountStart(PAccount account) override;
|
void onAccountStart(PAccount account) override;
|
||||||
|
|
||||||
// Called when account removed or failed (non zero error code)
|
// Called when account removed or failed (non zero error code)
|
||||||
void onAccountStop(PAccount account, int error) override;
|
void onAccountStop(PAccount account, int error) override;
|
||||||
|
|
||||||
// Called when connectivity checks failed.
|
// Called when connectivity checks failed.
|
||||||
void onConnectivityFailed(PSession s) override;
|
void onConnectivityFailed(PSession s) override;
|
||||||
|
|
||||||
// Called when new candidate is gathered
|
// Called when new candidate is gathered
|
||||||
void onCandidateGathered(PSession s, const char* address) override;
|
void onCandidateGathered(PSession s, const char* address) override;
|
||||||
|
|
||||||
// Called when network change detected
|
// Called when network change detected
|
||||||
void onNetworkChange(PSession s) override;
|
void onNetworkChange(PSession s) override;
|
||||||
|
|
||||||
// Called when all candidates are gathered
|
// Called when all candidates are gathered
|
||||||
void onGathered(PSession s) override;
|
void onGathered(PSession s) override;
|
||||||
|
|
||||||
// Called when new connectivity check is finished
|
// Called when new connectivity check is finished
|
||||||
void onCheckFinished(PSession s, const char* description) override;
|
void onCheckFinished(PSession s, const char* description) override;
|
||||||
|
|
||||||
// Called when log message must be recorded
|
// Called when log message must be recorded
|
||||||
void onLog(const char* msg) override;
|
void onLog(const char* msg) override;
|
||||||
|
|
||||||
// Called when problem with SIP connection(s) detected
|
// Called when problem with SIP connection(s) detected
|
||||||
void onSipConnectionFailed() override;
|
void onSipConnectionFailed() override;
|
||||||
|
|
||||||
// Called on incoming & outgoing audio for voice sessions
|
// Called on incoming & outgoing audio for voice sessions
|
||||||
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,129 +5,135 @@
|
|||||||
#define LOG_SUBSYSTEM "audio"
|
#define LOG_SUBSYSTEM "audio"
|
||||||
|
|
||||||
using namespace Audio;
|
using namespace Audio;
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
NullTimer::NullTimer(int interval, Delegate *delegate, const char* name)
|
NullTimer::NullTimer(std::chrono::milliseconds interval, Delegate *delegate, const char* name)
|
||||||
:mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
|
:mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
|
||||||
{
|
{
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
NullTimer::~NullTimer()
|
NullTimer::~NullTimer()
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullTimer::start()
|
void NullTimer::start()
|
||||||
{
|
{
|
||||||
mShutdown = false;
|
mShutdown = false;
|
||||||
mWorkerThread = std::thread(&NullTimer::run, this);
|
mWorkerThread = std::thread(&NullTimer::run, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullTimer::stop()
|
void NullTimer::stop()
|
||||||
{
|
{
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
if (mWorkerThread.joinable())
|
if (mWorkerThread.joinable())
|
||||||
mWorkerThread.join();
|
mWorkerThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullTimer::run()
|
void NullTimer::run()
|
||||||
{
|
{
|
||||||
mTail = 0;
|
mTail = 0us;
|
||||||
while (!mShutdown)
|
while (!mShutdown)
|
||||||
{
|
|
||||||
// Get current timestamp
|
|
||||||
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
|
|
||||||
|
|
||||||
while (mTail >= mInterval * 1000)
|
|
||||||
{
|
{
|
||||||
if (mDelegate)
|
// Get current timestamp
|
||||||
mDelegate->onTimerSignal(*this);
|
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
|
||||||
mTail -= mInterval * 1000;
|
|
||||||
|
while (mTail >= mInterval)
|
||||||
|
{
|
||||||
|
if (mDelegate)
|
||||||
|
mDelegate->onTimerSignal(*this);
|
||||||
|
mTail -= mInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep for mInterval - mTail milliseconds
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(mInterval * 1000 - mTail));
|
|
||||||
|
|
||||||
mTail += (int)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp).count();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------- NullInputDevice -------------------------
|
// --------------------- NullInputDevice -------------------------
|
||||||
NullInputDevice::NullInputDevice()
|
NullInputDevice::NullInputDevice()
|
||||||
:mBuffer(nullptr)
|
:mBuffer(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NullInputDevice::~NullInputDevice()
|
NullInputDevice::~NullInputDevice()
|
||||||
{
|
{
|
||||||
internalClose();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullInputDevice::open()
|
bool NullInputDevice::open()
|
||||||
{
|
{
|
||||||
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
|
ICELogInfo(<< "Starting NullInputDevice for " << AUDIO_MIC_BUFFER_LENGTH << "ms buffers");
|
||||||
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
|
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
|
||||||
mTimeCounter = 0; mDataCounter = 0;
|
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
|
||||||
// Creation of timer starts it also. So first onTimerSignal can come even before open() returns.
|
mTimeCounter = 0; mDataCounter = 0;
|
||||||
mTimer = std::make_shared<NullTimer>(AUDIO_MIC_BUFFER_LENGTH, this, "NullMicrophoneThread");
|
|
||||||
return true;
|
// 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");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::internalClose()
|
void NullInputDevice::internalClose()
|
||||||
{
|
{
|
||||||
mTimer.reset();
|
ICELogInfo(<< "Stopping NullInputDevice");
|
||||||
if (mBuffer)
|
mTimer.reset();
|
||||||
{
|
if (mBuffer)
|
||||||
free(mBuffer);
|
{
|
||||||
mBuffer = nullptr;
|
free(mBuffer);
|
||||||
}
|
mBuffer = nullptr;
|
||||||
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
}
|
||||||
|
ICELogInfo( << "Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::close()
|
void NullInputDevice::close()
|
||||||
{
|
{
|
||||||
internalClose();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Format NullInputDevice::getFormat()
|
Format NullInputDevice::getFormat()
|
||||||
{
|
{
|
||||||
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
|
||||||
return Format();
|
|
||||||
|
return {}; // Return library-define default format
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullInputDevice::onTimerSignal(NullTimer& timer)
|
void NullInputDevice::onTimerSignal(NullTimer& timer)
|
||||||
{
|
{
|
||||||
mTimeCounter += AUDIO_MIC_BUFFER_LENGTH;
|
mTimeCounter += AUDIO_MIC_BUFFER_LENGTH;
|
||||||
mDataCounter += AUDIO_MIC_BUFFER_SIZE;
|
mDataCounter += AUDIO_MIC_BUFFER_SIZE;
|
||||||
if (mConnection)
|
if (mConnection)
|
||||||
mConnection->onMicData(getFormat(), mBuffer, AUDIO_MIC_BUFFER_SIZE);
|
mConnection->onMicData(getFormat(), mBuffer, AUDIO_MIC_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------- NullOutputDevice --------------------------
|
// --------------------- NullOutputDevice --------------------------
|
||||||
NullOutputDevice::NullOutputDevice()
|
NullOutputDevice::NullOutputDevice()
|
||||||
:mBuffer(nullptr)
|
:mBuffer(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NullOutputDevice::~NullOutputDevice()
|
NullOutputDevice::~NullOutputDevice()
|
||||||
{
|
{
|
||||||
internalClose();
|
internalClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool NullOutputDevice::open()
|
bool NullOutputDevice::open()
|
||||||
{
|
{
|
||||||
mTimeCounter = 0; mDataCounter = 0;
|
mTimeCounter = 0; mDataCounter = 0;
|
||||||
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
|
mBuffer = malloc(AUDIO_SPK_BUFFER_SIZE);
|
||||||
// Creation of timer starts it also. So first onSpkData() can come before open() returns even.
|
// Creation of timer starts it also. So first onSpkData() can come before open() returns even.
|
||||||
mTimer = std::make_shared<NullTimer>(AUDIO_SPK_BUFFER_LENGTH, this, "NullSpeakerThread");
|
mTimer = std::make_shared<NullTimer>(std::chrono::milliseconds(AUDIO_SPK_BUFFER_LENGTH), this, "null_spk");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullOutputDevice::internalClose()
|
void NullOutputDevice::internalClose()
|
||||||
{
|
{
|
||||||
mTimer.reset();
|
mTimer.reset();
|
||||||
free(mBuffer); mBuffer = nullptr;
|
free(mBuffer); mBuffer = nullptr;
|
||||||
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullOutputDevice::close()
|
void NullOutputDevice::close()
|
||||||
@@ -137,16 +143,16 @@ void NullOutputDevice::close()
|
|||||||
|
|
||||||
Format NullOutputDevice::getFormat()
|
Format NullOutputDevice::getFormat()
|
||||||
{
|
{
|
||||||
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
|
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
|
||||||
return Format();
|
return Format();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullOutputDevice::onTimerSignal(NullTimer &timer)
|
void NullOutputDevice::onTimerSignal(NullTimer &timer)
|
||||||
{
|
{
|
||||||
mTimeCounter += AUDIO_SPK_BUFFER_LENGTH;
|
mTimeCounter += AUDIO_SPK_BUFFER_LENGTH;
|
||||||
mDataCounter += AUDIO_SPK_BUFFER_SIZE;
|
mDataCounter += AUDIO_SPK_BUFFER_SIZE;
|
||||||
if (mConnection)
|
if (mConnection)
|
||||||
mConnection->onSpkData(getFormat(), mBuffer, AUDIO_SPK_BUFFER_SIZE);
|
mConnection->onSpkData(getFormat(), mBuffer, AUDIO_SPK_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------- NullEnumerator --------------------------
|
// ---------------------- NullEnumerator --------------------------
|
||||||
@@ -164,25 +170,25 @@ void NullEnumerator::close()
|
|||||||
|
|
||||||
int NullEnumerator::count()
|
int NullEnumerator::count()
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tstring NullEnumerator::nameAt(int index)
|
std::tstring NullEnumerator::nameAt(int index)
|
||||||
{
|
{
|
||||||
#if defined(TARGET_WIN)
|
#if defined(TARGET_WIN)
|
||||||
return L"null";
|
return L"null";
|
||||||
#else
|
#else
|
||||||
return "null";
|
return "null";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int NullEnumerator::idAt(int index)
|
int NullEnumerator::idAt(int index)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NullEnumerator::indexOfDefaultDevice()
|
int NullEnumerator::indexOfDefaultDevice()
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,45 +2,46 @@
|
|||||||
#define __AUDIO_NULL_H
|
#define __AUDIO_NULL_H
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
#include "Audio_Interface.h"
|
#include "Audio_Interface.h"
|
||||||
|
|
||||||
namespace Audio
|
namespace Audio
|
||||||
{
|
{
|
||||||
class NullTimer
|
class NullTimer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class Delegate
|
class Delegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void onTimerSignal(NullTimer& timer) = 0;
|
virtual void onTimerSignal(NullTimer& timer) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::thread mWorkerThread;
|
std::thread mWorkerThread;
|
||||||
volatile bool mShutdown;
|
std::atomic_bool mShutdown = {false};
|
||||||
Delegate* mDelegate;
|
Delegate* mDelegate = nullptr;
|
||||||
int mInterval, // Interval - wanted number of milliseconds
|
std::chrono::milliseconds mInterval; // Interval - wanted number of milliseconds
|
||||||
mTail; // Number of milliseconds that can be sent immediately to sink
|
std::chrono::microseconds mTail; // Number of milliseconds that can be sent immediately to sink
|
||||||
std::string mThreadName;
|
std::string mThreadName;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
void run();
|
void run();
|
||||||
public:
|
public:
|
||||||
/* Interval is in milliseconds. */
|
/* Interval is in milliseconds. */
|
||||||
NullTimer(int interval, Delegate* delegate, const char* name = nullptr);
|
NullTimer(std::chrono::milliseconds interval, Delegate* delegate, const char* name = nullptr);
|
||||||
~NullTimer();
|
~NullTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullInputDevice: public InputDevice, public NullTimer::Delegate
|
class NullInputDevice: public InputDevice, public NullTimer::Delegate
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
int64_t mTimeCounter = 0, mDataCounter = 0;
|
int64_t mTimeCounter = 0, mDataCounter = 0;
|
||||||
void internalClose();
|
void internalClose();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NullInputDevice();
|
NullInputDevice();
|
||||||
virtual ~NullInputDevice();
|
virtual ~NullInputDevice();
|
||||||
|
|
||||||
@@ -49,17 +50,17 @@ namespace Audio
|
|||||||
Format getFormat() override;
|
Format getFormat() override;
|
||||||
|
|
||||||
void onTimerSignal(NullTimer& timer) override;
|
void onTimerSignal(NullTimer& timer) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullOutputDevice: public OutputDevice, public NullTimer::Delegate
|
class NullOutputDevice: public OutputDevice, public NullTimer::Delegate
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<NullTimer> mTimer;
|
std::shared_ptr<NullTimer> mTimer;
|
||||||
void* mBuffer = nullptr;
|
void* mBuffer = nullptr;
|
||||||
int64_t mDataCounter = 0, mTimeCounter = 0;
|
int64_t mDataCounter = 0, mTimeCounter = 0;
|
||||||
|
|
||||||
void internalClose();
|
void internalClose();
|
||||||
public:
|
public:
|
||||||
NullOutputDevice();
|
NullOutputDevice();
|
||||||
virtual ~NullOutputDevice();
|
virtual ~NullOutputDevice();
|
||||||
|
|
||||||
@@ -68,11 +69,11 @@ namespace Audio
|
|||||||
Format getFormat() override;
|
Format getFormat() override;
|
||||||
|
|
||||||
void onTimerSignal(NullTimer& timer) override;
|
void onTimerSignal(NullTimer& timer) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullEnumerator: public Enumerator
|
class NullEnumerator: public Enumerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NullEnumerator();
|
NullEnumerator();
|
||||||
~NullEnumerator();
|
~NullEnumerator();
|
||||||
|
|
||||||
@@ -83,8 +84,8 @@ namespace Audio
|
|||||||
std::tstring nameAt(int index) override;
|
std::tstring nameAt(int index) override;
|
||||||
int idAt(int index) override;
|
int idAt(int index) override;
|
||||||
int indexOfDefaultDevice() override;
|
int indexOfDefaultDevice() override;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ enum
|
|||||||
CONFIG_ACCOUNT, // VariantMap with account configuration
|
CONFIG_ACCOUNT, // VariantMap with account configuration
|
||||||
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
CONFIG_EXTERNALIP, // Use external/public IP in outgoing requests
|
||||||
CONFIG_OWN_DNS, // Use predefined DNS servers
|
CONFIG_OWN_DNS, // Use predefined DNS servers
|
||||||
CONFIG_REGID // reg-id value from RFC5626
|
CONFIG_REGID // reg-id value from RFC5626,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Conntype parameter for OnSessionEstablished event
|
// Conntype parameter for OnSessionEstablished event
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
|
|||||||
|
|
||||||
media = &stream;
|
media = &stream;
|
||||||
MT::Statistics s = stream.provider()->getStatistics();
|
MT::Statistics s = stream.provider()->getStatistics();
|
||||||
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos(4.14));
|
info[SessionInfo_NetworkMos] = static_cast<float>(s.calculateMos());
|
||||||
info[SessionInfo_AudioCodec] = s.mCodecName;
|
info[SessionInfo_AudioCodec] = s.mCodecName;
|
||||||
|
|
||||||
stat += s;
|
stat += s;
|
||||||
|
|||||||
@@ -344,14 +344,20 @@ void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length,
|
|||||||
}
|
}
|
||||||
|
|
||||||
mStat.mReceived += length;
|
mStat.mReceived += length;
|
||||||
|
auto& perDst = mStat.mPerDestination[source];
|
||||||
|
perDst.mReceivedBytes += length;
|
||||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||||
{
|
{
|
||||||
if (!mStat.mFirstRtpTime)
|
if (!mStat.mFirstRtpTime)
|
||||||
mStat.mFirstRtpTime = std::chrono::steady_clock::now();
|
mStat.mFirstRtpTime = std::chrono::steady_clock::now();
|
||||||
mStat.mReceivedRtp++;
|
mStat.mReceivedRtp++;
|
||||||
|
perDst.mReceivedRtp++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
mStat.mReceivedRtcp++;
|
mStat.mReceivedRtcp++;
|
||||||
|
perDst.mReceivedRtcp++;
|
||||||
|
}
|
||||||
|
|
||||||
mRtpSession.Poll(); // maybe it is extra with external transmitter
|
mRtpSession.Poll(); // maybe it is extra with external transmitter
|
||||||
bool hasData = mRtpSession.GotoFirstSourceWithData();
|
bool hasData = mRtpSession.GotoFirstSourceWithData();
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ bool NativeRtpSender::SendRTP(const void *data, size_t len)
|
|||||||
mSocket.mRtp->sendDatagram(mTarget.mRtp, mSendBuffer, sendLength);
|
mSocket.mRtp->sendDatagram(mTarget.mRtp, mSendBuffer, sendLength);
|
||||||
mStat.mSentRtp++;
|
mStat.mSentRtp++;
|
||||||
mStat.mSent += len;
|
mStat.mSent += len;
|
||||||
|
auto& perDst = mStat.mPerDestination[mTarget.mRtp];
|
||||||
|
perDst.mSentRtp++;
|
||||||
|
perDst.mSentBytes += len;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -73,6 +76,9 @@ bool NativeRtpSender::SendRTCP(const void *data, size_t len)
|
|||||||
mSocket.mRtcp->sendDatagram(mTarget.mRtcp, mSendBuffer, sendLength);
|
mSocket.mRtcp->sendDatagram(mTarget.mRtcp, mSendBuffer, sendLength);
|
||||||
mStat.mSentRtcp++;
|
mStat.mSentRtcp++;
|
||||||
mStat.mSent += len;
|
mStat.mSent += len;
|
||||||
|
auto& perDst = mStat.mPerDestination[mTarget.mRtcp];
|
||||||
|
perDst.mSentRtcp++;
|
||||||
|
perDst.mSentBytes += len;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "MT_Statistics.h"
|
#include "MT_Statistics.h"
|
||||||
#define LOG_SUBSYSTEM "media"
|
#define LOG_SUBSYSTEM "media"
|
||||||
@@ -203,7 +204,7 @@ void Statistics::calculateBurstr(double* burstr, double* lossr) const
|
|||||||
*lossr = 0;
|
*lossr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Statistics::calculateMos(double maximalMos) const
|
double Statistics::calculateMos() const
|
||||||
{
|
{
|
||||||
// Network MOS via the simplified ITU-T G.107 E-Model:
|
// Network MOS via the simplified ITU-T G.107 E-Model:
|
||||||
//
|
//
|
||||||
@@ -211,7 +212,7 @@ double Statistics::calculateMos(double maximalMos) const
|
|||||||
// Id = 0.024*d + 0.11*max(0, d - 177.3)
|
// Id = 0.024*d + 0.11*max(0, d - 177.3)
|
||||||
// Ie_eff = Ie + (95 - Ie) * Ppl / (Ppl + Bpl) (BurstR=1)
|
// Ie_eff = Ie + (95 - Ie) * Ppl / (Ppl + Bpl) (BurstR=1)
|
||||||
// R = 93.2 - Id - Ie_eff (clamped to [0,100])
|
// R = 93.2 - Id - Ie_eff (clamped to [0,100])
|
||||||
// MOS = 1 + 0.035*R + 7e-6*R*(R-60)*(100-R) (clamped to [1, maximalMos])
|
// MOS = 1 + 0.035*R + 7e-6*R*(R-60)*(100-R) (clamped >= 1)
|
||||||
//
|
//
|
||||||
// Ie/Bpl are looked up from a per-codec table; safe defaults are used
|
// Ie/Bpl are looked up from a per-codec table; safe defaults are used
|
||||||
// when the codec is unknown.
|
// when the codec is unknown.
|
||||||
@@ -253,8 +254,7 @@ double Statistics::calculateMos(double maximalMos) const
|
|||||||
else
|
else
|
||||||
mos = 1.0 + 0.035 * R + 7e-6 * R * (R - 60.0) * (100.0 - R);
|
mos = 1.0 + 0.035 * R + 7e-6 * R * (R - 60.0) * (100.0 - R);
|
||||||
|
|
||||||
if (mos < 1.0) mos = 1.0;
|
if (mos < 1.0) mos = 1.0;
|
||||||
if (mos > maximalMos) mos = maximalMos;
|
|
||||||
return mos;
|
return mos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,6 +306,17 @@ Statistics& Statistics::operator += (const Statistics& src)
|
|||||||
mRemotePeer = src.mRemotePeer;
|
mRemotePeer = src.mRemotePeer;
|
||||||
mSsrc = src.mSsrc;
|
mSsrc = src.mSsrc;
|
||||||
|
|
||||||
|
for (const auto& [addr, counts]: src.mPerDestination)
|
||||||
|
{
|
||||||
|
auto& dst = mPerDestination[addr];
|
||||||
|
dst.mSentRtp += counts.mSentRtp;
|
||||||
|
dst.mSentRtcp += counts.mSentRtcp;
|
||||||
|
dst.mSentBytes += counts.mSentBytes;
|
||||||
|
dst.mReceivedRtp += counts.mReceivedRtp;
|
||||||
|
dst.mReceivedRtcp += counts.mReceivedRtcp;
|
||||||
|
dst.mReceivedBytes += counts.mReceivedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,6 +341,19 @@ Statistics& Statistics::operator -= (const Statistics& src)
|
|||||||
mCodecCount[codecStat.first] -= codecStat.second;
|
mCodecCount[codecStat.first] -= codecStat.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& [addr, counts]: src.mPerDestination)
|
||||||
|
{
|
||||||
|
auto it = mPerDestination.find(addr);
|
||||||
|
if (it == mPerDestination.end())
|
||||||
|
continue;
|
||||||
|
it->second.mSentRtp -= counts.mSentRtp;
|
||||||
|
it->second.mSentRtcp -= counts.mSentRtcp;
|
||||||
|
it->second.mSentBytes -= counts.mSentBytes;
|
||||||
|
it->second.mReceivedRtp -= counts.mReceivedRtp;
|
||||||
|
it->second.mReceivedRtcp -= counts.mReceivedRtcp;
|
||||||
|
it->second.mReceivedBytes -= counts.mReceivedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,5 +369,16 @@ std::string Statistics::toString() const
|
|||||||
<< ", decode requested: " << mDecodeRequested.average()
|
<< ", decode requested: " << mDecodeRequested.average()
|
||||||
<< ", packet interval: " << mPacketInterval.average();
|
<< ", packet interval: " << mPacketInterval.average();
|
||||||
|
|
||||||
|
for (const auto& [addr, counts]: mPerDestination)
|
||||||
|
{
|
||||||
|
oss << "; peer " << addr.toBriefStdString()
|
||||||
|
<< " sent rtp=" << counts.mSentRtp
|
||||||
|
<< "/rtcp=" << counts.mSentRtcp
|
||||||
|
<< "/bytes=" << counts.mSentBytes
|
||||||
|
<< ", received rtp=" << counts.mReceivedRtp
|
||||||
|
<< "/rtcp=" << counts.mReceivedRtcp
|
||||||
|
<< "/bytes=" << counts.mReceivedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "helper/HL_Statistics.h"
|
#include "helper/HL_Statistics.h"
|
||||||
#include "helper/HL_Types.h"
|
#include "helper/HL_Types.h"
|
||||||
#include "helper/HL_InternetAddress.h"
|
#include "ice/ICEAddress.h"
|
||||||
|
|
||||||
#include "jrtplib/src/rtptimeutilities.h"
|
#include "jrtplib/src/rtptimeutilities.h"
|
||||||
#include "jrtplib/src/rtppacket.h"
|
#include "jrtplib/src/rtppacket.h"
|
||||||
@@ -73,6 +73,19 @@ struct Dtmf2833Event
|
|||||||
std::chrono::microseconds mTimestamp;
|
std::chrono::microseconds mTimestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Per-remote-address packet/byte counters. Split out so an aggregate
|
||||||
|
// Statistics can break its totals down by destination/source — useful
|
||||||
|
// for diagnosing ICE candidate switches or symmetric-RTP issues.
|
||||||
|
struct DestinationStats
|
||||||
|
{
|
||||||
|
size_t mSentRtp = 0;
|
||||||
|
size_t mSentRtcp = 0;
|
||||||
|
size_t mSentBytes = 0;
|
||||||
|
size_t mReceivedRtp = 0;
|
||||||
|
size_t mReceivedRtcp = 0;
|
||||||
|
size_t mReceivedBytes = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class Statistics
|
class Statistics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -88,6 +101,10 @@ public:
|
|||||||
mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б
|
mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б
|
||||||
mIllegalRtp = 0; // Number of rtp packets with bad payload type
|
mIllegalRtp = 0; // Number of rtp packets with bad payload type
|
||||||
|
|
||||||
|
// Per-remote-address breakdown of the totals above. Keyed by the remote
|
||||||
|
// RTP/RTCP socket address (NAT-mapped, after ICE selection).
|
||||||
|
std::map<ice::NetworkAddress, DestinationStats> mPerDestination;
|
||||||
|
|
||||||
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
|
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
|
||||||
mDecodeRequested, // Average amount of requested audio frames to play
|
mDecodeRequested, // Average amount of requested audio frames to play
|
||||||
mPacketInterval; // Average interval between packet adding to jitter buffer
|
mPacketInterval; // Average interval between packet adding to jitter buffer
|
||||||
@@ -115,7 +132,7 @@ public:
|
|||||||
|
|
||||||
// It is to calculate network MOS
|
// It is to calculate network MOS
|
||||||
void calculateBurstr(double* burstr, double* loss) const;
|
void calculateBurstr(double* burstr, double* loss) const;
|
||||||
double calculateMos(double maximalMos) const;
|
double calculateMos() const;
|
||||||
|
|
||||||
Statistics();
|
Statistics();
|
||||||
~Statistics();
|
~Statistics();
|
||||||
|
|||||||
Reference in New Issue
Block a user