- removed old Sevana routines (they are moved to pvqa++ / aqua++ header files)

- cleanups
This commit is contained in:
Dmytro Bogovych 2019-06-22 12:00:49 +03:00
parent 477932459f
commit 682362c6fe
11 changed files with 875 additions and 2117 deletions

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -11,11 +11,7 @@
#include <vector>
#include <algorithm>
#ifdef USE_RESIP_INTEGRATION
# include "resiprocate/rutil/ThreadIf.hxx"
#else
# include <thread>
#endif
#include <thread>
#include "HL_NetworkSocket.h"
#include "HL_Sync.h"
@ -25,91 +21,92 @@
class SocketSink
{
public:
virtual void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize) = 0;
virtual ~SocketSink();
virtual void onReceivedData(PDatagramSocket socket, InternetAddress& src, const void* receivedPtr, unsigned receivedSize) = 0;
};
// Class allocates new UDP sockets and tracks incoming packets on them. It runs in separate thread
class SocketHeap
{
public:
enum Multiplex
{
DoMultiplexing,
DontMultiplexing
};
enum Multiplex
{
DoMultiplexing,
DontMultiplexing
};
SocketHeap(unsigned short start, unsigned short finish);
virtual ~SocketHeap();
SocketHeap(unsigned short start, unsigned short finish);
virtual ~SocketHeap();
static SocketHeap& instance();
static SocketHeap& instance();
void start();
void stop();
void start();
void stop();
// Specifies ne\ port number range. The sockets will be allocated in range [start..finish]
void setRange(unsigned short start, unsigned short finish);
// 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);
// Returns used port number range
void range(unsigned short& start, unsigned short& finish);
// Stops receiving data for specified socket and frees socket itself.
void freeSocket(PDatagramSocket socket);
void freeSocketPair(const RtpPair<PDatagramSocket>& p);
// 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);
// 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)
{ }
struct SocketItem
{
// Local port number for socket
PDatagramSocket mSocket;
SocketItem(unsigned short portnumber, SocketSink* sink)
:mSink(sink)
{
mSocket->mLocalPort = portnumber;
}
~SocketItem()
{ }
};
// Data sink pointer
SocketSink* mSink;
typedef std::map<SOCKET, SocketItem> SocketMap;
typedef std::vector<unsigned short> PortVector;
typedef std::vector<PDatagramSocket> SocketVector;
SocketItem()
:mSink(nullptr)
{ }
Mutex mGuard;
SocketMap mSocketMap;
PortVector mPortVector;
unsigned short mStart,
mFinish;
SocketVector mDeleteVector;
Mutex mDeleteGuard;
SocketItem(unsigned short portnumber, SocketSink* sink)
:mSink(sink)
{
mSocket->mLocalPort = portnumber;
}
char mTempPacket[MAX_UDPPACKET_SIZE];
~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];
std::shared_ptr<std::thread> mWorkerThread;
std::thread::id mThreadId;
std::thread::id mThreadId;
bool mShutdown = false;
bool isShutdown() const { return mShutdown; }
void thread();
// Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector
void processDeleted();
// Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector
void processDeleted();
};

View File

@ -16,13 +16,13 @@
Variant::Variant()
{
mType = VTYPE_INT;
mInt = 0;
mInt64 = 0;
mBool = 0;
mFloat = 0;
mPointer = nullptr;
mType = VTYPE_INT;
mInt = 0;
mInt64 = 0;
mBool = 0;
mFloat = 0;
mPointer = nullptr;
}
Variant::~Variant()
@ -30,315 +30,319 @@ Variant::~Variant()
}
Variant::Variant(bool value)
:mType(VTYPE_BOOL), mBool(value)
:mType(VTYPE_BOOL), mBool(value)
{}
Variant::Variant(int value)
:mType(VTYPE_INT), mInt(value)
:mType(VTYPE_INT), mInt(value)
{}
Variant::Variant(int64_t value)
:mType(VTYPE_INT64), mInt64(value)
:mType(VTYPE_INT64), mInt64(value)
{}
Variant::Variant(float value)
:mType(VTYPE_FLOAT), mFloat(value)
:mType(VTYPE_FLOAT), mFloat(value)
{}
Variant::Variant(double value)
:mType(VTYPE_FLOAT), mFloat((float)value)
:mType(VTYPE_FLOAT), mFloat((float)value)
{}
Variant::Variant(const std::string& value)
:mType(VTYPE_STRING), mString(value)
:mType(VTYPE_STRING), mString(value)
{}
Variant& Variant::operator = (bool value)
{
mType = VTYPE_BOOL;
mBool = value;
mType = VTYPE_BOOL;
mBool = value;
return *this;
return *this;
}
Variant& Variant::operator = (int value)
{
mType = VTYPE_INT;
mInt = value;
mType = VTYPE_INT;
mInt = value;
return *this;
return *this;
}
Variant& Variant::operator = (int64_t value)
{
mType = VTYPE_INT64;
mInt64 = value;
mType = VTYPE_INT64;
mInt64 = value;
return *this;
return *this;
}
Variant& Variant::operator = (float value)
{
mType = VTYPE_FLOAT;
mFloat = value;
mType = VTYPE_FLOAT;
mFloat = value;
return *this;
return *this;
}
Variant& Variant::operator = (const std::string& value)
{
mType = VTYPE_STRING;
mString = value;
mType = VTYPE_STRING;
mString = value;
return *this;
return *this;
}
Variant& Variant::operator = (const char* value)
{
mType = VTYPE_STRING;
mString = value;
mType = VTYPE_STRING;
mString = value;
return *this;
return *this;
}
Variant& Variant::operator = (void* value)
{
mType = VTYPE_POINTER;
mPointer = value;
mType = VTYPE_POINTER;
mPointer = value;
return *this;
return *this;
}
Variant& Variant::operator = (PVariantMap map)
{
mType = VTYPE_VMAP;
mVMap = map;
mType = VTYPE_VMAP;
mVMap = map;
return *this;
return *this;
}
Variant Variant::operator + (const Variant& rhs)
{
switch (type())
{
case VTYPE_BOOL:
case VTYPE_INT: return asInt() + rhs.asInt();
case VTYPE_INT64: return asInt64() + rhs.asInt64();
case VTYPE_FLOAT: return asFloat() + rhs.asFloat();
case VTYPE_STRING: return asStdString() + rhs.asStdString();
default:
return false;
}
switch (type())
{
case VTYPE_BOOL:
case VTYPE_INT: return asInt() + rhs.asInt();
case VTYPE_INT64: return asInt64() + rhs.asInt64();
case VTYPE_FLOAT: return asFloat() + rhs.asFloat();
case VTYPE_STRING: return asStdString() + rhs.asStdString();
default:
return false;
}
}
Variant Variant::operator - (const Variant& rhs)
{
switch (type())
{
case VTYPE_BOOL:
case VTYPE_STRING:
case VTYPE_INT: return asInt() - rhs.asInt();
case VTYPE_INT64: return asInt64() - rhs.asInt64();
case VTYPE_FLOAT: return asFloat() - rhs.asFloat();
switch (type())
{
case VTYPE_BOOL:
case VTYPE_STRING:
case VTYPE_INT: return asInt() - rhs.asInt();
case VTYPE_INT64: return asInt64() - rhs.asInt64();
case VTYPE_FLOAT: return asFloat() - rhs.asFloat();
default:
return false;
}
default:
return false;
}
}
Variant Variant::operator * (const Variant& rhs)
{
switch (type())
{
case VTYPE_BOOL:
case VTYPE_STRING:
case VTYPE_INT: return asInt() * rhs.asInt();
case VTYPE_INT64: return asInt64() * rhs.asInt64();
case VTYPE_FLOAT: return asFloat() * rhs.asFloat();
default:
return false;
}
switch (type())
{
case VTYPE_BOOL:
case VTYPE_STRING:
case VTYPE_INT: return asInt() * rhs.asInt();
case VTYPE_INT64: return asInt64() * rhs.asInt64();
case VTYPE_FLOAT: return asFloat() * rhs.asFloat();
default:
return false;
}
}
Variant Variant::operator / (const Variant& rhs)
{
switch (type())
{
case VTYPE_BOOL:
case VTYPE_STRING:
case VTYPE_INT: return asInt() / rhs.asInt();
case VTYPE_INT64: return asInt64() / rhs.asInt64();
case VTYPE_FLOAT: return asFloat() / rhs.asFloat();
switch (type())
{
case VTYPE_BOOL:
case VTYPE_STRING:
case VTYPE_INT: return asInt() / rhs.asInt();
case VTYPE_INT64: return asInt64() / rhs.asInt64();
case VTYPE_FLOAT: return asFloat() / rhs.asFloat();
default:
return false;
}
default:
return false;
}
}
bool Variant::operator < (const Variant& rhs) const
{
switch (type())
{
case VTYPE_STRING: return asStdString() < rhs.asStdString();
case VTYPE_BOOL:
case VTYPE_INT: return asInt() < rhs.asInt();
case VTYPE_INT64: return asInt64() < rhs.asInt64();
case VTYPE_FLOAT: return asFloat() < rhs.asFloat();
default:
return false;
}
switch (type())
{
case VTYPE_STRING: return asStdString() < rhs.asStdString();
case VTYPE_BOOL:
case VTYPE_INT: return asInt() < rhs.asInt();
case VTYPE_INT64: return asInt64() < rhs.asInt64();
case VTYPE_FLOAT: return asFloat() < rhs.asFloat();
default:
return false;
}
}
bool Variant::operator > (const Variant& rhs) const
{
return !(*this == rhs) && !(*this < rhs);
return !(*this == rhs) && !(*this < rhs);
}
bool Variant::operator == (const Variant& rhs) const
{
switch (type())
{
case VTYPE_STRING: return asStdString() == rhs.asStdString();
case VTYPE_BOOL: return asBool() == rhs.asBool();
case VTYPE_INT: return asInt() == rhs.asInt();
case VTYPE_INT64: return asInt64() == rhs.asInt64();
case VTYPE_FLOAT: return asFloat() == rhs.asFloat();
case VTYPE_POINTER: return asPointer() == rhs.asPointer();
case VTYPE_VMAP: assert(0); break;
default:
return false;
}
switch (type())
{
case VTYPE_STRING: return asStdString() == rhs.asStdString();
case VTYPE_BOOL: return asBool() == rhs.asBool();
case VTYPE_INT: return asInt() == rhs.asInt();
case VTYPE_INT64: return asInt64() == rhs.asInt64();
case VTYPE_FLOAT: return asFloat() == rhs.asFloat();
case VTYPE_POINTER: return asPointer() == rhs.asPointer();
case VTYPE_VMAP: assert(0); break;
default:
return false;
}
return false;
return false;
}
bool Variant::operator != (const Variant& rhs) const
{
return !(*this == rhs);
return !(*this == rhs);
}
bool Variant::operator <= (const Variant& rhs) const
{
return (*this < rhs) || (*this == rhs);
return (*this < rhs) || (*this == rhs);
}
bool Variant::operator >= (const Variant& rhs) const
{
return (*this > rhs) || (*this == rhs);
return (*this > rhs) || (*this == rhs);
}
int Variant::asInt() const
{
if (mType != VTYPE_INT)
throw Exception(ERR_BAD_VARIANT_TYPE);
return mInt;
if (mType != VTYPE_INT)
throw Exception(ERR_BAD_VARIANT_TYPE);
return mInt;
}
int64_t Variant::asInt64() const
{
if (mType != VTYPE_INT64)
throw Exception(ERR_BAD_VARIANT_TYPE);
if (mType != VTYPE_INT64)
throw Exception(ERR_BAD_VARIANT_TYPE);
return mInt;
return mInt;
}
bool Variant::asBool() const
{
switch (mType)
{
case VTYPE_INT:
return mInt != 0;
switch (mType)
{
case VTYPE_INT:
return mInt != 0;
case VTYPE_INT64:
return mInt64 != 0;
case VTYPE_INT64:
return mInt64 != 0;
case VTYPE_BOOL:
return mBool;
case VTYPE_BOOL:
return mBool;
case VTYPE_FLOAT:
return mFloat != 0;
case VTYPE_FLOAT:
return mFloat != 0;
case VTYPE_STRING:
return mString.length() != 0;
case VTYPE_STRING:
return mString.length() != 0;
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
}
float Variant::asFloat() const
{
switch (mType)
{
case VTYPE_INT:
return (float)mInt;
switch (mType)
{
case VTYPE_INT:
return (float)mInt;
case VTYPE_INT64:
return (float)mInt64;
case VTYPE_INT64:
return (float)mInt64;
case VTYPE_FLOAT:
return mFloat;
case VTYPE_FLOAT:
return mFloat;
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
}
double Variant::asDouble() const
{
return static_cast<double>(asFloat());
}
std::string Variant::asStdString() const
{
char buffer[32];
switch (mType)
{
case VTYPE_STRING:
return mString;
case VTYPE_INT:
sprintf(buffer, "%d", mInt);
return buffer;
char buffer[32];
switch (mType)
{
case VTYPE_STRING:
return mString;
case VTYPE_INT64:
sprintf(buffer, "%lli", static_cast<long long int>(mInt64));
return buffer;
case VTYPE_INT:
sprintf(buffer, "%d", mInt);
return buffer;
case VTYPE_BOOL:
return mBool ? "true" : "false";
case VTYPE_INT64:
sprintf(buffer, "%lli", static_cast<long long int>(mInt64));
return buffer;
case VTYPE_FLOAT:
sprintf(buffer, "%f", mFloat);
return buffer;
case VTYPE_BOOL:
return mBool ? "true" : "false";
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
case VTYPE_FLOAT:
sprintf(buffer, "%f", mFloat);
return buffer;
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
}
void* Variant::asPointer() const
{
switch (mType)
{
case VTYPE_POINTER:
return mPointer;
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
switch (mType)
{
case VTYPE_POINTER:
return mPointer;
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
}
PVariantMap Variant::asVMap()
{
switch (mType)
{
case VTYPE_VMAP:
return mVMap;
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
switch (mType)
{
case VTYPE_VMAP:
return mVMap;
default:
throw Exception(ERR_BAD_VARIANT_TYPE);
}
}
VariantType Variant::type() const
{
return mType;
return mType;
}
VariantMap::VariantMap()
@ -351,25 +355,25 @@ VariantMap::~VariantMap()
bool VariantMap::empty() const
{
return mData.empty();
return mData.empty();
}
void VariantMap::clear()
{
mData.clear();
mData.clear();
}
bool VariantMap::exists(int itemId) const
{
return mData.find(itemId) != mData.end();
return mData.find(itemId) != mData.end();
}
Variant& VariantMap::operator [](int itemId)
{
return mData[itemId];
return mData[itemId];
}
Variant& VariantMap::at(int itemId)
{
return mData[itemId];
return mData[itemId];
}

View File

@ -60,7 +60,8 @@ public:
int asInt() const;
int64_t asInt64() const;
bool asBool() const;
float asFloat() const ;
float asFloat() const;
double asDouble() const;
std::string asStdString() const;
//const char* asString();
void* asPointer() const;

View File

@ -22,11 +22,6 @@
#include <map>
#if defined(USE_PVQA_LIBRARY)
# include "MT_SevanaMos.h"
#endif
// #define DUMP_DECODED
namespace MT

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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