- initial import

This commit is contained in:
Dmytro Bogovych 2018-06-05 11:05:37 +03:00
commit e1a4931375
4673 changed files with 1383093 additions and 0 deletions

57
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,57 @@
project(rtphone)
# Rely on C++ 11
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (rtphone_libs libs)
set (rtphone_engine engine)
set (RTPHONE_SOURCES
${rtphone_engine}/media/MT_Statistics.cpp
${rtphone_engine}/media/MT_WebRtc.cpp
${rtphone_engine}/media/MT_Stream.cpp
${rtphone_engine}/media/MT_SrtpHelper.cpp
${rtphone_engine}/media/MT_SingleAudioStream.cpp
${rtphone_engine}/media/MT_NativeRtpSender.cpp
${rtphone_engine}/media/MT_Dtmf.cpp
${rtphone_engine}/media/MT_CodecList.cpp
${rtphone_engine}/media/MT_Codec.cpp
${rtphone_engine}/media/MT_Box.cpp
${rtphone_engine}/media/MT_AudioStream.cpp
${rtphone_engine}/media/MT_AudioReceiver.cpp
${rtphone_engine}/media/MT_AudioCodec.cpp
${rtphone_engine}/media/MT_SevanaMos.cpp
${rtphone_engine}/media/MT_AmrCodec.cpp
${rtphone_engine}/media/MT_CngHelper.cpp
${rtphone_engine}/agent/Agent_Impl.cpp
${rtphone_engine}/agent/Agent_AudioManager.cpp
${rtphone_engine}/endpoint/EP_Account.cpp
${rtphone_engine}/endpoint/EP_AudioProvider.cpp
${rtphone_engine}/endpoint/EP_DataProvider.cpp
${rtphone_engine}/endpoint/EP_Engine.cpp
${rtphone_engine}/endpoint/EP_NetworkQueue.cpp
${rtphone_engine}/endpoint/EP_Observer.cpp
${rtphone_engine}/endpoint/EP_Session.cpp
)
add_library(rtphone STATIC ${RTPHONE_SOURCES})
add_subdirectory(${rtphone_engine}/helper)
add_subdirectory(${rtphone_engine}/audio)
add_subdirectory(${rtphone_libs}/resiprocate)
add_subdirectory(${rtphone_libs}/ice)
add_subdirectory(${rtphone_libs}/jrtplib/src)
add_subdirectory(${rtphone_libs}/libg729)
add_subdirectory(${rtphone_libs}/libgsm)
add_subdirectory(${rtphone_libs}/gsmhr)
add_subdirectory(${rtphone_libs}/g722)
add_subdirectory(${rtphone_libs}/speexdsp)
add_subdirectory(${rtphone_libs}/srtp)
add_subdirectory(${rtphone_libs}/webrtc)
target_link_libraries(rtphone ice_stack jrtplib g729_codec gsm_codec
gsmhr_codec g722_codec srtp resiprocate
opencore-amrnb opencore-amrwb
helper_lib audio_lib webrtc speexdsp
ssl crypto opus uuid dl)

View File

@ -0,0 +1,193 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Agent_AudioManager.h"
#include "../engine/audio/Audio_WavFile.h"
#include "../engine/helper/HL_String.h"
#include "../engine/audio/Audio_Null.h"
#if defined(TARGET_ANDROID)
# include "../engine/audio/Audio_Android.h"
#endif
#define LOG_SUBSYSTEM "AudioManager"
// ---------------- AudioManager -------------
//static AudioManager GAudioManager;
AudioManager::AudioManager()
:mTerminal(nullptr)
{
mPlayer.setDelegate(this);
}
AudioManager::~AudioManager()
{
//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)
{
mTerminal = terminal;
}
MT::Terminal* AudioManager::terminal()
{
return mTerminal;
}
#define LOCK_MANAGER std::unique_lock<std::mutex> l(mGuard)
void AudioManager::start(int usageId)
{
assert(mTerminal);
LOCK_MANAGER;
ICELogInfo(<< "Start main audio with usage id " << usageId);
if (mUsage.obtain(usageId) > 1)
return;
if (Audio::OsEngine::instance())
Audio::OsEngine::instance()->open();
if (!mAudioInput || !mAudioOutput)
{
// Disable AEC for now - because PVQA conflicts with speex AEC.
std::shared_ptr<Audio::Enumerator> enumerator(Audio::Enumerator::make(usageId == atNull));
if (!mTerminal->audio())
mTerminal->setAudio(std::make_shared<Audio::DevicePair>(false, true));
if (!mAudioInput)
{
enumerator->open(Audio::myMicrophone);
int inputIndex = enumerator->indexOfDefaultDevice();
// Construct and set to terminal's audio pair input device
if (usageId != atNull)
mAudioInput = Audio::PInputDevice(Audio::InputDevice::make(enumerator->idAt(inputIndex)));
else
mAudioInput = Audio::PInputDevice(new Audio::NullInputDevice());
mTerminal->audio()->setInput(mAudioInput);
}
if (!mAudioOutput)
{
Audio::Enumerator *enumerator = Audio::Enumerator::make(usageId == atNull);
enumerator->open(Audio::mySpeaker);
int outputIndex = enumerator->indexOfDefaultDevice();
// Construct and set terminal's audio pair output device
if (usageId != atNull)
{
if (outputIndex >= enumerator->count())
outputIndex = 0;
mAudioOutput = Audio::POutputDevice(
Audio::OutputDevice::make(enumerator->idAt(outputIndex)));
}
else
mAudioOutput = Audio::POutputDevice(new Audio::NullOutputDevice());
mTerminal->audio()->setOutput(mAudioOutput);
}
}
// Open audio
if (mAudioInput)
mAudioInput->open();
if (mAudioOutput)
mAudioOutput->open();
}
void AudioManager::close()
{
mUsage.clear();
if (mAudioInput)
{
mAudioInput->close();
mAudioInput.reset();
}
if (mAudioOutput)
{
mAudioOutput->close();
mAudioOutput.reset();
}
mPlayer.setOutput(Audio::POutputDevice());
}
void AudioManager::stop(int usageId)
{
LOCK_MANAGER;
ICELogInfo( << "Stop main audio with usage id " << usageId);
if (mTerminal)
{
if (mTerminal->audio())
mTerminal->audio()->player().release(usageId);
}
if (!mUsage.release(usageId))
{
close();
// Reset device pair on terminal side
mTerminal->setAudio(Audio::PDevicePair());
if (Audio::OsEngine::instance())
Audio::OsEngine::instance()->close();
}
}
void AudioManager::startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit)
{
// Check if file exists
Audio::PWavFileReader r = std::make_shared<Audio::WavFileReader>();
#ifdef TARGET_WIN
r->open(StringHelper::makeTstring(path));
#else
r->open(path);
#endif
if (!r->isOpened())
{
ICELogCritical(<< "Cannot open file to play");
return;
}
// Delegate processing to existing audio device pair manager
mTerminal->audio()->player().add(usageId, r, lm == lmLoopAudio, timelimit);
start(usageId);
}
void AudioManager::stopPlayFile(int usageId)
{
stop(usageId);
mPlayer.release(usageId);
}
void AudioManager::onFilePlayed(Audio::Player::PlaylistItem& item)
{
}
void AudioManager::process()
{
mPlayer.releasePlayed();
std::vector<int> ids;
mTerminal->audio()->player().retrieveUsageIds(ids);
for (unsigned i=0; i<ids.size(); i++)
stop(ids[i]);
}

View File

@ -0,0 +1,87 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __CLIENT_AUDIO_H
#define __CLIENT_AUDIO_H
#include "../engine/audio/Audio_Interface.h"
#include "../engine/audio/Audio_Player.h"
#include "../engine/endpoint/EP_Engine.h"
#include "../engine/media/MT_Box.h"
#include "../engine/helper/HL_Log.h"
#include "../engine/helper/HL_Sync.h"
enum
{
AudioPrefix_Ring = 1,
AudioPrefix_Zero,
AudioPrefix_One,
AudioPrefix_Two,
AudioPrefix_Three,
AudioPrefix_Four,
AudioPrefix_Five,
AudioPrefix_Six,
AudioPrefix_Seven,
AudioPrefix_Eight,
AudioPrefix_Nine,
AudioPrefix_Asterisk,
AudioPrefix_Diez
};
#define AudioSessionCoeff 64
class AudioManager: public Audio::Player::EndOfAudioDelegate
{
public:
AudioManager();
~AudioManager();
static AudioManager& instance();
// Enforces to close audio devices. Used to shutdown AudioManager on exit from application
void close();
// Terminal and settings must be available for AudioManager
void setTerminal(MT::Terminal* terminal);
MT::Terminal* terminal();
// 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);
enum AudioTarget
{
atNull,
atReceiver,
atRinger
};
enum LoopMode
{
lmLoopAudio,
lmNoloop
};
void startPlayFile(int usageId, const std::string& path, AudioTarget target, LoopMode lm, int timelimit = 0);
void stopPlayFile(int usageId);
void onFilePlayed(Audio::Player::PlaylistItem& item);
// Must be called from main loop to release used audio devices
void process();
protected:
Audio::PInputDevice mAudioInput;
Audio::POutputDevice mAudioOutput;
Audio::Player mPlayer;
MT::Terminal* mTerminal;
std::map<int, int> UsageMap;
UsageCounter mUsage;
std::mutex mGuard;
};
#endif

View File

@ -0,0 +1,908 @@
#include "Agent_Impl.h"
#include "json/json.h"
#include "helper/HL_String.h"
#include "helper/HL_StreamState.h"
#include "helper/HL_VariantMap.h"
#include "helper/HL_CsvReader.h"
#if defined(TARGET_ANDROID)
//# include "CAndroidSystemInfo.h"
#endif
#include <fstream>
const std::string Status_Ok = "ok";
const std::string Status_SessionNotFound = "session not found";
const std::string Status_AccountNotFound = "account not found";
const std::string Status_FailedToOpenFile = "failed to open file";
const std::string Status_NoActiveProvider = "no active provider";
const std::string Status_NoMediaAction = "no valid media action";
const std::string Status_NoCommand = "no valid command";
#define LOG_SUBSYSTEM "Agent"
AgentImpl::AgentImpl()
:mShutdown(false), mEventListChangeCondVar()
{
#if defined(TARGET_ANDROID) || defined(TARGET_WIN)
ice::GLogger.useDebugWindow(true);
#endif
ice::GLogger.setLevel(ice::LogLevel::LL_DEBUG);
}
AgentImpl::~AgentImpl()
{
stopAgentAndThread();
}
void AgentImpl::run()
{
while (!mShutdown)
{
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
process();
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
std::string AgentImpl::command(const std::string& command)
{
if (command.empty())
return "";
Json::Value d, answer;
try
{
Json::Reader r;
if (!r.parse(command, d))
return "";
std::string cmd = d["command"].asString();
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")
processCreateSession(d, answer);
else
if (cmd == "session_start")
processStartSession(d, answer);
else
if (cmd == "session_stop")
processStopSession(d, answer);
else
if (cmd == "session_accept")
processAcceptSession(d, answer);
else
if (cmd == "session_destroy")
processDestroySession(d, answer);
else
if (cmd == "session_use_stream")
processUseStreamForSession(d, answer);
else
if (cmd == "wait_for_event")
processWaitForEvent(d, answer);
else
if (cmd == "session_get_media_stats")
processGetMediaStats(d, answer);
else
if (cmd == "agent_network_changed")
processNetworkChanged(d, answer);
else
if (cmd == "agent_add_root_cert")
processAddRootCert(d, answer);
else
if (cmd == "detach_log")
{
GLogger.closeFile();
answer["status"] = Status_Ok;
}
else
if (cmd == "attach_log")
{
GLogger.openFile();
answer["status"] = Status_Ok;
}
else
if (cmd == "log_message")
processLogMessage(d, answer);
else
{
answer["status"] = Status_NoCommand;
}
}
catch(std::exception& e)
{
answer["status"] = e.what();
}
return answer.toStyledString();
}
bool AgentImpl::waitForData(int milliseconds)
{
return false;
}
std::string AgentImpl::read()
{
return "";
}
void AgentImpl::processConfig(Json::Value &d, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
#if !defined(TARGET_ANDROID) && defined(USE_PVQA_LIBRARY)
// It works for desktop OSes only
// Because Android requires special initializing procedure (valid JNI environment context)
std::string pvqaLicense = d["pvqa-license"].asString(), pvqaConfig = d["pvqa-config"].asString();
if (!pvqaLicense.empty() && !pvqaConfig.empty())
MT::SevanaPVQA::initializeLibrary(pvqaLicense, pvqaConfig);
#endif
#if !defined(TARGET_ANDROID) && defined(USE_AQUA_LIBRARY)
std::string aquaLicense = d["aqua-license"].asString();
if (!aquaLicense.empty())
MT::SevanaAqua::initializeLibrary(aquaLicense);
#endif
std::string transport = d["transport"].asString();
config()[CONFIG_TRANSPORT] = (transport == "any") ? 0 : (transport == "udp" ? 1 : (transport == "tcp" ? 2 : 3));
config()[CONFIG_IPV4] = d["ipv4"].asBool();
config()[CONFIG_IPV6] = d["ipv6"].asBool();
// Log file
std::string logfile = d["logfile"].asString();
ice::Logger& logger = ice::GLogger;
logger.useFile(logfile.empty() ? nullptr : logfile.c_str());
config()[CONFIG_MULTIPLEXING] = true;
config()[CONFIG_DISPLAYNAME] = "Voip quality tester";
mUseNativeAudio = d["nativeaudio"].asBool();
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
answer["status"] = Status_Ok;
}
void AgentImpl::processStart(Json::Value &request, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
if (mThread)
{
answer["status"] = Status_Ok;
return; // Started already
}
// Start socket thread
SocketHeap::instance().start();
// Initialize terminal
MT::CodecList::Settings settings;
mTerminal = std::make_shared<MT::Terminal>(settings);
// Enable/disable codecs
PVariantMap priorityConfig = std::make_shared<VariantMap>();
MT::CodecList& cl = mTerminal->codeclist();
for (int i=0; i<cl.count(); i++)
if (cl.codecAt(i).payloadType() < 96)
priorityConfig->at(i) = i;
else
priorityConfig->at(i) = -1;
config()[CONFIG_CODEC_PRIORITY] = priorityConfig;
// Enable audio
mAudioManager = std::make_shared<AudioManager>();
mAudioManager->setTerminal(mTerminal.get());
// Do not start here. Start right before call.
// Initialize endpoint
start();
// Start worker thread
mShutdown = false;
mThread = std::make_shared<std::thread>(&AgentImpl::run, this);
answer["status"] = Status_Ok;
}
void AgentImpl::processStop(Json::Value& request, Json::Value& answer)
{
stopAgentAndThread();
answer["status"] = Status_Ok;
}
void AgentImpl::processCreateAccount(Json::Value &d, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
PVariantMap c = std::make_shared<VariantMap>();
(*c)[CONFIG_USERNAME] = d["username"].asString();
(*c)[CONFIG_PASSWORD] = d["password"].asString();
(*c)[CONFIG_DOMAIN] = d["domain"].asString();
(*c)[CONFIG_EXTERNALIP] = d["use_external_ip"].asBool();
auto nameAndPort = StringHelper::parseHost(d["stun_server"].asString(), 3478);
(*c)[CONFIG_STUNSERVER_NAME] = nameAndPort.first;
(*c)[CONFIG_STUNSERVER_PORT] = nameAndPort.second;
PAccount account = createAccount(c);
mAccountMap[account->id()] = account;
answer["account_id"] = account->id();
answer["status"] = Status_Ok;
}
void AgentImpl::processStartAccount(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map
auto accountIter = mAccountMap.find(request["account_id"].asInt());
if (accountIter != mAccountMap.end())
{
accountIter->second->start();
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_AccountNotFound;
}
void AgentImpl::processSetUserInfoToAccount(Json::Value &request, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// Locate account in map
auto accountIter = mAccountMap.find(request["account_id"].asInt());
if (accountIter != mAccountMap.end())
{
Account::UserInfo info;
Json::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys)
info[k] = arg[k].asString();
accountIter->second->setUserInfo(info);
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_AccountNotFound;
}
void AgentImpl::processCreateSession(Json::Value &request, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto accountIter = mAccountMap.find(request["account_id"].asInt());
if (accountIter != mAccountMap.end())
{
PSession session = createSession(accountIter->second);
mSessionMap[session->id()] = session;
answer["session_id"] = session->id();
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_AccountNotFound;
}
void AgentImpl::processStartSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
// For debugging only
/*mIncomingAudioDump = std::make_shared<Audio::WavFileWriter>();
mIncomingAudioDump->open("incoming_dump", AUDIO_SAMPLERATE, AUDIO_CHANNELS);
mOutgoingAudioDump = std::make_shared<Audio::WavFileWriter>();
mOutgoingAudioDump->open("outgoing_dump", AUDIO_SAMPLERATE, AUDIO_CHANNELS);*/
// Start audio manager
if (!mAudioManager)
{
// Agent was not started
ICELogCritical(<< "No audio manager installed.");
answer["status"] = "Audio manager not started. Most probably agent is not started.";
return;
}
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end())
{
// Ensure audio provider is here
PSession session = sessionIter->second;
PDataProvider audioProvider = std::make_shared<AudioProvider>(*this, *mTerminal);
audioProvider->setState(audioProvider->state() | (int)StreamState::Grabbing | (int)StreamState::Playing);
#if defined(USE_AQUA_LIBRARY)
std::string temp_path = request["aqua_temp_path"].asString();
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";
if (temp_path.size())
config += " -fau " + temp_path;
MT::PSevanaAqua qc = std::make_shared<MT::SevanaAqua>();
qc->setTempPath(temp_path);
qc->configureWith(MT::SevanaAqua::parseConfig(config));
mAquaMap[sessionIter->first] = qc;
dynamic_cast<AudioProvider*>(audioProvider.get())->configureMediaObserver(this, (void*)qc.get());
#endif
// TODO: support SRTP via StreamState::Srtp option in audio provider state
// Get user headers
Session::UserHeaders info;
Json::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys)
info[k] = arg[k].asString();
session->setUserHeaders(info);
session->addProvider(audioProvider);
session->start(request["target"].asString());
answer["status"] = Status_Ok;
}
else
{
answer["status"] = Status_SessionNotFound;
}
}
void AgentImpl::processStopSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end())
{
PSession session = sessionIter->second;
session->stop();
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_SessionNotFound;
}
void AgentImpl::processAcceptSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
auto sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end())
{
// Ensure audio manager is here
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
// Accept session on SIP level
PSession session = sessionIter->second;
// Get user headers
Session::UserHeaders info;
Json::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys)
info[k] = arg[k].asString();
session->setUserHeaders(info);
// Accept finally
session->accept();
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_SessionNotFound;
}
void AgentImpl::processDestroySession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
int sessionId = request["session_id"].asInt();
auto sessionIter = mSessionMap.find(sessionId);
if (sessionIter != mSessionMap.end())
mSessionMap.erase(sessionIter);
#if defined(USE_AQUA_LIBRARY)
auto aquaIter = mAquaMap.find(sessionId);
if (aquaIter != mAquaMap.end())
mAquaMap.erase(aquaIter);
#endif
answer["status"] = Status_Ok;
}
void AgentImpl::processWaitForEvent(Json::Value &request, Json::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
//int x = 0;
//int y = 1/x;
int timeout = request["timeout"].asInt();
std::unique_lock<std::mutex> eventLock(mEventListMutex);
if (mEventList.empty())
mEventListChangeCondVar.wait_for(eventLock, chrono::milliseconds(timeout));
if (!mEventList.empty())
{
answer = mEventList.front();
mEventList.erase(mEventList.begin());
}
answer["status"] = Status_Ok;
}
#if defined(USE_PVQA_LIBRARY)
static Json::Value CsvReportToJson(const std::string& report)
{
Json::Value detectorValues;
std::istringstream iss(report);
CsvReader reader(iss);
std::vector<std::string> cells;
if (reader.readLine(cells))
{
Json::Value detectorNames;
for (int nameIndex = 0; nameIndex < (int)cells.size(); nameIndex++)
detectorNames[nameIndex] = StringHelper::trim(cells[nameIndex]);
// Put first line name of columns
detectorValues[0] = detectorNames;
int rowIndex = 1;
while (reader.readLine(cells))
{
// Skip last column for now
Json::Value row;
for (int valueIndex = 0; valueIndex < (int)cells.size(); valueIndex++)
{
bool isFloat = true;
float v = StringHelper::toFloat(cells[valueIndex], 0.0, &isFloat);
if (isFloat)
row[valueIndex] = v;
else
row[valueIndex] = cells[valueIndex];
}
detectorValues[rowIndex++] = row;
}
}
return detectorValues;
}
#endif
void AgentImpl::processGetMediaStats(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
int sessionId = request["session_id"].asInt();
SessionMap::iterator sessionIter = mSessionMap.find(sessionId);
if (sessionIter != mSessionMap.end())
{
PSession session = sessionIter->second;
VariantMap result;
bool includePvqa = request["include_pvqa"].asBool();
#if defined(USE_AQUA_LIBRARY)
bool includeAqua = request["include_aqua"].asBool();
std::string aquaReference = request["aqua_reference_audio"].asString();
#endif
session->getSessionInfo(includePvqa ? Session::InfoOptions::Detailed : Session::InfoOptions::Standard,
result);
if (result.exists(SessionInfo_AudioCodec))
answer["codec"] = result[SessionInfo_AudioCodec].asStdString();
if (result.exists(SessionInfo_NetworkMos))
answer["network_mos"] = result[SessionInfo_NetworkMos].asFloat();
#if defined(USE_PVQA_LIBRARY)
if (result.exists(SessionInfo_PvqaMos))
answer["pvqa_mos"] = result[SessionInfo_PvqaMos].asFloat();
if (result.exists(SessionInfo_PvqaReport))
answer["pvqa_report"] = CsvReportToJson(result[SessionInfo_PvqaReport].asStdString());
#endif
if (result.exists(SessionInfo_PacketLoss))
answer["rtp_lost"] = result[SessionInfo_LostRtp].asInt();
if (result.exists(SessionInfo_SentRtp))
answer["rtp_sent"] = result[SessionInfo_SentRtp].asInt();
if (result.exists(SessionInfo_ReceivedRtp))
answer["rtp_received"] = result[SessionInfo_ReceivedRtp].asInt();
if (result.exists(SessionInfo_Duration))
answer["duration"] = result[SessionInfo_Duration].asInt();
if (result.exists(SessionInfo_Jitter))
answer["jitter"] = result[SessionInfo_Jitter].asFloat() * 1000; // Take milliseconds
if (result.exists(SessionInfo_Rtt))
answer["rtt"] = result[SessionInfo_Rtt].asFloat();
if (result.exists(SessionInfo_BitrateSwitchCounter))
answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt();
if (result.exists(SessionInfo_SSRC))
answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt();
if (result.exists(SessionInfo_RemotePeer))
answer["rtp_remotepeer"] = result[SessionInfo_RemotePeer].asStdString();
#if defined(USE_AQUA_LIBRARY)
if (includeAqua)
{
ByteBuffer referenceAudio;
// Read AQuA reference audio from file if available
if (!aquaReference.empty())
{
Audio::WavFileReader reader;
reader.open(aquaReference);
if (reader.isOpened())
{
char buffer[1024];
int wasRead = 0;
do
{
wasRead = reader.read(buffer, 1024);
if (wasRead > 0)
referenceAudio.appendBuffer(buffer, wasRead);
}
while (wasRead == 1024);
}
}
MT::PSevanaAqua sa = mAquaMap[sessionIter->first];
MT::SevanaAqua::AudioBuffer test, reference;
test.mRate = AUDIO_SAMPLERATE; reference.mRate = AUDIO_SAMPLERATE;
test.mChannels = AUDIO_CHANNELS; reference.mChannels = AUDIO_CHANNELS;
test.mData = mAquaIncoming.mutableData(); reference.mData = referenceAudio.mutableData();
test.mSize = mAquaIncoming.size(); reference.mSize = referenceAudio.size();
MT::SevanaAqua::CompareResult r = sa->compare(reference, test);
answer["aqua_mos"] = r.mMos;
if (r.mFaults)
{
Json::Value json(r.mReport);
Json::Value faults = r.mFaults->toJson();
json["faults"] = faults;
answer["aqua_report"] = json.toStyledString();
}
else
answer["aqua_report"] = r.mReport;
}
#endif
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_SessionNotFound;
}
void AgentImpl::processNetworkChanged(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
}
const std::string BeginCertificate = "-----BEGIN CERTIFICATE-----";
const std::string EndCertificate = "-----END CERTIFICATE-----";
void AgentImpl::processAddRootCert(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
std::string pem = request["cert"].asString();
std::string::size_type pb = 0, pe = 0;
for(pb = pem.find(BeginCertificate, pb), pe = pem.find(EndCertificate, pe);
pb != std::string::npos && pe != std::string::npos;
pb = pem.find(BeginCertificate, pb + BeginCertificate.size()), pe = pem.find(EndCertificate, pe + EndCertificate.size()))
{
// Get single certificate
std::string cert = pem.substr(pb, pe + EndCertificate.size());
int size = cert.size();
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
// Delete processed part
pem.erase(0, pe + EndCertificate.size());
}
answer["status"] = Status_Ok;
}
void AgentImpl::processLogMessage(Json::Value &request, Json::Value &answer)
{
int level = request["level"].asInt();
std::string message = request["message"].asString();
ICELog((ice::LogLevel)level, "App", << message);
answer["status"] = Status_Ok;
}
void AgentImpl::stopAgentAndThread()
{
// Stop user agent
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
try
{
stop();
}
catch (...)
{}
// Stop worker thread
if (mThread)
{
mShutdown = true;
if (mThread->joinable())
{
l.unlock();
mThread->join();
l.lock();
}
mThread.reset();
}
// Close used audio
if (mAudioManager)
{
// Ensure audio manager is stopped
mAudioManager->stop(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
// Free audio manager
mAudioManager.reset();
}
// Close terminal after audio manager - because audio manager has reference to terminal
mTerminal.reset();
SocketHeap::instance().stop();
}
void AgentImpl::processUseStreamForSession(Json::Value& request, Json::Value& answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
SessionMap::iterator sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end())
{
// Extract ptr to session
PSession session = sessionIter->second;
// Parse command
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();
// Try to open file
AudioProvider* prov = session->findProviderForActiveAudio();
if (prov)
{
if (actionText == "read")
{
if (path.empty())
{
// Turn off playing into the stream
prov->readFile(Audio::PWavFileReader(), direction);
answer["status"] = Status_Ok;
}
else
{
Audio::PWavFileReader reader = std::make_shared<Audio::WavFileReader>();
if (!reader->open(StringHelper::makeTstring(path)))
answer["status"] = Status_FailedToOpenFile;
else
{
prov->readFile(reader, direction);
answer["status"] = Status_Ok;
}
}
}
else
if (actionText == "write")
{
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(StringHelper::makeTstring(path), AUDIO_SAMPLERATE, AUDIO_CHANNELS))
answer["status"] = Status_FailedToOpenFile;
else
{
prov->writeFile(writer, direction);
answer["status"] = Status_Ok;
}
}
}
else
if (actionText == "mirror")
{
prov->setupMirror(request["enable"].asBool());
answer["status"] = Status_Ok;
}
else
answer["status"] = Status_AccountNotFound;
}
else
answer["status"] = Status_NoMediaAction;
}
}
#if defined(USE_AQUA_LIBRARY)
void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag)
{
/* if (mIncomingAudioDump && direction == MT::Stream::MediaDirection::Incoming)
mIncomingAudioDump->write(data, length);
if (mOutgoingAudioDump && direction == MT::Stream::MediaDirection::Outgoing)
mOutgoingAudioDump->write(data, length);*/
// User tag points to accumulator object which includes
MT::SevanaAqua* sa = reinterpret_cast<MT::SevanaAqua*>(userTag);
switch (direction)
{
case MT::Stream::MediaDirection::Incoming: mAquaIncoming.appendBuffer(data, length); break;
case MT::Stream::MediaDirection::Outgoing: mAquaOutgoing.appendBuffer(data, length); break;
}
}
#endif
// Called on new incoming session; providers shoukld
#define EVENT_WITH_NAME(X) Json::Value v; v["event_name"] = X;
PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
{
EVENT_WITH_NAME("provider_needed");
v["provider_name"] = name;
addEvent(v);
return PDataProvider(new AudioProvider(*this, *mTerminal));
}
// Called on new session offer
void AgentImpl::onNewSession(PSession s)
{
EVENT_WITH_NAME("session_incoming");
v["session_id"] = s->id();
v["remote_peer"] = s->remoteAddress();
mSessionMap[s->id()] = s;
addEvent(v);
}
// Called when session is terminated
void AgentImpl::onSessionTerminated(PSession s, int responsecode, int reason)
{
/*if (mIncomingAudioDump)
mIncomingAudioDump->close();
if (mOutgoingAudioDump)
mOutgoingAudioDump->close();
*/
mAudioManager->stop(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
// Gather statistics before
EVENT_WITH_NAME("session_terminated");
v["session_id"] = s->id();
v["error_code"] = responsecode;
v["reason_code"] = reason;
addEvent(v);
}
// 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
void AgentImpl::onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p)
{
EVENT_WITH_NAME("session_established");
v["session_id"] = s->id();
v["conn_type"] = conntype == EV_SIP ? "sip" : "ice";
if (conntype == EV_ICE)
v["media_target"] = p.mRtp.toStdString();
addEvent(v);
}
void AgentImpl::onSessionProvisional(PSession s, int code)
{
EVENT_WITH_NAME("session_provisional");
v["session_id"] = s->id();
v["code"] = code;
addEvent(v);
}
// Called when user agent started
void AgentImpl::onStart(int errorcode)
{
EVENT_WITH_NAME("agent_started");
v["error_code"] = errorcode;
addEvent(v);
}
// Called when user agent stopped
void AgentImpl::onStop()
{
EVENT_WITH_NAME("agent_stopped");
addEvent(v);
}
// Called when account registered
void AgentImpl::onAccountStart(PAccount account)
{
EVENT_WITH_NAME("account_started");
v["account_id"] = account->id();
addEvent(v);
}
// Called when account removed or failed (non zero error code)
void AgentImpl::onAccountStop(PAccount account, int error)
{
EVENT_WITH_NAME("account_stopped");
v["account_id"] = account->id();
v["error_code"] = error;
addEvent(v);
}
// Called when connectivity checks failed.
void AgentImpl::onConnectivityFailed(PSession s)
{
EVENT_WITH_NAME("session_connectivity_failed");
v["session_id"] = s->id();
addEvent(v);
}
// Called when new candidate is gathered
void AgentImpl::onCandidateGathered(PSession s, const char* address)
{
EVENT_WITH_NAME("session_candidate_gathered");
v["session_id"] = s->id();
v["address"] = address;
addEvent(v);
}
// Called when network change detected
void AgentImpl::onNetworkChange(PSession s)
{
EVENT_WITH_NAME("session_network_changed");
v["session_id"] = s->id();
addEvent(v);
}
// Called when all candidates are gathered
void AgentImpl::onGathered(PSession s)
{
EVENT_WITH_NAME("session_candidates_gathered");
v["session_id"] = s->id();
addEvent(v);
}
// Called when new connectivity check is finished
void AgentImpl::onCheckFinished(PSession s, const char* description)
{
EVENT_WITH_NAME("session_conncheck_finished");
v["session_id"] = s->id();
v["description"] = description;
addEvent(v);
}
// Called when log message must be recorded
void AgentImpl::onLog(const char* msg)
{}
// Called when problem with SIP connection(s) detected
void AgentImpl::onSipConnectionFailed()
{
EVENT_WITH_NAME("sip_connection_failed");
addEvent(v);
}
void AgentImpl::addEvent(const Json::Value& v)
{
std::unique_lock<std::mutex> lock(mEventListMutex);
mEventList.push_back(v);
mEventListChangeCondVar.notify_one();
}

View File

@ -0,0 +1,132 @@
#ifndef __AGENT_IMPL_H
#define __AGENT_IMPL_H
#include <string>
#include <memory>
#include <thread>
#include "../endpoint/EP_Engine.h"
#include "../endpoint/EP_AudioProvider.h"
#include "../media/MT_Box.h"
#include "../helper/HL_ByteBuffer.h"
#include "json/json.h"
#include "Agent_AudioManager.h"
#include <mutex>
#include <condition_variable>
class AgentImpl: public UserAgent
#if defined(USE_AQUA_LIBRARY)
, public MT::Stream::MediaObserver
#endif
{
protected:
std::recursive_mutex mAgentMutex;
std::mutex mEventListMutex;
std::condition_variable mEventListChangeCondVar;
std::vector<Json::Value> mEventList;
bool mUseNativeAudio = false;
typedef std::map<int, PAccount> AccountMap;
AccountMap mAccountMap;
typedef std::map<int, PSession> SessionMap;
SessionMap mSessionMap;
#if defined(USE_AQUA_LIBRARY)
// Keys are the same as used in mSessionMap
typedef std::map<int, MT::PSevanaAqua> AquaMap;
AquaMap mAquaMap;
ByteBuffer mAquaIncoming, mAquaOutgoing;
#endif
std::shared_ptr<std::thread> mThread;
volatile bool mShutdown;
std::shared_ptr<MT::Terminal> mTerminal;
std::shared_ptr<AudioManager> mAudioManager;
//Audio::PWavFileWriter mIncomingAudioDump, mOutgoingAudioDump;
void run();
void addEvent(const Json::Value& v);
void processConfig(Json::Value& request, Json::Value& answer);
void processStart(Json::Value& request, Json::Value& answer);
void processStop(Json::Value& request, Json::Value& answer);
void processCreateAccount(Json::Value& request, Json::Value& answer);
void processStartAccount(Json::Value& request, Json::Value& answer);
void processSetUserInfoToAccount(Json::Value& request, Json::Value& answer);
void processCreateSession(Json::Value& request, Json::Value& answer);
void processStartSession(Json::Value& request, Json::Value& answer);
void processStopSession(Json::Value& request, Json::Value& answer);
void processAcceptSession(Json::Value& request, Json::Value& answer);
void processDestroySession(Json::Value& request, Json::Value& answer);
void processWaitForEvent(Json::Value& request, Json::Value& answer);
void processGetMediaStats(Json::Value& request, Json::Value& answer);
void processUseStreamForSession(Json::Value& request, Json::Value& answer);
void processNetworkChanged(Json::Value& request, Json::Value& answer);
void processAddRootCert(Json::Value& request, Json::Value& answer);
void processLogMessage(Json::Value& request, Json::Value& answer);
void stopAgentAndThread();
public:
AgentImpl();
~AgentImpl();
std::string command(const std::string& command);
bool waitForData(int milliseconds);
std::string read();
// UserAgent overrides
// Called on new incoming session; providers shoukld
PDataProvider onProviderNeeded(const std::string& name) override;
// Called on new session offer
void onNewSession(PSession s) override;
// Called when session is terminated
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
void onSessionEstablished(PSession s, int conntype, const RtpPair<InternetAddress>& p) override;
void onSessionProvisional(PSession s, int code) override;
// Called when user agent started
void onStart(int errorcode) override;
// Called when user agent stopped
void onStop() override;
// Called when account registered
void onAccountStart(PAccount account) override;
// Called when account removed or failed (non zero error code)
void onAccountStop(PAccount account, int error) override;
// Called when connectivity checks failed.
void onConnectivityFailed(PSession s) override;
// Called when new candidate is gathered
void onCandidateGathered(PSession s, const char* address) override;
// Called when network change detected
void onNetworkChange(PSession s) override;
// Called when all candidates are gathered
void onGathered(PSession s) override;
// Called when new connectivity check is finished
void onCheckFinished(PSession s, const char* description) override;
// Called when log message must be recorded
void onLog(const char* msg) override;
// Called when problem with SIP connection(s) detected
void onSipConnectionFailed() override;
#if defined(USE_AQUA_LIBRARY)
// Called on incoming & outgoing audio for voice sessions
void onMedia(const void* data, int length, MT::Stream::MediaDirection direction, void* context, void* userTag) override;
#endif
};
#endif

View File

@ -0,0 +1,27 @@
#include "Agent_Interface.h"
#include "Agent_Impl.h"
Agent::Agent()
:mContext(new AgentImpl());
{
}
Agent::~Agent()
{
}
void Agent::write(const std::string& command)
{
}
bool Agent::waitForData(int milliseconds)
{
return false;
}
std::string Agent::read()
{
}

View File

@ -0,0 +1,19 @@
#ifndef __AGENT_JSON_H
#define __AGENT_JSON_H
#include <string>
class Agent
{
protected:
void* mContext;
public:
Agent();
~Agent();
void write(const std::string& command);
bool waitForData(int milliseconds);
std::string read();
};
#endif

View File

@ -0,0 +1,583 @@
#include "Audio_Android.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Log.h"
#include <mutex>
#include <iostream>
#include "../helper/HL_String.h"
#ifdef TARGET_ANDROID
#define LOG_SUBSYSTEM "Audio"
using namespace Audio;
// -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator()
{}
AndroidEnumerator::~AndroidEnumerator()
{}
int AndroidEnumerator::indexOfDefaultDevice()
{
return 0;
}
int AndroidEnumerator::count()
{
return 1;
}
int AndroidEnumerator::idAt(int index)
{
return 0;
}
std::string AndroidEnumerator::nameAt(int index)
{
return "Audio";
}
void AndroidEnumerator::open(int direction)
{}
void AndroidEnumerator::close()
{}
// -----------------------
OpenSLEngine::OpenSLEngine()
{}
OpenSLEngine::~OpenSLEngine()
{}
void OpenSLEngine::open()
{
std::unique_lock<std::mutex> l(mMutex);
if (++mUsageCounter == 1)
internalOpen();
}
void OpenSLEngine::close()
{
std::unique_lock<std::mutex> l(mMutex);
if (--mUsageCounter == 0)
internalClose();
}
#define CHECK_OPENSLES_ERROR if (resultCode != SL_RESULT_SUCCESS) throw Exception(ERR_OPENSLES, (int)resultCode)
void OpenSLEngine::internalOpen()
{
SLresult resultCode;
// 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;
// Get interface finally
resultCode = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
CHECK_OPENSLES_ERROR;
ICELogInfo(<< "OpenSL engine object created.");
}
void OpenSLEngine::internalClose()
{
if (mEngineObject != nullptr)
{
ICELogInfo(<< "Destroy OpenSL engine object.");
(*mEngineObject)->Destroy(mEngineObject);
mEngineObject = nullptr;
mEngineInterface = nullptr;
}
}
SLEngineItf OpenSLEngine::getNativeEngine() const
{
return mEngineInterface;
}
static OpenSLEngine OpenSLEngineInstance;
OpenSLEngine& OpenSLEngine::instance()
{
return OpenSLEngineInstance;
}
// --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId)
{}
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} };
bool AndroidInputDevice::open()
{
if (active())
return true;
OpenSLEngine::instance().open();
// Probe few sampling rates
bool opened = false;
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
{
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();
}
}
mActive = 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;
}
Format AndroidInputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidInputDevice::active() const
{
return mActive;
}
bool AndroidInputDevice::fakeMode()
{
return false;
}
void AndroidInputDevice::setFakeMode(bool fakemode)
{}
int AndroidInputDevice::readBuffer(void* buffer)
{
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);
}
void AndroidInputDevice::internalOpen(int rateCode, int rate)
{
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 };
// 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 };
// 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 };
// 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 };
if (*OpenSLEngine::instance().getNativeEngine() == nullptr)
throw Exception(ERR_OPENSLES, -1);
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateAudioRecorder(
OpenSLEngine::instance().getNativeEngine(),
&mRecorderObject, &audioSource, &audioSink, 2, interfacesList, interfacesRequirements);
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;
// Get recorder interface
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_RECORD, &mRecorderInterface);
CHECK_OPENSLES_ERROR;
// Now buffer queue interface...
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
CHECK_OPENSLES_ERROR;
// 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;
// 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);
// Start finally
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
CHECK_OPENSLES_ERROR;
}
void AndroidInputDevice::internalClose()
{
if (!mRecorderObject)
return;
if (*mRecorderObject)
{
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);
}
(*mRecorderObject)->Destroy(mRecorderObject);
}
mRecorderObject = nullptr;
mRecorderInterface = nullptr;
mRecorderBufferInterface = nullptr;
mAndroidCfg = nullptr;
}
void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{
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)
{
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)
{
try
{
if (context)
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
}
catch(...)
{}
}
// ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId)
{
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
}
AndroidOutputDevice::~AndroidOutputDevice()
{
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
{
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;
}
}
if (opened)
ICELogInfo(<< "Speaker opened on rate " << mDeviceRate);
return opened;
}
void AndroidOutputDevice::close()
{
std::unique_lock<std::mutex> l(mMutex);
internalClose();
}
Format AndroidOutputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidOutputDevice::fakeMode()
{
return false;
}
void AndroidOutputDevice::setFakeMode(bool fakemode)
{
}
void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
{
mInShutdown = false;
SLresult resultCode;
SLuint32 channels = 1;
// 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;
// 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;
// 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 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 };
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);
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;
// 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;
// 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);
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;
}
void AndroidOutputDevice::internalClose()
{
if (mPlayer)
{
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);
}
}
}
// Clear buffer queue
ICELogInfo(<< "Clear player buffer queue");
(*mBufferQueue)->Clear(mBufferQueue);
ICELogInfo(<< "Destroy player object");
// Destroy player object
(*mPlayer)->Destroy(mPlayer);
mPlayer = nullptr;
mPlayerControl = nullptr;
mBufferQueue = nullptr;
mEffect = nullptr;
mAndroidConfig = nullptr;
}
}
if (mMixer)
{
if (*mMixer)
(*mMixer)->Destroy(mMixer);
mMixer = nullptr;
}
}
void AndroidOutputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{
if (mInShutdown)
{
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);
mBufferIndex++;
mBufferIndex %= AUDIO_SPK_BUFFER_COUNT;
}
void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
if (!context)
return;
try
{
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
}
catch(...)
{}
}
#endif // TARGET_ANDROID

View File

@ -0,0 +1,147 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_ANDROID_H
#define __AUDIO_ANDROID_H
#ifdef TARGET_ANDROID
#include "Audio_Interface.h"
#include "Audio_Helper.h"
#include "Audio_Resampler.h"
#include "Audio_DataWindow.h"
#include "../Helper/HL_Pointer.h"
#include "../Helper/HL_ByteBuffer.h"
#include "../Helper/HL_Exception.h"
#include <memory>
#include <string>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <SLES/OpenSLES_AndroidConfiguration.h>
namespace Audio
{
class AndroidEnumerator: public Enumerator
{
public:
AndroidEnumerator();
~AndroidEnumerator();
void open(int direction);
void close();
int count();
std::string nameAt(int index);
int idAt(int index);
int indexOfDefaultDevice();
protected:
};
class AndroidInputDevice: public InputDevice
{
public:
AndroidInputDevice(int devId);
~AndroidInputDevice();
bool open();
void close();
Format getFormat();
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;
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);
};
class AndroidOutputDevice: public OutputDevice
{
public:
AndroidOutputDevice(int devId);
~AndroidOutputDevice();
bool open();
void close();
Format getFormat();
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;
DataWindow mPlayBuffer;
int mBufferIndex = 0, mBufferSize = 0;
bool mInShutdown = false;
void internalOpen(int rateId, int rate, bool voice);
void internalClose();
void handleCallback(SLAndroidSimpleBufferQueueItf bq);
static void DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
};
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;
SLEngineItf getNativeEngine() const;
static OpenSLEngine& instance();
protected:
std::mutex mMutex;
int mUsageCounter = 0;
SLObjectItf mEngineObject = nullptr;
SLEngineItf mEngineInterface = nullptr;
void internalOpen();
void internalClose();
};
}
#endif // TARGET_ANDROID
#endif // __AUDIO_ANDROID_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,196 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_COREAUDIO_H
#define __AUDIO_COREAUDIO_H
#ifdef TARGET_OSX
#include "Audio_Interface.h"
#include "Audio_Helper.h"
#include "Audio_Resampler.h"
#include "Audio_DataWindow.h"
#include "../Helper/HL_Pointer.h"
#include "../Helper/HL_ByteBuffer.h"
#include "../Helper/HL_Exception.h"
#include <AudioToolbox/AudioQueue.h>
// Define CoreAudio buffer time length in milliseconds
#define COREAUDIO_BUFFER_TIME 20
namespace Audio
{
class AudioException: public Exception
{
public:
AudioException(int code, OSStatus subcode)
:Exception(code, int(subcode))
{}
};
//#ifndef AudioDeviceID
//# define AudioDeviceID unsigned
//#endif
class MacEnumerator: public Enumerator
{
public:
MacEnumerator();
~MacEnumerator();
void open(int direction);
void close();
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);
};
class CoreAudioUnit
{
public:
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();
protected:
AudioUnit mUnit;
};
class MacDevice
{
public:
MacDevice(int devId);
~MacDevice();
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);
protected:
AudioDeviceID mDeviceId;
bool mCapture, mRender;
bool mActive;
int mUsageCount;
Mutex mGuard;
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);
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);
#ifdef TARGET_IOS
static void propListener(void *inClientData,
AudioSessionPropertyID inID,
UInt32 inDataSize,
const void * inData);
static void interruptionListener(void *inClientData, UInt32 inInterruption);
#endif
};
typedef SharedPtr<MacDevice> PMacDevice;
class MacInputDevice: public InputDevice
{
public:
MacInputDevice(int devId);
~MacInputDevice();
bool open();
void close();
Format getFormat();
bool fakeMode();
void setFakeMode(bool fakemode);
int readBuffer(void* buffer);
protected:
PMacDevice mDevice;
};
class MacOutputDevice: public OutputDevice
{
public:
MacOutputDevice(int devId);
~MacOutputDevice();
bool open();
void close();
Format getFormat();
bool fakeMode();
void setFakeMode(bool fakemode);
protected:
PMacDevice mDevice;
};
}
#endif // TARGET_OSX
#endif // __AUDIO_COREAUDIO_H

View File

@ -0,0 +1,171 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Audio_DataWindow.h"
using namespace Audio;
DataWindow::DataWindow()
{
mFilled = 0;
mData = NULL;
mCapacity = 0;
}
DataWindow::~DataWindow()
{
if (mData)
free(mData);
}
void DataWindow::setCapacity(int capacity)
{
Lock l(mMutex);
int tail = capacity - mCapacity;
mData = (char*)realloc(mData, capacity);
if (tail > 0)
memset(mData + mCapacity, 0, tail);
mCapacity = capacity;
}
void DataWindow::addZero(int length)
{
Lock l(mMutex);
if (length > mCapacity)
length = mCapacity;
int avail = mCapacity - mFilled;
if (avail < length)
{
memmove(mData, mData + length - avail, mFilled - (length - avail));
mFilled -= length - avail;
}
memset(mData + mFilled, 0, length);
mFilled += length;
}
void DataWindow::add(const void* data, int length)
{
Lock l(mMutex);
if (length > mCapacity)
{
data = (char*)data + length - mCapacity;
length = mCapacity;
}
int avail = mCapacity - mFilled;
if (avail < length)
{
memmove(mData, mData + length - avail, mFilled - (length - avail));
mFilled -= length - avail;
}
memcpy(mData + mFilled, data, length);
mFilled += length;
}
void DataWindow::add(short sample)
{
add(&sample, sizeof sample);
}
void DataWindow::erase(int length)
{
Lock l(mMutex);
if (length > mFilled)
length = mFilled;
if (length != mFilled)
memmove(mData, mData + length, mFilled - length);
mFilled -= length;
}
const char* DataWindow::data() const
{
return mData;
}
char* DataWindow::mutableData()
{
return mData;
}
void DataWindow::clear()
{
Lock l(mMutex);
mFilled = 0;
}
short DataWindow::shortAt(int index) const
{
Lock l(mMutex);
assert(index < mFilled / 2);
return ((short*)mData)[index];
}
void DataWindow::setShortAt(short value, int index)
{
Lock l(mMutex);
assert(index < mFilled / 2);
((short*)mData)[index] = value;
}
int DataWindow::read(void* buffer, int length)
{
Lock l(mMutex);
if (length > mFilled)
length = mFilled;
if (length)
{
if (buffer)
memcpy(buffer, mData, length);
if (length < mFilled)
memmove(mData, mData+length, mFilled - length);
mFilled -= length;
}
return length;
}
int DataWindow::filled() const
{
Lock l(mMutex);
return mFilled;
}
void DataWindow::setFilled(int filled)
{
Lock l(mMutex);
mFilled = filled;
}
int DataWindow::capacity() const
{
Lock l(mMutex);
return mCapacity;
}
void DataWindow::zero(int length)
{
Lock l(mMutex);
assert(length <= mCapacity);
mFilled = length;
memset(mData, 0, mFilled);
}
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
{
Lock lockDst(dst.mMutex), lockSrc(src.mMutex);
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];
dst.mFilled = src.filled() * 2;
}

View File

@ -0,0 +1,47 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_BUFFER_H
#define __AUDIO_BUFFER_H
#include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Sync.h"
namespace Audio
{
class DataWindow
{
public:
DataWindow();
~DataWindow();
void setCapacity(int capacity);
int capacity() const;
void addZero(int length);
void add(const void* data, int length);
void add(short sample);
int read(void* buffer, int length);
void erase(int length = -1);
const char* data() const;
char* mutableData();
int filled() const;
void setFilled(int filled);
void clear();
short shortAt(int index) const;
void setShortAt(short value, int index);
void zero(int length);
static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
protected:
mutable Mutex mMutex;
char* mData;
int mFilled;
int mCapacity;
};
}
#endif

View File

@ -0,0 +1,239 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define NOMINMAX
#include "Audio_DevicePair.h"
#include <algorithm>
#define LOG_SUBSYSTEM "Audio"
using namespace Audio;
// --- DevicePair ---
DevicePair::DevicePair(bool aec, bool agc)
:mConfig(NULL), mDelegate(NULL), mAec(aec), mAgc(agc), mAecFilter(AUDIO_MIC_BUFFER_LENGTH*10, AUDIO_MIC_BUFFER_LENGTH, AUDIO_SAMPLERATE), mAgcFilter(AUDIO_CHANNELS)
{
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(NULL);
mInput.reset();
}
if (mOutput)
{
if (mOutput->connection() == this)
mOutput->setConnection(NULL);
mOutput.reset();
}
}
VariantMap* DevicePair::config()
{
return mConfig;
}
void DevicePair::setConfig(VariantMap* config)
{
mConfig = config;
}
PInputDevice DevicePair::input()
{
return mInput;
}
void DevicePair::setInput(PInputDevice input)
{
if (mInput == input)
return;
mInput = input;
mInput->setConnection(this);
if (mDelegate)
mDelegate->deviceChanged(this);
}
POutputDevice DevicePair::output()
{
return mOutput;
}
void DevicePair::setOutput(POutputDevice output)
{
if (output == mOutput)
return;
mOutput = output;
mOutput->setConnection(this);
if (mDelegate)
mDelegate->deviceChanged(this);
}
bool DevicePair::start()
{
bool result = false;
if (mInput)
result = mInput->open();
if (mOutput && result)
result &= mOutput->open();
return result;
}
void DevicePair::stop()
{
if (mInput)
mInput->close();
if (mOutput)
mOutput->close();
}
void DevicePair::setDelegate(Delegate* dc)
{
mDelegate = dc;
}
DevicePair::Delegate* DevicePair::delegate()
{
return mDelegate;
}
Player& DevicePair::player()
{
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);
#endif
// 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);
for (int blockIndex = 0; blockIndex < blocks; blockIndex++)
{
int wasProcessed = 0;
int 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));
}
}
void DevicePair::onSpkData(const Format& f, void* buffer, int length)
{
//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++)
{
memset(mOutput10msBuffer.mutableData(), 0, (size_t)mOutput10msBuffer.capacity());
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
int wasProcessed = 0;
int wasProduced = mSpkResampler.resample(AUDIO_SAMPLERATE, 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);
#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);
}
//ICELogMedia(<< "Audio::DevicePair::onSpkData() end")
}
void DevicePair::processMicData(const Format& f, void* buffer, int length)
{
if (mAgc)
mAgcFilter.process(buffer, length);
if (mAec)
mAecFilter.fromMic(buffer);
if (mDelegate)
mDelegate->onMicData(f, buffer, length);
}

View File

@ -0,0 +1,81 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_DEVICEPAIR_H
#define __AUDIO_DEVICEPAIR_H
#include "Audio_Interface.h"
#include "Audio_Player.h"
#include "Audio_Resampler.h"
#include "Audio_DataWindow.h"
//#define DUMP_NATIVEOUTPUT
//#define DUMP_NATIVEINPUT
namespace Audio
{
class DevicePair: protected DataConnection
{
public:
class Delegate: public DataConnection
{
public:
virtual void deviceChanged(DevicePair* dpair) = 0;
};
DevicePair(bool aec = true, bool agc = true);
virtual ~DevicePair();
void setAec(bool aec);
bool aec();
void setAgc(bool agc);
bool agc();
VariantMap* config();
void setConfig(VariantMap* config);
PInputDevice input();
void setInput(PInputDevice input);
POutputDevice output();
void setOutput(POutputDevice output);
bool start();
void stop();
void setDelegate(Delegate* dc);
Delegate* delegate();
Player& player();
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;
#ifdef DUMP_NATIVEOUTPUT
std::shared_ptr<WavFileWriter> mNativeOutputDump;
#endif
#ifdef DUMP_NATIVEINPUT
std::shared_ptr<WavFileWriter> mNativeInputDump;
#endif
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;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,187 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_DSOUND_H
#define __AUDIO_DSOUND_H
#include "../config.h"
#include <winsock2.h>
#include <windows.h>
#include <mmsystem.h>
#include "../Helper/HL_Sync.h"
#include "../Helper/HL_ByteBuffer.h"
#include "Audio_WavFile.h"
#include "Audio_Interface.h"
#include "Audio_Helper.h"
#include <deque>
#include <EndpointVolume.h>
#include <MMDeviceAPI.h>
#if defined(_MSC_VER)
# include <Functiondiscoverykeys_devpkey.h>
#endif
#include <vector>
#include <string>
#include <InitGuid.h>
#include <dsound.h>
namespace Audio
{
class VistaEnumerator: public Enumerator
{
public:
VistaEnumerator();
~VistaEnumerator();
void open(int direction);
void close();
int count();
std::tstring nameAt(int index);
int idAt(int index);
int indexOfDefaultDevice();
protected:
IMMDeviceCollection* mCollection;
IMMDevice* mDefaultDevice;
IMMDeviceEnumerator* mEnumerator;
EDataFlow mDirection;
std::vector<std::wstring> mNameList;
void enumerate();
IMMDevice* mapIndexToInterface(int index);
};
class XpEnumerator: public Enumerator
{
public:
XpEnumerator();
~XpEnumerator();
void open(int direction);
void close();
int count();
std::tstring nameAt(int index);
int idAt(int index);
int indexOfDefaultDevice();
protected:
std::vector<std::wstring> mNameList;
int mDirection;
};
class DSoundHelper
{
public:
static void checkComResult(HRESULT code);
static GUID deviceId2Guid(int deviceId, bool captureDevice);
};
#if !defined(_MSC_VER)
typedef struct IDirectSoundNotify8 *LPDIRECTSOUNDNOTIFY8;
#endif
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);
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
int mBufferIndex;
int mNextBuffer;
GUID mGUID;
HANDLE mThreadHandle;
HANDLE mShutdownSignal;
volatile bool mSimulate; /// Marks if simulate mode is active.
int mRefCount;
ByteBuffer mQueue;
unsigned mReadOffset;
DenoiseFilter mDenoiser;
volatile bool mEnableDenoiser;
char mTempBuffer[AUDIO_MIC_BUFFER_SIZE];
StubTimer mNullAudio;
#ifdef AUDIO_DUMPINPUT
WavFileWriter mDump;
#endif
bool tryReadBuffer(void* buffer);
void openDevice();
void closeDevice();
static void threadProc(void* arg);
};
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();
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;
void openDevice();
void closeDevice();
void restoreBuffer();
bool process();
bool getMediaFrame();
static void threadProc(void* arg);
};
}
#endif

View File

@ -0,0 +1,148 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef TARGET_WIN
# include <WinSock2.h>
#endif
#include "Audio_Helper.h"
#include "../helper/HL_Exception.h"
using namespace Audio;
// --- QPCSource
TimeSource::TimeSource(int quantTime, int nrOfQuants)
{
#ifdef TARGET_WIN
mCounter.QuadPart = 0;
#endif
#if defined(TARGET_OSX) || defined(TARGET_IOS)
mach_timebase_info(&mTimebase);
mRatio = ((double)mTimebase.numer / (double)mTimebase.denom) / 1000000;
#endif
mQuantTime = quantTime;
mDepthTime = quantTime * nrOfQuants;
mTailTime = 0;
}
TimeSource::~TimeSource()
{
}
void TimeSource::start()
{
#ifdef TARGET_WIN
if (!QueryPerformanceFrequency(&mFreq))
throw Exception(ERR_QPC, GetLastError());
if (!QueryPerformanceCounter(&mCounter))
throw Exception(ERR_QPC, GetLastError());
#endif
}
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;
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 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);
#endif
}
// --- StubTimer ---
StubTimer::StubTimer(int bufferTime, int bufferCount)
:mBufferTime(bufferTime), mBufferCount(bufferCount), mTimeSource(bufferTime, bufferCount), mActive(false)
{
#ifdef TARGET_WIN
mStubSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
}
StubTimer::~StubTimer()
{
#ifdef TARGET_WIN
::CloseHandle(mStubSignal);
#endif
}
void StubTimer::start()
{
mTimeSource.start();
mCurrentTime = mTimeSource.time();
mActive = true;
}
void StubTimer::stop()
{
mTimeSource.stop();
mActive = false;
}
void StubTimer::waitForBuffer()
{
if (!mActive)
start();
unsigned t = mTimeSource.time();
while (!t)
{
#ifdef TARGET_WIN
::WaitForSingleObject(mStubSignal, mBufferTime);
#endif
#if defined(TARGET_OSX) || defined(TARGET_IOS)
usleep(100);
#endif
t = mTimeSource.time();
}
}

View File

@ -0,0 +1,78 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_HELPER_H
#define __AUDIO_HELPER_H
#ifdef TARGET_WIN
#include <EndpointVolume.h>
#include <MMDeviceAPI.h>
#if defined(_MSC_VER)
# 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>
#endif
#include <vector>
#include "Audio_Interface.h"
namespace Audio
{
class TimeSource
{
protected:
#ifdef TARGET_WIN
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;
struct mach_timebase_info mTimebase;
double mRatio;
#endif
unsigned mQuantTime; /// Used time quants length in milliseconds.
unsigned mDepthTime; /// Number of available time quants.
unsigned mTailTime; /// Not-accounted milliseconds.
public:
TimeSource(int quantTime, int nrOfQuants);
~TimeSource();
void start();
void stop();
unsigned time();
};
class StubTimer
{
public:
StubTimer(int bufferTime, int bufferCount);
~StubTimer();
void start();
void stop();
void waitForBuffer();
protected:
unsigned mBufferTime;
unsigned mBufferCount;
unsigned mCurrentTime;
TimeSource mTimeSource;
#ifdef TARGET_WIN
HANDLE mStubSignal;
#endif
bool mActive;
};
}
#endif

View File

@ -0,0 +1,153 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Audio_Interface.h"
#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
#endif
#include "Audio_Helper.h"
#include "Audio_Null.h"
using namespace Audio;
Device::Device()
:mConnection(nullptr)
{
}
Device::~Device()
{
}
void Device::setConnection(DataConnection* connection)
{
mConnection = connection;
}
DataConnection* Device::connection()
{
return mConnection;
}
InputDevice::InputDevice()
{
}
InputDevice::~InputDevice()
{
}
InputDevice* InputDevice::make(int devId)
{
#if defined(USE_NULL_AUDIO)
return new NullInputDevice();
#else
#if defined(TARGET_WIN) && defined(_MSC_VER)
// return new WmmeInputDevice(index);
return new DSoundInputDevice(DSoundHelper::deviceId2Guid(devId, true));
#endif
#ifdef TARGET_OSX
return new MacInputDevice(devId);
#endif
#ifdef TARGET_ANDROID
return new AndroidInputDevice(devId);
#endif
#endif
return nullptr;
}
OutputDevice::OutputDevice()
{
}
OutputDevice::~OutputDevice()
{
}
OutputDevice* OutputDevice::make(int devId)
{
#if defined(USE_NULL_AUDIO)
return new NullOutputDevice();
#else
#if defined(TARGET_WIN)
//return new WmmeOutputDevice(index);
return new DSoundOutputDevice(DSoundHelper::deviceId2Guid(devId, false));
#endif
#ifdef TARGET_OSX
return new MacOutputDevice(devId);
#endif
#ifdef TARGET_ANDROID
return new AndroidOutputDevice(devId);
#endif
#endif
return nullptr;
}
// --- Enumerator ---
Enumerator::Enumerator()
{
}
Enumerator::~Enumerator()
{
}
int Enumerator::nameToIndex(const std::tstring& name)
{
for (int i = 0; i < count(); i++)
if (nameAt(i) == name)
return i;
return -1;
}
Enumerator* Enumerator::make(bool useNull)
{
if (useNull)
return new NullEnumerator();
#ifndef USE_NULL_AUDIO
#ifdef TARGET_WIN
if (winVersion() > Win_Xp)
return new VistaEnumerator();
else
return new XpEnumerator();
#endif
#ifdef TARGET_OSX
return new MacEnumerator();
#endif
#endif
return new NullEnumerator();
}
// ----- OsEngine ------------
OsEngine* OsEngine::instance()
{
#ifdef USE_NULL_AUDIO
return nullptr;
#endif
#ifdef TARGET_ANDROID
return &OpenSLEngine::instance();
#endif
return nullptr;
}

View File

@ -0,0 +1,135 @@
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_INTERFACE_H
#define __AUDIO_INTERFACE_H
#include <string>
#include "../config.h"
#include "../helper/HL_Types.h"
#include "../helper/HL_VariantMap.h"
#include "../helper/HL_Pointer.h"
#include "Audio_WavFile.h"
#include "Audio_Quality.h"
namespace Audio
{
enum
{
myMicrophone = 1,
mySpeaker = 2
};
struct Format
{
int mRate;
int mChannels;
Format()
:mRate(AUDIO_SAMPLERATE), mChannels(AUDIO_CHANNELS)
{}
Format(int rate, int channels)
:mRate(rate), mChannels(channels)
{}
int samplesFromSize(int length) const
{
return length / 2 / mChannels;
}
// Returns milliseconds
float timeFromSize(int length) const
{
return float(samplesFromSize(length) / (mRate / 1000.0));
}
float sizeFromTime(int milliseconds) const
{
return float((milliseconds * mRate) / 500.0 * mChannels);
}
std::string toString()
{
char buffer[64];
sprintf(buffer, "%dHz %dch", mRate, mChannels);
return std::string(buffer);
}
};
class DataConnection
{
public:
virtual void onMicData(const Format& format, const void* buffer, int length) = 0;
virtual void onSpkData(const Format& format, void* buffer, int length) = 0;
};
class Device
{
public:
Device();
virtual ~Device();
void setConnection(DataConnection* connection);
DataConnection* connection();
virtual bool open() = 0;
virtual void close() = 0;
virtual Format getFormat() = 0;
protected:
DataConnection* mConnection;
};
class InputDevice: public Device
{
public:
InputDevice();
virtual ~InputDevice();
static InputDevice* make(int devId);
};
typedef std::shared_ptr<InputDevice> PInputDevice;
class OutputDevice: public Device
{
public:
OutputDevice();
virtual ~OutputDevice();
static OutputDevice* make(int devId);
};
typedef std::shared_ptr<OutputDevice> POutputDevice;
class Enumerator
{
public:
Enumerator();
virtual ~Enumerator();
int nameToIndex(const std::tstring& name);
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);
};
class OsEngine
{
public:
virtual void open() = 0;
virtual void close() = 0;
static OsEngine* instance();
};
};
#endif

View File

@ -0,0 +1,330 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h"
#include "../helper/HL_Exception.h"
#include "Audio_Mixer.h"
#include <algorithm>
#include "../helper/HL_Log.h"
#define LOG_SUBSYSTEM "Mixer"
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 = NULL;
mSSRC = 0;
mFadeOutCounter = 0;
mData.setCapacity(AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT);
}
Mixer::Stream::~Stream()
{
}
void Mixer::Stream::setSsrc(unsigned ssrc)
{
mSSRC = ssrc;
}
unsigned Mixer::Stream::ssrc()
{
return mSSRC;
}
void Mixer::Stream::setContext(void* context)
{
mContext = context;
}
void* Mixer::Stream::context()
{
return mContext;
}
DataWindow& Mixer::Stream::data()
{
return mData;
}
bool Mixer::Stream::active()
{
return mActive;
}
void Mixer::Stream::setActive(bool active)
{
mActive = active;
}
void Mixer::Stream::addPcm(int rate, const void* input, int length)
{
// Resample to internal sample rate
unsigned outputSize = unsigned(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));
int 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);
}
Mixer::~Mixer()
{
}
void Mixer::unregisterChannel(void* channel)
{
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
{
Stream& c = mChannelList[i];
if (c.active() && c.context() == channel)
{
c.setActive(false); // stream is not active anymore
c.data().clear(); // clear data
mActiveCounter--;
}
}
}
void Mixer::clear(void* context, unsigned ssrc)
{
for (int i=0; i<AUDIO_MIX_CHANNEL_COUNT; i++)
{
Stream& c = mChannelList[i];
if (c.active() && c.context() == context && c.ssrc() == ssrc)
{
c.setActive(false);
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())
{
channel->setSsrc(ssrc);
channel->setContext(context);
channel->data().clear();
mActiveCounter++;
channel->setActive(true);
return channel;
}
}
return NULL;
}
void Mixer::addPcm(void* context, unsigned ssrc,
const void* inputData, int inputLength,
int inputRate, bool fadeOut)
{
assert(inputRate == 8000 || inputRate == 16000 || inputRate == 32000);
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);
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);
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);
if (!channel)
throw Exception(ERR_MIXER_OVERFLOW);
}
channel->addPcm(rate, w.data(), w.filled());
//ICELogCritical(<<"Mixer stream " << int(this) << " has " << w.filled() << " bytes");
}
void Mixer::mix()
{
// Current sample
int sample = 0;
// Counter of processed active channels
int processed = 0;
// Samples & sources counters
unsigned sampleCounter = 0, sourceCounter;
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)
{
//ICELogCritical(<< "No active channel");
return;
}
// Optimized versions for 1& 2 active channels
if (activeCounter == 1)
{
// Copy much samples as we have
Stream& audio = *channelList[0];
mOutput.add(audio.data().data(), audio.data().filled());
audio.data().erase(audio.data().filled());
//ICELogCritical(<<"Length of mixer stream " << audio.data().filled());
}
else
if (activeCounter == 2)
{
Stream& audio1 = *channelList[0];
Stream& audio2 = *channelList[1];
int filled1 = audio1.data().filled() / 2, filled2 = audio2.data().filled() / 2;
int available = filled1 > filled2 ? filled1 : filled2;
// Find how much samples can be mixed
int filled = mOutput.filled() / 2;
int maxsize = mOutput.capacity() / 2;
if (maxsize - filled < available)
available = maxsize - filled;
short sample = 0;
for (int i=0; i<available; i++)
{
short sample1 = filled1 > i ? audio1.data().shortAt(i) : 0;
short sample2 = filled2 > i ? audio2.data().shortAt(i) : 0;
sample = (abs(sample1) > abs(sample2)) ? sample1 : sample2;
mOutput.add(sample);
}
audio1.data().erase(available*2);
audio2.data().erase(available*2);
}
else
{
do
{
sample = 0;
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)
{
if (mOutput.filled() < outputLength)
mix();
//ICELogCritical(<<"Mixer has " << mOutput.filled() << " available bytes");
memset(outputData, 0, outputLength);
return mOutput.read(outputData, outputLength);
}
int Mixer::mixAndGetPcm(Audio::DataWindow& output)
{
// Mix
mix();
// Set output space
output.setCapacity(mOutput.filled());
// Read mixed data to output
return mOutput.read(output.mutableData(), output.capacity());
}
int Mixer::available()
{
return mOutput.filled();
}

View File

@ -0,0 +1,72 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _RX_MIXER_H
#define _RX_MIXER_H
#include "../config.h"
#include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Sync.h"
#include "Audio_Resampler.h"
#include "Audio_DataWindow.h"
#include <map>
#include <atomic>
namespace Audio
{
class Mixer
{
protected:
class Stream
{
protected:
DataWindow mData;
Resampler mResampler8,
mResampler16,
mResampler32,
mResampler48;
bool mActive;
void* mContext;
unsigned mSSRC;
unsigned mFadeOutCounter;
ByteBuffer mTempBuffer;
public:
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);
};
Stream mChannelList[AUDIO_MIX_CHANNEL_COUNT];
Mutex mMutex;
DataWindow mOutput;
std::atomic_int mActiveCounter;
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
#endif

View File

@ -0,0 +1,177 @@
#include "Audio_Null.h"
#include "helper/HL_Log.h"
#define LOG_SUBSYSTEM "NULL audio"
using namespace Audio;
NullTimer::NullTimer(int interval, Delegate *delegate, const char* name)
:mShutdown(false), mDelegate(delegate), mInterval(interval), mThreadName(name)
{
start();
}
NullTimer::~NullTimer()
{
stop();
}
void NullTimer::start()
{
mShutdown = false;
mWorkerThread = std::thread(&NullTimer::run, this);
}
void NullTimer::stop()
{
mShutdown = true;
if (mWorkerThread.joinable())
mWorkerThread.join();
}
void NullTimer::run()
{
mTail = 0;
while (!mShutdown)
{
// Get current timestamp
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
while (mTail >= mInterval * 1000)
{
if (mDelegate)
mDelegate->onTimerSignal(*this);
mTail -= mInterval * 1000;
}
// Sleep for mInterval - mTail milliseconds
std::this_thread::sleep_for(std::chrono::microseconds(mInterval * 1000 - mTail));
mTail += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - timestamp).count();
}
}
// --------------------- NullInputDevice -------------------------
NullInputDevice::NullInputDevice()
:mBuffer(nullptr)
{
}
NullInputDevice::~NullInputDevice()
{
close();
}
bool NullInputDevice::open()
{
mBuffer = malloc(AUDIO_MIC_BUFFER_SIZE);
memset(mBuffer, 0, AUDIO_MIC_BUFFER_SIZE);
mTimeCounter = 0; mDataCounter = 0;
// Creation of timer starts it also. So first onTimerSignal can come even before open() returns.
mTimer = std::make_shared<NullTimer>(AUDIO_MIC_BUFFER_LENGTH, this, "NullMicrophoneThread");
return true;
}
void NullInputDevice::close()
{
mTimer.reset();
if (mBuffer)
{
free(mBuffer);
mBuffer = nullptr;
}
ICELogInfo(<<"Pseudocaptured " << mTimeCounter << " milliseconds , " << mDataCounter << " bytes.");
}
Format NullInputDevice::getFormat()
{
assert (Format().sizeFromTime(AUDIO_MIC_BUFFER_LENGTH) == AUDIO_MIC_BUFFER_SIZE);
return Format();
}
void NullInputDevice::onTimerSignal(NullTimer& timer)
{
mTimeCounter += AUDIO_MIC_BUFFER_LENGTH;
mDataCounter += AUDIO_MIC_BUFFER_SIZE;
if (mConnection)
mConnection->onMicData(getFormat(), mBuffer, AUDIO_MIC_BUFFER_SIZE);
}
// --------------------- NullOutputDevice --------------------------
NullOutputDevice::NullOutputDevice()
:mBuffer(nullptr)
{
}
NullOutputDevice::~NullOutputDevice()
{
close();
}
bool NullOutputDevice::open()
{
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>(AUDIO_SPK_BUFFER_LENGTH, this, "NullSpeakerThread");
return true;
}
void NullOutputDevice::close()
{
mTimer.reset();
free(mBuffer); mBuffer = nullptr;
ICELogInfo(<< "Pseudoplayed " << mTimeCounter << " milliseconds, " << mDataCounter << " bytes.");
}
Format NullOutputDevice::getFormat()
{
assert (Format().sizeFromTime(AUDIO_SPK_BUFFER_LENGTH) == AUDIO_SPK_BUFFER_SIZE);
return Format();
}
void NullOutputDevice::onTimerSignal(NullTimer &timer)
{
mTimeCounter += AUDIO_SPK_BUFFER_LENGTH;
mDataCounter += AUDIO_SPK_BUFFER_SIZE;
if (mConnection)
mConnection->onSpkData(getFormat(), mBuffer, AUDIO_SPK_BUFFER_SIZE);
}
// ---------------------- NullEnumerator --------------------------
NullEnumerator::NullEnumerator()
{}
NullEnumerator::~NullEnumerator()
{}
void NullEnumerator::open(int direction)
{}
void NullEnumerator::close()
{}
int NullEnumerator::count()
{
return 1;
}
std::tstring NullEnumerator::nameAt(int index)
{
#if defined(TARGET_WIN)
return L"null";
#else
return "null";
#endif
}
int NullEnumerator::idAt(int index)
{
return 0;
}
int NullEnumerator::indexOfDefaultDevice()
{
return 0;
}

View File

@ -0,0 +1,86 @@
#ifndef __AUDIO_NULL_H
#define __AUDIO_NULL_H
#include <thread>
#include "Audio_Interface.h"
namespace Audio
{
class NullTimer
{
public:
class Delegate
{
public:
virtual void onTimerSignal(NullTimer& timer) = 0;
};
protected:
std::thread mWorkerThread;
volatile bool mShutdown;
Delegate* mDelegate;
int mInterval, // Interval - wanted number of milliseconds
mTail; // Number of milliseconds that can be sent immediately to sink
std::string mThreadName;
void start();
void stop();
void run();
public:
/* Interval is in milliseconds. */
NullTimer(int interval, Delegate* delegate, const char* name = nullptr);
~NullTimer();
};
class NullInputDevice: public InputDevice, public NullTimer::Delegate
{
protected:
void* mBuffer = nullptr;
std::shared_ptr<NullTimer> mTimer;
int64_t mTimeCounter = 0, mDataCounter = 0;
public:
NullInputDevice();
virtual ~NullInputDevice();
bool open() override;
void close() override;
Format getFormat() override;
void onTimerSignal(NullTimer& timer) override;
};
class NullOutputDevice: public OutputDevice, public NullTimer::Delegate
{
protected:
std::shared_ptr<NullTimer> mTimer;
void* mBuffer = nullptr;
int64_t mDataCounter = 0, mTimeCounter = 0;
public:
NullOutputDevice();
virtual ~NullOutputDevice();
bool open() override;
void close() override;
Format getFormat() override;
void onTimerSignal(NullTimer& timer) override;
};
class NullEnumerator: public Enumerator
{
public:
NullEnumerator();
~NullEnumerator();
void open(int direction) override;
void close() override;
int count() override;
std::tstring nameAt(int index) override;
int idAt(int index) override;
int indexOfDefaultDevice() override;
};
}
#endif

View File

@ -0,0 +1,167 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Audio_Player.h"
using namespace Audio;
// -------------- Player -----------
Player::Player()
:mDelegate(NULL), mPlayedTime(0)
{
}
Player::~Player()
{
}
void Player::setDelegate(EndOfAudioDelegate* d)
{
mDelegate = d;
}
Player::EndOfAudioDelegate* Player::getDelegate() const
{
return mDelegate;
}
void Player::setOutput(POutputDevice output)
{
mOutput = output;
if (mOutput)
mOutput->setConnection(this);
}
POutputDevice Player::getOutput() const
{
return mOutput;
}
void Player::onMicData(const Format& f, const void* buffer, int length)
{
// 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);
// 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)
{
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));
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;
}
void Player::obtain(int usage)
{
Lock l(mGuard);
UsageMap::iterator usageIter = mUsage.find(usage);
if (usageIter == mUsage.end())
mUsage[usage] = 1;
else
usageIter->second = usageIter->second + 1;
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;
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);
if (mUsage.empty() && mOutput)
mOutput->close();
}
int Player::releasePlayed()
{
Lock l(mGuard);
int result = mFinishedUsages.size();
while (mFinishedUsages.size())
{
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);
obtain(usageId);
}
void Player::clear()
{
Lock l(mGuard);
while (mPlaylist.size())
onFilePlayed();
}
void Player::retrieveUsageIds(std::vector<int>& ids)
{
ids.assign(mFinishedUsages.begin(), mFinishedUsages.end());
mFinishedUsages.clear();
}

View File

@ -0,0 +1,67 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_PLAYER_H
#define __AUDIO_PLAYER_H
#include "../helper/HL_Log.h"
#include "../helper/HL_Sync.h"
#include "Audio_Interface.h"
#include <deque>
#include <map>
#include <vector>
namespace Audio
{
class Player: public DataConnection
{
friend class DevicePair;
public:
struct PlaylistItem
{
PWavFileReader mFile;
bool mLoop;
int mTimelength;
int mUsageId;
};
typedef std::deque<PlaylistItem> Playlist;
class EndOfAudioDelegate
{
public:
virtual void onFilePlayed(PlaylistItem& item) = 0;
};
protected:
typedef std::map<int, int> UsageMap;
Audio::POutputDevice mOutput;
UsageMap mUsage; // References map
std::vector<int> mFinishedUsages; // Finished plays
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 scheduleRelease();
void obtain(int usageId);
public:
Player();
~Player();
void setDelegate(EndOfAudioDelegate* d);
EndOfAudioDelegate* getDelegate() 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);
};
}
#endif

View File

@ -0,0 +1,235 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h"
#include "Audio_Quality.h"
#include "../helper/HL_Exception.h"
#include "../helper/HL_Types.h"
#include "speex/speex_preprocess.h"
#ifdef WIN32
# include <malloc.h>
#endif
#include <assert.h>
#include <string.h>
using namespace Audio;
#define SHRT_MAX 32767 /* maximum (signed) short value */
AgcFilter::AgcFilter(int channels)
{
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;
c.mIpeak = (int)(SHRT_MAX * level * 65536);
c.mSilenceCounter = 0;
mChannelList.push_back(c);
}
}
AgcFilter::~AgcFilter()
{
}
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);
}
void AgcFilter::processChannel(short* pcm, int nrOfSamples, int channelIndex)
{
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)
{
/* update the max */
channel.mSampleMax = (unsigned int)sample;
}
channel.mCounter ++;
/* Will we get an overflow with the current gain factor? */
if (((sample * channel.mIgain) >> 16) > channel.mIpeak)
{
/* Yes: Calculate new gain. */
channel.mIgain = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16;
channel.mSilenceCounter = 0;
pcm[sampleIndex] = (short) ((pcm[sampleIndex] * channel.mIgain) >> 16);
continue;
}
/* Calculate new gain factor 10x per second */
if (channel.mCounter >= AUDIO_SAMPLERATE / 10)
{
if (channel.mSampleMax > AUDIO_SAMPLERATE / 10) /* speaking? */
{
gain_new = ((channel.mIpeak / channel.mSampleMax) * 62259) >> 16;
if (channel.mSilenceCounter > 40) /* pause -> speaking */
channel.mIgain += (gain_new - channel.mIgain) >> 2;
else
channel.mIgain += (gain_new - channel.mIgain) / 20;
channel.mSilenceCounter = 0;
}
else /* silence */
{
channel.mSilenceCounter++;
/* silence > 2 seconds: reduce gain */
if ((channel.mIgain > 65536) && (channel.mSilenceCounter >= 20))
channel.mIgain = (channel.mIgain * 62259) >> 16;
}
channel.mCounter = 0;
channel.mSampleMax = 1;
}
pcm[sampleIndex] = (short) ((pcm[sampleIndex] * channel.mIgain) >> 16);
}
}
// --- AecFilter ---
#ifdef USE_SPEEX_AEC
# include "speex/speex_echo.h"
#include "Audio_Interface.h"
#if !defined(TARGET_WIN)
# include <alloca.h>
#endif
#endif
#ifdef USE_WEBRTC_AEC
# include "aec/echo_cancellation.h"
#endif
#ifdef USE_WEBRTC_AEC
static void CheckWRACode(unsigned errorcode)
{
if (errorcode)
throw Exception(ERR_WEBRTC, errorcode);
}
#endif
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);
#endif
#ifdef USE_WEBRTC_AEC
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;
}
#endif
#ifdef USE_WEBRTC_AEC
CheckWRACode(WebRtcAec_Free(mCtx));
mCtx = NULL;
#endif
}
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);
#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));
#endif
}
void AecFilter::toSpeaker(void *data)
{
#ifdef USE_SPEEX_AEC
speex_echo_playback((SpeexEchoState*)mCtx, (short*)data);
#endif
#ifdef USE_WEBRTC_AEC
CheckWRACode(WebRtcAec_BufferFarend(mCtx, (short*)data, length / 2 / AUDIO_CHANNELS));
#endif
}
int AecFilter::frametime()
{
return mFrameTime;
}
DenoiseFilter::DenoiseFilter(int rate)
:mRate(rate)
{
mCtx = speex_preprocess_state_init(mRate/100, mRate);
}
DenoiseFilter::~DenoiseFilter()
{
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);
}
}
int DenoiseFilter::rate()
{
return mRate;
}

View File

@ -0,0 +1,69 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_QUALITY_H
#define __AUDIO_QUALITY_H
#include "../config.h"
#include "../helper/HL_Sync.h"
#include <vector>
namespace Audio
{
class AgcFilter
{
protected:
struct Channel
{
unsigned int mSampleMax;
int mCounter;
long mIgain;
int mIpeak;
int mSilenceCounter;
};
std::vector<Channel> mChannelList;
void processChannel(short* pcm, int nrOfSamples, int channelIndex);
public:
AgcFilter(int channels);
~AgcFilter();
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;
};
class DenoiseFilter
{
public:
DenoiseFilter(int rate);
~DenoiseFilter();
void fromMic(void* data, int timelength);
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)
};
}
#endif

View File

@ -0,0 +1,268 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h"
#include "Audio_Resampler.h"
#include <stdlib.h>
#include <assert.h>
#include <memory.h>
#include <algorithm>
#include "speex/speex_resampler.h"
using namespace Audio;
#define IS_FRACTIONAL_RATE(X) (((X) % 8000) != 0)
SpeexResampler::SpeexResampler()
:mContext(NULL), mErrorCode(0), mSourceRate(0), mDestRate(0), mLastSample(0)
{
}
void SpeexResampler::start(int channels, int sourceRate, int destRate)
{
if (mSourceRate == sourceRate && mDestRate == destRate && mContext)
return;
if (mContext)
stop();
mSourceRate = sourceRate;
mDestRate = destRate;
mChannels = channels;
if (sourceRate != destRate)
{
// Defer context creation until first request
//mContext = speex_resampler_init(channels, sourceRate, destRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
//assert(mContext != NULL);
}
}
void SpeexResampler::stop()
{
if (mContext)
{
speex_resampler_destroy((SpeexResamplerState*)mContext);
mContext = NULL;
}
}
SpeexResampler::~SpeexResampler()
{
stop();
}
int SpeexResampler::processBuffer(const void* src, int sourceLength, int& sourceProcessed, void* dest, int destCapacity)
{
assert(mSourceRate != 0 && mDestRate != 0);
if (mDestRate == mSourceRate)
{
assert(destCapacity >= sourceLength);
memcpy(dest, src, (size_t)sourceLength);
sourceProcessed = sourceLength;
return sourceLength;
}
if (!mContext)
{
mContext = speex_resampler_init(mChannels, mSourceRate, mDestRate, AUDIO_RESAMPLER_QUALITY, &mErrorCode);
if (!mContext)
return 0;
}
// Check if there is zero samples passed
if (sourceLength / (sizeof(short) * mChannels) == 0)
{
// Consume all data
sourceProcessed = sourceLength;
// But no output
return 0;
}
unsigned outLen = getDestLength(sourceLength);
if (outLen > (unsigned)destCapacity)
return 0; // Skip resampling if not enough space
assert((unsigned)destCapacity >= outLen);
// Calculate number of samples - input length is in bytes
unsigned inLen = sourceLength / (sizeof(short) * mChannels);
outLen /= sizeof(short) * mChannels;
assert(mContext != NULL);
int speexCode = speex_resampler_process_interleaved_int((SpeexResamplerState *)mContext, (spx_int16_t*)src, &inLen,
(spx_int16_t*)dest, &outLen);
assert(speexCode == RESAMPLER_ERR_SUCCESS);
// Return results in bytes
sourceProcessed = inLen * sizeof(short) * mChannels;
return outLen * sizeof(short) * mChannels;
}
int SpeexResampler::sourceRate()
{
return mSourceRate;
}
int SpeexResampler::destRate()
{
return mDestRate;
}
int SpeexResampler::getDestLength(int sourceLen)
{
return int(sourceLen * (float(mDestRate) / mSourceRate) + 0.5) / 2 * 2;
}
int SpeexResampler::getSourceLength(int destLen)
{
return int(destLen * (float(mSourceRate) / mDestRate) + 0.5) / 2 * 2;
}
// Returns instance + speex resampler size in bytes
int SpeexResampler::getSize() const
{
return sizeof(*this) + 200; // 200 is approximate size of speex resample structure
}
// -------------------------- ChannelConverter --------------------
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++)
{
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)
{
assert(destLength == sourceLength * 2);
const short* input = (const short*)source;
short* output = (short*)dest;
// Convert starting from the end of buffer to allow inplace conversion
for (int sampleIndex = sourceLength/2 - 1; sampleIndex >= 0; sampleIndex--)
{
output[2*sampleIndex] = output[2*sampleIndex+1] = input[sampleIndex];
}
return sourceLength * 2;
}
Resampler48kTo16k::Resampler48kTo16k()
{
WebRtcSpl_ResetResample48khzTo16khz(&mContext);
}
Resampler48kTo16k::~Resampler48kTo16k()
{
WebRtcSpl_ResetResample48khzTo16khz(&mContext);
}
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;
assert(inputLen % 480 == 0);
int frames = inputLen / 480;
for (int i=0; i<frames; i++)
WebRtcSpl_Resample48khzTo16khz(input + i * 480, output + i * 160, &mContext, mTemp);
return sourceLen / 3;
}
Resampler16kto48k::Resampler16kto48k()
{
WebRtcSpl_ResetResample16khzTo48khz(&mContext);
}
Resampler16kto48k::~Resampler16kto48k()
{
WebRtcSpl_ResetResample16khzTo48khz(&mContext);
}
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;
assert(inputLen % 160 == 0);
int frames = inputLen / 160;
for (int i=0; i<frames; i++)
WebRtcSpl_Resample16khzTo48khz(input + i * 160, output + i * 480, &mContext, mTemp);
return sourceLen * 3;
}
// ---------------- UniversalResampler -------------------
UniversalResampler::UniversalResampler()
{
}
UniversalResampler::~UniversalResampler()
{
}
int UniversalResampler::resample(int sourceRate, const void *sourceBuffer, int sourceLength, int& sourceProcessed, int destRate, void *destBuffer, int destCapacity)
{
assert(destBuffer && sourceBuffer);
int result;
if (sourceRate == destRate)
{
assert(destCapacity >= sourceLength);
memcpy(destBuffer, sourceBuffer, (size_t)sourceLength);
sourceProcessed = sourceLength;
result = sourceLength;
}
else
{
PResampler r = findResampler(sourceRate, destRate);
result = r->processBuffer(sourceBuffer, sourceLength, sourceProcessed, destBuffer, destCapacity);
}
return result;
}
void UniversalResampler::preload()
{
}
int UniversalResampler::getDestLength(int sourceRate, int destRate, int sourceLength)
{
if (sourceRate == destRate)
return sourceLength;
else
return findResampler(sourceRate, destRate)->getDestLength(sourceLength);
}
int UniversalResampler::getSourceLength(int sourceRate, int destRate, int destLength)
{
if (sourceRate == destRate)
return destLength;
else
return findResampler(sourceRate, destRate)->getSourceLength(destLength);
}
PResampler UniversalResampler::findResampler(int sourceRate, int destRate)
{
assert(sourceRate != destRate);
ResamplerMap::iterator resamplerIter = mResamplerMap.find(RatePair(sourceRate, destRate));
PResampler r;
if (resamplerIter == mResamplerMap.end())
{
r = PResampler(new Resampler());
r->start(AUDIO_CHANNELS, sourceRate, destRate);
mResamplerMap[RatePair(sourceRate, destRate)] = r;
}
else
r = resamplerIter->second;
return r;
}

View File

@ -0,0 +1,97 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_RESAMPLER_H
#define __AUDIO_RESAMPLER_H
#include "signal_processing_library/signal_processing_library.h"
#include "../helper/HL_Pointer.h"
#include <vector>
#include <memory>
namespace Audio
{
class SpeexResampler
{
public:
SpeexResampler();
~SpeexResampler();
void start(int channels, int sourceRate, int destRate);
void stop();
int processBuffer(const void* source, int sourceLength, int& sourceProcessed, void* dest, int destCapacity);
int sourceRate();
int destRate();
int getDestLength(int sourceLen);
int getSourceLength(int destLen);
// Returns instance + speex encoder size in bytes
int getSize() const;
protected:
void* mContext;
int mErrorCode;
int mSourceRate,
mDestRate,
mChannels;
short mLastSample;
};
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);
};
// Operates with AUDIO_CHANNELS number of channels
class UniversalResampler
{
public:
UniversalResampler();
~UniversalResampler();
int resample(int sourceRate, const void* sourceBuffer, int sourceLength, int& sourceProcessed, int destRate, void* destBuffer, int destCapacity);
int getDestLength(int sourceRate, int destRate, int sourceLength);
int getSourceLength(int sourceRate, int destRate, int destLength);
protected:
typedef std::pair<int, int> RatePair;
typedef std::map<RatePair, PResampler> ResamplerMap;
ResamplerMap mResamplerMap;
PResampler findResampler(int sourceRate, int destRate);
void preload();
};
// 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;
};
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

View File

@ -0,0 +1,374 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Audio_WavFile.h"
#include "helper/HL_Exception.h"
#include "helper/HL_String.h"
#include "helper/HL_Log.h"
#include "../config.h"
#include <memory.h>
#ifndef WORD
# define WORD unsigned short
#endif
#ifndef DWORD
# define DWORD unsigned int
#endif
typedef struct {
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
}WAVEFORMATEX;
#define WAVE_FORMAT_PCM 1
#define LOG_SUBSYSTEM "WavFileReader"
using namespace Audio;
// ---------------------- WavFileReader -------------------------
WavFileReader::WavFileReader()
:mHandle(NULL), mRate(0)
{
mDataOffset = 0;
}
WavFileReader::~WavFileReader()
{
}
#define THROW_READERROR throw Exception(ERR_WAVFILE_FAILED);
std::string WavFileReader::readChunk()
{
char name[5];
if (fread(name, 1, 4, mHandle) != 4)
THROW_READERROR;
name[4] = 0;
std::string result = name;
unsigned size;
if (fread(&size, 4, 1, mHandle) != 1)
THROW_READERROR;
if (result == "fact")
fread(&mDataLength, 4, 1, mHandle);
else
if (result != "data")
fseek(mHandle, size, SEEK_CUR);
else
mDataLength = size;
return result;
}
bool WavFileReader::open(const std::tstring& filename)
{
Lock lock(mFileMtx);
try
{
#ifdef WIN32
mHandle = _wfopen(filename.c_str(), L"rb");
#else
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "rb");
#endif
if (NULL == mHandle)
return false;
// Read the .WAV header
char riff[4];
if (fread(riff, 4, 1, mHandle) < 1)
THROW_READERROR;
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
THROW_READERROR;
// Read the file size
unsigned int filesize = 0;
if (fread(&filesize, 4, 1, mHandle) < 1)
THROW_READERROR;
char wavefmt[9];
if (fread(wavefmt, 8, 1, mHandle) < 1)
THROW_READERROR;
wavefmt[8] = 0;
if (strcmp(wavefmt, "WAVEfmt ") != 0)
THROW_READERROR;
unsigned fmtSize = 0;
if (fread(&fmtSize, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned fmtStart = ftell(mHandle);
unsigned short formattag = 0;
if (fread(&formattag, 2, 1, mHandle) < 1)
THROW_READERROR;
if (formattag != 1/*WAVE_FORMAT_PCM*/)
THROW_READERROR;
mChannels = 0;
if (fread(&mChannels, 2, 1, mHandle) < 1)
THROW_READERROR;
mRate = 0;
if (fread(&mRate, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned int avgbytespersec = 0;
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned short blockalign = 0;
if (fread(&blockalign, 2, 1, mHandle) < 1)
THROW_READERROR;
mBits = 0;
if (fread(&mBits, 2, 1, mHandle) < 1)
THROW_READERROR;
if (mBits !=8 && mBits != 16)
THROW_READERROR;
// Read the "chunk"
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
//unsigned pos = ftell(mHandle);
mDataLength = 0;
while (readChunk() != "data")
;
mFileName = filename;
mDataOffset = ftell(mHandle);
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
}
catch(...)
{
fclose(mHandle); mHandle = NULL;
}
return isOpened();
}
void WavFileReader::close()
{
Lock lock(mFileMtx);
if (NULL != mHandle)
fclose(mHandle);
mHandle = NULL;
}
int WavFileReader::rate() const
{
return mRate;
}
unsigned WavFileReader::read(void* buffer, unsigned bytes)
{
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
}
unsigned WavFileReader::read(short* buffer, unsigned samples)
{
Lock lock(mFileMtx);
if (!mHandle)
return 0;
// Get number of samples that must be read from source file
int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
void* temp = alloca(requiredBytes);
memset(temp, 0, requiredBytes);
// Find required size of input buffer
if (mDataLength)
{
unsigned filePosition = ftell(mHandle);
// Check how much data we can read
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
}
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
int processedBytes = 0;
int result = mResampler.processBuffer(temp, requiredBytes, processedBytes, buffer, samples * 2 * AUDIO_CHANNELS);
return result / 2 / AUDIO_CHANNELS;
}
bool WavFileReader::isOpened()
{
Lock lock(mFileMtx);
return (mHandle != 0);
}
void WavFileReader::rewind()
{
Lock l(mFileMtx);
if (mHandle)
fseek(mHandle, mDataOffset, SEEK_SET);
}
std::tstring WavFileReader::filename() const
{
Lock lock(mFileMtx);
return mFileName;
}
unsigned WavFileReader::size() const
{
Lock l(mFileMtx);
return mDataLength;
}
// ------------------------- WavFileWriter -------------------------
#define LOG_SUBSYTEM "WavFileWriter"
#define BITS_PER_CHANNEL 16
WavFileWriter::WavFileWriter()
:mHandle(NULL), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1)
{
}
WavFileWriter::~WavFileWriter()
{
close();
}
void WavFileWriter::checkWriteResult(int result)
{
if (result < 1)
throw Exception(ERR_WAVFILE_FAILED, errno);
}
bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
{
Lock lock(mFileMtx);
close();
mRate = rate;
mChannels = channels;
#ifdef WIN32
mHandle = _wfopen(filename.c_str(), L"wb");
#else
mHandle = fopen(StringHelper::makeUtf8(filename).c_str(), "wb");
#endif
if (NULL == mHandle)
{
ICELogCritical(<< "Failed to create .wav file: filename = " << StringHelper::makeUtf8(filename) << " , error = " << errno);
return false;
}
// Write the .WAV header
const char* riff = "RIFF";
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
// Write the file size
unsigned int filesize = 0;
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
const char* wavefmt = "WAVEfmt ";
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
// Set the format description
DWORD dwFmtSize = 16; /*= 16L*/;
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
WAVEFORMATEX format;
format.wFormatTag = WAVE_FORMAT_PCM;
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
format.nChannels = mChannels;
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
format.nSamplesPerSec = mRate;
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
format.nAvgBytesPerSec = mRate * 2 * mChannels;
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
format.nBlockAlign = 2 * mChannels;
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
format.wBitsPerSample = BITS_PER_CHANNEL;
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
const char* data = "data";
checkWriteResult( fwrite(data, 4, 1, mHandle));
mFileName = filename;
mWritten = 0;
mLengthOffset = ftell(mHandle);
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
return isOpened();
}
void WavFileWriter::close()
{
Lock lock(mFileMtx);
if (mHandle)
{
fclose(mHandle);
mHandle = NULL;
}
}
unsigned WavFileWriter::write(const void* buffer, unsigned bytes)
{
Lock l(mFileMtx);
if (!mHandle)
return 0;
// Seek the end of file
fseek(mHandle, 0, SEEK_END);
mWritten += bytes;
// Write the data
fwrite(buffer, bytes, 1, mHandle);
// Write file length
fseek(mHandle, 4, SEEK_SET);
unsigned int fl = mWritten + 36;
fwrite(&fl, sizeof(fl), 1, mHandle);
// Write data length
fseek(mHandle, mLengthOffset, SEEK_SET);
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
return bytes;
}
bool WavFileWriter::isOpened()
{
Lock lock(mFileMtx);
return (mHandle != 0);
}
std::tstring WavFileWriter::filename()
{
Lock lock(mFileMtx);
return mFileName;
}

View File

@ -0,0 +1,82 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_WAVFILE_H
#define __AUDIO_WAVFILE_H
#include "helper/HL_Sync.h"
#include "helper/HL_Types.h"
#include "Audio_Resampler.h"
#include <stdio.h>
#include <string>
#include <memory>
namespace Audio
{
class WavFileReader
{
protected:
FILE* mHandle;
short mChannels;
short mBits;
int mRate;
std::tstring mFileName;
mutable Mutex mFileMtx;
unsigned mDataOffset;
unsigned mDataLength;
Resampler mResampler;
std::string readChunk();
public:
WavFileReader();
~WavFileReader();
bool open(const std::tstring& filename);
void close();
bool isOpened();
void rewind();
int rate() const;
// This method returns number of read bytes
unsigned read(void* buffer, unsigned bytes);
// This method returns number of read samples
unsigned read(short* buffer, unsigned samples);
std::tstring filename() const;
unsigned size() const;
};
typedef std::shared_ptr<WavFileReader> PWavFileReader;
class WavFileWriter
{
protected:
FILE* mHandle; /// Handle of audio file.
std::tstring mFileName; /// Path to requested audio file.
Mutex mFileMtx; /// Mutex to protect this instance.
int mWritten; /// Amount of written data (in bytes)
int mLengthOffset; /// Position of length field.
int mRate, mChannels;
void checkWriteResult(int result);
public:
WavFileWriter();
~WavFileWriter();
bool open(const std::tstring& filename, int rate, int channels);
void close();
bool isOpened();
unsigned write(const void* buffer, unsigned bytes);
std::tstring filename();
};
typedef std::shared_ptr<WavFileWriter> PWavFileWriter;
}
#endif

View File

@ -0,0 +1,555 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef TARGET_WIN
#include "Audio_Wmme.h"
#include "Audio_Helper.h"
#include "../Helper/HL_Exception.h"
#include <process.h>
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);
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);
}
}
bool WmmeInputDevice::Buffer::prepare(HWAVEIN device)
{
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 << ".");
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;
}
bool WmmeInputDevice::Buffer::isFinished()
{
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;
}
void* WmmeInputDevice::Buffer::data()
{
return mData;
}
WmmeInputDevice::WmmeInputDevice(int deviceId)
: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;
}
WmmeInputDevice::~WmmeInputDevice()
{
close();
::CloseHandle(mDoneSignal);
::CloseHandle(mShutdownSignal);
}
bool WmmeInputDevice::fakeMode()
{
return mFakeMode;
}
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;
case WIM_CLOSE:
break;
case WIM_OPEN:
break;
}
}
void WmmeInputDevice::openDevice()
{
// 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;
// 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)
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;
}
}
void WmmeInputDevice::close()
{
Lock l(mGuard);
mRefCount--;
if (mRefCount != 0)
return;
// Set shutdown signal
if (!mThreadHandle)
return;
::SetEvent(mShutdownSignal);
::WaitForSingleObject(mThreadHandle, INFINITE);
mThreadHandle = 0;
}
bool WmmeInputDevice::tryReadBuffer(void* buffer)
{
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;
}
void WmmeInputDevice::setFakeMode(bool fakeMode)
{
mFakeMode = fakeMode;
}
int WmmeInputDevice::readBuffer(void* buffer)
{
//Lock lock(mGuard);
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;
}
void WmmeInputDevice::threadProc(void* arg)
{
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)
{
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)
{
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);
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);
}
}
bool WmmeOutputDevice::Buffer::prepare(HWAVEOUT device)
{
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;
}
bool WmmeOutputDevice::Buffer::write(HWAVEOUT device)
{
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)
{
mDoneSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL);
}
WmmeOutputDevice::~WmmeOutputDevice()
{
close();
// Destroy used signals
CloseHandle(mDoneSignal); CloseHandle(mShutdownSignal);
}
bool WmmeOutputDevice::open()
{
// Start thread
mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this);
return true;
}
void WmmeOutputDevice::close()
{
// Tell the thread to exit
SetEvent(mShutdownSignal);
mShutdownMarker = true;
// 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;
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);
}
}
void WmmeOutputDevice::closeDevice()
{
Lock l(mGuard);
mClosing = true;
bool finished = false;
while (!finished)
{
WaitForSingleObject(mDoneSignal, 10);
finished = areBuffersFinished();
}
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)
{
/* if (mBufferList[i].mHeader->dwFlags & WHDR_PREPARED)
mBufferList[i].Unprepare(mDevice); */
}
result &= finished;
}
return result;
}
void WmmeOutputDevice::threadProc(void* arg)
{
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++)
{
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;
}
unsigned WmmeOutputDevice::playedTime()
{
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;
else
{
result = mmt.u.ms - mPlayedTime;
mPlayedTime = mmt.u.ms - result % 8;
}
}
return result / 8;
}
void WmmeOutputDevice::setFakeMode(bool fakemode)
{
closeDevice();
}
bool WmmeOutputDevice::fakeMode()
{
return mFailed;
}
bool WmmeOutputDevice::closing()
{
return mClosing;
}
void CALLBACK WmmeOutputDevice::callbackProc(HWAVEOUT hwo, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
WmmeOutputDevice* impl;
if (msg == WOM_DONE)
{
impl = (WmmeOutputDevice*)dwInstance;
InterlockedIncrement(&impl->mPlayedCount);
SetEvent(impl->mDoneSignal);
}
}
#endif

View File

@ -0,0 +1,148 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_WMME_H
#define __AUDIO_WMME_H
#ifdef TARGET_WIN
#include "../config.h"
#include <winsock2.h>
#include <windows.h>
#include <mmsystem.h>
#include "../Helper/HL_Sync.h"
#include "Audio_Interface.h"
#include <deque>
#include <EndpointVolume.h>
#include <MMDeviceAPI.h>
#if defined(_MSC_VER)
#include <Functiondiscoverykeys_devpkey.h>
#endif
#include <vector>
#include <string>
namespace Audio
{
class WmmeInputDevice: public InputDevice
{
public:
WmmeInputDevice(int index);
~WmmeInputDevice();
bool open();
void close();
bool fakeMode();
void setFakeMode(bool fakeMode);
int readBuffer(void* buffer);
HWAVEIN handle();
protected:
class Buffer
{
public:
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;
};
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();
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:
WmmeOutputDevice(int index);
~WmmeOutputDevice();
bool open();
void close();
HWAVEOUT handle();
unsigned playedTime();
void setFakeMode(bool fakemode);
bool fakeMode();
bool closing();
protected:
class Buffer
{
friend class WmmeOutputDevice;
public:
Buffer();
~Buffer();
bool prepare(HWAVEOUT device);
bool unprepare(HWAVEOUT device);
bool write(HWAVEOUT device);
protected:
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;
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);
};
}
#endif
#endif

View File

@ -0,0 +1,2 @@
#include "Audio_iOS.h"

View File

@ -0,0 +1,38 @@
#ifndef __AUDIO_IOS
#define __AUDIO_IOS
class IosInputDevice: public InputDevice
{
protected:
public:
IosInputDevice();
~IosInputDevice();
void open();
void close();
};
class IosOutputDevice: public OutputDevice
{
protected:
public:
IosOutputDevice();
~IosOutputDevice();
enum
{
Receiver,
Speaker,
Bluetooth
};
int route();
void setRoute(int route);
void open();
void close();
};
#endif

View File

@ -0,0 +1,22 @@
project (audio_lib)
# Rely on C++ 11
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (AUDIOLIB_SOURCES
Audio_Resampler.cpp
Audio_Quality.cpp
Audio_Mixer.cpp
Audio_Interface.cpp
Audio_Helper.cpp
Audio_DataWindow.cpp
Audio_DevicePair.cpp
Audio_Player.cpp
Audio_Null.cpp
Audio_CoreAudio.cpp
Audio_DirectSound.cpp
Audio_WavFile.cpp
)
add_library(audio_lib ${AUDIOLIB_SOURCES})

110
src/engine/config.h Normal file
View File

@ -0,0 +1,110 @@
/* Copyright(C) 2007-2015 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __TOOLKIT_CONFIG_H
#define __TOOLKIT_CONFIG_H
#define USE_SPEEX_AEC
// TODO: test implementation with webrtc aec; be careful - it needs fixes!
//#define USE_WEBRTC_AEC
#define USER
#define AUDIO_SAMPLE_WIDTH 16
#define AUDIO_CHANNELS 1
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
#define AUDIO_SAMPLERATE 8000
#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_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)
#define AUDIO_MIX_CHANNEL_COUNT 16
#define AUDIO_DEVICEPAIR_INPUTBUFFER 16384
// Avoid too high resampler quality - it can take many CPU and cause gaps in playing
#define AUDIO_RESAMPLER_QUALITY 1
#define AEC_FRAME_TIME 10
#define AEC_TAIL_TIME 160
// Defined these two lines to get dumping of audio input/output
//#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 MT_SAMPLERATE AUDIO_SAMPLERATE
#define MT_MAXAUDIOFRAME 1440
#define MT_MAXRTPPACKET 1500
#define MT_DTMF_END_PACKETS 3
#define RTP_BUFFER_HIGH 480
#define RTP_BUFFER_LOW 10
#define RTP_BUFFER_PREBUFFER 80
#define RTP_DECODED_CAPACITY 2048
#define DEFAULT_SUBSCRIPTION_TIME 1200
#define DEFAULT_SUBSCRIPTION_REFRESHTIME 500
#define PRESENCE_IN_REG_HEADER "PresenceInReg"
// Maximum UDP packet length
#define MAX_UDPPACKET_SIZE 65535
#define MAX_VALID_UDPPACKET_SIZE 2048
// AMR codec defines - it requires USE_AMR_CODEC defined
// #define USE_AMR_CODEC
#define MT_AMRNB_PAYLOADTYPE 97
#define MT_AMRNB_CODECNAME "amr"
#define MT_AMRNB_OCTET_PAYLOADTYPE 103
#define MT_AMRWB_PAYLOADTYPE 96
#define MT_AMRWB_CODECNAME "amr-wb"
#define MT_AMRWB_OCTET_PAYLOADTYPE 104
#define MT_GSMEFR_PAYLOADTYPE 110
#define MT_GSMEFR_CODECNAME "GERAN-EFR"
// OPUS codec defines
// #define USE_OPUS_CODEC
#define MT_OPUS_CODEC_PT 106
// ILBC codec defines
#define MT_ILBC20_PAYLOADTYPE 98
#define MT_ILBC30_PAYLOADTYPE 99
// ISAC codec defines
#define MT_ISAC16K_PAYLOADTYPE 100
#define MT_ISAC32K_PAYLOADTYPE 101
// GSM HR payload type
#define MT_GSMHR_PAYLOADTYPE 111
// Mirror buffer capacity
#define MT_MIRROR_CAPACITY 32768
// Mirror buffer readiness threshold - 50 milliseconds
#define MT_MIRROR_PREBUFFER (MT_SAMPLERATE / 10)
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
# define TEXT(X) X
#endif
// In milliseconds
#define MT_SEVANA_FRAME_TIME 680
#endif

View File

@ -0,0 +1,747 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EP_Engine.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_Exception.h"
#include <resip/stack/ExtensionHeader.hxx>
#include <resip/stack/Pidf.hxx>
#include <resip/stack/PlainContents.hxx>
#define LOG_SUBSYSTEM "Account"
#define CONFIG(X) mConfig->at(X)
#define CONFIG_EXISTS(X) mConfig->exists(X)
//#define MODIFY_VIA_BEHIND_NAT
// NAT decorator
class NATDecorator: public resip::MessageDecorator
{
protected:
UserAgent& mUserAgent;
resip::SipMessage mMessage;
resip::Data mViaHost;
unsigned short mViaPort;
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 MessageDecorator* clone() const;
};
NATDecorator::NATDecorator(UserAgent& ua)
:mUserAgent(ua), mViaPort(0), mContactsPort(0)
{
}
NATDecorator::~NATDecorator()
{
}
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());
// Check From: header and find the account
resip::NameAddr from;
if (msg.isRequest())
from = msg.header(resip::h_From);
else
from = msg.header(resip::h_To);
PAccount account = mUserAgent.getAccount(from);
if (!account)
{
ICELogDebug(<< "Bad from header " << from.uri().getAor().c_str() << ". Will skip it");
return;
}
if (!account->mConfig->at(CONFIG_EXTERNALIP).asBool())
return;
if (!account->mExternalAddress.isEmpty())
{
#ifdef MODIFY_VIA_BEHIND_NAT
if (msg.header(resip::h_Vias).size() > 0)
{
resip::Via& via = msg.header(resip::h_Vias).front();
mViaHost = via.sentHost();
mViaPort = via.sentPort();
via.sentHost() = resip::Data(account->mExternalAddress.ip());
via.sentPort() = account->mExternalAddress.port();
}
#endif
if (msg.header(resip::h_Contacts).size() > 0)
{
resip::Uri& uri = msg.header(resip::h_Contacts).front().uri();
mContactsHost = uri.host();
mContactsPort = uri.port();
mContactsScheme = uri.scheme();
uri.host() = resip::Data(account->mExternalAddress.ip());
uri.port() = account->mExternalAddress.port();
if (account->mConfig->at(CONFIG_SIPS).asBool())
{
//uri.scheme() = "sips";
//uri.param(resip::p_transport) = "tls";
}
//uri.scheme() = account->mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
}
}
}
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);
if (!account)
return;
if (!account->mExternalAddress.isEmpty())
{
#ifdef MODIFY_VIA_BEHIND_NAT
if (msg.header(resip::h_Vias).size() > 0)
{
resip::Via& via = msg.header(resip::h_Vias).front();
if ((via.sentHost() == resip::Data(account->mExternalAddress.ip())) &&
(via.sentPort() == account->mExternalAddress.port()))
{
via.sentHost() = mViaHost;
via.sentPort() = mViaPort;
}
}
#endif
if (msg.header(resip::h_Contacts).size() > 0)
{
resip::Uri& uri = msg.header(resip::h_Contacts).front().uri();
if ((uri.host() == resip::Data(account->mExternalAddress.ip())) &&
(uri.port() == account->mExternalAddress.port()))
{
uri.host() = mContactsHost;
uri.port() = mContactsPort;
//uri.scheme() = mContactsScheme;
}
}
}
}
resip::MessageDecorator* NATDecorator::clone() const
{
return new NATDecorator(mUserAgent);
}
Account::Account(PVariantMap config, UserAgent& agent)
:mAgent(agent), mId(0), mConfig(config), mRegistrationState(RegistrationState::None),
mRegistration(NULL)
{
mProfile = resip::SharedPtr<resip::UserProfile>(new resip::UserProfile(agent.mProfile));
mId = Account::generateId();
setup(*config);
}
Account::~Account()
{
}
void Account::setup(VariantMap &config)
{
// Credentials
if (!config.exists(CONFIG_USERNAME) || !config.exists(CONFIG_PASSWORD) || !config.exists(CONFIG_DOMAIN))
throw Exception(ERR_NO_CREDENTIALS);
mProfile->clearDigestCredentials();
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());
// Proxy
mProfile->unsetOutboundProxy();
if (config.exists(CONFIG_PROXY))
{
if (!config[CONFIG_PROXY].asStdString().empty())
{
resip::Uri proxyAddr;
proxyAddr.host() = resip::Data(config[CONFIG_PROXY].asStdString());
proxyAddr.port() = 5060;
if (config.exists(CONFIG_PROXYPORT))
{
if (config[CONFIG_PROXYPORT].asInt())
proxyAddr.port() = config[CONFIG_PROXYPORT].asInt();
}
if (config[CONFIG_SIPS].asBool())
proxyAddr.param(resip::p_transport) = "tls";
mProfile->setOutboundProxy(proxyAddr);
}
}
// NAT decorator
mProfile->setOutboundDecorator(resip::SharedPtr<resip::MessageDecorator>(new NATDecorator(mAgent)));
// Rinstance
if (config.exists(CONFIG_INSTANCE_ID))
{
if (!config[CONFIG_INSTANCE_ID].asStdString().empty())
mProfile->setInstanceId(config[CONFIG_INSTANCE_ID].asStdString().c_str());
}
else
mProfile->setInstanceId(resip::Data::Empty);
if (config.exists(CONFIG_REGID))
mProfile->setRegId(config[CONFIG_REGID].asInt());
if (config.exists(CONFIG_RINSTANCE))
mProfile->setRinstanceEnabled(config[CONFIG_RINSTANCE].asBool());
else
mProfile->setRinstanceEnabled(true);
if (config.exists(CONFIG_RPORT))
mProfile->setRportEnabled(config[CONFIG_RPORT].asBool());
else
mProfile->setRportEnabled(true);
// From header
resip::NameAddr from;
if (config.exists(CONFIG_DISPLAYNAME))
from.displayName() = resip::Data(config[CONFIG_DISPLAYNAME].asStdString().c_str());
from.uri().scheme() = config[CONFIG_SIPS].asBool() ? "sips" : "sip";
if (config[CONFIG_DOMAINPORT].asInt() != 0)
from.uri().port() = config[CONFIG_DOMAINPORT].asInt();
else
from.uri().port();// = 5060;
from.uri().user() = resip::Data(config[CONFIG_USERNAME].asStdString());
from.uri().host() = resip::Data(config[CONFIG_DOMAIN].asStdString());
mProfile->setDefaultFrom(from);
if (!CONFIG_EXISTS(CONFIG_REGISTERDURATION))
CONFIG(CONFIG_REGISTERDURATION) = UA_REGISTRATION_TIME;
}
int Account::id() const
{
return mId;
}
void Account::start()
{
ICELogInfo(<< "Starting account " << this->name());
if (mRegistrationState != RegistrationState::None)
{
ICELogInfo(<< "Registration is active or in progress already.");
return;
}
if (!mAgent.mDum)
{
ICELogInfo(<< "DUM is not started yet.");
return;
}
// Create registration
mRegistration = new ResipSession(*mAgent.mDum);
resip::SharedPtr<resip::SipMessage> 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()));
mRegistrationState = RegistrationState::Registering;
// Send packet here
mAgent.mDum->send(regmessage);
// Check if STUN IP is required
bool noStunServerIp = !CONFIG_EXISTS(CONFIG_STUNSERVER_IP);
//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.");
mRefreshStunServerIpTimer.start(CONFIG(CONFIG_DNS_CACHE_TIME).asInt() * 1000);
mRefreshStunServerIpTimer.isTimeToSend();
queryStunServerIp();
}
}
void Account::stop()
{
// Close presence publication
if (mPublication.isValid())
{
mPublication->end();
mPublication = resip::ClientPublicationHandle();
}
// Close client subscriptions
// Close registration
if (mRegistrationHandle.isValid())
{
mRegistrationHandle->removeAll();
mRegistrationHandle = resip::ClientRegistrationHandle();
}
else
if (mRegistration)
{
mRegistration->end();
}
mRegistration = NULL;
mRegistrationState = RegistrationState::None;
}
void Account::refresh()
{
if (mRegistrationHandle.isValid())
{
mRegistrationState = RegistrationState::Registering;
mRegistrationHandle->requestRefresh();
}
if (mPublication.isValid())
{
mPublication->refresh();
}
}
bool Account::active()
{
return mRegistrationState == RegistrationState::Registered ||
mRegistrationState == RegistrationState::Registering ||
mRegistrationState == RegistrationState::Reregistering;
}
std::string Account::name()
{
return contact(SecureScheme::Nothing).uri().toString().c_str();
}
Account::RegistrationState Account::registrationState()
{
return mRegistrationState;
}
void Account::publishPresence(bool online, const std::string& content, int seconds)
{
if (online == mPresenceOnline && content == mPresenceContent)
return;
mPresenceOnline = online;
mPresenceContent = content;
resip::Pidf p;
p.setEntity(contact(SecureScheme::Nothing).uri());
p.setSimpleId(resip::Data(CONFIG(CONFIG_PRESENCE_ID).asStdString()));
p.setSimpleStatus(online, resip::Data(content));
if (mPublication.isValid())
mPublication->update(&p);
else
mAgent.mDum->send(mAgent.mDum->makePublication(contact(SecureScheme::TlsOnly), mProfile, p, resip::Symbols::Presence, seconds));
}
void Account::stopPublish()
{
if (mPublication.isValid())
mPublication->end();
}
PClientObserver Account::observe(const std::string& target, const std::string& package, void* tag)
{
// Add subscription functionality
PClientObserver observer(new ClientObserver());
observer->mSession = new ResipSession(*mAgent.mDum);
observer->mSession->setRemoteAddress(target);
observer->mSession->setTag(tag);
observer->mSessionId = observer->mSession->sessionId();
observer->mPeer = target;
resip::SharedPtr<resip::SipMessage> msg;
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->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
mAgent.mClientObserverMap[observer->mSessionId] = observer;
mAgent.mDum->send(msg);
return observer;
}
/* Queues message to peer with specified mime type. Returns ID of message. */
int Account::sendMsg(const std::string& peer, const void* ptr, unsigned length, const std::string& mime, void* tag)
{
ResipSession* s = new ResipSession(*mAgent.mDum);
s->setUa(&mAgent);
s->setTag(tag);
s->setRemoteAddress(peer);
// Find 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)));
else
type = resip::Mime(resip::Data(mime), resip::Data());
resip::ClientPagerMessageHandle msgHandle = mAgent.mDum->makePagerMessage(resip::NameAddr(resip::Data(peer)), mProfile, s);
auto_ptr<resip::Contents> contentPtr(new resip::PlainContents(resip::Data(std::string((const char*)ptr, length)),type));
int result = s->sessionId();
msgHandle->page(contentPtr);
return result;
}
resip::NameAddr Account::contact(SecureScheme ss)
{
resip::NameAddr result;
switch (ss)
{
case SecureScheme::Nothing:
break;
case SecureScheme::SipsOnly:
result.uri().scheme() = mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
break;
case SecureScheme::SipsAndTls:
result.uri().scheme() = mConfig->at(CONFIG_SIPS).asBool() ? "sips" : "sip";
if (mConfig->at(CONFIG_SIPS).asBool())
result.uri().param(resip::p_transport) = "tls";
break;
case SecureScheme::TlsOnly:
if (mConfig->at(CONFIG_SIPS).asBool())
result.uri().param(resip::p_transport) = "tls";
break;
}
result.uri().user() = resip::Data(mConfig->at(CONFIG_USERNAME).asStdString());
result.uri().host() = resip::Data(mConfig->at(CONFIG_DOMAIN).asStdString());
result.uri().port() = mConfig->at(CONFIG_DOMAINPORT).asInt();
return result;
}
void Account::queryStunServerIp()
{
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();
// Start lookup
mAgent.mStack->getDnsStub().lookup<resip::RR_SRV>(resip::Data(target), this);
}
else
{
// Check if host name is ip already
std::string server = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString();
if (ice::NetworkAddress::isIp(server))
{
mConfig->at(CONFIG_STUNSERVER_IP) = server;
mRefreshStunServerIpTimer.stop();
}
else
mAgent.mStack->getDnsStub().lookup<resip::RR_A>(resip::Data(server), this);
}
}
void Account::prepareIceStack(Session *session, ice::AgentRole icerole)
{
ice::ServerConfig config;
ice::NetworkAddress addr;
addr.setIp(mConfig->at(CONFIG_STUNSERVER_IP).asStdString());
if (mConfig->at(CONFIG_STUNSERVER_PORT).asInt())
addr.setPort(mConfig->at(CONFIG_STUNSERVER_PORT).asInt());
else
addr.setPort(3478);
config.mServerList4.push_back(addr);
config.mRelay = mConfig->at(CONFIG_RELAY).asBool();
if (mConfig->exists(CONFIG_ICETIMEOUT))
config.mTimeout = mConfig->at(CONFIG_ICETIMEOUT).asInt();
config.mUsername = mConfig->at(CONFIG_ICEUSERNAME).asStdString();
config.mPassword = mConfig->at(CONFIG_ICEPASSWORD).asStdString();
config.mUseIPv4 = mAgent.config()[CONFIG_IPV4].asBool();
config.mUseIPv6 = mAgent.config()[CONFIG_IPV6].asBool();
//config.mDetectNetworkChange = true;
//config.mNetworkCheckInterval = 5000;
session->mIceStack = resip::SharedPtr<ice::Stack>(ice::Stack::makeICEBox(config));
session->mIceStack->setEventHandler(session, this);
session->mIceStack->setRole(icerole);
}
void Account::process()
{
if (mRefreshStunServerIpTimer.isTimeToSend())
queryStunServerIp();
}
void Account::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage &response)
{
// Save registration handle
mRegistrationHandle = h;
// Copy user info to registration handle
for (UserInfo::iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
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();
// Get the sent host
const resip::Data& sentHost = via.sentHost();//response.header(h_Contacts).front().uri().host();
// Get the sentPort
int sentPort = via.sentPort();
const resip::Data& sourceHost = response.getSource().toData(resip::UDP);
int rport = 0;
if (via.exists(resip::p_rport))
rport = via.param(resip::p_rport).port();
resip::Data received = "";
if (via.exists(resip::p_received))
received = via.param(resip::p_received);
bool hostChanged = sentHost != received && received.size() > 0;
bool portChanged = sentPort != rport && rport != 0;
// Save external port and IP address
if (received.size() > 0 /*&& mConfig->at(CONFIG_EXTERNALIP).asBool()*/)
{
mExternalAddress.setIp(received.c_str());
mExternalAddress.setPort(rport ? rport : sentPort);
// Add new external address to domain list
if (mAgent.mStack)
mAgent.mStack->addAlias(resip::Data(mExternalAddress.ip()), mExternalAddress.port());
if (mAgent.mDum)
mAgent.mDum->addDomain(resip::Data(mExternalAddress.ip()));
}
const resip::Transport* transport = response.getReceivedTransport();
mUsedTransport = transport->transport();
bool streamTransport = transport->transport() == resip::TCP || transport->transport() == resip::TLS;
// Retry registration for stream based transport too
if ( (hostChanged || portChanged) && mRegistrationState == RegistrationState::Registering /*&& !streamTransport*/ && mConfig->at(CONFIG_EXTERNALIP).asBool())
{
//mRegistrationHandle->requestRefresh();
// Unregister at first
mRegistrationHandle->removeAll();
mRegistrationState = RegistrationState::Reregistering;
return;
}
// So here we registered ok
mRegistrationState = RegistrationState::Registered;
mAgent.onAccountStart(mAgent.getAccount(this));
}
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)
{
resip::Uri hostport(contact(SecureScheme::TlsOnly).uri());
hostport.host() = resip::Data(mExternalAddress.ip());
hostport.port() = mExternalAddress.port();
if (mUsedTransport != resip::UDP)
{
const char* transportName = nullptr;
switch (mUsedTransport)
{
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->setRegId(mConfig->at(CONFIG_REGID).asInt());
resip::SharedPtr<resip::SipMessage> 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()));
mAgent.mDum->send(regmessage);
return;
}
else
{
mRegistration = NULL;
mRegistrationState = RegistrationState::None;
mRegistrationHandle = resip::ClientRegistrationHandle();
mAgent.onAccountStop(mAgent.getAccount(this), response.header(resip::h_StatusLine).statusCode());
}
}
void Account::onFailure(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
{
// Reset registration handle
mRegistrationHandle = resip::ClientRegistrationHandle();
mRegistrationState = RegistrationState::None;
mRegistration = NULL;
mAgent.onAccountStop(mAgent.getAccount(this), response.header(resip::h_StatusLine).statusCode());
}
void Account::onDnsResult(const resip::DNSResult<resip::DnsHostRecord>& result)
{
if (result.status == 0)
{
resip::Data foundAddress = result.records.front().host();
ICELogCritical( << "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.
if (mRegistrationState == RegistrationState::Registered)
mAgent.onAccountStart(mAgent.getAccount(this));
}
else
{
ICELogCritical( << "Failed to resolve STUN or TURN server IP address.");
if (mRegistrationState == RegistrationState::Registered)
{
int startCode = mConfig->at(CONFIG_STUNSERVER_NAME).asStdString().empty() ? 0 : 503;
mAgent.onAccountStop(mAgent.getAccount(this), startCode);
}
}
}
void Account::onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&)
{
}
void Account::onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>& result)
{
if (result.status == 0)
{
// Find lowest priority
int priority = 0x7FFFFFFF;
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;
for (size_t i=0; i<result.records.size(); i++)
{
if (result.records[i].priority() == priority && result.records[i].weight() >= weight)
{
index = i;
weight = result.records[i].weight();
}
}
mConfig->at(CONFIG_STUNSERVER_PORT) = result.records[index].port();
const char* host = result.records[index].target().c_str();
ICELogCritical( << "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);
}
else
{
mConfig->at(CONFIG_STUNSERVER_IP) = std::string(host);
}
}
else
{
ICELogCritical( << "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::DnsCnameRecord>&)
{
}
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();
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
if (mConfig->exists(CONFIG_DOMAINPORT))
return mConfig->at(CONFIG_DOMAINPORT).asInt() == p || !p;
else
return true;
}
else
return false;
}
void Account::setUserInfo(const UserInfo &info)
{
mUserInfo = info;
if (mRegistrationHandle.isValid())
{
for (UserInfo::iterator iter = mUserInfo.begin(); iter != mUserInfo.end(); iter++)
mRegistrationHandle->setCustomHeader(resip::Data(iter->first.c_str()), resip::Data(iter->second.c_str()));
}
}
Account::UserInfo Account::getUserInfo() const
{
return mUserInfo;
}
resip::AtomicCounter Account::IdGenerator;
int Account::generateId()
{
return IdGenerator.increment();
}

View File

@ -0,0 +1,143 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef EP_ACCOUNT_H
#define EP_ACCOUNT_H
#include "../helper/HL_Pointer.h"
#include "../helper/HL_VariantMap.h"
#include "ice/ICEAddress.h"
#include "ice/ICETime.h"
#include "ice/ICEBox.h"
#include "resip/dum/UserProfile.hxx"
#include "resip/dum/ClientRegistration.hxx"
#include "resip/dum/ClientPublication.hxx"
#include "resip/stack/DnsInterface.hxx"
#include "resip/stack/NameAddr.hxx"
#include "EP_Observer.h"
class UserAgent;
class Session;
class Account: public resip::DnsResultSink
{
friend class UserAgent;
friend class NATDecorator;
public:
Account(PVariantMap config, UserAgent& agent);
~Account();
void start();
void stop();
void refresh();
bool active();
int id() const;
enum class RegistrationState
{
None,
Registering,
Reregistering,
Registered,
Unregistering
};
RegistrationState registrationState();
/* Publishes new presence information */
void publishPresence(bool online, const std::string& content, int seconds = 600);
/* Stops publishing of presence */
void stopPublish();
/* Starts observing on specified target / package */
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);
/* Returns name of account - <sip:user@domain> */
std::string name();
/* Updates account with configuration */
void setup(VariantMap& config);
/* Returns corresponding resiprocate profile */
resip::SharedPtr<resip::UserProfile> getUserProfile() const { return mProfile; }
typedef std::map<std::string, std::string> UserInfo;
void setUserInfo(const UserInfo& info);
UserInfo getUserInfo() const;
protected:
PVariantMap mConfig;
// Registration
ResipSession* mRegistration;
resip::ClientRegistrationHandle mRegistrationHandle;
resip::ClientPublicationHandle mPublication;
resip::TransportType mUsedTransport;
RegistrationState mRegistrationState;
ice::NetworkAddress mExternalAddress;
resip::SharedPtr<resip::UserProfile> mProfile;
UserAgent& mAgent;
bool mPresenceOnline;
std::string mPresenceContent;
// Timer to refresh STUN server IP
ice::ICEScheduleTimer mRefreshStunServerIpTimer;
// Cached auth
resip::Auth mCachedAuth;
// Id of account
int mId;
// User info about current state
UserInfo mUserInfo;
// List of client subscriptions sent from this account
typedef std::set<PClientObserver> ClientObserverSet;
ClientObserverSet mClientObserverSet;
void process();
// Method queries new stun server ip from dns (if stun server is specified as dns name)
void queryStunServerIp();
bool isResponsibleFor(const resip::NameAddr& addr);
enum class SecureScheme
{
SipsAndTls,
SipsOnly,
TlsOnly,
Nothing
};
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);
#pragma region DnsResultSink implementation
void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&);
void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&);
#pragma endregion
static int generateId();
static resip::AtomicCounter IdGenerator;
};
typedef std::shared_ptr<Account> PAccount;
#endif // EP_ACCOUNT_H

View File

@ -0,0 +1,380 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EP_AudioProvider.h"
#include "EP_Engine.h"
#include "../media/MT_Box.h"
#include "../media/MT_AudioStream.h"
#include "../media/MT_SrtpHelper.h"
#include "../media/MT_Stream.h"
#include "../helper/HL_Rtp.h"
#include "../helper/HL_StreamState.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_String.h"
#define LOG_SUBSYSTEM "AudioProvider"
AudioProvider::AudioProvider(UserAgent& agent, MT::Terminal& terminal)
:mUserAgent(agent), mTerminal(terminal), mState(0),
mRemoteTelephoneCodec(0), mRemoteNoSdp(false)
{
mActive = mfActive;
mRemoteState = msSendRecv;
mActiveStream = mTerminal.createStream(MT::Stream::Audio, mUserAgent.config());
if (mUserAgent.config().exists(CONFIG_CODEC_PRIORITY))
mCodecPriority.setupFrom(mUserAgent.config()[CONFIG_CODEC_PRIORITY].asVMap());
mSrtpSuite = SRTP_NONE;
setState((int)StreamState::SipRecv | (int)StreamState::SipSend | (int)StreamState::Receiving | (int)StreamState::Sending);
}
AudioProvider::~AudioProvider()
{
}
std::string AudioProvider::streamName()
{
return "audio";
}
std::string AudioProvider::streamProfile()
{
if (mState & (int)StreamState::Srtp)
return "RTP/SAVP";
else
return "RTP/AVP";
}
// Sets destination IP address
void AudioProvider::setDestinationAddress(const RtpPair<InternetAddress>& addr)
{
if (!mActiveStream)
return;
mActiveStream->setDestination(addr);
}
void AudioProvider::configureMediaObserver(MT::Stream::MediaObserver *observer, void* userTag)
{
mMediaObserver = observer;
mMediaObserverTag = userTag;
if (mActiveStream)
mActiveStream->configureMediaObserver(observer, userTag);
}
// Processes incoming data
void AudioProvider::processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source)
{
if (!mActiveStream)
return;
if (RtpHelper::isRtpOrRtcp(dataBuffer, dataSize))
{
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(PDatagramSocket s, InternetAddress& destination, const void* buffer, unsigned int size)
{
s->sendDatagram(destination, buffer, size);
}
// Create SDP offer
void AudioProvider::updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction)
{
if (mRemoteNoSdp)
return;
if (mState & (int)StreamState::Srtp)
{
// Check if SRTP suite is found already or not
if (mSrtpSuite == SRTP_NONE)
{
for (int suite = SRTP_AES_128_AUTH_80; suite <= SRTP_LAST; suite++)
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute((SrtpSuite)suite)));
}
else
sdp.addAttribute("crypto", resip::Data(createCryptoAttribute(mSrtpSuite)));
}
// Use CodecListPriority mCodecPriority adapter to work with codec priorities
if (mAvailableCodecs.empty())
{
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);
}
else
{
mAvailableCodecs.front().mFactory->updateSdp(sdp.codecs(), direction);
if (mRemoteTelephoneCodec)
sdp.addCodec(resip::SdpContents::Session::Codec::TelephoneEvent);
}
// Publish stream state
const char* attr = nullptr;
switch (mActive)
{
case mfActive:
switch(mRemoteState)
{
case msSendonly: attr = "recvonly"; break;
case msInactive: attr = "recvonly"; break;
}
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;
}
break;
}
if (attr)
sdp.addAttribute(attr);
}
void AudioProvider::sessionDeleted()
{
sessionTerminated();
}
void AudioProvider::sessionTerminated()
{
ICELogDebug(<< "sessionTerminated() for audio provider");
setState(state() & ~((int)StreamState::Sending | (int)StreamState::Receiving));
if (mActiveStream)
{
ICELogDebug(<< "Copy statistics from existing stream before freeing.");
// Copy statistics - maybe it will be requested later
mBackupStats = mActiveStream->statistics();
ICELogDebug(<< "Remove stream from terminal");
mTerminal.freeStream(mActiveStream);
// Retrieve final statistics
MT::AudioStream* audio_stream = dynamic_cast<MT::AudioStream*>(mActiveStream.get());
if (audio_stream)
audio_stream->setFinalStatisticsOutput(&mBackupStats);
ICELogDebug(<< "Reset reference to stream.");
mActiveStream.reset();
}
}
void AudioProvider::sessionEstablished(int conntype)
{
// Start media streams
setState(state() | (int)StreamState::Receiving | (int)StreamState::Sending);
// Available codec list can be empty in case of no-sdp offers.
if (conntype == EV_SIP && !mAvailableCodecs.empty() && mActiveStream)
{
RemoteCodec& rc = mAvailableCodecs.front();
mActiveStream->setTransmittingCodec(*rc.mFactory, rc.mRemotePayloadType);
dynamic_cast<MT::AudioStream*>(mActiveStream.get())->setTelephoneCodec(mRemoteTelephoneCodec);
}
}
void AudioProvider::setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6)
{
mSocket4 = p4;
mSocket6 = p6;
mActiveStream->setSocket(p4);
}
RtpPair<PDatagramSocket>& AudioProvider::socket(int family)
{
switch (family)
{
case AF_INET:
return mSocket4;
case AF_INET6:
return mSocket6;
}
return mSocket4;
}
bool AudioProvider::processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection)
{
// Check if there is compatible codec
mAvailableCodecs.clear();
mRemoteTelephoneCodec = 0;
// Check if there is SDP at all
mRemoteNoSdp = media.codecs().empty();
if (mRemoteNoSdp)
return true;
// Update RFC2833 related information
findRfc2833(media.codecs());
// Use CodecListPriority mCodecPriority to work with codec priorities
int pt;
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)
mAvailableCodecs.push_back(RemoteCodec(&factory, pt));
}
if (!mAvailableCodecs.size())
return false;
// Iterate SRTP crypto: attributes
if (media.exists("crypto"))
{
// Find the most strong crypt suite
const std::list<resip::Data>& vl = media.getValues("crypto");
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;
SrtpSuite suite = processCryptoAttribute(attr, tempkey);
if (suite > ss)
{
ss = suite;
mSrtpSuite = suite;
key = tempkey;
}
}
// If SRTP suite is agreed
if (ss != SRTP_NONE)
{
ICELogInfo(<< "Found SRTP suite " << ss);
mActiveStream->srtp().open(key, ss);
setState(state() | (int)StreamState::Srtp);
}
else
ICELogInfo(<< "Did not find valid SRTP suite");
}
DataProvider::processSdpOffer(media, sdpDirection);
return true;
}
void AudioProvider::setState(unsigned state)
{
mState = state;
if (mActiveStream)
mActiveStream->setState(state);
}
unsigned AudioProvider::state()
{
return mState;
}
MT::Statistics AudioProvider::getStatistics()
{
if (mActiveStream)
return mActiveStream->statistics();
else
return mBackupStats;
}
MT::PStream AudioProvider::activeStream()
{
return mActiveStream;
}
std::string AudioProvider::createCryptoAttribute(SrtpSuite suite)
{
if (!mActiveStream)
return "";
// Use tag 1 - it is ok, as we use only single crypto attribute
int srtpTag = 1;
// Print key to base64 string
PByteBuffer keyBuffer = mActiveStream->srtp().outgoingKey(suite).first;
resip::Data d(keyBuffer->data(), keyBuffer->size());
resip::Data keyText = d.base64encode();
// Create "crypto" attribute value
char buffer[512];
const char* suiteName = NULL;
switch (suite)
{
case SRTP_AES_128_AUTH_80: suiteName = SRTP_SUITE_NAME_1; break;
case SRTP_AES_256_AUTH_80: suiteName = SRTP_SUITE_NAME_2; break;
default: assert(0);
}
sprintf(buffer, "%d %s inline:%s", srtpTag, suiteName, keyText.c_str());
return buffer;
}
SrtpSuite AudioProvider::processCryptoAttribute(const resip::Data& value, ByteBuffer& key)
{
int srtpTag = 0;
char suite[64], keyChunk[256];
int components = sscanf(value.c_str(), "%d %63s inline: %255s", &srtpTag, suite, keyChunk);
if (components != 3)
return SRTP_NONE;
const char* delimiter = strchr(keyChunk, '|');
resip::Data keyText;
if (delimiter)
keyText = resip::Data(keyChunk, delimiter - keyChunk);
else
keyText = resip::Data(keyChunk);
resip::Data rawkey = keyText.base64decode();
key = ByteBuffer(rawkey.c_str(), rawkey.size());
// Open srtp
SrtpSuite result = SRTP_NONE;
if (strcmp(suite, SRTP_SUITE_NAME_1) == 0)
result = SRTP_AES_128_AUTH_80;
else
if (strcmp(suite, SRTP_SUITE_NAME_2) == 0)
result = SRTP_AES_256_AUTH_80;
return result;
}
void AudioProvider::findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs)
{
resip::SdpContents::Session::Medium::CodecContainer::const_iterator codecIter;
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)
mRemoteTelephoneCodec = codecIter->payloadType();
}
}
void AudioProvider::readFile(const Audio::PWavFileReader& stream, MT::Stream::MediaDirection direction)
{
// Iterate stream list
if (mActiveStream)
mActiveStream->readFile(stream, direction);
}
void AudioProvider::writeFile(const Audio::PWavFileWriter& stream, MT::Stream::MediaDirection direction)
{
if (mActiveStream)
mActiveStream->writeFile(stream, direction);
}
void AudioProvider::setupMirror(bool enable)
{
if (mActiveStream)
mActiveStream->setupMirror(enable);
}

View File

@ -0,0 +1,117 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_PROVIDER_H
#define __AUDIO_PROVIDER_H
#include "EP_DataProvider.h"
#include "../helper/HL_InternetAddress.h"
#include "../helper/HL_Rtp.h"
#include "../media/MT_Box.h"
#include "../media/MT_Stream.h"
#include "../media/MT_Codec.h"
#include "../audio/Audio_Interface.h"
#include "rutil/ThreadIf.hxx"
#include <vector>
#include <string>
class UserAgent;
class AudioProvider: public DataProvider
{
public:
AudioProvider(UserAgent& agent, MT::Terminal& terminal);
virtual ~AudioProvider();
// Returns provider RTP name
std::string streamName();
// Returns provider RTP profile name
std::string streamProfile();
// Sets destination IP address
void setDestinationAddress(const RtpPair<InternetAddress>& addr);
// Processes incoming data
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source);
// This method is called by user agent to send ICE packet from mediasocket
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
// Updates SDP offer
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction);
// Called by user agent when session is deleted.
void sessionDeleted();
// Called by user agent when session is terminated.
void sessionTerminated();
// Called by user agent when session is started.
void sessionEstablished(int conntype);
// Called by user agent to save media socket for this provider
void setSocket(const RtpPair<PDatagramSocket>& p4, const RtpPair<PDatagramSocket>& p6);
// Called by user agent to get media socket for this provider
RtpPair<PDatagramSocket>& socket(int family);
// 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);
void setState(unsigned state);
unsigned state();
MT::Statistics getStatistics();
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 configureMediaObserver(MT::Stream::MediaObserver* observer, void* userTag);
protected:
// SDP's stream name
std::string mStreamName;
// Socket handles to operate
RtpPair<PDatagramSocket> mSocket4, mSocket6;
// Destination IP4/6 address
RtpPair<InternetAddress> mDestination;
MT::PStream mActiveStream;
UserAgent& mUserAgent;
MT::Terminal& mTerminal;
MT::Statistics mBackupStats;
unsigned mState;
SrtpSuite mSrtpSuite;
struct RemoteCodec
{
RemoteCodec(MT::Codec::Factory* factory, int payloadType)
:mFactory(factory), mRemotePayloadType(payloadType)
{ }
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);
SrtpSuite processCryptoAttribute(const resip::Data& value, ByteBuffer& key);
void findRfc2833(const resip::SdpContents::Session::Medium::CodecContainer& codecs);
};
#endif

View File

@ -0,0 +1,74 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EP_DataProvider.h"
#include "../helper/HL_StreamState.h"
bool DataProvider::isSupported(const char* name)
{
return !strcmp(name, "audio");
//return (!strcmp(name, "screen") || !strcmp(name, "data") || !strcmp(name, "audio") || !strcmp(name, "video"));
}
void DataProvider::pause()
{
/*if (state() & STATE_SIPRECV)
setState( state() & ~STATE_SIPRECV );*/
// Stop receive RTP stream
if (state() & (int)StreamState::Receiving)
setState( state() & ~(int)StreamState::Receiving );
mActive = mfPaused;
}
void DataProvider::resume()
{
// Tell remote peer about resumed receiving in SDP
//setState( state() | STATE_SIPRECV );
// Start receive RTP stream
setState( state() | (int)StreamState::Receiving );
mActive = mfActive;
}
bool DataProvider::processSdpOffer(const resip::SdpContents::Session::Medium& media, SdpDirection sdpDirection)
{
// Process paused and inactive calls
if (media.exists("sendonly"))
{
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
{
mRemoteState = msSendRecv;
switch (mActive)
{
case mfActive:
setState(state() | (int)StreamState::Sending | (int)StreamState::Receiving);
break;
case mfPaused:
setState(state() | (int)StreamState::Sending );
break;
}
}
return true;
}

View File

@ -0,0 +1,92 @@
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __DATA_PROVIDER_H
#define __DATA_PROVIDER_H
#include <string>
#include <vector>
#include "resip/stack/SdpContents.hxx"
#include "rutil/SharedPtr.hxx"
#include "../helper/HL_InternetAddress.h"
#include "../helper/HL_NetworkSocket.h"
#include "../helper/HL_Pointer.h"
#include "../media/MT_Stream.h"
class DataProvider
{
public:
enum MediaFlow
{
mfActive,
mfPaused
};
enum MediaState
{
msSendRecv,
msSendonly,
msRecvonly,
msInactive
};
static bool isSupported(const char* name);
// Returns provider RTP name
virtual std::string streamName() = 0;
// Returns provider RTP profile name
virtual std::string streamProfile() = 0;
// Sets destination IP address
virtual void setDestinationAddress(const RtpPair<InternetAddress>& addr) = 0;
// Processes incoming data
virtual void processData(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(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;
// Called by user agent when session is deleted. Comes after sessionTerminated().
virtual void sessionDeleted() = 0;
// Called by user agent when session is terminated.
virtual void sessionTerminated() = 0;
// Called by user agent when session is started.
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;
// 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 unsigned state() = 0;
virtual void setState(unsigned state) = 0;
virtual void pause();
virtual void resume();
virtual MT::Statistics getStatistics() = 0;
protected:
MediaFlow mActive;
MediaState mRemoteState;
};
typedef std::shared_ptr<DataProvider> PDataProvider;
typedef std::vector<PDataProvider> DataProviderVector;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,484 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __ENGINE_H
#define __ENGINE_H
#include "resip/stack/SdpContents.hxx"
#include "resip/stack/SipMessage.hxx"
#include "resip/stack/ShutdownMessage.hxx"
#include "resip/stack/SipStack.hxx"
#include "resip/stack/InternalTransport.hxx"
#include "resip/dum/ClientAuthManager.hxx"
#include "resip/dum/ClientInviteSession.hxx"
#include "resip/dum/ClientRegistration.hxx"
#include "resip/dum/DialogUsageManager.hxx"
#include "resip/dum/DumShutdownHandler.hxx"
#include "resip/dum/InviteSessionHandler.hxx"
#include "resip/dum/MasterProfile.hxx"
#include "resip/dum/RegistrationHandler.hxx"
#include "resip/dum/ServerInviteSession.hxx"
#include "resip/dum/ServerOutOfDialogReq.hxx"
#include "resip/dum/OutOfDialogHandler.hxx"
#include "resip/dum/AppDialog.hxx"
#include "resip/dum/AppDialogSet.hxx"
#include "resip/dum/AppDialogSetFactory.hxx"
#include "resip/dum/ClientPublication.hxx"
#include "resip/dum/ClientSubscription.hxx"
#include "resip/dum/SubscriptionHandler.hxx"
#include "resip/dum/PagerMessageHandler.hxx"
#include "resip/dum/PublicationHandler.hxx"
#include "resip/dum/ClientPagerMessage.hxx"
#include "resip/dum/ServerPagerMessage.hxx"
#include "rutil/Log.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Random.hxx"
#include "rutil/WinLeakCheck.hxx"
#include "rutil/DnsUtil.hxx"
#include "resip/stack/DnsResult.hxx"
#include "resip/stack/SipStack.hxx"
#include "rutil/dns/RRVip.hxx"
#include "rutil/dns/QueryTypes.hxx"
#include "rutil/dns/DnsStub.hxx"
#include "../ice/ICEBox.h"
#include "../ice/ICETime.h"
#include <sstream>
#include <time.h>
#include "../config.h"
#include "EP_Session.h"
#include "EP_Observer.h"
#include "EP_DataProvider.h"
#include "../helper/HL_VariantMap.h"
#include "../helper/HL_SocketHeap.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_ByteBuffer.h"
#include "../media/MT_Stream.h"
#define RESIPROCATE_SUBSYSTEM Subsystem::TEST
using namespace std;
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_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
};
// Conntype parameter for OnSessionEstablished event
enum
{
EV_SIP = 1,
EV_ICE = 2
};
class UserAgent;
// Define a type for asynchronous requests to user agent
class SIPAction
{
public:
virtual void Run(UserAgent& ua) = 0;
};
typedef std::vector<SIPAction*> SIPActionVector;
// Session termination reason
enum
{
Error,
Timeout,
Replaced,
LocalBye,
RemoteBye,
LocalCancel,
RemoteCancel,
Rejected, //Only as UAS, UAC has distinct onFailure callback
Referred
};
class UserAgent: public resip::ClientRegistrationHandler,
public resip::InviteSessionHandler,
public resip::DumShutdownHandler,
public resip::ExternalLogger,
public resip::DnsResultSink,
public resip::ClientSubscriptionHandler,
public resip::ServerSubscriptionHandler,
public resip::ClientPagerMessageHandler,
public resip::ServerPagerMessageHandler,
public resip::ClientPublicationHandler,
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(std::string sip1, std::string sip2);
static std::string formatSipAddress(std::string sip);
static bool isSipAddressValid(std::string sip);
struct SipAddress
{
bool mValid;
std::string mScheme;
std::string mUsername;
std::string mDomain;
std::string mDisplayname;
};
static SipAddress parseSipAddress(const std::string& sip);
UserAgent();
virtual ~UserAgent();
/* Brings user agent online. Basically it creates a signalling socket(s).
This is asynchronous method. */
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();
/* 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();
/* Used to refresh existing registration(s), publication, subscriptions. */
void refresh();
/* Runs sip & ice stacks. Event handlers are called in its context. */
void process();
/* Adds root cert in PEM format. Usable after start() call. */
void addRootCert(const ByteBuffer& data);
PAccount createAccount(PVariantMap config);
void deleteAccount(PAccount account);
/* Creates session. Returns session ID. */
PSession createSession(PAccount account);
// Must be called when IP interface list is changed
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;
// Called when session is terminated
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;
// Called when client session gets
virtual void onSessionProvisional(PSession s, int code) = 0;
// Called when user agent started
virtual void onStart(int errorcode) = 0;
// Called when user agent stopped
virtual void onStop() = 0;
// Called when account registered
virtual void onAccountStart(PAccount account) = 0;
// Called when account removed or failed (non zero error code)
virtual void onAccountStop(PAccount account, int error) = 0;
// Called when connectivity checks failed.
virtual void onConnectivityFailed(PSession s) = 0;
// Called when new candidate is gathered
virtual void onCandidateGathered(PSession s, const char* address);
// Called when network change detected
virtual void onNetworkChange(PSession s) = 0;
// Called when all candidates are gathered
virtual void onGathered(PSession s);
// Called when new connectivity check is finished
virtual void onCheckFinished(PSession s, const char* description);
// Called when log message must be recorded
virtual void onLog(const char* msg);
// Called when problem with SIP connection(s) detected
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 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();
public:
// 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;
/// 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;
/// called when dialog enters the Early state - typically after getting 18x
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage&) override;
/// called when a dialog initiated as a UAC enters the connected state
virtual void onConnected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// 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;
/// 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.
virtual void onForkDestroyed(resip::ClientInviteSessionHandle) override;
/// called when a 3xx with valid targets is encountered in an early dialog
/// This is different then getting a 3xx in onTerminated, as another
/// request will be attempted, so the DialogSet will not be destroyed.
/// Basically an onTermintated that conveys more information.
/// checking for 3xx respones in onTerminated will not work as there may
/// be no valid targets.
virtual void onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an SDP answer is received - has nothing to do with user
/// answering the call
virtual void onAnswer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
/// called when an SDP offer is received - must send an answer soon after this
virtual void onOffer(resip::InviteSessionHandle, const resip::SipMessage& msg, const resip::SdpContents&) override;
/// called when an Invite w/out SDP is sent, or any other context which
/// requires an SDP offer from the user
virtual void onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called if an offer in a UPDATE or re-INVITE was rejected - not real
/// useful. A SipMessage is provided if one is available
virtual void onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage* msg) override;
/// called when INFO message is received
virtual void onInfo(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when response to INFO message is received
virtual void onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when MESSAGE message is received
virtual void onMessage(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when response to MESSAGE message is received
virtual void onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
virtual void onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message is received. The refer is accepted or
/// 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 onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage& msg) override;
/// called when an REFER message receives a failure response
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;
#pragma endregion
// ClientRegistrationHandler implementation
#pragma region ClientRegistrationHandler implementation
/// Called when registraion succeeds or each time it is sucessfully
/// refreshed.
void onSuccess(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
// Called when all of my bindings have been removed
void onRemoved(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
/// 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;
/// Called if registration fails, usage will be destroyed (unless a
/// Registration retry interval is enabled in the Profile)
void onFailure(resip::ClientRegistrationHandle, const resip::SipMessage& response) override;
#pragma endregion
#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) override;
#pragma endregion
#pragma region DnsResultSink implementation
virtual void onDnsResult(const resip::DNSResult<resip::DnsHostRecord>&) override;
virtual void onDnsResult(const resip::DNSResult<resip::DnsAAAARecord>&) override;
virtual void onDnsResult(const resip::DNSResult<resip::DnsSrvRecord>&) override;
virtual void onDnsResult(const resip::DNSResult<resip::DnsNaptrRecord>&) override;
virtual void onDnsResult(const resip::DNSResult<resip::DnsCnameRecord>&) override;
#pragma endregion
#pragma region TransportLogger implementation
void onSipMessage(int flow, const char* msg, unsigned int length, const sockaddr* addr, unsigned int addrlen) override;
#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;
#pragma endregion
#pragma region SubscriptionHandler
void onUpdate(resip::ClientSubscriptionHandle h, const resip::SipMessage& notify);
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
void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage& notify, bool outOfOrder) override;
int onRequestRetry(resip::ClientSubscriptionHandle, int retrySeconds, const resip::SipMessage& notify) override;
//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.
void onNewSubscription(resip::ClientSubscriptionHandle, const resip::SipMessage& notify) override;
/// called to allow app to adorn a message.
void onReadyToSend(resip::ClientSubscriptionHandle, resip::SipMessage& msg) override;
void onNotifyNotReceived(resip::ClientSubscriptionHandle) override;
/// Called when a TCP or TLS flow to the server has terminated. This can be caused by socket
/// errors, or missing CRLF keep alives pong responses from the server.
// Called only if clientOutbound is enabled on the UserProfile and the first hop server
/// supports RFC5626 (outbound).
/// Default implementation is to re-form the subscription using a new flow
void onFlowTerminated(resip::ClientSubscriptionHandle) override;
void onNewSubscription(resip::ServerSubscriptionHandle, const resip::SipMessage& sub) override;
void onTerminated(resip::ServerSubscriptionHandle) override;
#pragma endregion
#pragma region PagerHandler
void onSuccess(resip::ClientPagerMessageHandle, const resip::SipMessage& status) override;
void onFailure(resip::ClientPagerMessageHandle, const resip::SipMessage& status, std::auto_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;
// Smart pointer to resiprocate's master profile instance. The stack configuration holds here.
resip::SharedPtr<resip::MasterProfile> mProfile;
// Resiprocate's SIP stack object pointer
resip::SipStack* mStack;
// Resiprocate's dialog usage manager object pointer
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;
// Session's map
SessionMap mSessionMap;
// Used configuration
VariantMap mConfig;
// Action vector
SIPActionVector mActionVector;
typedef std::map<int, PClientObserver> ClientObserverMap;
ClientObserverMap mClientObserverMap;
typedef std::map<int, PServerObserver> ServerObserverMap;
ServerObserverMap mServerObserverMap;
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);
PSession getUserSession(int sessionId);
PAccount getAccount(const resip::NameAddr& myAddr);
PAccount getAccount(Account* account);
PAccount getAccount(int sessionId);
};
#endif

View File

@ -0,0 +1,182 @@
#include "EP_NetworkQueue.h"
#include "EP_Engine.h"
WatcherQueue::WatcherQueue(UserAgent& ua)
:mActiveId(0), mAgent(ua)
{}
WatcherQueue::~WatcherQueue()
{}
int WatcherQueue::add(std::string peer, std::string package, void* tag)
{
ice::Lock l(mGuard);
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
{
Item& item = mItemList[i];
if (item.mTarget == peer && item.mPackage == package &&
item.mState != Item::State_Deleting)
return item.mId;
}
Item item;
item.mTarget = peer;
item.mPackage = package;
item.mTag = tag;
item.mState = Item::State_ScheduledToAdd;
item.mSession = new ResipSession(*mAgent.mDum);
item.mSession->setUa(&mAgent);
item.mSession->setType(ResipSession::Type_Subscription);
item.mSession->setTag(tag);
item.mId = item.mSession->sessionId();
item.mSession->setRemoteAddress(peer);
item.mTag = tag;
mItemList.push_back(item);
process();
return item.mId;
}
void WatcherQueue::remove(int id)
{
ice::Lock l(mGuard);
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
{
Item& item = mItemList[i];
if (item.mId == id && !id)
{
if (item.mState != Item::State_Deleting)
item.mState = Item::State_ScheduledToDelete;
}
}
process();
}
void WatcherQueue::refresh(int id)
{
ice::Lock l(mGuard);
// Check if queue has similar item
for (unsigned i=0; i<mItemList.size(); i++)
{
Item& item = mItemList[i];
if (item.mId == id && !id)
{
if (item.mState == Item::State_ScheduledToDelete || item.mState == Item::State_Active)
item.mState = Item::State_ScheduledToRefresh;
}
}
process();
}
void WatcherQueue::process()
{
while (!mActiveId)
{
// Find next item to process
ItemList::iterator i = mItemList.begin();
for (;i != mItemList.end() && !i->scheduled(); i++)
;
if (i == mItemList.end())
return;
resip::SharedPtr<resip::SipMessage> msg;
int expires = DEFAULT_SUBSCRIPTION_TIME, refresh = DEFAULT_SUBSCRIPTION_REFRESHTIME;
switch (i->mState)
{
case Item::State_ScheduledToAdd:
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_TIME))
expires = mAgent.mConfig[CONFIG_SUBSCRIPTION_TIME].asInt();
if (mAgent.mConfig.exists(CONFIG_SUBSCRIPTION_REFRESHTIME))
refresh = mAgent.mConfig[CONFIG_SUBSCRIPTION_REFRESHTIME].asInt();
msg = mAgent.mDum->makeSubscription(resip::NameAddr(resip::Data(i->mTarget)), resip::Data(i->mPackage),
expires, refresh, i->mSession);
msg->header(resip::h_Accepts) = mAgent.mDum->getMasterProfile()->getSupportedMimeTypes(resip::NOTIFY);
mActiveId = i->mId;
i->mState = Item::State_Adding;
mAgent.mDum->send(msg);
break;
case Item::State_ScheduledToDelete:
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, 0, 0);
if (i->mHandle.isValid())
{
mActiveId = i->mId;
i->mHandle->end();
i->mState = Item::State_Deleting;
break;
}
else
mItemList.erase(i);
break;
case Item::State_ScheduledToRefresh:
if (i->mHandle.isValid())
{
mActiveId = i->mId;
i->mState = Item::State_Refreshing;
i->mHandle->requestRefresh();
}
else
mItemList.erase(i);
break;
default:
break;
}
}
}
void WatcherQueue::onTerminated(int id, int code)
{
ice::Lock l(mGuard);
ItemList::iterator i = findById(id);
if (i != mItemList.end())
{
if (i->mSession)
i->mSession->runTerminatedEvent(ResipSession::Type_Subscription, code, 0);
mItemList.erase(i);
if (i->mId == mActiveId)
mActiveId = 0;
}
process();
}
void WatcherQueue::onEstablished(int id, int code)
{
ice::Lock l(mGuard);
ItemList::iterator i = findById(id);
if (i != mItemList.end())
{
i->mState = Item::State_Active;
if (i->mId == mActiveId)
mActiveId = 0;
}
process();
}
WatcherQueue::ItemList::iterator WatcherQueue::findById(int id)
{
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
if (i->mId == id)
return i;
return mItemList.end();
}
void WatcherQueue::clear()
{
ice::Lock l(mGuard);
for (ItemList::iterator i=mItemList.begin(); i != mItemList.end(); i++)
{
if (i->mHandle.isValid())
i->mHandle->end();
}
mItemList.clear();
}

View File

@ -0,0 +1,69 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __NETWORK_QUEUE_H
#define __NETWORK_QUEUE_H
#include "EP_Session.h"
#include <resip/dum/ClientSubscription.hxx>
class UserAgent;
class WatcherQueue
{
public:
struct Item
{
enum State
{
State_None,
State_Active,
State_ScheduledToAdd,
State_Adding,
State_ScheduledToRefresh,
State_Refreshing,
State_ScheduledToDelete,
State_Deleting
};
resip::ClientSubscriptionHandle mHandle; // Subscription handle
ResipSession* mSession;
State mState;
std::string mTarget; // Target's address
std::string mPackage; // Event package
void* mTag; // User tag
int mId;
Item()
:mSession(NULL), mState(State_None), mTag(NULL), mId(0)
{}
bool scheduled()
{
return mState == State_ScheduledToAdd || mState == State_ScheduledToDelete || mState == State_ScheduledToRefresh;
}
};
WatcherQueue(UserAgent& agent);
~WatcherQueue();
int add(std::string peer, std::string package, void* tag);
void remove(int id);
void refresh(int id);
void clear();
void onTerminated(int id, int code);
void onEstablished(int id, int code);
protected:
typedef std::vector<Item> ItemList;
ItemList mItemList;
ice::Mutex mGuard;
UserAgent& mAgent;
int mActiveId;
void process();
ItemList::iterator findById(int id);
};
#endif

View File

@ -0,0 +1,106 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EP_Observer.h"
#include "EP_Session.h"
#include <resip/stack/Pidf.hxx>
#include <resip/dum/ClientSubscription.hxx>
ClientObserver::ClientObserver()
{
}
ClientObserver::~ClientObserver()
{
}
void ClientObserver::refresh()
{
if (mHandle.isValid())
mHandle->requestRefresh();
}
void ClientObserver::stop()
{
if (mHandle.isValid())
mHandle->end();
else
if (mSession)
{
mSession->runTerminatedEvent(ResipSession::Type_Subscription);
if (mSession)
mSession->end();
}
mSession = NULL;
}
std::string ClientObserver::peer()
{
return mPeer;
}
ServerObserver::ServerObserver()
:mState(State_Incoming)
{
}
ServerObserver::~ServerObserver()
{
stop();
}
std::string ServerObserver::peer() const
{
return mPeer;
}
std::string ServerObserver::package() const
{
return mPackage;
}
void ServerObserver::update(std::string simpleId, bool online, std::string msg)
{
if (mState != State_Active)
return;
resip::Pidf p;
p.setEntity(mContact);
p.setSimpleId(resip::Data(simpleId));
p.setSimpleStatus(online, resip::Data(msg));
if (mHandle.isValid())
mHandle->send(mHandle->update(&p));
}
void ServerObserver::accept()
{
if (mHandle.isValid() && mState == State_Incoming)
{
mState = State_Active;
mHandle->accept();
}
}
void ServerObserver::stop()
{
if (!mHandle.isValid())
return;
switch (mState)
{
case State_Incoming:
mHandle->reject(404);
break;
case State_Active:
mHandle->end();
break;
default:
break;
}
mState = State_Closed;
}

View File

@ -0,0 +1,73 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef EP_OBSERVER_H
#define EP_OBSERVER_H
#include "../helper/HL_Pointer.h"
#include "../helper/HL_VariantMap.h"
#include "ice/ICEAddress.h"
#include "ice/ICETime.h"
#include "resip/dum/UserProfile.hxx"
#include "resip/dum/ClientRegistration.hxx"
#include "resip/dum/ClientPublication.hxx"
#include "resip/stack/DnsInterface.hxx"
class UserAgent;
class Session;
class ResipSession;
class ClientObserver
{
friend class Account;
friend class UserAgent;
public:
ClientObserver();
~ClientObserver();
void refresh();
void stop();
std::string peer();
protected:
resip::ClientSubscriptionHandle mHandle;
ResipSession* mSession;
int mSessionId;
std::string mPeer;
};
typedef SharedPtr<ClientObserver> PClientObserver;
class ServerObserver
{
friend class UserAgent;
public:
ServerObserver();
~ServerObserver();
std::string peer() const;
std::string package() const;
void accept();
void update(std::string simpleId, bool online, std::string msg);
void stop();
protected:
enum State
{
State_Incoming,
State_Active,
State_Closed
};
State mState;
resip::ServerSubscriptionHandle mHandle;
std::string mPeer, mPackage;
resip::Uri mContact;
int mSessionId;
};
typedef SharedPtr<ServerObserver> PServerObserver;
#endif // EP_OBSERVER_H

View File

@ -0,0 +1,304 @@
/*
* Copyright (C) 2007-2012 Dmytro Bogovych <dmytro.bogovych@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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
*/
#ifdef _WIN32
# include <winsock2.h>
# include <windows.h>
#endif
#include <algorithm>
#include "EP_ReliableTunnel.h"
#include "EP_Engine.h"
#include "Log.h"
#include "../ICE/ICECRC32.h"
enum
{
CONFIRMATION_PT = 1,
DATA_PT = 2
};
#define CONFIRMATION_TIMEOUT 500
#define LOG_SUBSYSTEM "RT"
ReliableTunnel::ReliableTunnel(const char* streamname)
{
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);
}
std::string ReliableTunnel::streamName()
{
return mStreamName;
}
std::string ReliableTunnel::streamProfile()
{
return "RTP/DP";
}
void ReliableTunnel::setDestinationAddress(InternetAddress& addr)
{
mDestination = addr;
}
void ReliableTunnel::queueData(const void* bufferptr, int buffersize)
{
assert(bufferptr != NULL);
assert(buffersize != 0);
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;
case AF_INET6:
mSocket4.sendDatagram(addr, dataBuffer, datasize);
return;
}
}
void ReliableTunnel::sessionEstablished(int conntype)
{
// Start worker thread
if (conntype == EV_ICE)
run();
}
void ReliableTunnel::sessionTerminated()
{
// 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));
}
void ReliableTunnel::setSocket(DatagramSocket& socket4, DatagramSocket& socket6)
{
mSocket4 = socket4;
mSocket6 = socket6;
}
DatagramSocket& ReliableTunnel::socket(int family)
{
switch (family)
{
case AF_INET:
return mSocket4;
case AF_INET6:
return mSocket4;
default:
assert(0);
}
}
bool ReliableTunnel::processSdpOffer(const resip::SdpContents::Session::Medium& media)
{
//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)
{
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;
}
}
void ReliableTunnel::setBandwidth(unsigned int bytesPerSecond)
{
mBandwidth = bytesPerSecond;
}
unsigned int ReliableTunnel::bandwidth()
{
return mBandwidth;
}
void ReliableTunnel::processData(const void* dataptr, int datasize)
{
resip::Lock l(mStackGuard);
mStack.processIncoming(dataptr, datasize);
}
bool ReliableTunnel::hasData()
{
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;
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;
}
}
return dataOut - (char*)ptr;
}
// Returns block size for encryption algorythm
int ReliableTunnel::blockSize()
{
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;
#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);
#endif
#ifdef USE_CRYPTOPP
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;
#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);
#endif
#ifdef USE_CRYPTOPP
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;
}
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);
}
}
void ReliableTunnel::setEncryptionKey(void* ptr, unsigned length)
{
#ifdef USE_OPENSSL
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);
#endif
// Save key
mEncryptionKey = std::string((const char*)ptr, length);
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (C) 2007-2010 Dmytro Bogovych <dmytro.bogovych@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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
*/
#ifndef __RELIABLE_TUNNEL_H
#define __RELIABLE_TUNNEL_H
#include "DataProvider.h"
#include "InternetAddress.h"
#include "rutil/ThreadIf.hxx"
#include <vector>
#include <string>
#include "../ICE/ICEReliableTransport.h"
#ifdef USE_CRYPTOPP
# include "../Libs/CryptoPP/blowfish.h"
#endif
#ifdef USE_OPENSSL
# include "../Libs/openssl/include/openssl/blowfish.h"
#endif
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);
// Processes incoming data
virtual void processData(const void* dataBuffer, int dataSize);
// 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);
// 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);
virtual void thread();
// 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;
// Transport stack
ICEImpl::ReliableTransport mStack;
// 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;
#ifdef USE_CRYPTOPP
CryptoPP::BlowfishEncryption mEncryptor;
CryptoPP::BlowfishDecryption mDecryptor;
#endif
#ifdef USE_OPENSSL
BF_KEY mCipher;
#endif
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);
// Decrypts dataPtr buffer inplace. dataSize must be odd to GetBlockSize() returned value.
void decrypt(void* dataPtr, int dataSize);
// Calculates CRC
unsigned crc(const void* dataptr, int datasize);
void sendOutgoing();
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,410 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __SESSION_H
#define __SESSION_H
#include "resip/stack/SdpContents.hxx"
#include "resip/stack/SipMessage.hxx"
#include "resip/stack/ShutdownMessage.hxx"
#include "resip/stack/SipStack.hxx"
#include "resip/dum/ClientAuthManager.hxx"
#include "resip/dum/ClientInviteSession.hxx"
#include "resip/dum/ClientRegistration.hxx"
#include "resip/dum/DialogUsageManager.hxx"
#include "resip/dum/DumShutdownHandler.hxx"
#include "resip/dum/InviteSessionHandler.hxx"
#include "resip/dum/MasterProfile.hxx"
#include "resip/dum/RegistrationHandler.hxx"
#include "resip/dum/ServerInviteSession.hxx"
#include "resip/dum/ServerOutOfDialogReq.hxx"
#include "resip/dum/OutOfDialogHandler.hxx"
#include "resip/dum/AppDialog.hxx"
#include "resip/dum/AppDialogSet.hxx"
#include "resip/dum/AppDialogSetFactory.hxx"
#include "rutil/Log.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Random.hxx"
#include "rutil/WinLeakCheck.hxx"
#include "rutil/AtomicCounter.hxx"
#include "../ice/ICEBox.h"
#include <sstream>
#include <time.h>
#include "../config.h"
#include "EP_Session.h"
#include "EP_Account.h"
#include "EP_DataProvider.h"
#include "EP_AudioProvider.h"
#include "../helper/HL_VariantMap.h"
#include "../helper/HL_SocketHeap.h"
using namespace std;
class UserAgent;
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_Duration,
SessionInfo_Jitter,
SessionInfo_Rtt,
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
SessionInfo_RemotePeer,
SessionInfo_SSRC
};
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)
{
mPort4 = mPort6 = 0;
mComponentId.mRtp = mComponentId.mRtcp = -1;
}
RtpPair<int> mComponentId;
int mStreamId;
unsigned short mPort4;
unsigned short mPort6;
};
// Describes media stream (audio/video) in session
class Stream
{
public:
Stream();
~Stream();
void setProvider(PDataProvider provider);
PDataProvider provider();
void setSocket4(const RtpPair<PDatagramSocket>& socket);
RtpPair<PDatagramSocket>& socket4();
void setSocket6(const RtpPair<PDatagramSocket>& socket);
RtpPair<PDatagramSocket>& socket6();
void setIceInfo(const IceInfo& info);
IceInfo iceInfo() const;
// rtcpAttr/rtcpMuxAttr signals about corresponding sip attribute in offer/answer from remote peer
bool rtcpAttr() const;
void setRtcpAttr(bool value);
bool rtcpMuxAttr() const;
void setRtcpMuxAttr(bool value);
protected:
// Provider for corresponding stream
PDataProvider mProvider;
// Socket for stream
RtpPair<PDatagramSocket> mSocket4, mSocket6;
bool mRtcpAttr;
bool mRtcpMuxAttr;
IceInfo mIceInfo;
};
Session(PAccount account);
virtual ~Session();
// Starts call to specified peer
void start(const std::string& peer);
// Stops call
void stop();
// Accepts call
void accept();
// Rejects call
void reject(int code);
enum class InfoOptions
{
Standard = 0,
Detailed = 1,
};
void getSessionInfo(InfoOptions options, VariantMap& result);
// Returns integer identifier of the session; it is unique amongst all session in application
int id() const;
// Returns owning account
PAccount account();
typedef std::map<std::string, std::string> UserHeaders;
void setUserHeaders(const UserHeaders& headers);
// Called when new media data are available for this session
void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize);
// Called when new candidate is gathered
void onCandidateGathered(ice::Stack* stack, void* tag, const char* address);
// Called when connectivity check is finished
void onCheckFinished(ice::Stack* stack, void* tag, const char* checkDescription);
// Called when ICE candidates are gathered - with success or timeout.
void onGathered(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 connectivity check is failed for all of required streams
void onFailed(ice::Stack* stack, void* tag);
// Called when ICE stack detects network change during the call
void onNetworkChange(ice::Stack* stack, void* tag);
// Fills SDP according to ICE and provider's data
void buildSdp(resip::SdpContents& sdp, SdpDirection sdpDirection);
// Searches provider by its local port number
PDataProvider findProviderByPort(int family, unsigned short port);
// Add provider to internal list
void addProvider(PDataProvider provider);
PDataProvider providerAt(int index);
int getProviderCount();
void setUserAgent(UserAgent* agent);
UserAgent* userAgent();
// Pauses and resumes all providers; updates states
void pause();
void resume();
void refreshMediaPath();
// Processes new sdp from offer. Returns response code (200 is ok, 488 bad codec, 503 internal error).
int processSdp(UInt64 version, bool iceAvailable, std::string icePwd, std::string iceUfrag,
std::string remoteIp, const resip::SdpContents::Session::MediumContainer& media);
// Session ID
int mSessionId;
// Media streams collection
std::vector<Stream> mStreamList;
// Smart pointer to ICE stack. Actually stack is created in CreateICEStack() method
resip::SharedPtr<ice::Stack> mIceStack;
// Pointer to owner user agent instance
UserAgent* mUserAgent;
// Remote peer SIP address
resip::NameAddr mRemotePeer;
// Mutex to protect this instance
Mutex mGuard;
// SDP's origin version for sending
int mOriginVersion;
UInt64 mRemoteOriginVersion;
// SDP's session version
int mSessionVersion;
// Marks if this session does not need OnNewSession event
bool mAcceptedByEngine;
bool mAcceptedByUser;
// Invite session handle
resip::InviteSessionHandle mInviteHandle;
// Dialog set object pointer
ResipSession* mResipSession;
// Reference counter
int mRefCount;
enum
{
Initiator = 1,
Acceptor = 2
};
// Specifies session role - caller (Initiator) or callee (Acceptor)
volatile int mRole;
// Marks if candidates are gather already
volatile bool mGatheredCandidates;
// Marks if OnTerminated event was called already on session
volatile bool mTerminated;
// User friend remote peer's sip address
std::string mRemoteAddress;
// Application specific data
void* mTag;
// Used to count number of transistions to Connected state and avoid multiple onEstablished events.
int mOfferAnswerCounter;
// List of turn prefixes related to sessioj
std::vector<int> mTurnPrefixList;
// True if user agent has to send offer
bool mHasToSendOffer;
// True if user agent has to enqueue offer after ice gather finished
bool mSendOfferUpdateAfterIceGather;
// Related sip account
PAccount mAccount;
// User headers for INVITE transaction
UserHeaders mUserHeaders;
std::string remoteAddress() const;
void setRemoteAddress(const std::string& address);
void* tag();
void setTag(void* tag);
int sessionId();
int increaseSdpVersion();
int addRef();
int release();
// Deletes providers and media sockets
void clearProvidersAndSockets();
// 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 resip::AtomicCounter IdGenerator;
static resip::AtomicCounter InstanceCounter;
};
typedef SharedPtr<Session> PSession;
/////////////////////////////////////////////////////////////////////////////////
//
// 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();
};
class ResipSession: public resip::AppDialogSet
{
friend class UserAgent;
friend class Account;
public:
enum Type
{
Type_None,
Type_Registration,
Type_Subscription,
Type_Call,
Type_Auto
};
static resip::AtomicCounter InstanceCounter;
ResipSession(resip::DialogUsageManager& dum);
virtual ~ResipSession();
virtual resip::AppDialog* createAppDialog(const resip::SipMessage& msg);
virtual resip::SharedPtr<resip::UserProfile> selectUASUserProfile(const resip::SipMessage& msg);
void setType(Type type);
Type type();
Session* session();
void setSession(Session* session);
UserAgent* ua();
void setUa(UserAgent* ua);
// Used for subscriptions/messages
int sessionId();
// Used for subscriptions/messages
void* tag() const;
void setTag(void* tag);
// Used for subscriptions/messages
std::string remoteAddress() const;
void setRemoteAddress(std::string address);
void runTerminatedEvent(Type type, int code = 0, int reason = 0);
void setUASProfile(SharedPtr<resip::UserProfile> profile);
protected:
bool mTerminated;
UserAgent* mUserAgent;
Type mType;
Session* mSession;
int mSessionId;
std::string mRemoteAddress;
void* mTag;
bool mOnWatchingStartSent;
SharedPtr<resip::UserProfile> mUASProfile;
};
class ResipSessionFactory : public resip::AppDialogSetFactory
{
public:
ResipSessionFactory(UserAgent* agent);
virtual resip::AppDialogSet* createAppDialogSet(resip::DialogUsageManager& dum, const resip::SipMessage& msg);
protected:
UserAgent* mAgent;
};
#endif

View File

@ -0,0 +1,29 @@
project (helper_lib)
# Rely on C++ 11
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (HELPER_LIB_SOURCES
HL_AsyncCommand.cpp
HL_Calculator.cpp
HL_CsvReader.cpp
HL_Epoll.cpp
HL_HepSupport.cpp
HL_IuUP.cpp
HL_Log.cpp
HL_NetworkFrame.cpp
HL_NetworkSocket.cpp
HL_OsVersion.cpp
HL_Pointer.cpp
HL_Rtp.cpp
HL_Singletone.cpp
HL_SocketHeap.cpp
HL_String.cpp
HL_Sync.cpp
HL_Usb.cpp
HL_Uuid.cpp
HL_VariantMap.cpp
)
add_library(helper_lib ${HELPER_LIB_SOURCES})

View File

@ -0,0 +1,16 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HL_AsyncCommand.h"
AsyncCommand::AsyncCommand()
{
}
AsyncCommand::~AsyncCommand()
{
}

View File

@ -0,0 +1,19 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef HL_ASYNCCOMMAND_H
#define HL_ASYNCCOMMAND_H
class AsyncCommand
{
public:
AsyncCommand();
virtual ~AsyncCommand();
virtual void run(void* environment) = 0;
virtual bool finished() = 0;
};
#endif // HL_ASYNCCOMMAND_H

View File

@ -0,0 +1,256 @@
#ifndef HL_BASE64_H
#define HL_BASE64_H
#include <string>
const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"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];
out->resize(EncodedLength(in));
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);
for (i = 0; i < 4; i++) {
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) {
a3[j] = '\0';
}
a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++) {
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
}
while ((i++ < 3)) {
(*out)[enc_len++] = '=';
}
}
return (enc_len == out->size());
}
static bool Encode(const char *input, size_t input_length, char *out, size_t out_length) {
int i = 0, j = 0;
char *out_begin = out;
unsigned char a3[3];
unsigned char a4[4];
size_t encoded_length = EncodedLength(input_length);
if (out_length < encoded_length) return false;
while (input_length--) {
a3[i++] = *input++;
if (i == 3) {
a3_to_a4(a4, a3);
for (i = 0; i < 4; i++) {
*out++ = kBase64Alphabet[a4[i]];
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) {
a3[j] = '\0';
}
a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++) {
*out++ = kBase64Alphabet[a4[j]];
}
while ((i++ < 3)) {
*out++ = '=';
}
}
return (out == (out_begin + encoded_length));
}
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]);
}
a4_to_a3(a3,a4);
for (i = 0; i < 3; i++) {
(*out)[dec_len++] = 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)[dec_len++] = a3[j];
}
}
return (dec_len == out->size());
}
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];
size_t decoded_length = DecodedLength(input, input_length);
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]);
}
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));
}
static int DecodedLength(const char *in, size_t in_length) {
int numEq = 0;
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 * n) / 8) - numEq;
}
inline static int EncodedLength(size_t length) {
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
}
inline static int EncodedLength(const std::string &in) {
return EncodedLength(in.length());
}
inline static void StripPadding(std::string *in) {
while (!in->empty() && *(in->rbegin()) == '=') in->resize(in->size() - 1);
}
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

View File

@ -0,0 +1,18 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HL_BYTEBUFFER_H
#define __HL_BYTEBUFFER_H
#include "ice/ICEByteBuffer.h"
typedef ice::ByteBuffer ByteBuffer;
typedef ice::PByteBuffer PByteBuffer;
typedef ice::BitReader BitReader;
typedef ice::BitWriter BitWriter;
typedef ice::BufferReader BufferReader;
typedef ice::BufferWriter BufferWriter;
#endif

View File

View File

@ -0,0 +1,625 @@
#ifndef __HL_CALCULATOR_H
#define __HL_CALCULATOR_H
#include <iostream>
#include <string>
#include "helper/HL_VariantMap.h"
#include "helper/HL_String.h"
#include "helper/HL_InternetAddress.h"
namespace Calc
{
class Parser;
namespace Ast
{
enum class Type
{
None,
And,
Or,
Equal,
NotEqual,
Less,
LessOrEqual,
Greater,
GreatorOrEqual,
Add,
Sub,
Mul,
Div,
Number,
String,
Var
};
class Item;
typedef Item* PItem;
class Item
{
friend class Calc::Parser;
public:
bool isVariable() const
{
return mType == Type::Var;
}
bool isFixed() const
{
return mType == Type::Number || mType == Type::String;
}
bool isOperation() const
{
return mType >= Type::And && mType <= Type::Div;
}
bool hasBrackets() const
{
return mHasBrackets;
}
int getOperatorLevel() const
{
switch (mType)
{
case Type::Or:
return -2;
case Type::And:
return -1;
case Type::Equal:
case Type::NotEqual:
return 0;
case Type::Less:
case Type::LessOrEqual:
case Type::Greater:
case Type::GreatorOrEqual:
return 1;
case Type::Add:
case Type::Sub:
return 2;
case Type::Mul:
case Type::Div:
return 3;
default:
return 4;
}
assert(0);
}
Type getType() const
{
return mType;
}
std::string getName() const
{
return mName;
}
Variant& value()
{
return mValue;
}
std::vector<PItem>& children()
{
return mChildren;
}
typedef std::map<std::string, std::string> NameMap;
std::ostream& print(std::ostream& oss, const NameMap& nm)
{
oss << " ( ";
if (isOperation())
mChildren.front()->print(oss, nm);
oss << " ";
switch (mType)
{
case Type::Number: oss << mValue.asStdString(); break;
case Type::String: oss << '"' << mValue.asStdString() << '"'; break;
case Type::Var: { NameMap::const_iterator iter = nm.find(mName); oss << ((iter != nm.end()) ? iter->second : mName);} break;
case Type::Add: oss << "+"; break;
case Type::Mul: oss << "*"; break;
case Type::Div: oss << "/"; break;
case Type::Sub: oss << "-"; break;
case Type::Equal: oss << "=="; break;
case Type::NotEqual: oss << "!="; break;
case Type::Less: oss << "<"; break;
case Type::LessOrEqual: oss << "<="; break;
case Type::Greater: oss << ">"; break;
case Type::GreatorOrEqual: oss << ">="; break;
case Type::Or: oss << "or"; break;
case Type::And: oss << "and"; break;
default:
throw std::runtime_error("operator expected");
}
oss << " ";
if (isOperation() && mChildren.size() == 2 && mChildren.back())
mChildren.back()->print(oss, nm);
oss << " ) ";
return oss;
}
typedef std::map<std::string, Variant> ValueMap;
Variant eval(const ValueMap& vm)
{
Variant result, left, right;
if (isOperation())
{
left = mChildren.front()->eval(vm);
right = mChildren.back()->eval(vm);
}
switch (mType)
{
case Type::Number:
case Type::String: result = mValue; break;
case Type::Var: { auto iter = vm.find(mName); if (iter != vm.end()) return iter->second; else throw std::runtime_error("Variable " + mName + " did not find."); }
break;
case Type::Add: result = left + right; break;
case Type::Mul: result = left * right; break;
case Type::Div: result = left / right; break;
case Type::Sub: result = left - right; break;
case Type::Equal: result = left == right; break;
case Type::NotEqual: result = left != right; break;
case Type::Less: result = left < right; break;
case Type::LessOrEqual: result = left <= right; break;
case Type::Greater: result = left > right; break;
case Type::GreatorOrEqual: result = left >= right; break;
case Type::Or: result = left.asBool() || right.asBool(); break;
case Type::And: result = left.asBool() && right.asBool(); break;
default:
assert(0);
}
return result;
}
~Item()
{
for (auto node: mChildren)
delete node;
mChildren.clear();
}
private:
Type mType = Type::None;
std::string mName;
Variant mValue;
std::vector<PItem> mChildren;
bool mHasBrackets = false;
};
}
static bool ishex(int c)
{
if (isdigit(c))
return true;
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
class Parser
{
private:
enum class LexemType
{
None,
Hex,
Dec,
Float,
Str,
Oper,
Var,
OpenBracket,
CloseBracket
};
struct Lexem
{
LexemType mType = LexemType::None;
std::string mValue;
operator bool () const
{
return mType != LexemType::None;
}
std::string toString() const
{
return std::to_string((int)mType) + " : " + mValue;
}
};
Lexem mCurrentLexem;
Lexem processNewLexem(int c)
{
Lexem result;
if (c == '(')
mCurrentLexem.mType = LexemType::OpenBracket;
else
if (c == ')')
mCurrentLexem.mType = LexemType::CloseBracket;
else
if (isdigit(c))
mCurrentLexem.mType = LexemType::Dec;
else
if (isalpha(c))
mCurrentLexem.mType = LexemType::Var;
else
if (c == '+' || c == '-' || c == '/' || c == '*' || c == '=' || c == '<' || c == '>' || c == '&' || c == '|')
mCurrentLexem.mType = LexemType::Oper;
else
if (c == '"')
mCurrentLexem.mType = LexemType::Str;
else
return Lexem();
mCurrentLexem.mValue.push_back(c);
// Can we return result here already ?
if (mCurrentLexem.mType == LexemType::OpenBracket || mCurrentLexem.mType == LexemType::CloseBracket)
{
// Lexem finished
result = mCurrentLexem;
mCurrentLexem = Lexem();
return result;
}
if (mCurrentLexem.mType == LexemType::Oper)
{
if (mCurrentLexem.mValue == "+" ||
mCurrentLexem.mValue == "-" ||
mCurrentLexem.mValue == "*" ||
mCurrentLexem.mValue == "/" ||
mCurrentLexem.mValue == ">=" ||
mCurrentLexem.mValue == "<=" ||
mCurrentLexem.mValue == "==" ||
mCurrentLexem.mValue == "||" ||
mCurrentLexem.mValue == "&&")
{
// Lexem finished
result = mCurrentLexem;
mCurrentLexem = Lexem();
return result;
}
}
return Lexem();
}
void checkNumericLexem()
{
if (mCurrentLexem.mType != LexemType::Dec)
return;
// Check if there is ".:" characters
if (mCurrentLexem.mValue.find('.') != std::string::npos)
{
// Dot is here - is it float
bool isFloat = false;
StringHelper::toFloat(mCurrentLexem.mValue, 0.0f, &isFloat);
if (isFloat)
mCurrentLexem.mType = LexemType::Float;
else
{
// Maybe it is IP4/6 address ?
InternetAddress addr(mCurrentLexem.mValue, 8000);
if (!addr.isEmpty())
{
mCurrentLexem.mValue = "\"" + mCurrentLexem.mValue + "\"";
mCurrentLexem.mType = LexemType::Str;
}
}
}
}
Lexem getLexem(std::istream& input)
{
Lexem result;
// Iterate while characters avaialbe from input stream & lexem is not finished
bool putback = false;
int c = input.get();
while (!input.eof() && c && result.mType == LexemType::None)
{
switch (mCurrentLexem.mType)
{
case LexemType::None:
result = processNewLexem(c);
break;
case LexemType::Hex:
if (!ishex(c))
{
// Finish Hex lexem
result = mCurrentLexem;
putback = true;
}
else
mCurrentLexem.mValue.push_back(c);
break;
case LexemType::Dec:
if (c == 'x' && mCurrentLexem.mValue == "0")
mCurrentLexem.mType = LexemType::Hex;
else
if (isdigit(c) || c == '.')
{
mCurrentLexem.mValue.push_back(c);
}
else
{
checkNumericLexem();
result = mCurrentLexem;
putback = true;
}
break;
case LexemType::Oper:
// It must be one of two-characters operations
if (c == '<' || c == '>' || c == '=' || c == '&' || c == '|')
{
mCurrentLexem.mValue.push_back(c);
result = mCurrentLexem;
mCurrentLexem = Lexem();
}
else
{
result = mCurrentLexem;
putback = true;
}
break;
case LexemType::Var:
if (isdigit(c) || isalpha(c) || c == '.' || c == '_')
{
mCurrentLexem.mValue.push_back(c);
}
else
{
result = mCurrentLexem;
putback = true;
}
break;
case LexemType::Str:
mCurrentLexem.mValue.push_back(c);
if (c == '"')
{
result = mCurrentLexem;
// String lexem is finished
mCurrentLexem.mType = LexemType::None;
putback = false;
}
break;
default:
assert(0);
}
if (putback)
input.putback(c);
else
if (!result)
c = input.get();
}
checkNumericLexem();
// Recover partially processed lexem - maybe we finish processing at all but there is dec / float / string / variable
if (mCurrentLexem.mType != LexemType::None && result.mType == LexemType::None)
result = mCurrentLexem;
// Reset current lexem
mCurrentLexem = Lexem();
return result;
}
// Make AST node from lexem
Ast::PItem makeAst(const Lexem& l)
{
Ast::PItem result(new Ast::Item());
switch (l.mType)
{
case LexemType::Oper:
if (l.mValue == "-")
result->mType = Ast::Type::Sub;
else
if (l.mValue == "+")
result->mType = Ast::Type::Add;
else
if (l.mValue == "*")
result->mType = Ast::Type::Mul;
else
if (l.mValue == "/")
result->mType = Ast::Type::Div;
else
if (l.mValue == "<")
result->mType = Ast::Type::Less;
else
if (l.mValue == "<=")
result->mType = Ast::Type::LessOrEqual;
else
if (l.mValue == ">")
result->mType = Ast::Type::Greater;
else
if (l.mValue == ">=")
result->mType = Ast::Type::GreatorOrEqual;
else
if (l.mValue == "==")
result->mType = Ast::Type::Equal;
else
if (l.mValue == "!=")
result->mType = Ast::Type::NotEqual;
else
if (l.mValue == "&&")
result->mType = Ast::Type::And;
else
if (l.mValue == "||")
result->mType = Ast::Type::Or;
break;
case LexemType::Var:
result->mType = Ast::Type::Var;
result->mName = l.mValue;
break;
case LexemType::Dec:
result->mType = Ast::Type::Number;
result->mValue = atoi(l.mValue.c_str());
break;
case LexemType::Hex:
result->mType = Ast::Type::Number;
result->mValue = StringHelper::fromHex2Int(l.mValue);
break;
case LexemType::Float:
result->mType = Ast::Type::Number;
result->mValue = (float)atof(l.mValue.c_str());
break;
case LexemType::Str:
result->mType = Ast::Type::String;
result->mValue = l.mValue.substr(1, l.mValue.size() - 2);
break;
default:
throw std::runtime_error("Unexpected lexem.");
}
return result;
}
Lexem mLexem;
public:
Ast::PItem parseExpression(std::istream& input)
{
Ast::PItem operationNode(nullptr), leftNode(nullptr), rightNode(nullptr),
currentOperation(nullptr);
// While we have lexem
while (mLexem = getLexem(input))
{
std::cout << "Returned lexem: " << mLexem.toString() << std::endl;
if (!leftNode)
{
// It must be first operand!
switch (mLexem.mType)
{
case LexemType::OpenBracket:
leftNode = parseExpression(input);
leftNode->mHasBrackets = true;
break;
case LexemType::CloseBracket:
throw std::runtime_error("Expected +/-/constant/variable here.");
case LexemType::Dec:
case LexemType::Hex:
case LexemType::Str:
case LexemType::Var:
case LexemType::Float:
leftNode = makeAst(mLexem);
break;
default:
throw std::runtime_error("Open bracket or constant / number / string / variable expected.");
}
}
else
if (!operationNode)
{
// Well, there is left node already
// See operation here
switch (mLexem.mType)
{
case LexemType::Oper:
operationNode = makeAst(mLexem);
break;
case LexemType::None:
// Finish the tree building
break;
case LexemType::CloseBracket:
// Finish the tree building in this level
if (leftNode)
return leftNode;
break;
default:
throw std::runtime_error("Expected operation.");
}
// Parse rest of expression
rightNode = parseExpression(input);
// If right part of expression is operation - make left side child of right part - to allow calculation in right order
if (operationNode)
{
if (rightNode->isOperation() && rightNode->getOperatorLevel() <= operationNode->getOperatorLevel() && !rightNode->hasBrackets())
{
// Get left child of right expression - make it our right child
operationNode->children().push_back(leftNode);
operationNode->children().push_back(rightNode->children().front());
rightNode->children().front() = operationNode;
currentOperation = rightNode;
}
else
{
operationNode->children().push_back(leftNode);
operationNode->children().push_back(rightNode);
currentOperation = operationNode;
}
}
if (mLexem.mType == LexemType::CloseBracket)
break; // Exit from loop
}
}
return currentOperation ? currentOperation : leftNode;
}
public:
Ast::PItem parse(std::istream& input)
{
return nullptr;
}
void testLexemParser(const std::string& test)
{
std::istringstream iss(test);
for (Lexem l = getLexem(iss); l.mType != LexemType::None; l = getLexem(iss))
{
std::cout << "Lexem type: " << (int)l.mType << ", value: " << l.mValue << std::endl;
}
}
};
class Worker
{
public:
Variant eval(Ast::PItem ast);
};
}
#endif

View File

@ -0,0 +1,26 @@
#include "HL_CsvReader.h"
#include "HL_String.h"
// --------- CsvFile ----------------
CsvReader::CsvReader(std::istream& stream)
:mInputStream(stream)
{}
CsvReader::~CsvReader()
{}
std::istream& CsvReader::stream() const
{
return mInputStream;
}
bool CsvReader::readLine(std::vector<std::string>& cells)
{
cells.clear();
std::string line;
if (!std::getline(mInputStream, line))
return false;
StringHelper::split(line, cells, ",;");
return true;
}

View File

@ -0,0 +1,23 @@
#ifndef __HL_CSVREADER_H
#define __HL_CSVREADER_H
#include <string>
#include <istream>
#include <vector>
class CsvReader
{
public:
CsvReader(std::istream& stream);
~CsvReader();
void setStream(std::istream& input);
std::istream& stream() const;
bool readLine(std::vector<std::string>& cells);
protected:
std::istream& mInputStream;
};
#endif

View File

View File

View File

@ -0,0 +1,85 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __EXCEPTION_H
#define __EXCEPTION_H
#include <exception>
#include <memory.h>
#include <stdio.h>
#include <string.h>
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_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.
};
class Exception: public std::exception
{
public:
Exception(int code, int subcode = 0)
:mCode(code), mSubcode(subcode)
{
sprintf(mMessage, "%d-%d", code, subcode);
}
Exception(int code, const char* message)
{
if (message)
strncpy(mMessage, message, (sizeof mMessage) - 1 );
}
Exception(const Exception& src)
:mCode(src.mCode), mSubcode(src.mSubcode)
{
memcpy(mMessage, src.mMessage, sizeof mMessage);
}
~Exception()
{ }
int code() const
{
return mCode;
}
int subcode() const
{
return mSubcode;
}
const char* what() const noexcept
{
return mMessage;
}
protected:
int mCode, mSubcode;
char mMessage[256];
};
#endif

View File

@ -0,0 +1,220 @@
#include "HL_HepSupport.h"
using namespace HEP;
static const uint32_t HEPID1 = 0x011002;
static const uint32_t HEPID2 = 0x021002;
static const uint32_t HEPID3 = 0x48455033;
bool Packet::parseV3(const ByteBuffer& packet)
{
if (packet.size() < 30)
return false;
BufferReader r(packet);
char signature[4];
r.readBuffer(signature, 4);
if (signature[0] != 'H' || signature[1] != 'E' || signature[2] != 'P' || signature[3] != '3')
return false;
// Total length
int l = r.readUShort();
l -= 6;
InternetAddress sourceAddr4, destAddr4, sourceAddr6, destAddr6;
uint16_t sourcePort = 0, destPort = 0;
while (r.count() < packet.size())
{
mVendorId = (VendorId)r.readUShort();
ChunkType chunkType = (ChunkType)r.readUShort();
int chunkLength = r.readUShort();
switch (chunkType)
{
case ChunkType::IPProtocolFamily:
mIpProtocolFamily = r.readUChar();
break;
case ChunkType::IPProtocolID:
mIpProtocolId = r.readUChar();
break;
case ChunkType::IP4SourceAddress:
sourceAddr4 = r.readIp(AF_INET);
break;
case ChunkType::IP4DestinationAddress:
destAddr4 = r.readIp(AF_INET);
break;
case ChunkType::IP6SourceAddress:
sourceAddr6 = r.readIp(AF_INET);
break;
case ChunkType::IP6DestinationAddress:
destAddr6 = r.readIp(AF_INET6);
break;
case ChunkType::SourcePort:
sourcePort = r.readUShort();
break;
case ChunkType::DestinationPort:
destPort = r.readUShort();
break;
case ChunkType::Timestamp:
mTimestamp.tv_sec = r.readUInt();
break;
case ChunkType::TimestampMicro:
mTimestamp.tv_usec = r.readUInt() * 1000;
break;
case ChunkType::ProtocolType:
mProtocolType = (ProtocolId)r.readUChar();
break;
case ChunkType::CaptureAgentID:
mCaptureAgentId = r.readUInt();
break;
case ChunkType::KeepAliveTimer:
mKeepAliveTimer = r.readUShort();
break;
case ChunkType::AuthenticationKey:
mAuthenticateKey.resize(chunkLength - 6);
r.readBuffer(mAuthenticateKey.mutableData(), mAuthenticateKey.size());
break;
case ChunkType::PacketPayload:
r.readBuffer(mBody, chunkLength - 6);
break;
default:
r.readBuffer(nullptr, chunkLength - 6);
}
}
if (!sourceAddr4.isEmpty())
mSourceAddress = sourceAddr4;
else
if (!sourceAddr6.isEmpty())
mSourceAddress = sourceAddr6;
if (!mSourceAddress.isEmpty())
mSourceAddress.setPort(sourcePort);
if (!destAddr4.isEmpty())
mDestinationAddress = destAddr4;
else
if (!destAddr6.isEmpty())
mDestinationAddress = destAddr6;
if (!mDestinationAddress.isEmpty())
mDestinationAddress.setPort(destPort);
return true;
}
bool Packet::parseV2(const ByteBuffer &packet)
{
if (packet.size() < 31)
return false;
if (packet[0] != 0x02)
return false;
BufferReader r(packet);
r.readBuffer(nullptr, 4);
uint16_t sourcePort = r.readUShort();
uint16_t dstPort = r.readUShort();
mSourceAddress = r.readIp(AF_INET);
mSourceAddress.setPort(sourcePort);
mDestinationAddress = r.readIp(AF_INET);
mDestinationAddress.setPort(dstPort);
mTimestamp.tv_sec = r.readUInt();
mTimestamp.tv_usec = r.readUInt() * 1000;
mCaptureAgentId = r.readUShort();
r.readBuffer(nullptr, 2);
mBody.clear();
r.readBuffer(mBody, 65536 - 28);
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());}
ByteBuffer Packet::buildV3()
{
ByteBuffer r; r.resize(mBody.size() + 512);
BufferWriter w(r);
// Signature
w.writeBuffer("HEP3", 4);
// Reserve place for total length
w.writeUShort(0);
WRITE_CHUNK_UCHAR(ChunkType::IPProtocolFamily, mIpProtocolFamily);
WRITE_CHUNK_UCHAR(ChunkType::IPProtocolID, mIpProtocolId);
// Source address
if (!mSourceAddress.isEmpty())
{
if (mSourceAddress.isV4())
WRITE_CHUNK_IP4(ChunkType::IP4SourceAddress, mSourceAddress)
else
if (mSourceAddress.isV6())
WRITE_CHUNK_IP6(ChunkType::IP6SourceAddress, mSourceAddress);
WRITE_CHUNK_USHORT(ChunkType::SourcePort, mSourceAddress.port());
}
// Destination address
if (!mDestinationAddress.isEmpty())
{
if (mDestinationAddress.isV4())
WRITE_CHUNK_IP4(ChunkType::IP4DestinationAddress, mDestinationAddress)
else
if (mDestinationAddress.isV6())
WRITE_CHUNK_IP6(ChunkType::IP6DestinationAddress, mDestinationAddress);
WRITE_CHUNK_USHORT(ChunkType::DestinationPort, mDestinationAddress.port());
}
// Timestamp
WRITE_CHUNK_UINT(ChunkType::Timestamp, mTimestamp.tv_sec);
// TimestampMicro
WRITE_CHUNK_UINT(ChunkType::TimestampMicro, mTimestamp.tv_usec / 1000);
// Protocol type
WRITE_CHUNK_UINT(ChunkType::ProtocolType, mProtocolType);
// Capture agent ID
WRITE_CHUNK_UINT(ChunkType::CaptureAgentID, mCaptureAgentId);
// Keep alive timer value
WRITE_CHUNK_USHORT(ChunkType::KeepAliveTimer, mKeepAliveTimer);
// Authentication key
WRITE_CHUNK_BUFFER(ChunkType::AuthenticationKey, mAuthenticateKey);
// Payload
WRITE_CHUNK_BUFFER(ChunkType::PacketPayload, mBody);
r.resize(w.offset());
w.rewind(); w.skip(4); w.writeUShort((uint16_t)r.size());
return r;
}

View File

@ -0,0 +1,84 @@
#ifndef __HELPER_HEP_SUPPORT_H
#define __HELPER_HEP_SUPPORT_H
#include "HL_ByteBuffer.h"
#include "HL_InternetAddress.h"
namespace HEP
{
enum class ChunkType
{
None = 0,
IPProtocolFamily,
IPProtocolID,
IP4SourceAddress,
IP4DestinationAddress,
IP6SourceAddress,
IP6DestinationAddress,
SourcePort,
DestinationPort,
Timestamp,
TimestampMicro,
ProtocolType, // Maps to Protocol Types below
CaptureAgentID,
KeepAliveTimer,
AuthenticationKey,
PacketPayload,
CompressedPayload,
InternalC
};
enum class VendorId
{
None,
FreeSwitch,
Kamailio,
OpenSIPS,
Asterisk,
Homer,
SipXecs
};
enum class ProtocolId
{
Reserved = 0,
SIP,
XMPP,
SDP,
RTP,
RTCP,
MGCP,
MEGACO,
M2UA,
M3UA,
IAX,
H322,
H321
};
struct Packet
{
bool parseV3(const ByteBuffer& packet);
bool parseV2(const ByteBuffer& packet);
ByteBuffer buildV3();
uint8_t
mIpProtocolFamily,
mIpProtocolId;
InternetAddress
mSourceAddress,
mDestinationAddress;
timeval mTimestamp;
ProtocolId mProtocolType;
uint16_t mCaptureAgentId;
uint16_t mKeepAliveTimer;
ByteBuffer mAuthenticateKey;
ByteBuffer mBody;
VendorId mVendorId;
};
}
#endif

View File

@ -0,0 +1,12 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HL_INTERNETADDRESS_H
#define __HL_INTERNETADDRESS_H
#include "ice/ICEAddress.h"
typedef ice::NetworkAddress InternetAddress;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
#ifndef HL_IUUP_H
#define HL_IUUP_H
#include <memory>
class IuUP
{
public:
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;
};
/* 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);
};
#endif // HL_IUUP_H

View File

@ -0,0 +1,5 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -0,0 +1,20 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __LOG_H
#define __LOG_H
#include "ice/ICELog.h"
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_NONE;
#endif

View File

@ -0,0 +1,141 @@
#include "HL_NetworkFrame.h"
#include "HL_InternetAddress.h"
#define ETHERTYPE_MPLS_UC (0x8847)
#define ETHERTYPE_MPLS_MC (0x8848)
#define MPLS_STACK_MASK (0x00000100)
#define MPLS_STACK_SHIFT (8)
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{
PacketData result(packet);
const EthernetHeader* ethernet = reinterpret_cast<const EthernetHeader*>(packet.mData);
// Skip ethernet header
packet.mData += sizeof(EthernetHeader);
packet.mLength -= sizeof(EthernetHeader);
// See if there is Vlan header
uint16_t proto = 0;
if (ethernet->mEtherType == 129)
{
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData);
packet.mData += sizeof(VlanHeader);
packet.mLength -= sizeof(VlanHeader);
proto = ntohs(vlan->mData);
}
// Skip MPLS headers
if (proto == ETHERTYPE_MPLS_UC || proto == ETHERTYPE_MPLS_MC)
{
// Parse MPLS here until marker "bottom of mpls stack"
for(bool bottomOfStack = false; !bottomOfStack;
bottomOfStack = ((ntohl(*(uint32_t*)(packet.mData - 4)) & MPLS_STACK_MASK) >> MPLS_STACK_SHIFT) != 0)
{
packet.mData += 4;
packet.mLength -=4;
}
}
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
if (ip4->mProtocol != IPPROTO_UDP)
return PacketData();
switch (ip4->version())
{
case 4:
return GetUdpPayloadForIp4(packet, source, destination);
case 6:
return GetUdpPayloadForIp6(packet, source, destination);
default:
return PacketData();
}
}
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForSLL(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{
PacketData result(packet);
if (packet.mLength < 16)
return PacketData();
const LinuxSllHeader* sll = reinterpret_cast<const LinuxSllHeader*>(packet.mData);
packet.mData += sizeof(LinuxSllHeader);
packet.mLength -= sizeof(LinuxSllHeader);
switch (ntohs(sll->mProtocolType))
{
case 0x0800:
return GetUdpPayloadForIp4(packet, source, destination);
case 0x86DD:
return GetUdpPayloadForIp6(packet, source, destination);
default:
return PacketData();
}
}
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp4(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{
PacketData result(packet);
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
if (ip4->mProtocol != IPPROTO_UDP)
return PacketData(nullptr, 0);
result.mData += ip4->headerLength();
result.mLength -= ip4->headerLength();
const UdpHeader* udp = reinterpret_cast<const UdpHeader*>(result.mData);
result.mData += sizeof(UdpHeader);
result.mLength -= sizeof(UdpHeader);
// Check if UDP payload length is smaller than full packet length. It can be VLAN trailer data - we need to skip it
size_t length = ntohs(udp->mDatagramLength);
if (length - sizeof(UdpHeader) < (size_t)result.mLength)
result.mLength = length - sizeof(UdpHeader);
source.setIp(ip4->mSource);
source.setPort(ntohs(udp->mSourcePort));
destination.setIp(ip4->mDestination);
destination.setPort(ntohs(udp->mDestinationPort));
return result;
}
NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForIp6(NetworkFrame::PacketData& packet, InternetAddress& source, InternetAddress& destination)
{
PacketData result(packet);
const Ip4Header* ip4 = reinterpret_cast<const Ip4Header*>(packet.mData);
if (ip4->mProtocol != IPPROTO_UDP)
return PacketData(nullptr, 0);
result.mData += ip4->headerLength();
result.mLength -= ip4->headerLength();
const UdpHeader* udp = reinterpret_cast<const UdpHeader*>(packet.mData);
result.mData += sizeof(UdpHeader);
result.mLength -= sizeof(UdpHeader);
/*
if (result.mLength != ntohs(udp->mDatagramLength))
return PacketData(nullptr, 0);
*/
source.setIp(ip4->mSource);
source.setPort(ntohs(udp->mSourcePort));
destination.setIp(ip4->mDestination);
destination.setPort(ntohs(udp->mDestinationPort));
return result;
}

View File

@ -0,0 +1,114 @@
#ifndef _HL_NETWORK_FRAME_H
#define _HL_NETWORK_FRAME_H
#include <stdint.h>
#include "HL_InternetAddress.h"
class NetworkFrame
{
public:
struct PacketData
{
const uint8_t* mData;
int mLength;
PacketData(const uint8_t* data, int length)
:mData(data), mLength(length)
{}
PacketData()
:mData(nullptr), mLength(0)
{}
};
static PacketData GetUdpPayloadForEthernet(PacketData& packet, InternetAddress& source, InternetAddress& destination);
static PacketData GetUdpPayloadForIp4(PacketData& packet, InternetAddress& source, InternetAddress& destination);
static PacketData GetUdpPayloadForIp6(PacketData& packet, InternetAddress& source, InternetAddress& destination);
static PacketData GetUdpPayloadForSLL(PacketData& packet, InternetAddress& source, InternetAddress& destination);
struct EthernetHeader
{
/* 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 */
};
struct __attribute__((packed)) LinuxSllHeader
{
uint16_t mPacketType;
uint16_t mARPHRD;
uint16_t mAddressLength;
uint64_t mAddress;
uint16_t mProtocolType;
};
struct VlanHeader
{
uint16_t mMagicId;
uint16_t mData;
};
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 */
int headerLength() const
{
return (mVhl & 0x0f) * 4;
}
int version() const
{
return mVhl >> 4;
}
};
struct UdpHeader
{
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;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#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 */
};
};
#endif

View File

@ -0,0 +1,180 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID)
# include <asm/ioctls.h>
#endif
#include "../config.h"
#include "HL_NetworkSocket.h"
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
# include <fcntl.h>
#endif
#include <unistd.h>
#include <assert.h>
DatagramSocket::DatagramSocket()
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
{
}
DatagramSocket::~DatagramSocket()
{
closeSocket();
}
void DatagramSocket::open(int family)
{
if (mHandle != INVALID_SOCKET || mFamily != family)
closeSocket();
assert(family == AF_INET || family == AF_INET6);
mFamily = 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));
socklen_t l = mFamily == AF_INET ? sizeof(addr4) : sizeof(addr6);
int retcode = getsockname(mHandle, (mFamily == AF_INET ? (sockaddr*)&addr4 : (sockaddr*)&addr6), &l);
if (!retcode)
{
mLocalPort = ntohs(mFamily == AF_INET ? addr4.sin_port : addr6.sin6_port);
}
}
}
int DatagramSocket::localport()
{
return mLocalPort;
}
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());
}
unsigned DatagramSocket::recvDatagram(InternetAddress &src, void *packetBuffer, unsigned packetCapacity)
{
if (mHandle == INVALID_SOCKET)
return 0;
sockaddr_in sourceaddr;
#ifdef WIN32
int addrlen = sizeof(sourceaddr);
#else
socklen_t addrlen = sizeof(sourceaddr);
#endif
int received = ::recvfrom(mHandle, (char*)packetBuffer, packetCapacity, 0, (sockaddr*)&sourceaddr, &addrlen);
if (received > 0)
{
src = InternetAddress((sockaddr&)sourceaddr, addrlen);
return received;
}
else
return 0;
}
void DatagramSocket::closeSocket()
{
if (mHandle != INVALID_SOCKET)
{
#ifdef WIN32
::closesocket(mHandle);
#else
close(mHandle);
#endif
mHandle = INVALID_SOCKET;
}
}
bool DatagramSocket::isValid() const
{
return mHandle != INVALID_SOCKET;
}
int DatagramSocket::family() const
{
return mFamily;
}
bool DatagramSocket::setBlocking(bool blocking)
{
#if defined(TARGET_WIN)
unsigned long mode = blocking ? 0 : 1;
return (ioctlsocket(mHandle, FIONBIO, &mode) == 0) ? true : false;
#endif
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
int flags = fcntl(mHandle, F_GETFL, 0);
if (flags < 0)
return false;
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
return (fcntl(mHandle, F_SETFL, flags) == 0) ? true : false;
#endif
#if defined(TARGET_ANDROID)
unsigned long mode = blocking ? 0 : 1;
return (ioctl(mHandle, FIONBIO, &mode) == 0) ? true : false;
#endif
return false;
}
SOCKET DatagramSocket::socket() const
{
return mHandle;
}
DatagramAgreggator::DatagramAgreggator()
{
FD_ZERO(&mReadSet);
mMaxHandle = 0;
}
DatagramAgreggator::~DatagramAgreggator()
{
}
void DatagramAgreggator::addSocket(PDatagramSocket socket)
{
if (socket->mHandle == INVALID_SOCKET)
return;
FD_SET(socket->mHandle, &mReadSet);
if (socket->mHandle > mMaxHandle)
mMaxHandle = socket->mHandle;
mSocketVector.push_back(socket);
}
unsigned DatagramAgreggator::count()
{
return mSocketVector.size();
}
bool DatagramAgreggator::hasDataAtIndex(unsigned index)
{
PDatagramSocket socket = mSocketVector[index];
return (FD_ISSET(socket->mHandle, &mReadSet) != 0);
}
PDatagramSocket DatagramAgreggator::socketAt(unsigned index)
{
return mSocketVector[index];
}
bool DatagramAgreggator::waitForData(unsigned milliseconds)
{
timeval tv;
tv.tv_sec = milliseconds / 1000;
tv.tv_usec = (milliseconds % 1000) * 1000;
int rescode = ::select(mMaxHandle, &mReadSet, NULL, NULL, &tv);
return rescode > 0;
}

View File

@ -0,0 +1,66 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __NETWORK_SOCKET_H
#define __NETWORK_SOCKET_H
#include "HL_InternetAddress.h"
#include <vector>
#include <memory>
class NetworkSocket
{
public:
virtual int localport() = 0;
};
class DatagramSocket
{
friend class SocketHeap;
friend class DatagramAgreggator;
public:
DatagramSocket();
virtual ~DatagramSocket();
virtual int localport();
virtual void sendDatagram(InternetAddress& dest, const void* packetData, unsigned packetSize);
virtual unsigned recvDatagram(InternetAddress& src, void* packetBuffer, unsigned packetCapacity);
virtual void closeSocket();
virtual bool isValid() const;
virtual int family() const;
virtual bool setBlocking(bool blocking);
virtual SOCKET socket() const;
virtual void open(int family);
protected:
int mFamily;
SOCKET mHandle;
int mLocalPort;
};
typedef std::shared_ptr<DatagramSocket> PDatagramSocket;
class DatagramAgreggator
{
public:
DatagramAgreggator();
~DatagramAgreggator();
void addSocket(PDatagramSocket socket);
unsigned count();
bool hasDataAtIndex(unsigned index);
PDatagramSocket socketAt(unsigned index);
bool waitForData(unsigned milliseconds);
protected:
typedef std::vector<PDatagramSocket> SocketList;
SocketList mSocketVector;
fd_set mReadSet;
SOCKET mMaxHandle;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HL_OsVersion.h"
#ifdef TARGET_WIN
#include <windows.h>
#if defined(USE_MINIDUMP)
# include <DbgHelp.h>
#endif
int winVersion()
{
DWORD dwVersion = 0;
DWORD dwMajorVersion = 0;
DWORD dwMinorVersion = 0;
DWORD dwBuild = 0;
dwVersion = GetVersion();
// Get the Windows version.
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
// Get the build number.
if (dwVersion < 0x80000000)
dwBuild = (DWORD)(HIWORD(dwVersion));
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 );
if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )
{
// Create the minidump
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = ExceptionInfo;
mdei.ClientPointers = FALSE;
MINIDUMP_TYPE mdt = MiniDumpWithFullMemory;
BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(),
hFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, 0 );
// Close the file
CloseHandle( hFile );
}
else
{
}
return EXCEPTION_CONTINUE_SEARCH;
}
static LPTOP_LEVEL_EXCEPTION_FILTER OldExceptionHandler = nullptr;
void CrashMiniDump::registerHandler()
{
OldExceptionHandler = ::SetUnhandledExceptionFilter(&MyExceptionHandler);
}
void CrashMiniDump::unregisterHandler()
{
::SetUnhandledExceptionFilter(nullptr);
}
#endif
#endif
#ifdef TARGET_IOS
int iosVersion()
{
return 4; // Stick with this for now
}
#endif
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
#include <stdio.h>
#include <sys/select.h>
#include <termios.h>
#include <sys/ioctl.h>
int _kbhit()
{
static const int STDIN = 0;
static bool initialized = false;
if (! initialized) {
// Use termios to turn off line buffering
termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initialized = true;
}
int bytesWaiting;
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
#endif

View File

@ -0,0 +1,51 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __OS_VERSION_H
#define __OS_VERSION_H
#ifdef TARGET_WIN
enum
{
Win_Xp = 0,
Win_Vista = 1,
Win_Seven = 2,
Win_Eight = 3,
Win_Ten = 4
};
extern int winVersion();
class CrashMiniDump
{
public:
static void registerHandler();
static void unregisterHandler();
};
extern void writeMiniDump();
#endif
#ifdef TARGET_IOS
int iosVersion();
#endif
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
#include <stdio.h>
#include <sys/select.h>
#include <termios.h>
#if defined(TARGET_LINUX)
# include <stropts.h>
#endif
extern int _kbhit();
#endif
#endif

View File

@ -0,0 +1,55 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HL_Pointer.h"
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();
}
int UsageCounter::release(int usageId)
{
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();
}
int UsageCounter::usageCount()
{
Lock l(mGuard);
UsageMap::const_iterator usageIter;
int result = 0;
for (usageIter = mUsage.begin(); usageIter != mUsage.end(); usageIter++)
result += usageIter->second;
return result;
}
void UsageCounter::clear()
{
Lock l(mGuard);
mUsage.clear();
}

View File

@ -0,0 +1,34 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __SMART_POINTER_H
#define __SMART_POINTER_H
#ifdef USE_NATIVE_SMARTPTR
# include <memory>
# define SharedPtr std::shared_ptr
#else
#include "../../libs/resiprocate/rutil/SharedPtr.hxx"
using resip::SharedPtr;
#endif
#include "HL_Sync.h"
#include <map>
class UsageCounter
{
public:
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;
};
#endif

View File

@ -0,0 +1,222 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HL_Rtp.h"
#include "HL_Exception.h"
#include "HL_String.h"
#include "rtprawpacket.h"
#include "rtpipv4address.h"
#include <alloc.h>
#include <sstream>
#include <tuple>
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 short seq; /* sequence number */
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:8; /* payload type */
uint16_t len; /* length */
uint32_t ssrc; /* synchronization source */
};
bool RtpHelper::isRtp(const void* buffer, int length)
{
if (length < 12)
return false;
unsigned char _type = reinterpret_cast<const RtpHeader*>(buffer)->pt;
bool rtp = ( (_type & 0x7F) >= 96 && (_type & 0x7F) < 127) || ((_type & 0x7F) < 35);
return rtp;
}
bool RtpHelper::isRtpOrRtcp(const void* buffer, int length)
{
if (length < 12)
return false;
unsigned char b = ((const unsigned char*)buffer)[0];
return (b & 0xC0 ) == 128;
}
bool RtpHelper::isRtcp(const void* buffer, int length)
{
return (isRtpOrRtcp(buffer, length) && !isRtp(buffer, length));
}
unsigned RtpHelper::findSsrc(const void* buffer, int length)
{
if (isRtp(buffer, length))
return reinterpret_cast<const RtpHeader*>(buffer)->ssrc;
else
return reinterpret_cast<const RtcpHeader*>(buffer)->ssrc;
}
int RtpHelper::findPtype(const void* buffer, int length)
{
if (isRtp(buffer, length))
return reinterpret_cast<const RtpHeader*>(buffer)->pt;
else
return -1;
}
int RtpHelper::findPayloadLength(const void* buffer, int length)
{
if (isRtp(buffer, length))
{
return length - 12;
}
else
return -1;
}
RtpDump::RtpDump(const char *filename)
:mFilename(filename)
{}
RtpDump::~RtpDump()
{
flush();
for (PacketList::iterator packetIter=mPacketList.begin(); packetIter!=mPacketList.end(); ++packetIter)
{
//free(packetIter->mData);
delete packetIter->mPacket;
}
}
void RtpDump::load()
{
FILE* f = fopen(mFilename.c_str(), "rb");
if (!f)
throw Exception(ERR_WAVFILE_FAILED);
while (!feof(f))
{
RtpData data;
fread(&data.mLength, sizeof data.mLength, 1, f);
data.mData = new char[data.mLength];
fread(data.mData, 1, data.mLength, f);
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
jrtplib::RTPTime t(0);
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)data.mData, data.mLength, &addr, t, true);
data.mPacket = new jrtplib::RTPPacket(*raw);
mPacketList.push_back(data);
}
}
int RtpDump::count() const
{
return mPacketList.size();
}
jrtplib::RTPPacket& RtpDump::packetAt(int index)
{
return *mPacketList[index].mPacket;
}
void RtpDump::add(const void* buffer, int len)
{
RtpData data;
data.mData = malloc(len);
memcpy(data.mData, buffer, len);
data.mLength = len;
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address);
jrtplib::RTPTime t(0);
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)const_cast<void*>(data.mData), data.mLength, &addr, t, true);
data.mPacket = new jrtplib::RTPPacket(*raw);
//delete raw;
mPacketList.push_back(data);
}
void RtpDump::flush()
{
FILE* f = fopen(mFilename.c_str(), "wb");
if (!f)
throw Exception(ERR_WAVFILE_FAILED);
PacketList::iterator packetIter = mPacketList.begin();
for (;packetIter != mPacketList.end(); ++packetIter)
{
RtpData& data = *packetIter;
// Disabled for debugging only
//fwrite(&data.mLength, sizeof data.mLength, 1, f);
fwrite(data.mData, data.mLength, 1, f);
}
fclose(f);
}
// -------------- MediaStreamId --------------------
bool MediaStreamId::operator < (const MediaStreamId& right) const
{
if (mSsrcIsId)
return std::tie(mSSRC, mSource, mDestination) < std::tie(right.mSSRC, right.mSource, right.mDestination);
else
return std::tie(mSource, mDestination) < std::tie(right.mSource, right.mDestination);
}
bool MediaStreamId::operator == (const MediaStreamId& right) const
{
if (mSsrcIsId)
return std::tie(mSSRC, mSource, mDestination) == std::tie(right.mSSRC, right.mSource, right.mDestination);
else
return std::tie(mSource, mDestination) == std::tie(right.mSource, right.mDestination);
}
std::string MediaStreamId::toString() const
{
std::ostringstream oss;
oss << "src: " << mSource.toStdString() <<
" dst: " << mDestination.toStdString() <<
" ssrc: " << StringHelper::toHex(mSSRC);
return oss.str();
}
void writeToJson(const MediaStreamId& id, std::ostringstream& oss)
{
oss << " \"src\": \"" << id.mSource.toStdString() << "\"," << std::endl
<< " \"dst\": \"" << id.mDestination.toStdString() << "\"," << std::endl
<< " \"ssrc\": \"" << StringHelper::toHex(id.mSSRC) << "\"," << std::endl
<< " \"link_id\": \"" << id.mLinkId.toString() << "\"" << std::endl;
}
std::string MediaStreamId::getDetectDescription() const
{
std::ostringstream oss;
oss << "{\"event\": \"stream_detected\"," << std::endl;
writeToJson(*this, oss);
oss << "}";
return oss.str();
}
std::string MediaStreamId::getFinishDescription() const
{
std::ostringstream oss;
oss << "{" << std::endl
<< " \"event\": \"stream_finished\", " << std::endl;
writeToJson(*this, oss);
oss << "}";
return oss.str();
}

View File

@ -0,0 +1,85 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HL_RTP_H
#define __HL_RTP_H
#include "jrtplib/src/rtppacket.h"
#include "HL_Uuid.h"
#include "HL_InternetAddress.h"
#include <vector>
#include <string>
// Class to carry rtp/rtcp socket pair
template<class T>
struct RtpPair
{
T mRtp;
T mRtcp;
RtpPair()
{}
RtpPair(const T& rtp, const T& rtcp)
:mRtp(rtp), mRtcp(rtcp)
{}
bool multiplexed() { return mRtp == mRtcp; }
};
class RtpHelper
{
public:
static bool isRtp(const void* buffer, int length);
static int findPtype(const void* buffer, int length);
static bool isRtpOrRtcp(const void* buffer, int length);
static bool isRtcp(const void* buffer, int length);
static unsigned findSsrc(const void* buffer, int length);
static int findPayloadLength(const void* buffer, int length);
};
class RtpDump
{
protected:
struct RtpData
{
jrtplib::RTPPacket* mPacket;
void* mData;
unsigned mLength;
};
typedef std::vector<RtpData> PacketList;
PacketList mPacketList;
std::string mFilename;
public:
RtpDump(const char* filename);
~RtpDump();
void load();
int count() const;
jrtplib::RTPPacket& packetAt(int index);
void add(const void* data, int len);
void flush();
};
struct MediaStreamId
{
InternetAddress mSource;
InternetAddress mDestination;
uint32_t mSSRC = 0;
bool mSsrcIsId = true;
Uuid mLinkId;
bool operator < (const MediaStreamId& s2) const;
bool operator == (const MediaStreamId& right) const;
std::string toString() const;
std::string getDetectDescription() const;
std::string getFinishDescription() const;
};
#endif

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,38 @@
#ifndef __HL_SINGLETONE_H
#define __HL_SINGLETONE_H
#include <atomic>
#include <mutex>
template <class T>
class SafeSingleton
{
protected:
static std::atomic<T*> SharedInstance;
static std::mutex mMutex;
public:
static T& instance()
{
T* tmp = SharedInstance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (tmp == nullptr)
{
std::lock_guard<std::mutex> lock(mMutex);
tmp = SharedInstance.load(std::memory_order_relaxed);
if (tmp == nullptr)
{
tmp = new T();
std::atomic_thread_fence(std::memory_order_release);
SharedInstance.store(tmp, std::memory_order_relaxed);
}
}
return *tmp;
}
};
template <class T>
std::atomic<T*> SafeSingleton<T>::SharedInstance;
template <class T>
std::mutex SafeSingleton<T>::mMutex;
#endif

View File

@ -0,0 +1,290 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../config.h"
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#endif
#include <set>
#include <assert.h>
#include "HL_SocketHeap.h"
#include "HL_Log.h"
#include "HL_Sync.h"
#include "HL_Exception.h"
#define LOG_SUBSYSTEM "[SocketHeap]"
#ifndef WIN32
#define WSAGetLastError(X) errno
#define closesocket(X) close(X)
#define WSAEADDRINUSE EADDRINUSE
#endif
// ----------------------------- SocketHeap -------------------------
SocketHeap::SocketHeap(unsigned short start, unsigned short finish)
{
mStart = start;
mFinish = finish;
}
SocketHeap::~SocketHeap()
{
stop();
}
void SocketHeap::start()
{
#if defined(USE_RESIP_INTEGRATION)
if (!mId)
run();
#else
#endif
}
void SocketHeap::stop()
{
#if defined(USE_RESIP_INTEGRATION)
if (mId)
{
shutdown();
// Wait for worker thread
join();
}
#endif
}
void SocketHeap::setRange(unsigned short start, unsigned short finish)
{
assert(mStart <= mFinish);
Lock l(mGuard);
mStart = start;
mFinish = finish;
}
void SocketHeap::range(unsigned short &start, unsigned short &finish)
{
Lock l(mGuard);
start = mStart;
finish = mFinish;
}
RtpPair<PDatagramSocket> SocketHeap::allocSocketPair(int family, SocketSink *sink, Multiplex m)
{
PDatagramSocket rtp, rtcp;
for (int attempt=0; (!rtp || !rtcp) && attempt < (mFinish - mStart)/2; attempt++)
{
// Allocate RTP
try
{
rtp = allocSocket(family, sink);
if (m == DoMultiplexing)
rtcp = rtp;
else
rtcp = allocSocket(family, sink, rtp->localport() + 1);
}
catch(...)
{}
}
if (!rtp || !rtcp)
{
if (rtp)
freeSocket(rtp);
if (rtcp)
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());
return RtpPair<PDatagramSocket>(rtp, rtcp);
}
void SocketHeap::freeSocketPair(const RtpPair<PDatagramSocket> &p)
{
freeSocket(p.mRtp);
freeSocket(p.mRtcp);
}
PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
{
Lock l(mGuard);
SOCKET sock = ::socket(family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
{
// Return null socket
PDatagramSocket result(new DatagramSocket());
result->mLocalPort = port;
result->mFamily = family;
return result;
}
// Obtain port number
sockaddr_in addr;
sockaddr_in6 addr6;
int result;
int testport;
do
{
testport = port ? port : rand() % ((mFinish - mStart) / 2) * 2 + mStart;
switch (family)
{
case AF_INET:
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(testport);
result = ::bind(sock, (const sockaddr*)&addr, sizeof addr);
if (result)
result = WSAGetLastError();
break;
case AF_INET6:
memset(&addr6, 0, sizeof addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(testport);
result = ::bind(sock, (const sockaddr*)&addr6, sizeof addr6);
if (result)
result = WSAGetLastError();
break;
}
} while (result == WSAEADDRINUSE);
if (result)
{
closesocket(sock);
throw Exception(ERR_NET_FAILED, WSAGetLastError());
}
PDatagramSocket resultObject(new DatagramSocket());
resultObject->mLocalPort = testport;
resultObject->mHandle = sock;
if (!resultObject->setBlocking(false))
{
resultObject->closeSocket();
throw Exception(ERR_NET_FAILED, WSAGetLastError());
}
// Put socket object to the map
mSocketMap[sock].mSink = sink;
mSocketMap[sock].mSocket = resultObject;
return resultObject;
}
void SocketHeap::freeSocket(PDatagramSocket socket)
{
if (!socket)
return;
Lock l(mDeleteGuard);
mDeleteVector.push_back(socket);
}
void SocketHeap::processDeleted()
{
Lock l(mDeleteGuard);
SocketVector::iterator socketIter = mDeleteVector.begin();
while (socketIter != mDeleteVector.end())
{
// Find socket to delete in main socket map
SocketMap::iterator itemIter = mSocketMap.find((*socketIter)->mHandle);
if (itemIter != mSocketMap.end())
{
// If found - delete socket object from map
mSocketMap.erase(itemIter);
}
socketIter++;
}
mDeleteVector.clear();
}
void SocketHeap::thread()
{
/*#ifdef __linux__
// TODO: make epoll implementation for massive polling
#else*/
while (!isShutdown())
{
// Define socket agreggator
DatagramAgreggator agreggator;
// Make a protected copy of sockets
{
Lock l(mGuard);
// Remove deleted sockets from map and close them
{
processDeleted();
}
// Update socket set
for (SocketMap::iterator socketIter = mSocketMap.begin(); socketIter != mSocketMap.end(); ++socketIter)
{
// Add handle to set
agreggator.addSocket(socketIter->second.mSocket);
}
}
// If set is not empty
if (agreggator.count() > 0)
{
if (agreggator.waitForData(10))
{
ICELogMedia(<< "There is data on UDP sockets");
Lock l(mGuard);
// Remove deleted sockets to avoid call non-existant sinks
processDeleted();
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);
// Find corresponding data sink
SocketMap::iterator socketItemIter = mSocketMap.find(sock->mHandle);
if (socketItemIter != mSocketMap.end())
{
InternetAddress src;
unsigned received = sock->recvDatagram(src, mTempPacket, sizeof mTempPacket);
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
}
}
else
SyncHelper::delay(1000); // Delay for 1 millisecond
}
mId = 0;
//#endif
}
static SocketHeap GRTPSocketHeap(20002, 25100);
SocketHeap& SocketHeap::instance()
{
return GRTPSocketHeap;
}

View File

@ -0,0 +1,114 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __SOCKET_HEAP_H
#define __SOCKET_HEAP_H
#include "../config.h"
#include <map>
#include <vector>
#include <algorithm>
#ifdef USE_RESIP_INTEGRATION
# include "resiprocate/rutil/ThreadIf.hxx"
#else
# include <thread>
#endif
#include "HL_NetworkSocket.h"
#include "HL_Sync.h"
#include "HL_Rtp.h"
// Class is used to process incoming datagrams
class SocketSink
{
public:
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
#ifdef USE_RESIP_INTEGRATION
class SocketHeap: public resip::ThreadIf
#else
class SocketHeap: public std::thread
#endif
{
public:
enum Multiplex
{
DoMultiplexing,
DontMultiplexing
};
SocketHeap(unsigned short start, unsigned short finish);
~SocketHeap();
static SocketHeap& instance();
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);
// Returns used port number range
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);
// Stops receiving data for specified socket and frees socket itself.
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);
protected:
struct SocketItem
{
// Local port number for socket
PDatagramSocket mSocket;
// Data sink pointer
SocketSink* mSink;
SocketItem()
:mSink(NULL)
{ }
SocketItem(unsigned short portnumber, SocketSink* sink)
:mSink(sink)
{
mSocket->mLocalPort = portnumber;
}
~SocketItem()
{ }
};
typedef std::map<SOCKET, SocketItem> SocketMap;
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;
char mTempPacket[MAX_UDPPACKET_SIZE];
virtual void thread();
// Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector
void processDeleted();
};
#endif

View File

@ -0,0 +1,24 @@
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HL_STREAM_STATE
#define __HL_STREAM_STATE
// How to use stream state flags.
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
};
#endif

View File

@ -0,0 +1,439 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HL_String.h"
#include <sstream>
#include <iomanip>
#include <memory.h>
#include <algorithm>
#ifdef TARGET_WIN
# include <WinSock2.h>
# include <Windows.h>
# include <cctype>
#endif
std::string StringHelper::extractFilename(const std::string& path)
{
// Look for separator from end of string
for (int i = path.size() - 1; i >= 0; i--)
{
if (path[i] == '/' || path[i] == '\\')
return path.substr(i+1);
}
return "";
}
std::string StringHelper::makeUtf8(const std::tstring &arg)
{
#if defined(TARGET_WIN)
size_t required = WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, NULL, 0, NULL, NULL);
char *result = (char*)_alloca(required + 1);
WideCharToMultiByte(CP_UTF8, 0, arg.c_str(), -1, result, required+1, NULL, NULL);
return result;
#else
return arg;
#endif
}
std::tstring StringHelper::makeTstring(const std::string& arg)
{
#if defined(TARGET_WIN)
size_t count = MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, NULL, 0);
wchar_t* result = (wchar_t*)_alloca(count * 2);
MultiByteToWideChar(CP_UTF8, 0, arg.c_str(), -1, result, count);
return result;
#else
return arg;
#endif
}
int StringHelper::toInt(const char *s, int defaultValue, bool* isOk)
{
int result;
if (sscanf(s, "%d", &result) != 1)
{
if (isOk)
*isOk = false;
result = defaultValue;
}
else
if (isOk)
*isOk = true;
return result;
}
uint64_t StringHelper::toUint64(const char* s, uint64_t def, bool *isOk)
{
uint64_t result = def;
#if defined(TARGET_WIN)
if (sscanf(s, "%d", &result) != 1)
#else
if (sscanf(s, "%llu", &result) != 1)
#endif
{
if (isOk)
*isOk = false;
result = def;
}
else
if (isOk)
*isOk = true;
return result;
}
std::string StringHelper::toHex(unsigned int value)
{
char buffer[32];
sprintf(buffer, "%x", value);
return buffer;
}
std::string StringHelper::toHex(const void *ptr)
{
std::ostringstream oss;
oss << std::hex << ptr;
return oss.str();
}
//must be lowercase for MD5
static const char hexmap[] = "0123456789abcdef";
std::string StringHelper::toHex(const uint8_t* input, size_t inputLength)
{
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)
{
unsigned char temp = *p++;
int hi = (temp & 0xf0)>>4;
int low = (temp & 0xf);
*r++ = hexmap[hi];
*r++ = hexmap[low];
}
*r = 0;
return result;
}
std::string StringHelper::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))
{
oss << prefix << line << std::endl;
}
return oss.str();
}
std::string StringHelper::doubleToString(double value, int precision)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(precision) << value;
return ss.str();
}
const char* StringHelper::findSubstring(const char* buffer, const char* substring, size_t bufferLength)
{
#if defined(TARGET_WIN)
return (const char*)strstr(buffer, substring);
#else
return (const char*)memmem(buffer, bufferLength, substring, strlen(substring));
#endif
}
void StringHelper::split(const std::string& src, std::vector<std::string>& dst, const std::string& delims)
{
dst.clear();
std::string::size_type p = 0;
while (p < src.size())
{
std::string::size_type f = src.find_first_of(delims, p);
if (f == std::string::npos)
{
std::string t = src.substr(p);
if (!t.empty())
dst.push_back(t);
p = src.size();
}
else
{
std::string t = src.substr(p, f-p);
if (!t.empty())
dst.push_back(t);
p = f + 1;
}
}
}
std::pair<std::string, int> StringHelper::parseHost(const std::string& host, int defaultPort)
{
std::pair<std::string, int> result;
std::size_t p = host.find(':');
if (p != std::string::npos)
{
result.first = host.substr(0, p);
result.second = StringHelper::toInt(host.c_str() + p + 1, defaultPort);
}
else
{
result.first = host;
result.second = defaultPort;
}
return result;
}
std::pair<std::string, std::string> StringHelper::parseAssignment(const std::string& s, bool trimQuotes)
{
std::pair<std::string, std::string> result;
std::string::size_type p = s.find('=');
if (p != std::string::npos)
{
result.first = StringHelper::trim(s.substr(0, p));
result.second = StringHelper::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] == '\'')
result.second = result.second.substr(1, result.second.size() - 2);
}
}
else
result.first = StringHelper::trim(s);
return result;
}
std::string StringHelper::intToString(int value)
{
char buffer[32];
sprintf(buffer, "%d", value);
return buffer;
}
float StringHelper::toFloat(const std::string &s, float v, bool* isOk)
{
float result = 0.0;
int code = sscanf(s.c_str(), "%f", &result);
if (code == 1)
{
if (isOk)
*isOk = true;
}
else
{
result = v;
if (isOk)
*isOk = false;
}
return result;
}
std::string StringHelper::trim(const std::string &s)
{
auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { return std::isspace(c); });
auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { return std::isspace(c); }).base();
return (wsback <= wsfront ? std::string() : std::string(wsfront,wsback));
}
std::string StringHelper::timeToString(time_t t)
{
char buffer[96];
struct tm lt;
#if defined(TARGET_LINUX) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
localtime_r(&t, &lt);
#else
lt = *localtime(&t);
#endif
strftime(buffer, sizeof(buffer)-1, "%Y-%m-%d %H:%M:%S", &lt);
return buffer;
}
std::string StringHelper::millisecondsToString(uint64_t t)
{
return timeToString(t/1000);
}
int StringHelper::fromHex2Int(const std::string &s)
{
int result = 0;
sscanf(s.c_str(), "%x", &result);
return result;
}
static int hex2code(char s)
{
if (s >= '0' && s <= '9')
return s - '0';
if (s >= 'a' && s <= 'f')
return s - 'a' + 10;
if (s >= 'A' && s <= 'F')
return s - 'A' + 10;
return 0;
}
static int hex2code(const char* s)
{
return hex2code(s[0]) << 4 + hex2code(s[1]);
}
std::string StringHelper::fromHex2String(const std::string& s)
{
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] = hex2code(t[i*2]);
return result;
}
std::string StringHelper::replace(const std::string& s, char f, char r)
{
std::string result(s);
for (std::string::size_type i = 0; i < result.size(); i++)
if (result[i] == 'f')
result[i] = r;
return result;
}
std::string StringHelper::replace(const std::string& s, const std::string& tmpl, const std::string& n)
{
std::string result(s);
std::string::size_type p;
while ( (p = result.find(tmpl)) != std::string::npos)
{
result.replace(p, tmpl.size(), n);
}
return result;
}
std::string StringHelper::decodeUri(const std::string& s)
{
std::string ret;
ret.reserve(s.size());
char ch;
int i, ii;
for (i=0; i<(int)s.length(); i++)
{
if (s[i] == 37)
{
sscanf(s.substr(i+1,2).c_str(), "%x", &ii);
ch = static_cast<char>(ii);
ret += ch;
i += 2;
}
else
ret += s[i];
}
return ret;
}
#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
// --------------------- XcapHelper -----------------
std::string XcapHelper::buildBuddyList(std::string listName, std::vector<std::string> buddies)
{
std::ostringstream result;
result << XML_HEADER <<
"<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">" <<
"<list name=\"" << listName.c_str() << "\">";
// to test CT only!
//result << "<entry uri=\"" << "sip:dbogovych1@10.11.1.25" << "\"/>";
//result << "<entry uri=\"" << "sip:dbogovych2@10.11.1.25" << "\"/>";
for (unsigned i = 0; i<buddies.size(); i++)
{
result << "<entry uri=\"" << normalizeSipUri(buddies[i]).c_str() << "\"/>";
}
result << "</list></resource-lists>";
return result.str();
}
std::string XcapHelper::buildRules(std::vector<std::string> buddies)
{
std::ostringstream result;
result << XML_HEADER <<
"<ruleset xmlns=\"urn:ietf:params:xml:ns:common-policy\">" <<
"<rule id=\"presence_allow\">" <<
"<conditions>";
for (unsigned i = 0; i<buddies.size(); i++)
{
result << "<identity><one id=\"" <<
normalizeSipUri(buddies[i]).c_str() << "\"/></identity>";
}
result << "</conditions>" <<
"<actions>" <<
"<sub-handling xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"allow" <<
"</sub-handling>" <<
"</actions>" <<
"<transformations>" <<
"<provide-devices xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-devices/>" <<
"</provide-devices>" <<
"<provide-persons xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-persons/>" <<
"</provide-persons>" <<
"<provide-services xmlns=\"urn:ietf:params:xml:ns:pres-rules\">" <<
"<all-services/>" <<
"</provide-services>" <<
"</transformations>" <<
"</rule>" <<
"</ruleset>";
return result.str();
}
std::string XcapHelper::buildServices(std::string serviceUri, std::string listRef)
{
std::ostringstream result;
result << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl <<
"<rls-services xmlns=\"urn:ietf:params:xml:ns:rls-services\"" << std::endl <<
"xmlns:rl=\"urn:ietf:params:xml:ns:resource-lists\"" << std::endl <<
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << std::endl <<
"<service uri=\"" << normalizeSipUri(serviceUri).c_str() << "\">" << std::endl <<
"<resource-list>" << listRef.c_str() << "</resource-list>" << std::endl <<
"<packages>" << std::endl <<
"<package>presence</package>" << std::endl <<
"</packages>" << std::endl <<
"</service>" << std::endl <<
"</rls-services>";
return result.str();
}
std::string XcapHelper::normalizeSipUri(std::string uri)
{
if (uri.length())
{
if (uri[0] == '<')
uri.erase(0,1);
if (uri.length())
{
if (uri[uri.length()-1] == '>')
uri.erase(uri.length()-1, 1);
}
}
return uri;
}

View File

@ -0,0 +1,73 @@
/* Copyright(C) 2007-2018 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HELPER_STRING_H
#define __HELPER_STRING_H
#include <vector>
#include <string>
#include <sstream>
#include "HL_Types.h"
#ifdef TARGET_OSX
#define stricmp strcasecmp
#endif
class StringHelper
{
public:
static std::string extractFilename(const std::string& path);
static std::string makeUtf8(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 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);
template <typename T>
static std::string join(const std::vector<T>& v, const std::string& delimiter)
{
std::ostringstream s;
for (const auto& i : v)
{
if (&i != &v[0])
s << delimiter;
s << i;
}
return s.str();
}
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 float toFloat(const std::string& s, float defaultValue = 0.0, bool* isOk = nullptr);
static std::string trim(const std::string& s);
static std::string timeToString(time_t t);
static std::string millisecondsToString(uint64_t t);
static int fromHex2Int(const std::string& s);
static std::string fromHex2String(const std::string& s);
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);
};
class XcapHelper
{
public:
static std::string buildBuddyList(std::string listName, std::vector<std::string> buddies);
static std::string buildRules(std::vector<std::string> buddies);
static std::string buildServices(std::string serviceUri, std::string listRef);
static std::string normalizeSipUri(std::string uri);
};
#endif

View File

@ -0,0 +1,86 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HL_Sync.h"
#include <assert.h>
#include <atomic>
#ifdef TARGET_OSX
# include <libkern/OSAtomic.h>
#endif
#ifdef TARGET_WIN
# include <Windows.h>
#endif
void SyncHelper::delay(unsigned int microseconds)
{
#ifdef TARGET_WIN
::Sleep(microseconds/1000);
#endif
#if defined(TARGET_OSX) || defined(TARGET_LINUX)
timespec requested, remaining;
requested.tv_sec = microseconds / 1000000;
requested.tv_nsec = (microseconds % 1000000) * 1000;
remaining.tv_nsec = 0;
remaining.tv_sec = 0;
nanosleep(&requested, &remaining);
#endif
}
long SyncHelper::increment(long *value)
{
assert(value);
#ifdef TARGET_WIN
return ::InterlockedIncrement((LONG*)value);
#elif TARGET_OSX
return OSAtomicIncrement32((int32_t*)value);
#elif TARGET_LINUX
return -1;
#endif
}
// ------------------- ThreadHelper -------------------
void ThreadHelper::setName(const std::string &name)
{
#if defined(TARGET_LINUX)
pthread_setname_np(pthread_self(), name.c_str());
#endif
}
// ------------------- TimeHelper ---------------
using namespace std::chrono;
static uint64_t TimestampStartPoint = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
static time_t TimestampBase = time(nullptr);
uint64_t TimeHelper::getTimestamp()
{
time_point<steady_clock> t = steady_clock::now();
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
return ms - TimestampStartPoint + TimestampBase * 1000;
}
uint64_t TimeHelper::getUptime()
{
time_point<steady_clock> t = steady_clock::now();
uint64_t ms = duration_cast< milliseconds >(t.time_since_epoch()).count();
return ms - TimestampStartPoint;
}
uint32_t TimeHelper::getDelta(uint32_t later, uint32_t earlier)
{
if (later > earlier)
return later - earlier;
if (later < earlier && later < 0x7FFFFFFF && earlier >= 0x7FFFFFFF)
return 0xFFFFFFFF - earlier + later;
return 0;
}

View File

@ -0,0 +1,80 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HL_SYNC_H
#define __HL_SYNC_H
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <thread>
typedef std::recursive_mutex Mutex;
typedef std::unique_lock<std::recursive_mutex> Lock;
class SyncHelper
{
public:
static void delay(unsigned microseconds);
static long increment(long* value);
};
class Semaphore
{
private:
unsigned int m_uiCount;
std::mutex m_mutex;
std::condition_variable m_condition;
public:
inline Semaphore(unsigned int uiCount)
: m_uiCount(uiCount) { }
inline void Wait()
{
std::unique_lock< std::mutex > lock(m_mutex);
m_condition.wait(lock,[&]()->bool{ return m_uiCount>0; });
--m_uiCount;
}
template< typename R,typename P >
bool Wait(const std::chrono::duration<R,P>& crRelTime)
{
std::unique_lock< std::mutex > lock(m_mutex);
if (!m_condition.wait_for(lock,crRelTime,[&]()->bool{ return m_uiCount>0; }))
return false;
--m_uiCount;
return true;
}
inline void Signal()
{
std::unique_lock< std::mutex > lock(m_mutex);
++m_uiCount;
m_condition.notify_one();
}
};
class ThreadHelper
{
public:
static void setName(const std::string& name);
};
class TimeHelper
{
public:
// Returns current timestamp in milliseconds
static uint64_t getTimestamp();
// Returns uptime (of calling process) in milliseconds
static uint64_t getUptime();
// 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);
};
#endif

View File

@ -0,0 +1,98 @@
#ifndef __HL_THREAD_POOL_H
#define __HL_THREAD_POOL_H
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
class ThreadPool {
public:
ThreadPool(size_t);
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>;
~ThreadPool();
private:
// need to keep track of threads so we can join them
std::vector< std::thread > workers;
// the task queue
std::queue< std::function<void()> > tasks;
// synchronization
std::mutex queue_mutex;
std::condition_variable condition;
bool stop;
};
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads)
: stop(false)
{
for(size_t i = 0;i<threads;++i)
workers.emplace_back(
[this]
{
for(;;)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock,
[this]{ return this->stop || !this->tasks.empty(); });
if(this->stop && this->tasks.empty())
return;
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
}
);
}
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
// don't allow enqueueing after stopping the pool
if(stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace([task](){ (*task)(); });
}
condition.notify_one();
return res;
}
// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for(std::thread &worker: workers)
worker.join();
}
#endif

View File

@ -0,0 +1,29 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __HL_TYPES_H
#define __HL_TYPES_H
#ifdef WIN32
# define tstring wstring
# define to_tstring to_wstring
#else
# define tstring string
# define to_tstring to_string
#endif
#ifdef WIN32
#define ALLOCA(X) _alloca(X)
#else
#define ALLOCA(X) alloca(X)
#endif
enum SdpDirection
{
Sdp_Answer,
Sdp_Offer
};
#endif

View File

@ -0,0 +1,109 @@
#include "HL_Usb.h"
#include "HL_Exception.h"
#ifdef TARGET_WIN
#include <devguid.h>
#define ADR_WINDOW_CLASS_NAME L"HIDDEN_USB_CHANGE_DELEGATE_WINDOWCLASS_%u"
#define ADR_WINDOW_NAME L"HIDDEN_USB_CHANGE_DELEGATE_WINDOW_%u"
UsbChangeListener::UsbChangeListener()
:mNotifyHandle(NULL), mHiddenWindow(NULL), mDelegate(NULL)
{
wsprintf(mWindowClassName, ADR_WINDOW_CLASS_NAME, (unsigned int)rand());
}
UsbChangeListener::~UsbChangeListener()
{
stop();
}
void UsbChangeListener::setDelegate(Delegate* d)
{
mDelegate = d;
}
UsbChangeListener::Delegate* UsbChangeListener::getDelegate() const
{
return mDelegate;
}
void UsbChangeListener::start()
{
// Exposing Window to Mixer
WNDCLASSEX wcx;
memset( &wcx, 0, sizeof(WNDCLASSEX) );
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpszClassName = mWindowClassName;
wcx.lpfnWndProc = (WNDPROC)ADRWindowProc;
::RegisterClassEx(&wcx);
wchar_t windowname[128];
wsprintf(windowname, ADR_WINDOW_NAME, rand());
mHiddenWindow = CreateWindow( mWindowClassName,
windowname,
WS_POPUP | WS_DISABLED,
0, 0, 0, 0,
NULL, NULL, NULL, NULL );
if (!mHiddenWindow)
throw Exception(ERR_CREATEWINDOW, GetLastError());
if (!SetWindowLongPtr(mHiddenWindow, GWLP_USERDATA, (LONG_PTR)this))
throw Exception(ERR_CREATEWINDOW, GetLastError());
// Adjust notification filter
memset(&mNotificationFilter, 0, sizeof(mNotificationFilter));
mNotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
mNotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
// Register notification
if (!RegisterDeviceNotification(mHiddenWindow, &mNotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES))
throw Exception(ERR_REGISTERNOTIFICATION, GetLastError());
}
LRESULT CALLBACK UsbChangeListener::ADRWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if ( uMsg == WM_DEVICECHANGE )
{
if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE)
{
LONG_PTR l = GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (l)
{
UsbChangeListener* obj = reinterpret_cast<UsbChangeListener*>(l);
switch (wParam)
{
case DBT_DEVICEARRIVAL:
obj->getDelegate()->onDeviceInsert(NULL);
break;
case DBT_DEVICEREMOVECOMPLETE:
obj->getDelegate()->onDeviceRemove(NULL);
break;
}
}
}
}
return ::DefWindowProc( hwnd, uMsg, wParam, lParam);
}
void UsbChangeListener::stop()
{
// Unregister
if (mNotifyHandle != NULL)
{
::UnregisterDeviceNotification(mNotifyHandle);
mNotifyHandle = NULL;
}
//Destroy the window
if (mHiddenWindow != NULL)
{
::DestroyWindow(mHiddenWindow);
mHiddenWindow = NULL;
::UnregisterClass(mWindowClassName, NULL);
}
}
#endif

View File

@ -0,0 +1,47 @@
#ifndef __HELPER_USB_H
#define __HELPER_USB_H
#ifdef TARGET_WIN
#include <winsock2.h>
#include <windows.h>
#include <Dbt.h>
class UsbChangeListener
{
public:
class Delegate
{
public:
virtual void onDeviceInsert(const wchar_t* name) = 0;
virtual void onDeviceRemove(const wchar_t* name) = 0;
};
UsbChangeListener();
~UsbChangeListener();
void setDelegate(Delegate* d);
Delegate* getDelegate() const;
void start();
void stop();
protected:
HDEVNOTIFY mNotifyHandle; /// Handle to track notifications about USB insert/removal.
HWND mHiddenWindow; /// Hidden window to receive notifications
DEV_BROADCAST_DEVICEINTERFACE mNotificationFilter; /// Notifications filter
wchar_t mWindowClassName[256]; /// Hidden window class
Delegate* mDelegate; /// Event handler pointer
/// Hidden window procedure.
/// @param hwnd Window handle
/// @param uMsg Message ID
/// @param wParam First param
/// @param lParam Second param
static LRESULT CALLBACK ADRWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
};
#endif
#endif

View File

@ -0,0 +1,67 @@
#include "HL_Uuid.h"
#include <memory.h>
Uuid::Uuid()
{
#if defined(TARGET_WIN) || defined(TARGET_LINUX) || defined(TARGET_OSX)
memset(mUuid, 0, sizeof mUuid);
#endif
}
Uuid Uuid::generateOne()
{
Uuid result;
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
uuid_generate(result.mUuid);
#endif
#if defined(TARGET_WIN)
UuidCreate(&result.mUuid);
#endif
return result;
}
Uuid Uuid::parse(const std::string &s)
{
Uuid result;
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
uuid_parse(s.c_str(), result.mUuid);
#endif
#if defined(TARGET_WIN)
UuidFromStringA((RPC_CSTR)s.c_str(), &result.mUuid);
#endif
return result;
}
std::string Uuid::toString() const
{
char buf[64];
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
uuid_unparse_lower(mUuid, buf);
#endif
#if defined(TARGET_WIN)
RPC_CSTR s = nullptr;
UuidToStringA(&mUuid, &s);
if (s)
{
strcpy(buf, (const char*)s);
RpcStringFreeA(&s);
s = nullptr;
}
#endif
return buf;
}
bool Uuid::operator < (const Uuid& right) const
{
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
return memcmp(mUuid, right.mUuid, sizeof(mUuid)) < 0;
#endif
#if defined(TARGET_WIN)
return memcmp(&mUuid, &right.mUuid, sizeof(mUuid)) < 0;
#endif
}

View File

@ -0,0 +1,35 @@
#ifndef __HL_UUID_H
#define __HL_UUID_H
#include <string>
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
# include <uuid/uuid.h>
#endif
#if defined(TARGET_WIN)
# include <rpc.h>
#endif
class Uuid
{
public:
Uuid();
static Uuid generateOne();
static Uuid parse(const std::string& s);
std::string toString() const;
bool operator < (const Uuid& right) const;
protected:
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
uuid_t mUuid;
#endif
#if defined(TARGET_WIN)
UUID mUuid;
#endif
#if defined(TARGET_ANDROID)
// Stub only
#endif
};
#endif

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