- removed old Sevana routines (they are moved to pvqa++ / aqua++ header files)
- cleanups
This commit is contained in:
parent
477932459f
commit
682362c6fe
|
|
@ -91,7 +91,6 @@ set (RTPHONE_SOURCES
|
|||
${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_EvsCodec.cpp
|
||||
${rtphone_engine}/media/MT_CngHelper.cpp
|
||||
|
|
@ -120,7 +119,6 @@ set (RTPHONE_HEADERS
|
|||
${rtphone_engine}/media/MT_AudioStream.h
|
||||
${rtphone_engine}/media/MT_AudioReceiver.h
|
||||
${rtphone_engine}/media/MT_AudioCodec.h
|
||||
${rtphone_engine}/media/MT_SevanaMos.h
|
||||
${rtphone_engine}/media/MT_AmrCodec.h
|
||||
${rtphone_engine}/media/MT_EvsCodec.h
|
||||
${rtphone_engine}/media/MT_CngHelper.h
|
||||
|
|
|
|||
|
|
@ -4,12 +4,16 @@
|
|||
#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>
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
# include "pvqa++.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
# include "aqua++.h"
|
||||
#endif
|
||||
|
||||
const std::string Status_Ok = "ok";
|
||||
const std::string Status_SessionNotFound = "session not found";
|
||||
const std::string Status_AccountNotFound = "account not found";
|
||||
|
|
@ -18,7 +22,6 @@ 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()
|
||||
|
|
@ -135,7 +138,7 @@ std::string AgentImpl::command(const std::string& command)
|
|||
return answer.toStyledString();
|
||||
}
|
||||
|
||||
bool AgentImpl::waitForData(int milliseconds)
|
||||
bool AgentImpl::waitForData(int /*milliseconds*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -154,13 +157,13 @@ void AgentImpl::processConfig(Json::Value &d, Json::Value &answer)
|
|||
// 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);
|
||||
sevana::pvqa::initialize(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);
|
||||
sevana::aqua::initializeLibrary(aquaLicense);
|
||||
#endif
|
||||
|
||||
std::string transport = d["transport"].asString();
|
||||
|
|
@ -182,7 +185,7 @@ void AgentImpl::processConfig(Json::Value &d, Json::Value &answer)
|
|||
answer["status"] = Status_Ok;
|
||||
}
|
||||
|
||||
void AgentImpl::processStart(Json::Value &request, Json::Value &answer)
|
||||
void AgentImpl::processStart(Json::Value& /*request*/, Json::Value &answer)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||
if (mThread)
|
||||
|
|
@ -224,7 +227,7 @@ void AgentImpl::processStart(Json::Value &request, Json::Value &answer)
|
|||
answer["status"] = Status_Ok;
|
||||
}
|
||||
|
||||
void AgentImpl::processStop(Json::Value& request, Json::Value& answer)
|
||||
void AgentImpl::processStop(Json::Value& /*request*/, Json::Value& answer)
|
||||
{
|
||||
stopAgentAndThread();
|
||||
answer["status"] = Status_Ok;
|
||||
|
|
@ -326,7 +329,7 @@ void AgentImpl::processStartSession(Json::Value& request, Json::Value& answer)
|
|||
// 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);
|
||||
audioProvider->setState(audioProvider->state() | static_cast<int>(StreamState::Grabbing) | static_cast<int>(StreamState::Playing));
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
std::string temp_path = request["aqua_temp_path"].asString();
|
||||
|
|
@ -453,8 +456,8 @@ static Json::Value CsvReportToJson(const std::string& report)
|
|||
if (reader.readLine(cells))
|
||||
{
|
||||
Json::Value detectorNames;
|
||||
for (int nameIndex = 0; nameIndex < (int)cells.size(); nameIndex++)
|
||||
detectorNames[nameIndex] = StringHelper::trim(cells[nameIndex]);
|
||||
for (size_t nameIndex = 0; nameIndex < cells.size(); nameIndex++)
|
||||
detectorNames[static_cast<int>(nameIndex)] = StringHelper::trim(cells[nameIndex]);
|
||||
// Put first line name of columns
|
||||
detectorValues[0] = detectorNames;
|
||||
|
||||
|
|
@ -463,14 +466,14 @@ static Json::Value CsvReportToJson(const std::string& report)
|
|||
{
|
||||
// Skip last column for now
|
||||
Json::Value row;
|
||||
for (int valueIndex = 0; valueIndex < (int)cells.size(); valueIndex++)
|
||||
for (size_t valueIndex = 0; valueIndex < cells.size(); valueIndex++)
|
||||
{
|
||||
bool isFloat = true;
|
||||
float v = StringHelper::toFloat(cells[valueIndex], 0.0, &isFloat);
|
||||
if (isFloat)
|
||||
row[valueIndex] = v;
|
||||
row[static_cast<int>(valueIndex)] = static_cast<double>(v);
|
||||
else
|
||||
row[valueIndex] = cells[valueIndex];
|
||||
row[static_cast<int>(valueIndex)] = cells[valueIndex];
|
||||
}
|
||||
detectorValues[rowIndex++] = row;
|
||||
}
|
||||
|
|
@ -575,7 +578,7 @@ void AgentImpl::processGetMediaStats(Json::Value& request, Json::Value& answer)
|
|||
answer["status"] = Status_SessionNotFound;
|
||||
}
|
||||
|
||||
void AgentImpl::processNetworkChanged(Json::Value& request, Json::Value& answer)
|
||||
void AgentImpl::processNetworkChanged(Json::Value& /*request*/, Json::Value& /*answer*/)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
|
||||
}
|
||||
|
|
@ -596,7 +599,7 @@ void AgentImpl::processAddRootCert(Json::Value& request, Json::Value& answer)
|
|||
{
|
||||
// Get single certificate
|
||||
std::string cert = pem.substr(pb, pe + EndCertificate.size());
|
||||
int size = cert.size();
|
||||
//int size = cert.size();
|
||||
addRootCert(ByteBuffer(cert.c_str(), cert.size()));
|
||||
|
||||
// Delete processed part
|
||||
|
|
@ -611,7 +614,7 @@ 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);
|
||||
ICELog(static_cast<ice::LogLevel>(level), "App", << message);
|
||||
|
||||
answer["status"] = Status_Ok;
|
||||
}
|
||||
|
|
@ -889,7 +892,7 @@ void AgentImpl::onCheckFinished(PSession s, const char* description)
|
|||
}
|
||||
|
||||
// Called when log message must be recorded
|
||||
void AgentImpl::onLog(const char* msg)
|
||||
void AgentImpl::onLog(const char* /*msg*/)
|
||||
{}
|
||||
|
||||
// Called when problem with SIP connection(s) detected
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
||||
/* Copyright(C) 2007-2019 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/. */
|
||||
|
|
@ -29,6 +29,10 @@
|
|||
#endif
|
||||
|
||||
|
||||
// ----------------------------- SocketSink -------------------------
|
||||
SocketSink::~SocketSink()
|
||||
{}
|
||||
|
||||
// ----------------------------- SocketHeap -------------------------
|
||||
|
||||
SocketHeap::SocketHeap(unsigned short start, unsigned short finish)
|
||||
|
|
@ -132,7 +136,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||
// Obtain port number
|
||||
sockaddr_in addr;
|
||||
sockaddr_in6 addr6;
|
||||
int result;
|
||||
int result = 0;
|
||||
int testport;
|
||||
do
|
||||
{
|
||||
|
|
@ -144,7 +148,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(testport);
|
||||
result = ::bind(sock, (const sockaddr*)&addr, sizeof addr);
|
||||
result = ::bind(sock, reinterpret_cast<const sockaddr*>(&addr), sizeof addr);
|
||||
if (result)
|
||||
result = WSAGetLastError();
|
||||
break;
|
||||
|
|
@ -153,7 +157,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
|||
memset(&addr6, 0, sizeof addr6);
|
||||
addr6.sin6_family = AF_INET6;
|
||||
addr6.sin6_port = htons(testport);
|
||||
result = ::bind(sock, (const sockaddr*)&addr6, sizeof addr6);
|
||||
result = ::bind(sock, reinterpret_cast<const sockaddr*>(&addr6), sizeof addr6);
|
||||
if (result)
|
||||
result = WSAGetLastError();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -11,11 +11,7 @@
|
|||
#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"
|
||||
|
|
@ -25,6 +21,7 @@
|
|||
class SocketSink
|
||||
{
|
||||
public:
|
||||
virtual ~SocketSink();
|
||||
virtual void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize) = 0;
|
||||
};
|
||||
|
||||
|
|
@ -73,7 +70,7 @@ protected:
|
|||
SocketSink* mSink;
|
||||
|
||||
SocketItem()
|
||||
:mSink(NULL)
|
||||
:mSink(nullptr)
|
||||
{ }
|
||||
|
||||
SocketItem(unsigned short portnumber, SocketSink* sink)
|
||||
|
|
|
|||
|
|
@ -285,6 +285,10 @@ float Variant::asFloat() const
|
|||
}
|
||||
}
|
||||
|
||||
double Variant::asDouble() const
|
||||
{
|
||||
return static_cast<double>(asFloat());
|
||||
}
|
||||
|
||||
std::string Variant::asStdString() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public:
|
|||
int64_t asInt64() const;
|
||||
bool asBool() const;
|
||||
float asFloat() const;
|
||||
double asDouble() const;
|
||||
std::string asStdString() const;
|
||||
//const char* asString();
|
||||
void* asPointer() const;
|
||||
|
|
|
|||
|
|
@ -22,11 +22,6 @@
|
|||
|
||||
#include <map>
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
# include "MT_SevanaMos.h"
|
||||
#endif
|
||||
|
||||
|
||||
// #define DUMP_DECODED
|
||||
|
||||
namespace MT
|
||||
|
|
|
|||
|
|
@ -1,991 +0,0 @@
|
|||
#define NOMINMAX
|
||||
|
||||
//#include "config.h"
|
||||
#include "MT_SevanaMos.h"
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
|
||||
#if defined(TARGET_SERVER)
|
||||
# include <filesystem>
|
||||
#endif
|
||||
|
||||
#include "../engine/helper/HL_Log.h"
|
||||
#include "../engine/helper/HL_CsvReader.h"
|
||||
#include "../engine/helper/HL_String.h"
|
||||
#include "../engine/audio/Audio_WavFile.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <fstream>
|
||||
#include <streambuf>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(TARGET_SERVER)
|
||||
extern std::string IntervalCacheDir;
|
||||
#endif
|
||||
|
||||
#define LOG_SUBSYSTEM "Sevana"
|
||||
|
||||
#define PVQA_ECHO_DETECTOR_NAME "ECHO"
|
||||
//#define PVQA_ECHO_DETECTOR_NAME "EchoM-00"
|
||||
|
||||
namespace MT {
|
||||
|
||||
#if !defined(MOS_BEST_COLOR)
|
||||
# define MOS_BEST_COLOR 0x11FF11
|
||||
# define MOS_BAD_COLOR 0x000000
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_WIN)
|
||||
# define popen _popen
|
||||
# define pclose _pclose
|
||||
#endif
|
||||
|
||||
static std::string execCommand(const std::string& cmd)
|
||||
{
|
||||
std::cout << cmd << "\n";
|
||||
|
||||
std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
|
||||
if (!pipe)
|
||||
throw std::runtime_error("Failed to run.");
|
||||
|
||||
char buffer[1024];
|
||||
std::string result = "";
|
||||
while (!feof(pipe.get()))
|
||||
{
|
||||
if (fgets(buffer, 1024, pipe.get()) != nullptr)
|
||||
result += buffer;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// -------------- SevanaMosUtility --------------
|
||||
void SevanaMosUtility::run(const std::string& pcmPath, const std::string& intervalPath,
|
||||
std::string& estimation, std::string& intervals)
|
||||
{
|
||||
/*
|
||||
#if defined(TARGET_SERVER)
|
||||
path sevana = current_path() / "sevana";
|
||||
#if defined(TARGET_LINUX) || defined(TARGET_OSX)
|
||||
path exec = sevana / "pvqa";
|
||||
#else
|
||||
path exec = sevana / "pvqa.exe";
|
||||
#endif
|
||||
path lic = sevana / "pvqa.lic";
|
||||
path cfg = sevana / "settings.cfg";
|
||||
|
||||
estimation.clear();
|
||||
char cmdbuffer[1024];
|
||||
sprintf(cmdbuffer, "%s %s analysis %s %s %s 0.799", exec.string().c_str(), lic.string().c_str(),
|
||||
intervalPath.c_str(), cfg.string().c_str(), pcmPath.c_str());
|
||||
std::string output = execCommand(cmdbuffer);
|
||||
|
||||
//ICELogDebug(<< "Got PVQA analyzer output: " << output);
|
||||
|
||||
std::string line;
|
||||
std::istringstream is(output);
|
||||
while (std::getline(is, line))
|
||||
{
|
||||
std::string::size_type mosPosition = line.find("MOS = ");
|
||||
if ( mosPosition != std::string::npos)
|
||||
{
|
||||
estimation = line.substr(mosPosition + 6);
|
||||
boost::algorithm::trim(estimation);
|
||||
}
|
||||
}
|
||||
|
||||
if (!estimation.size())
|
||||
{
|
||||
// Dump utility output if estimation failed
|
||||
ICELogError(<< "PVQA failed with message: " << output);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read intervals report file
|
||||
if (boost::filesystem::exists(intervalPath) && !estimation.empty())
|
||||
{
|
||||
std::ifstream t(intervalPath);
|
||||
std::string str((std::istreambuf_iterator<char>(t)),
|
||||
std::istreambuf_iterator<char>());
|
||||
intervals = str;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
float getSevanaMos(const std::string& audioPath, const std::string& intervalReportPath,
|
||||
std::string& intervalReport)
|
||||
{
|
||||
return 0.0f;
|
||||
/*
|
||||
// Find Sevana MOS estimation
|
||||
ICELogDebug( << "Running MOS utitlity on resulted PCM file " << audioPath );
|
||||
try
|
||||
{
|
||||
std::string buffer;
|
||||
SevanaMosUtility::run(audioPath, intervalReportPath, buffer, intervalReport);
|
||||
ICELogDebug( << "MOS utility is finished on PCM file " << audioPath );
|
||||
return (float)atof(buffer.c_str());
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
ICELogError( << "MOS utility failed on PCM file " << audioPath << ". Error msg: " << e.what() );
|
||||
return 0.0;
|
||||
}*/
|
||||
}
|
||||
|
||||
// ------------------- SevanaPVQA -------------------
|
||||
void* SevanaPVQA::mLibraryConfiguration = nullptr;
|
||||
int SevanaPVQA::mLibraryErrorCode = 0;
|
||||
std::atomic_int SevanaPVQA::mInstanceCounter;
|
||||
std::atomic_uint_least64_t SevanaPVQA::mAllProcessedMilliseconds;
|
||||
bool SevanaPVQA::mPvqaLoaded = false;
|
||||
|
||||
std::string SevanaPVQA::getVersion()
|
||||
{
|
||||
return PVQA_GetVersion();
|
||||
}
|
||||
|
||||
#if defined(TARGET_ANDROID)
|
||||
void SevanaPVQA::setupAndroidEnvironment(void *environment, void *appcontext)
|
||||
{
|
||||
PVQA_SetupAndroidEnvironment(environment, appcontext);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SevanaPVQA::initializeLibrary(const std::string& pathToLicenseFile, const std::string& pathToConfigFile)
|
||||
{
|
||||
mPvqaLoaded = false;
|
||||
|
||||
ICELogInfo(<< "Sevana PVQA is about to be initialized.");
|
||||
|
||||
// Initialize PVQA library
|
||||
if (!mLibraryConfiguration)
|
||||
{
|
||||
mInstanceCounter = 0;
|
||||
mLibraryErrorCode = PVQA_InitLib(const_cast<char*>(pathToLicenseFile.c_str()));
|
||||
if (mLibraryErrorCode)
|
||||
{
|
||||
ICELogError(<< "Problem when initializing PVQA library. Error code: " << mLibraryErrorCode
|
||||
<< ". Path to license file is " << pathToLicenseFile
|
||||
<< ". Path to config file is " << pathToConfigFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pathToConfigFile.size())
|
||||
{
|
||||
mLibraryConfiguration = PVQA_LoadCFGFile(const_cast<char*>(pathToConfigFile.c_str()), &mLibraryErrorCode);
|
||||
if (!mLibraryConfiguration)
|
||||
{
|
||||
PVQA_ReleaseLib();
|
||||
ICELogError(<< "Problem with PVQA configuration file.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mPvqaLoaded = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SevanaPVQA::initializeLibraryWithData(const void* license_buffer, size_t license_len,
|
||||
const void* config_buffer, size_t config_len)
|
||||
{
|
||||
mPvqaLoaded = false;
|
||||
#if defined(OLD_PVQA)
|
||||
return false;
|
||||
#else
|
||||
ICELogInfo(<< "Sevana PVQA is about to be initialized via byte buffers.");
|
||||
|
||||
// Initialize PVQA library
|
||||
if (!mLibraryConfiguration)
|
||||
{
|
||||
mInstanceCounter = 0;
|
||||
mLibraryErrorCode = PVQA_InitLibWithLicData(license_buffer, license_len);
|
||||
if (mLibraryErrorCode)
|
||||
{
|
||||
ICELogError(<< "Problem when initializing PVQA library. Error code: " << mLibraryErrorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config_buffer && config_len)
|
||||
{
|
||||
mLibraryConfiguration = PVQA_LoadCFGData(config_buffer, config_len, &mLibraryErrorCode);
|
||||
if (!mLibraryConfiguration)
|
||||
{
|
||||
PVQA_ReleaseLib();
|
||||
ICELogError(<< "Problem with PVQA configuration file.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mPvqaLoaded = true;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SevanaPVQA::isInitialized()
|
||||
{
|
||||
return mPvqaLoaded;
|
||||
}
|
||||
|
||||
int SevanaPVQA::getLibraryError()
|
||||
{
|
||||
return mLibraryErrorCode;
|
||||
}
|
||||
|
||||
void SevanaPVQA::releaseLibrary()
|
||||
{
|
||||
PVQA_ReleaseLib();
|
||||
}
|
||||
|
||||
SevanaPVQA::SevanaPVQA()
|
||||
{
|
||||
}
|
||||
|
||||
SevanaPVQA::~SevanaPVQA()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void SevanaPVQA::open(double interval, Model model)
|
||||
{
|
||||
if (!isInitialized())
|
||||
{
|
||||
ICELogError(<< "PVQA library is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOpenFailed)
|
||||
{
|
||||
ICELogError(<< "Open failed already, reject this attempt.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mContext)
|
||||
{
|
||||
ICELogError(<< "Already opened (context is not nullptr).");
|
||||
return;
|
||||
}
|
||||
|
||||
ICELogDebug(<<"Attempt to create PVQA instance.");
|
||||
mProcessedSamples = 0;
|
||||
mModel = model;
|
||||
mIntervalLength = interval;
|
||||
mAudioLineInitialized = false;
|
||||
|
||||
mContext = PVQA_CreateAudioQualityAnalyzer(mLibraryConfiguration);
|
||||
if (!mContext)
|
||||
{
|
||||
ICELogError(<< "Failed to create PVQA instance. Instance counter: " << mInstanceCounter);
|
||||
mOpenFailed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mInstanceCounter++;
|
||||
|
||||
int rescode = 0;
|
||||
rescode = PVQA_AudioQualityAnalyzerSetIntervalLength(mContext, interval);
|
||||
if (rescode)
|
||||
{
|
||||
ICELogError(<< "Failed to set interval length on PVQA instance. Result code: " << rescode);
|
||||
close();
|
||||
mOpenFailed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mModel == Model::Stream)
|
||||
{
|
||||
rescode = PVQA_OnStartStreamData(mContext);
|
||||
if (rescode)
|
||||
{
|
||||
ICELogError(<< "Failed to start streaming analysis on PVQA instance. Result code: " << rescode);
|
||||
close();
|
||||
mOpenFailed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ICELogDebug(<<"PVQA instance is created. Instance counter: " << mInstanceCounter);
|
||||
}
|
||||
|
||||
void SevanaPVQA::close()
|
||||
{
|
||||
if (mContext)
|
||||
{
|
||||
ICELogDebug(<< "Attempt to destroy PVQA instance.");
|
||||
PVQA_ReleaseAudioQualityAnalyzer(mContext);
|
||||
mInstanceCounter--;
|
||||
ICELogDebug(<< "PVQA instance destroyed. Current instance counter: " << mInstanceCounter);
|
||||
mContext = nullptr;
|
||||
mOpenFailed = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SevanaPVQA::isOpen() const
|
||||
{
|
||||
return mContext != nullptr;
|
||||
}
|
||||
|
||||
void SevanaPVQA::update(int samplerate, int channels, const void *pcmBuffer, int pcmLength)
|
||||
{
|
||||
if (!mContext)
|
||||
{
|
||||
ICELogError(<< "No PVQA context.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Model is assert here as it can be any if context is not created.
|
||||
assert (mModel == Model::Stream);
|
||||
|
||||
TPVQA_AudioItem item;
|
||||
item.dNChannels = channels;
|
||||
item.dSampleRate = samplerate;
|
||||
item.dNSamples = pcmLength / 2 / channels;
|
||||
item.pSamples = (short*)pcmBuffer;
|
||||
int rescode = PVQA_OnAddStreamAudioData(mContext, &item);
|
||||
if (rescode)
|
||||
{
|
||||
ICELogError(<< "Failed to stream data to PVQA instance. Result code: " << rescode);
|
||||
}
|
||||
|
||||
|
||||
int milliseconds = pcmLength / 2 / channels / (samplerate / 1000);
|
||||
mProcessedMilliseconds += milliseconds;
|
||||
mAllProcessedMilliseconds += milliseconds;
|
||||
}
|
||||
|
||||
SevanaPVQA::DetectorsList SevanaPVQA::getDetectorsNames(const std::string& report)
|
||||
{
|
||||
DetectorsList result;
|
||||
|
||||
if (!report.empty())
|
||||
{
|
||||
std::istringstream iss(report);
|
||||
CsvReader reader(iss);
|
||||
reader.readLine(result.mNames);
|
||||
result.mStartIndex = 2;
|
||||
|
||||
// Remove first columns
|
||||
if (result.mStartIndex < (int)result.mNames.size() - 1)
|
||||
{
|
||||
result.mNames.erase(result.mNames.begin(), result.mNames.begin() + result.mStartIndex);
|
||||
|
||||
// Remove last column
|
||||
result.mNames.erase(result.mNames.begin() + result.mNames.size() - 1);
|
||||
|
||||
for (auto& name: result.mNames)
|
||||
name = StringHelper::trim(name);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float SevanaPVQA::getResults(std::string& report, EchoData** echo, int samplerate, Codec codec)
|
||||
{
|
||||
if (!mContext)
|
||||
{
|
||||
ICELogError(<< "No PVQA context.");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (mModel == Model::Stream)
|
||||
{
|
||||
if (mProcessedMilliseconds == 0)
|
||||
{
|
||||
ICELogError(<< "No audio in PVQA.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (PVQA_OnFinalizeStream(mContext, (long)samplerate))
|
||||
{
|
||||
ICELogError(<< "Failed to finalize results from PVQA.");
|
||||
return -1;
|
||||
}
|
||||
ICELogInfo(<< "Processed " << mProcessedMilliseconds << " milliseconds.");
|
||||
}
|
||||
|
||||
TPVQA_Results results;
|
||||
if (PVQA_FillQualityResultsStruct(mContext, &results))
|
||||
{
|
||||
ICELogError(<< "Failed to get results from PVQA.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int reportLength = PVQA_GetQualityStringSize(mContext);
|
||||
if (reportLength)
|
||||
{
|
||||
char* buffer = (char*)alloca(reportLength + 1);
|
||||
if (PVQA_FillQualityString(mContext, buffer))
|
||||
{
|
||||
ICELogError(<< "Failed to fill intervals report.");
|
||||
}
|
||||
else
|
||||
report = buffer;
|
||||
}
|
||||
|
||||
#if defined(TARGET_LINUX) && defined(PVQA_WITH_ECHO_DATA)
|
||||
if (mModel == SevanaPVQA::Model::Stream && echo)
|
||||
{
|
||||
// Return echo detector counters
|
||||
// Get list of names for echo detector - for debugging only
|
||||
std::vector<std::string> names;
|
||||
int errCode = 0;
|
||||
const char** iNames = (const char **)PVQA_GetProcessorValuesNamesList(mContext, PVQA_ECHO_DETECTOR_NAME, &errCode);
|
||||
if (!errCode && iNames)
|
||||
{
|
||||
int nameIndex = 0;
|
||||
for(const char * locName = iNames[nameIndex]; locName; locName = iNames[++nameIndex])
|
||||
names.push_back(locName);
|
||||
|
||||
// Get values for echo detector
|
||||
PVQA_Array2D* array = PVQA_GetProcessorValuesList(mContext, PVQA_ECHO_DETECTOR_NAME, 0, mProcessedMilliseconds, "values", &errCode);
|
||||
if (array)
|
||||
{
|
||||
*echo = new std::vector<std::vector<float>>();
|
||||
for (int r = 0; r < array->rows; r++)
|
||||
{
|
||||
std::vector<float> row;
|
||||
for (int c = 0; c < array->columns; c++)
|
||||
row.push_back(array->data[r * array->columns + c]);
|
||||
(*echo)->push_back(row);
|
||||
}
|
||||
PVQA_ReleaseArray2D(array); array = nullptr;
|
||||
}
|
||||
// For debugging only
|
||||
/*if (*echo)
|
||||
{
|
||||
for (const auto& row: **echo)
|
||||
{
|
||||
std::cout << "<";
|
||||
for (const auto& v: row)
|
||||
std::cout << v << " ";
|
||||
std::cout << ">" << std::endl;
|
||||
}
|
||||
}*/
|
||||
// No need to delete maxValues - it will be deleted on PVQA analyzer context freeing.
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Limit maximal value of MOS depending on codec
|
||||
float result = (float)results.dMOSLike;
|
||||
float mv = 5.0f;
|
||||
switch (codec)
|
||||
{
|
||||
case Codec::G711: mv = 4.1f; break;
|
||||
case Codec::G729: mv = 3.92f; break;
|
||||
default:
|
||||
mv = 5.0;
|
||||
}
|
||||
|
||||
return std::min(result, mv);
|
||||
}
|
||||
|
||||
void SevanaPVQA::setPathToDumpFile(const std::string& path)
|
||||
{
|
||||
mDumpWavPath = path;
|
||||
}
|
||||
|
||||
float SevanaPVQA::process(int samplerate, int channels, const void *pcmBuffer, int pcmLength, std::string &report, Codec codec)
|
||||
{
|
||||
//std::cout << "Sent " << pcmLength << " bytes of audio to analyzer." << std::endl;
|
||||
assert (mModel == Model::Interval);
|
||||
if (!mContext)
|
||||
return 0.0;
|
||||
|
||||
/*if (!mAudioLineInitialized)
|
||||
{
|
||||
mAudioLineInitialized = true;
|
||||
if (PVQA_AudioQualityAnalyzerCreateDelayLine(mContext, samplerate, channels, 20))
|
||||
ICELogError(<< "Failed to create delay line.");
|
||||
}*/
|
||||
|
||||
TPVQA_AudioItem item;
|
||||
item.dNChannels = channels;
|
||||
item.dSampleRate = samplerate;
|
||||
item.dNSamples = pcmLength / 2 / channels;
|
||||
item.pSamples = (short*)pcmBuffer;
|
||||
|
||||
//std::cout << "Sending chunk of audio with rate = " << samplerate << ", channels = " << channels << ", number of samples " << item.dNSamples << std::endl;
|
||||
|
||||
/*
|
||||
if (!mDumpWavPath.empty())
|
||||
{
|
||||
WavFileWriter writer;
|
||||
writer.open(mDumpWavPath, samplerate, channels);
|
||||
writer.write(item.pSamples, item.dNSamples * 2 * channels);
|
||||
writer.close();
|
||||
ICELogError(<< "Sending chunk of audio with rate = " << samplerate << ", channels = " << channels << ", number of samples " << item.dNSamples);
|
||||
}
|
||||
*/
|
||||
int code = PVQA_OnTestAudioData(mContext, &item);
|
||||
if (code)
|
||||
{
|
||||
ICELogError(<< "Failed to run PVQA on audio buffer with code " << code);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
if (item.pSamples != pcmBuffer || item.dNSamples != pcmLength / 2 / channels || item.dSampleRate != samplerate || item.dNChannels != channels)
|
||||
{
|
||||
ICELogError(<< "PVQA changed input parameters!!!!");
|
||||
}
|
||||
*/
|
||||
// Increase counter of processed samples
|
||||
mProcessedSamples += pcmLength / channels / 2;
|
||||
|
||||
int milliseconds = pcmLength / channels / 2 / (samplerate / 1000);
|
||||
mProcessedMilliseconds += milliseconds;
|
||||
|
||||
// Overall counter
|
||||
mAllProcessedMilliseconds += milliseconds;
|
||||
|
||||
// Get results
|
||||
return getResults(report, nullptr, samplerate, codec);
|
||||
}
|
||||
|
||||
struct RgbColor
|
||||
{
|
||||
uint8_t mRed = 0;
|
||||
uint8_t mGreen = 0;
|
||||
uint8_t mBlue = 0;
|
||||
|
||||
static RgbColor parse(uint32_t rgb)
|
||||
{
|
||||
RgbColor result;
|
||||
result.mBlue = (uint8_t)(rgb & 0xff);
|
||||
result.mGreen = (uint8_t)((rgb >> 8) & 0xff);
|
||||
result.mRed = (uint8_t)((rgb >> 16) & 0xff);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string toHex() const
|
||||
{
|
||||
char result[7];
|
||||
sprintf(result, "%02x%02x%02x", int(mRed), int(mGreen), int(mBlue));
|
||||
return std::string(result);
|
||||
}
|
||||
};
|
||||
|
||||
int SevanaPVQA::getSize() const
|
||||
{
|
||||
int result = 0;
|
||||
result += sizeof(*this);
|
||||
|
||||
// TODO: add PVQA analyzer size
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string SevanaPVQA::mosToColor(float mos)
|
||||
{
|
||||
// Limit MOS value by 5.0
|
||||
mos = mos > 5.0f ? 5.0f : mos;
|
||||
mos = mos < 1.0f ? 1.0f : mos;
|
||||
|
||||
// Split to components
|
||||
RgbColor start = RgbColor::parse(MOS_BEST_COLOR), end = RgbColor::parse(MOS_BAD_COLOR);
|
||||
|
||||
float mosFraction = (mos - 1.0f) / 4.0f;
|
||||
|
||||
end.mBlue += (uint8_t)((start.mBlue - end.mBlue) * mosFraction);
|
||||
end.mGreen += (uint8_t)((start.mGreen - end.mGreen) * mosFraction);
|
||||
end.mRed += (uint8_t)((start.mRed - end.mRed) * mosFraction);
|
||||
|
||||
return end.toHex();
|
||||
}
|
||||
|
||||
} // end of namespace MT
|
||||
#endif
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
|
||||
#include <string.h>
|
||||
#include "helper/HL_String.h"
|
||||
#include <sstream>
|
||||
#include <json/json.h>
|
||||
|
||||
namespace MT
|
||||
{
|
||||
|
||||
int SevanaAqua::initializeLibrary(const std::string& pathToLicenseFile)
|
||||
{
|
||||
//char buffer[pathToLicenseFile.length() + 1];
|
||||
//strcpy(buffer, pathToLicenseFile.c_str());
|
||||
return SSA_InitLib(const_cast<char*>(pathToLicenseFile.data()));
|
||||
}
|
||||
|
||||
|
||||
int SevanaAqua::initializeLibrary(const void* buffer, size_t len)
|
||||
{
|
||||
return SSA_InitLibWithData(buffer, len);
|
||||
}
|
||||
|
||||
void SevanaAqua::releaseLibrary()
|
||||
{
|
||||
SSA_ReleaseLib();
|
||||
}
|
||||
|
||||
std::string SevanaAqua::FaultsReport::toText() const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
if (mSignalAdvancedInMilliseconds > -4999.0)
|
||||
oss << "Signal advanced in milliseconds: " << mSignalAdvancedInMilliseconds << std::endl;
|
||||
if (mMistimingInPercents > -4999.0)
|
||||
oss << "Mistiming in percents: " << mMistimingInPercents << std::endl;
|
||||
|
||||
for (ResultMap::const_iterator resultIter = mResultMap.begin(); resultIter != mResultMap.end(); resultIter++)
|
||||
{
|
||||
oss << resultIter->first << ":\t\t\t" << resultIter->second.mSource << " : \t" << resultIter->second.mDegrated << " \t" << resultIter->second.mUnit << std::endl;
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
Json::Value SevanaAqua::FaultsReport::toJson() const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
Json::Value result;
|
||||
|
||||
result["Mistiming"] = mMistimingInPercents;
|
||||
result["SignalAdvanced"] = mSignalAdvancedInMilliseconds;
|
||||
Json::Value items;
|
||||
for (ResultMap::const_iterator resultIter = mResultMap.begin(); resultIter != mResultMap.end(); resultIter++)
|
||||
{
|
||||
Json::Value item;
|
||||
item["name"] = resultIter->first;
|
||||
item["source"] = resultIter->second.mSource;
|
||||
item["degrated"] = resultIter->second.mDegrated;
|
||||
item["unit"] = resultIter->second.mUnit;
|
||||
|
||||
items.append(item);
|
||||
}
|
||||
|
||||
result["items"] = items;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string SevanaAqua::getVersion()
|
||||
{
|
||||
TSSA_AQuA_Info* info = SSA_GetPAQuAInfo();
|
||||
if (info)
|
||||
return info->dVersionString;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
SevanaAqua::SevanaAqua()
|
||||
{
|
||||
open();
|
||||
}
|
||||
|
||||
SevanaAqua::~SevanaAqua()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void SevanaAqua::open()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (mContext)
|
||||
return;
|
||||
|
||||
mContext = SSA_CreateAudioQualityAnalyzer();
|
||||
if (!mContext)
|
||||
;
|
||||
|
||||
//setParam("OutputFormats", "json");
|
||||
}
|
||||
|
||||
void SevanaAqua::close()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (!mContext)
|
||||
return;
|
||||
SSA_ReleaseAudioQualityAnalyzer(mContext);
|
||||
mContext = nullptr;
|
||||
}
|
||||
|
||||
bool SevanaAqua::isOpen() const
|
||||
{
|
||||
return mContext != nullptr;
|
||||
}
|
||||
|
||||
void SevanaAqua::setTempPath(const std::string& temp_path)
|
||||
{
|
||||
mTempPath = temp_path;
|
||||
}
|
||||
|
||||
std::string SevanaAqua::getTempPath() const
|
||||
{
|
||||
return mTempPath;
|
||||
}
|
||||
|
||||
SevanaAqua::CompareResult SevanaAqua::compare(AudioBuffer& reference, AudioBuffer& test)
|
||||
{
|
||||
// Clear previous temporary file
|
||||
if (!mTempPath.empty())
|
||||
::remove(mTempPath.c_str());
|
||||
|
||||
// Result value
|
||||
CompareResult r;
|
||||
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
|
||||
if (!mContext || !reference.isInitialized() || !test.isInitialized())
|
||||
return r;
|
||||
|
||||
// Make analysis
|
||||
TSSA_AQuA_AudioData aad;
|
||||
aad.dSrcData.dNChannels = reference.mChannels;
|
||||
aad.dSrcData.dSampleRate = reference.mRate;
|
||||
aad.dSrcData.pSamples = (short*)reference.mData->data();
|
||||
aad.dSrcData.dNSamples = (long)reference.mData->size() / 2 / reference.mChannels;
|
||||
|
||||
aad.dTstData.dNChannels = test.mChannels;
|
||||
aad.dTstData.dSampleRate = test.mRate;
|
||||
aad.dTstData.pSamples = (short*)test.mData->data();
|
||||
aad.dTstData.dNSamples = (long)test.mData->size() / 2 / test.mChannels;
|
||||
|
||||
int rescode;
|
||||
rescode = SSA_OnTestAudioData(mContext, &aad);
|
||||
if (rescode)
|
||||
return r;
|
||||
|
||||
// Get results
|
||||
|
||||
int len = SSA_GetQualityStringSize(mContext);
|
||||
char* qs = (char*)alloca(len + 10);
|
||||
SSA_FillQualityString(mContext, qs);
|
||||
//std::cout << qs << std::endl;
|
||||
std::istringstream iss(qs);
|
||||
while (!iss.eof())
|
||||
{
|
||||
std::string l;
|
||||
std::getline(iss, l);
|
||||
|
||||
// Split by :
|
||||
std::vector<std::string> p;
|
||||
StringHelper::split(l, p, "\t");
|
||||
if (p.size() == 3)
|
||||
{
|
||||
p[1] = StringHelper::trim(p[1]);
|
||||
p[2] = StringHelper::trim(p[2]);
|
||||
r.mReport[p[1]] = p[2];
|
||||
}
|
||||
}
|
||||
|
||||
len = SSA_GetSrcSignalSpecSize(mContext);
|
||||
float* srcSpecs = new float[len];
|
||||
SSA_FillSrcSignalSpecArray(mContext, srcSpecs);
|
||||
Json::Value src_spec_signal;
|
||||
|
||||
for(int i=0; i<16 && i<len; i++)
|
||||
src_spec_signal.append(srcSpecs[i]);
|
||||
delete[] srcSpecs;
|
||||
|
||||
r.mReport["SrcSpecSignal"] = src_spec_signal;
|
||||
|
||||
len = SSA_GetTstSignalSpecSize(mContext);
|
||||
float* tstSpecs = new float[len];
|
||||
SSA_FillTstSignalSpecArray(mContext, tstSpecs);
|
||||
Json::Value tst_spec_signal;
|
||||
|
||||
for(int i=0; i<16 && i<len; i++)
|
||||
tst_spec_signal.append(tstSpecs[i]);
|
||||
r.mReport["TstSpecSignal"] = tst_spec_signal;
|
||||
|
||||
delete[] tstSpecs;
|
||||
|
||||
char* faults_str = nullptr;
|
||||
int faults_str_len = 0;
|
||||
|
||||
if (mTempPath.empty())
|
||||
{
|
||||
faults_str_len = SSA_GetFaultsAnalysisStringSize(mContext);
|
||||
|
||||
if (faults_str_len > 0) {
|
||||
faults_str = new char[faults_str_len + 1];
|
||||
SSA_FillFaultsAnalysisString(mContext, faults_str);
|
||||
faults_str[faults_str_len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char* pairs_str = nullptr;
|
||||
int pairs_str_len = SSA_GetSpecPairsStringSize(mContext);
|
||||
if (pairs_str_len > 0)
|
||||
{
|
||||
char *pairs_str = new char[pairs_str_len + 1];
|
||||
SSA_FillSpecPairsString(mContext, pairs_str, pairs_str_len);
|
||||
pairs_str[pairs_str_len] = 0;
|
||||
}
|
||||
|
||||
TSSA_AQuA_Results iResults;
|
||||
SSA_FillQualityResultsStruct(mContext, &iResults);
|
||||
r.mReport["dPercent"] = iResults.dPercent;
|
||||
r.mReport["dMOSLike"] = iResults.dMOSLike;
|
||||
|
||||
if (faults_str_len > 0)
|
||||
{
|
||||
std::istringstream iss(faults_str);
|
||||
r.mFaults = loadFaultsReport(iss);
|
||||
}
|
||||
else
|
||||
if (!mTempPath.empty())
|
||||
{
|
||||
std::ifstream ifs(mTempPath.c_str());
|
||||
r.mFaults = loadFaultsReport(ifs);
|
||||
}
|
||||
|
||||
delete[] faults_str; faults_str = nullptr;
|
||||
delete[] pairs_str; pairs_str = nullptr;
|
||||
|
||||
r.mMos = (float)iResults.dMOSLike;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void SevanaAqua::configureWith(const Config& config)
|
||||
{
|
||||
if (!mContext)
|
||||
return;
|
||||
|
||||
for (auto& item: config)
|
||||
{
|
||||
const std::string& name = item.first;
|
||||
const std::string& value = item.second;
|
||||
if (!SSA_SetAnyString(mContext, const_cast<char *>(name.c_str()), const_cast<char *>(value.c_str())))
|
||||
throw std::runtime_error(std::string("SSA_SetAnyString returned failed for pair ") + name + " " + value);
|
||||
}
|
||||
}
|
||||
|
||||
SevanaAqua::Config SevanaAqua::parseConfig(const std::string& line)
|
||||
{
|
||||
Config result;
|
||||
|
||||
// Split command line to parts
|
||||
std::vector<std::string> pl;
|
||||
StringHelper::split(line, pl, "-");
|
||||
|
||||
for (const std::string& s: pl)
|
||||
{
|
||||
std::string::size_type p = s.find(' ');
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
std::string name = StringHelper::trim(s.substr(0, p));
|
||||
std::string value = StringHelper::trim(s.substr(p + 1));
|
||||
|
||||
result[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
SevanaAqua::PFaultsReport SevanaAqua::loadFaultsReport(std::istream& input)
|
||||
{
|
||||
PFaultsReport result = std::make_shared<FaultsReport>();
|
||||
std::string line;
|
||||
std::vector<std::string> parts;
|
||||
|
||||
// Parse output
|
||||
while (!input.eof())
|
||||
{
|
||||
std::getline(input, line);
|
||||
if (line.size() < 3)
|
||||
continue;
|
||||
|
||||
std::string::size_type p = line.find(":");
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
std::string name = StringHelper::trim(line.substr(0, p));
|
||||
|
||||
FaultsReport::Result r;
|
||||
|
||||
// Split report line to components
|
||||
parts.clear();
|
||||
StringHelper::split(line.substr(p + 1), parts, " \t");
|
||||
|
||||
// Remove empty components
|
||||
parts.erase(std::remove_if(parts.begin(), parts.end(), [](const std::string& item){return item.empty();}), parts.end());
|
||||
|
||||
if (parts.size() >= 2)
|
||||
{
|
||||
r.mSource = parts[0];
|
||||
r.mDegrated = parts[1];
|
||||
if (parts.size()> 2)
|
||||
r.mUnit = parts[2];
|
||||
result->mResultMap[name] = r;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p = line.find("ms.");
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
parts.clear();
|
||||
StringHelper::split(line, parts, " \t");
|
||||
if (parts.size() >= 3)
|
||||
{
|
||||
if (parts.back() == "ms.")
|
||||
result->mSignalAdvancedInMilliseconds = (float)std::atof(parts[parts.size() - 2].c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p = line.find("percent.");
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
parts.clear();
|
||||
StringHelper::split(line, parts, " \t");
|
||||
if (parts.size() >= 3)
|
||||
{
|
||||
if (parts.back() == "percent.")
|
||||
result->mMistimingInPercents = (float)std::atof(parts[parts.size() - 2].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // end of namespace MT
|
||||
|
||||
// It is to workaround old AQuA NDK build - it has reference to ftime
|
||||
/*#if defined(TARGET_ANDROID)
|
||||
#include <sys/timeb.h>
|
||||
|
||||
// This was removed from POSIX 2008.
|
||||
int ftime(struct timeb* tb) {
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
if (gettimeofday(&tv, &tz) < 0)
|
||||
return -1;
|
||||
|
||||
tb->time = tv.tv_sec;
|
||||
tb->millitm = (tv.tv_usec + 500) / 1000;
|
||||
|
||||
if (tb->millitm == 1000) {
|
||||
++tb->time;
|
||||
tb->millitm = 0;
|
||||
}
|
||||
|
||||
tb->timezone = tz.tz_minuteswest;
|
||||
tb->dstflag = tz.tz_dsttime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif*/
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
#ifndef _SEVANA_MOS_H
|
||||
#define _SEVANA_MOS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory.h>
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
# include "pvqa.h"
|
||||
# if !defined(PVQA_INTERVAL)
|
||||
# define PVQA_INTERVAL (0.68)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
# include "aqua.h"
|
||||
# include <json/json.h>
|
||||
#endif
|
||||
# include "helper/HL_ByteBuffer.h"
|
||||
|
||||
|
||||
namespace MT
|
||||
{
|
||||
enum class ReportType
|
||||
{
|
||||
PlainText,
|
||||
Html
|
||||
};
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
class SevanaMosUtility
|
||||
{
|
||||
public:
|
||||
// Returns MOS estimation as text representation of float value or "failed" word.
|
||||
static void run(const std::string& pcmPath, const std::string& intervalPath,
|
||||
std::string& estimation, std::string& intervals);
|
||||
};
|
||||
|
||||
extern float getSevanaMos(const std::string& audioPath, const std::string& intervalReportPath,
|
||||
std::string& intervalReport);
|
||||
|
||||
class SevanaPVQA
|
||||
{
|
||||
public:
|
||||
enum class Model
|
||||
{
|
||||
Stream,
|
||||
Interval
|
||||
};
|
||||
|
||||
protected:
|
||||
static void* mLibraryConfiguration;
|
||||
static int mLibraryErrorCode;
|
||||
static std::atomic_int mInstanceCounter;
|
||||
static std::atomic_uint_least64_t mAllProcessedMilliseconds;
|
||||
static bool mPvqaLoaded;
|
||||
|
||||
void* mContext = nullptr;
|
||||
Model mModel = Model::Interval;
|
||||
double mIntervalLength = 0.68;
|
||||
uint64_t mProcessedSamples = 0,
|
||||
mProcessedMilliseconds = 0;
|
||||
|
||||
bool mAudioLineInitialized = false;
|
||||
std::string mDumpWavPath;
|
||||
bool mOpenFailed = false;
|
||||
|
||||
ByteBuffer mAudioDump;
|
||||
public:
|
||||
static std::string getVersion();
|
||||
|
||||
// Required to call before any call to SevanaPVQA instance methods
|
||||
|
||||
#if defined(TARGET_ANDROID)
|
||||
static void setupAndroidEnvironment(void* environment, void* appcontext);
|
||||
#endif
|
||||
// Path to config file can be empty
|
||||
// In this case library will be considered initialized (but will produce zero MOS)
|
||||
static bool initializeLibrary(const std::string& pathToLicenseFile, const std::string& pathToConfigFile);
|
||||
static bool initializeLibraryWithData(const void* license_buffer, size_t license_len,
|
||||
const void* config_buffer, size_t config_len);
|
||||
static bool isInitialized();
|
||||
|
||||
static int getLibraryError();
|
||||
static void releaseLibrary();
|
||||
static int getInstanceCounter() { return mInstanceCounter; }
|
||||
static uint64_t getProcessedMilliseconds() { return mAllProcessedMilliseconds; }
|
||||
|
||||
SevanaPVQA();
|
||||
~SevanaPVQA();
|
||||
|
||||
void open(double interval, Model model);
|
||||
void close();
|
||||
bool isOpen() const;
|
||||
|
||||
// Update/Get model
|
||||
void update(int samplerate, int channels, const void* pcmBuffer, int pcmLength);
|
||||
|
||||
typedef std::vector<std::vector<float>> EchoData;
|
||||
enum class Codec
|
||||
{
|
||||
None,
|
||||
G711,
|
||||
ILBC,
|
||||
G722,
|
||||
G729,
|
||||
GSM,
|
||||
AMRNB,
|
||||
AMRWB,
|
||||
OPUS
|
||||
};
|
||||
|
||||
float getResults(std::string& report, EchoData** echo, int samplerate, Codec codec);
|
||||
|
||||
// Report is interval report. Names are output detector names. startIndex is column's start index in interval report of first detector.
|
||||
struct DetectorsList
|
||||
{
|
||||
std::vector<std::string> mNames;
|
||||
int mStartIndex = 0;
|
||||
};
|
||||
|
||||
static DetectorsList getDetectorsNames(const std::string& report);
|
||||
|
||||
// Get MOS in one shot
|
||||
void setPathToDumpFile(const std::string& path);
|
||||
float process(int samplerate, int channels, const void* pcmBuffer, int pcmLength, std::string& report, Codec codec);
|
||||
|
||||
Model getModel() const { return mModel; }
|
||||
|
||||
int getSize() const;
|
||||
|
||||
static std::string mosToColor(float mos);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<SevanaPVQA> PSevanaPVQA;
|
||||
#endif
|
||||
|
||||
#if defined(USE_AQUA_LIBRARY)
|
||||
class SevanaAqua
|
||||
{
|
||||
protected:
|
||||
void* mContext = nullptr;
|
||||
std::mutex mMutex;
|
||||
std::string mTempPath;
|
||||
|
||||
public:
|
||||
// Returns 0 (zero) on successful initialization, otherwise it is error code
|
||||
static int initializeLibrary(const std::string& pathToLicenseFile);
|
||||
static int initializeLibrary(const void* buffer, size_t len);
|
||||
|
||||
static void releaseLibrary();
|
||||
static std::string getVersion();
|
||||
|
||||
SevanaAqua();
|
||||
~SevanaAqua();
|
||||
|
||||
void open();
|
||||
void close();
|
||||
bool isOpen() const;
|
||||
|
||||
void setTempPath(const std::string& temp_path);
|
||||
std::string getTempPath() const;
|
||||
|
||||
typedef std::map<std::string, std::string> Config;
|
||||
void configureWith(const Config& config);
|
||||
static Config parseConfig(const std::string& line);
|
||||
|
||||
// Report is returned in JSON format
|
||||
struct AudioBuffer
|
||||
{
|
||||
AudioBuffer()
|
||||
{}
|
||||
|
||||
AudioBuffer(int size)
|
||||
{
|
||||
mData = std::make_shared<std::vector<unsigned char>>();
|
||||
mData->resize(size);
|
||||
}
|
||||
|
||||
AudioBuffer(const void* data, int size)
|
||||
{
|
||||
mData = std::make_shared<std::vector<unsigned char>>();
|
||||
mData->resize(size);
|
||||
memcpy(mData->data(), data, size);
|
||||
}
|
||||
|
||||
void* data()
|
||||
{
|
||||
return mData ? mData->data() : nullptr;
|
||||
}
|
||||
|
||||
const void* data() const
|
||||
{
|
||||
return mData ? mData->data() : nullptr;
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return mData ? mData->size() : 0;
|
||||
}
|
||||
|
||||
int mRate = 8000;
|
||||
int mChannels = 1;
|
||||
std::shared_ptr<std::vector<unsigned char>> mData;
|
||||
bool isInitialized() const { return mRate > 0 && mChannels > 0 && mData; }
|
||||
};
|
||||
|
||||
|
||||
struct FaultsReport
|
||||
{
|
||||
float mSignalAdvancedInMilliseconds = -5000.0;
|
||||
float mMistimingInPercents = -5000.0;
|
||||
|
||||
struct Result
|
||||
{
|
||||
std::string mSource, mDegrated, mUnit;
|
||||
};
|
||||
typedef std::map<std::string, Result> ResultMap;
|
||||
ResultMap mResultMap;
|
||||
|
||||
Json::Value toJson() const;
|
||||
std::string toText() const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<FaultsReport> PFaultsReport;
|
||||
|
||||
static PFaultsReport loadFaultsReport(std::istream& input);
|
||||
|
||||
// Compare in one shot. Report will include text representation of json report.
|
||||
struct CompareResult
|
||||
{
|
||||
float mMos = 0.0f;
|
||||
Json::Value mReport;
|
||||
PFaultsReport mFaults;
|
||||
};
|
||||
|
||||
CompareResult compare(AudioBuffer& reference, AudioBuffer& test);
|
||||
};
|
||||
typedef std::shared_ptr<SevanaAqua> PSevanaAqua;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -9,8 +9,6 @@
|
|||
#include "jrtplib/src/rtptimeutilities.h"
|
||||
#include "jrtplib/src/rtppacket.h"
|
||||
|
||||
#include "MT_SevanaMos.h"
|
||||
|
||||
using std::experimental::optional;
|
||||
|
||||
namespace MT
|
||||
|
|
@ -79,8 +77,8 @@ class JitterStatistics
|
|||
{
|
||||
public:
|
||||
void process(jrtplib::RTPPacket* packet, int samplerate);
|
||||
ProbeStats<double> get() const { return mJitter; }
|
||||
double getMaxDelta() const { return mMaxDelta; }
|
||||
ProbeStats<float> get() const { return mJitter; }
|
||||
float getMaxDelta() const { return mMaxDelta; }
|
||||
|
||||
protected:
|
||||
// Jitter calculation
|
||||
|
|
@ -90,13 +88,13 @@ protected:
|
|||
uint32_t mReceiveTimestamp = 0;
|
||||
|
||||
// It is classic jitter value in units
|
||||
optional<double> mLastJitter;
|
||||
optional<float> mLastJitter;
|
||||
|
||||
// Some statistics for jitter value in seconds
|
||||
ProbeStats<double> mJitter;
|
||||
ProbeStats<float> mJitter;
|
||||
|
||||
// Maximal delta in seconds
|
||||
double mMaxDelta = 0.0;
|
||||
float mMaxDelta = 0.0f;
|
||||
};
|
||||
|
||||
class Statistics
|
||||
|
|
|
|||
|
|
@ -21,10 +21,6 @@
|
|||
#include <chrono>
|
||||
#include "../helper/HL_Optional.hpp"
|
||||
|
||||
#if defined(USE_PVQA_LIBRARY)
|
||||
# include "MT_SevanaMos.h"
|
||||
#endif
|
||||
|
||||
using std::experimental::optional;
|
||||
|
||||
namespace MT
|
||||
|
|
|
|||
Loading…
Reference in New Issue