- 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_AudioStream.cpp
|
||||||
${rtphone_engine}/media/MT_AudioReceiver.cpp
|
${rtphone_engine}/media/MT_AudioReceiver.cpp
|
||||||
${rtphone_engine}/media/MT_AudioCodec.cpp
|
${rtphone_engine}/media/MT_AudioCodec.cpp
|
||||||
${rtphone_engine}/media/MT_SevanaMos.cpp
|
|
||||||
${rtphone_engine}/media/MT_AmrCodec.cpp
|
${rtphone_engine}/media/MT_AmrCodec.cpp
|
||||||
${rtphone_engine}/media/MT_EvsCodec.cpp
|
${rtphone_engine}/media/MT_EvsCodec.cpp
|
||||||
${rtphone_engine}/media/MT_CngHelper.cpp
|
${rtphone_engine}/media/MT_CngHelper.cpp
|
||||||
|
|
@ -120,7 +119,6 @@ set (RTPHONE_HEADERS
|
||||||
${rtphone_engine}/media/MT_AudioStream.h
|
${rtphone_engine}/media/MT_AudioStream.h
|
||||||
${rtphone_engine}/media/MT_AudioReceiver.h
|
${rtphone_engine}/media/MT_AudioReceiver.h
|
||||||
${rtphone_engine}/media/MT_AudioCodec.h
|
${rtphone_engine}/media/MT_AudioCodec.h
|
||||||
${rtphone_engine}/media/MT_SevanaMos.h
|
|
||||||
${rtphone_engine}/media/MT_AmrCodec.h
|
${rtphone_engine}/media/MT_AmrCodec.h
|
||||||
${rtphone_engine}/media/MT_EvsCodec.h
|
${rtphone_engine}/media/MT_EvsCodec.h
|
||||||
${rtphone_engine}/media/MT_CngHelper.h
|
${rtphone_engine}/media/MT_CngHelper.h
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------- SocketSink -------------------------
|
||||||
|
SocketSink::~SocketSink()
|
||||||
|
{}
|
||||||
|
|
||||||
// ----------------------------- SocketHeap -------------------------
|
// ----------------------------- SocketHeap -------------------------
|
||||||
|
|
||||||
SocketHeap::SocketHeap(unsigned short start, unsigned short finish)
|
SocketHeap::SocketHeap(unsigned short start, unsigned short finish)
|
||||||
|
|
@ -132,7 +136,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||||
// Obtain port number
|
// Obtain port number
|
||||||
sockaddr_in addr;
|
sockaddr_in addr;
|
||||||
sockaddr_in6 addr6;
|
sockaddr_in6 addr6;
|
||||||
int result;
|
int result = 0;
|
||||||
int testport;
|
int testport;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
|
@ -144,7 +148,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||||
memset(&addr, 0, sizeof addr);
|
memset(&addr, 0, sizeof addr);
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(testport);
|
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)
|
if (result)
|
||||||
result = WSAGetLastError();
|
result = WSAGetLastError();
|
||||||
break;
|
break;
|
||||||
|
|
@ -153,7 +157,7 @@ PDatagramSocket SocketHeap::allocSocket(int family, SocketSink* sink, int port)
|
||||||
memset(&addr6, 0, sizeof addr6);
|
memset(&addr6, 0, sizeof addr6);
|
||||||
addr6.sin6_family = AF_INET6;
|
addr6.sin6_family = AF_INET6;
|
||||||
addr6.sin6_port = htons(testport);
|
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)
|
if (result)
|
||||||
result = WSAGetLastError();
|
result = WSAGetLastError();
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#ifdef USE_RESIP_INTEGRATION
|
#include <thread>
|
||||||
# include "resiprocate/rutil/ThreadIf.hxx"
|
|
||||||
#else
|
|
||||||
# include <thread>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "HL_NetworkSocket.h"
|
#include "HL_NetworkSocket.h"
|
||||||
#include "HL_Sync.h"
|
#include "HL_Sync.h"
|
||||||
|
|
@ -25,91 +21,92 @@
|
||||||
class SocketSink
|
class SocketSink
|
||||||
{
|
{
|
||||||
public:
|
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 allocates new UDP sockets and tracks incoming packets on them. It runs in separate thread
|
||||||
class SocketHeap
|
class SocketHeap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Multiplex
|
enum Multiplex
|
||||||
{
|
{
|
||||||
DoMultiplexing,
|
DoMultiplexing,
|
||||||
DontMultiplexing
|
DontMultiplexing
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketHeap(unsigned short start, unsigned short finish);
|
SocketHeap(unsigned short start, unsigned short finish);
|
||||||
virtual ~SocketHeap();
|
virtual ~SocketHeap();
|
||||||
|
|
||||||
static SocketHeap& instance();
|
static SocketHeap& instance();
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
// Specifies ne\ port number range. The sockets will be allocated in range [start..finish]
|
// Specifies ne\ port number range. The sockets will be allocated in range [start..finish]
|
||||||
void setRange(unsigned short start, unsigned short finish);
|
void setRange(unsigned short start, unsigned short finish);
|
||||||
|
|
||||||
// Returns used port number range
|
// Returns used port number range
|
||||||
void range(unsigned short& start, unsigned short& finish);
|
void range(unsigned short& start, unsigned short& finish);
|
||||||
|
|
||||||
// Attempts to allocate and return socket + allocated port number. REQUIRES pointer to data sink - it will be used to process incoming datagrams
|
// 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);
|
PDatagramSocket allocSocket(int family, SocketSink* sink, int port = 0);
|
||||||
RtpPair<PDatagramSocket> allocSocketPair(int family, SocketSink* sink, Multiplex m);
|
RtpPair<PDatagramSocket> allocSocketPair(int family, SocketSink* sink, Multiplex m);
|
||||||
|
|
||||||
// Stops receiving data for specified socket and frees socket itself.
|
// Stops receiving data for specified socket and frees socket itself.
|
||||||
void freeSocket(PDatagramSocket socket);
|
void freeSocket(PDatagramSocket socket);
|
||||||
void freeSocketPair(const RtpPair<PDatagramSocket>& p);
|
void freeSocketPair(const RtpPair<PDatagramSocket>& p);
|
||||||
|
|
||||||
// Sends data to specified address on specified socket.
|
// Sends data to specified address on specified socket.
|
||||||
void sendData(DatagramSocket& socket, InternetAddress& dest, const void* dataPtr, int dataSize);
|
void sendData(DatagramSocket& socket, InternetAddress& dest, const void* dataPtr, int dataSize);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct SocketItem
|
struct SocketItem
|
||||||
{
|
|
||||||
// Local port number for socket
|
|
||||||
PDatagramSocket mSocket;
|
|
||||||
|
|
||||||
// Data sink pointer
|
|
||||||
SocketSink* mSink;
|
|
||||||
|
|
||||||
SocketItem()
|
|
||||||
:mSink(NULL)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
SocketItem(unsigned short portnumber, SocketSink* sink)
|
|
||||||
:mSink(sink)
|
|
||||||
{
|
{
|
||||||
mSocket->mLocalPort = portnumber;
|
// Local port number for socket
|
||||||
}
|
PDatagramSocket mSocket;
|
||||||
|
|
||||||
~SocketItem()
|
// Data sink pointer
|
||||||
{ }
|
SocketSink* mSink;
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::map<SOCKET, SocketItem> SocketMap;
|
SocketItem()
|
||||||
typedef std::vector<unsigned short> PortVector;
|
:mSink(nullptr)
|
||||||
typedef std::vector<PDatagramSocket> SocketVector;
|
{ }
|
||||||
|
|
||||||
Mutex mGuard;
|
SocketItem(unsigned short portnumber, SocketSink* sink)
|
||||||
SocketMap mSocketMap;
|
:mSink(sink)
|
||||||
PortVector mPortVector;
|
{
|
||||||
unsigned short mStart,
|
mSocket->mLocalPort = portnumber;
|
||||||
mFinish;
|
}
|
||||||
SocketVector mDeleteVector;
|
|
||||||
Mutex mDeleteGuard;
|
|
||||||
|
|
||||||
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::shared_ptr<std::thread> mWorkerThread;
|
||||||
|
|
||||||
std::thread::id mThreadId;
|
std::thread::id mThreadId;
|
||||||
bool mShutdown = false;
|
bool mShutdown = false;
|
||||||
bool isShutdown() const { return mShutdown; }
|
bool isShutdown() const { return mShutdown; }
|
||||||
|
|
||||||
void thread();
|
void thread();
|
||||||
|
|
||||||
// Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector
|
// Processes mDeleteVector -> updates mSocketMap, removes socket items and closes sockets specified in mDeleteVector
|
||||||
void processDeleted();
|
void processDeleted();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@
|
||||||
|
|
||||||
Variant::Variant()
|
Variant::Variant()
|
||||||
{
|
{
|
||||||
mType = VTYPE_INT;
|
mType = VTYPE_INT;
|
||||||
mInt = 0;
|
mInt = 0;
|
||||||
mInt64 = 0;
|
mInt64 = 0;
|
||||||
mBool = 0;
|
mBool = 0;
|
||||||
mFloat = 0;
|
mFloat = 0;
|
||||||
|
|
||||||
mPointer = nullptr;
|
mPointer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant::~Variant()
|
Variant::~Variant()
|
||||||
|
|
@ -30,315 +30,319 @@ Variant::~Variant()
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant::Variant(bool value)
|
Variant::Variant(bool value)
|
||||||
:mType(VTYPE_BOOL), mBool(value)
|
:mType(VTYPE_BOOL), mBool(value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Variant::Variant(int value)
|
Variant::Variant(int value)
|
||||||
:mType(VTYPE_INT), mInt(value)
|
:mType(VTYPE_INT), mInt(value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Variant::Variant(int64_t value)
|
Variant::Variant(int64_t value)
|
||||||
:mType(VTYPE_INT64), mInt64(value)
|
:mType(VTYPE_INT64), mInt64(value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Variant::Variant(float value)
|
Variant::Variant(float value)
|
||||||
:mType(VTYPE_FLOAT), mFloat(value)
|
:mType(VTYPE_FLOAT), mFloat(value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Variant::Variant(double value)
|
Variant::Variant(double value)
|
||||||
:mType(VTYPE_FLOAT), mFloat((float)value)
|
:mType(VTYPE_FLOAT), mFloat((float)value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Variant::Variant(const std::string& value)
|
Variant::Variant(const std::string& value)
|
||||||
:mType(VTYPE_STRING), mString(value)
|
:mType(VTYPE_STRING), mString(value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Variant& Variant::operator = (bool value)
|
Variant& Variant::operator = (bool value)
|
||||||
{
|
{
|
||||||
mType = VTYPE_BOOL;
|
mType = VTYPE_BOOL;
|
||||||
mBool = value;
|
mBool = value;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& Variant::operator = (int value)
|
Variant& Variant::operator = (int value)
|
||||||
{
|
{
|
||||||
mType = VTYPE_INT;
|
mType = VTYPE_INT;
|
||||||
mInt = value;
|
mInt = value;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& Variant::operator = (int64_t value)
|
Variant& Variant::operator = (int64_t value)
|
||||||
{
|
{
|
||||||
mType = VTYPE_INT64;
|
mType = VTYPE_INT64;
|
||||||
mInt64 = value;
|
mInt64 = value;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& Variant::operator = (float value)
|
Variant& Variant::operator = (float value)
|
||||||
{
|
{
|
||||||
mType = VTYPE_FLOAT;
|
mType = VTYPE_FLOAT;
|
||||||
mFloat = value;
|
mFloat = value;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& Variant::operator = (const std::string& value)
|
Variant& Variant::operator = (const std::string& value)
|
||||||
{
|
{
|
||||||
mType = VTYPE_STRING;
|
mType = VTYPE_STRING;
|
||||||
mString = value;
|
mString = value;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& Variant::operator = (const char* value)
|
Variant& Variant::operator = (const char* value)
|
||||||
{
|
{
|
||||||
mType = VTYPE_STRING;
|
mType = VTYPE_STRING;
|
||||||
mString = value;
|
mString = value;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& Variant::operator = (void* value)
|
Variant& Variant::operator = (void* value)
|
||||||
{
|
{
|
||||||
mType = VTYPE_POINTER;
|
mType = VTYPE_POINTER;
|
||||||
mPointer = value;
|
mPointer = value;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& Variant::operator = (PVariantMap map)
|
Variant& Variant::operator = (PVariantMap map)
|
||||||
{
|
{
|
||||||
mType = VTYPE_VMAP;
|
mType = VTYPE_VMAP;
|
||||||
mVMap = map;
|
mVMap = map;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant Variant::operator + (const Variant& rhs)
|
Variant Variant::operator + (const Variant& rhs)
|
||||||
{
|
{
|
||||||
switch (type())
|
switch (type())
|
||||||
{
|
{
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
case VTYPE_INT: return asInt() + rhs.asInt();
|
case VTYPE_INT: return asInt() + rhs.asInt();
|
||||||
case VTYPE_INT64: return asInt64() + rhs.asInt64();
|
case VTYPE_INT64: return asInt64() + rhs.asInt64();
|
||||||
case VTYPE_FLOAT: return asFloat() + rhs.asFloat();
|
case VTYPE_FLOAT: return asFloat() + rhs.asFloat();
|
||||||
case VTYPE_STRING: return asStdString() + rhs.asStdString();
|
case VTYPE_STRING: return asStdString() + rhs.asStdString();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant Variant::operator - (const Variant& rhs)
|
Variant Variant::operator - (const Variant& rhs)
|
||||||
{
|
{
|
||||||
switch (type())
|
switch (type())
|
||||||
{
|
{
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
case VTYPE_STRING:
|
case VTYPE_STRING:
|
||||||
case VTYPE_INT: return asInt() - rhs.asInt();
|
case VTYPE_INT: return asInt() - rhs.asInt();
|
||||||
case VTYPE_INT64: return asInt64() - rhs.asInt64();
|
case VTYPE_INT64: return asInt64() - rhs.asInt64();
|
||||||
case VTYPE_FLOAT: return asFloat() - rhs.asFloat();
|
case VTYPE_FLOAT: return asFloat() - rhs.asFloat();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant Variant::operator * (const Variant& rhs)
|
Variant Variant::operator * (const Variant& rhs)
|
||||||
{
|
{
|
||||||
switch (type())
|
switch (type())
|
||||||
{
|
{
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
case VTYPE_STRING:
|
case VTYPE_STRING:
|
||||||
case VTYPE_INT: return asInt() * rhs.asInt();
|
case VTYPE_INT: return asInt() * rhs.asInt();
|
||||||
case VTYPE_INT64: return asInt64() * rhs.asInt64();
|
case VTYPE_INT64: return asInt64() * rhs.asInt64();
|
||||||
case VTYPE_FLOAT: return asFloat() * rhs.asFloat();
|
case VTYPE_FLOAT: return asFloat() * rhs.asFloat();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant Variant::operator / (const Variant& rhs)
|
Variant Variant::operator / (const Variant& rhs)
|
||||||
{
|
{
|
||||||
switch (type())
|
switch (type())
|
||||||
{
|
{
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
case VTYPE_STRING:
|
case VTYPE_STRING:
|
||||||
case VTYPE_INT: return asInt() / rhs.asInt();
|
case VTYPE_INT: return asInt() / rhs.asInt();
|
||||||
case VTYPE_INT64: return asInt64() / rhs.asInt64();
|
case VTYPE_INT64: return asInt64() / rhs.asInt64();
|
||||||
case VTYPE_FLOAT: return asFloat() / rhs.asFloat();
|
case VTYPE_FLOAT: return asFloat() / rhs.asFloat();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Variant::operator < (const Variant& rhs) const
|
bool Variant::operator < (const Variant& rhs) const
|
||||||
{
|
{
|
||||||
switch (type())
|
switch (type())
|
||||||
{
|
{
|
||||||
case VTYPE_STRING: return asStdString() < rhs.asStdString();
|
case VTYPE_STRING: return asStdString() < rhs.asStdString();
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
case VTYPE_INT: return asInt() < rhs.asInt();
|
case VTYPE_INT: return asInt() < rhs.asInt();
|
||||||
case VTYPE_INT64: return asInt64() < rhs.asInt64();
|
case VTYPE_INT64: return asInt64() < rhs.asInt64();
|
||||||
case VTYPE_FLOAT: return asFloat() < rhs.asFloat();
|
case VTYPE_FLOAT: return asFloat() < rhs.asFloat();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Variant::operator > (const Variant& rhs) const
|
bool Variant::operator > (const Variant& rhs) const
|
||||||
{
|
{
|
||||||
return !(*this == rhs) && !(*this < rhs);
|
return !(*this == rhs) && !(*this < rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Variant::operator == (const Variant& rhs) const
|
bool Variant::operator == (const Variant& rhs) const
|
||||||
{
|
{
|
||||||
switch (type())
|
switch (type())
|
||||||
{
|
{
|
||||||
case VTYPE_STRING: return asStdString() == rhs.asStdString();
|
case VTYPE_STRING: return asStdString() == rhs.asStdString();
|
||||||
case VTYPE_BOOL: return asBool() == rhs.asBool();
|
case VTYPE_BOOL: return asBool() == rhs.asBool();
|
||||||
case VTYPE_INT: return asInt() == rhs.asInt();
|
case VTYPE_INT: return asInt() == rhs.asInt();
|
||||||
case VTYPE_INT64: return asInt64() == rhs.asInt64();
|
case VTYPE_INT64: return asInt64() == rhs.asInt64();
|
||||||
case VTYPE_FLOAT: return asFloat() == rhs.asFloat();
|
case VTYPE_FLOAT: return asFloat() == rhs.asFloat();
|
||||||
case VTYPE_POINTER: return asPointer() == rhs.asPointer();
|
case VTYPE_POINTER: return asPointer() == rhs.asPointer();
|
||||||
case VTYPE_VMAP: assert(0); break;
|
case VTYPE_VMAP: assert(0); break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Variant::operator != (const Variant& rhs) const
|
bool Variant::operator != (const Variant& rhs) const
|
||||||
{
|
{
|
||||||
return !(*this == rhs);
|
return !(*this == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Variant::operator <= (const Variant& rhs) const
|
bool Variant::operator <= (const Variant& rhs) const
|
||||||
{
|
{
|
||||||
return (*this < rhs) || (*this == rhs);
|
return (*this < rhs) || (*this == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Variant::operator >= (const Variant& rhs) const
|
bool Variant::operator >= (const Variant& rhs) const
|
||||||
{
|
{
|
||||||
return (*this > rhs) || (*this == rhs);
|
return (*this > rhs) || (*this == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Variant::asInt() const
|
int Variant::asInt() const
|
||||||
{
|
{
|
||||||
if (mType != VTYPE_INT)
|
if (mType != VTYPE_INT)
|
||||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||||
|
|
||||||
return mInt;
|
return mInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t Variant::asInt64() const
|
int64_t Variant::asInt64() const
|
||||||
{
|
{
|
||||||
if (mType != VTYPE_INT64)
|
if (mType != VTYPE_INT64)
|
||||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||||
|
|
||||||
return mInt;
|
return mInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Variant::asBool() const
|
bool Variant::asBool() const
|
||||||
{
|
{
|
||||||
switch (mType)
|
switch (mType)
|
||||||
{
|
{
|
||||||
case VTYPE_INT:
|
case VTYPE_INT:
|
||||||
return mInt != 0;
|
return mInt != 0;
|
||||||
|
|
||||||
case VTYPE_INT64:
|
case VTYPE_INT64:
|
||||||
return mInt64 != 0;
|
return mInt64 != 0;
|
||||||
|
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
return mBool;
|
return mBool;
|
||||||
|
|
||||||
case VTYPE_FLOAT:
|
case VTYPE_FLOAT:
|
||||||
return mFloat != 0;
|
return mFloat != 0;
|
||||||
|
|
||||||
case VTYPE_STRING:
|
case VTYPE_STRING:
|
||||||
return mString.length() != 0;
|
return mString.length() != 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float Variant::asFloat() const
|
float Variant::asFloat() const
|
||||||
{
|
{
|
||||||
switch (mType)
|
switch (mType)
|
||||||
{
|
{
|
||||||
case VTYPE_INT:
|
case VTYPE_INT:
|
||||||
return (float)mInt;
|
return (float)mInt;
|
||||||
|
|
||||||
case VTYPE_INT64:
|
case VTYPE_INT64:
|
||||||
return (float)mInt64;
|
return (float)mInt64;
|
||||||
|
|
||||||
case VTYPE_FLOAT:
|
case VTYPE_FLOAT:
|
||||||
return mFloat;
|
return mFloat;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Variant::asDouble() const
|
||||||
|
{
|
||||||
|
return static_cast<double>(asFloat());
|
||||||
|
}
|
||||||
|
|
||||||
std::string Variant::asStdString() const
|
std::string Variant::asStdString() const
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
switch (mType)
|
switch (mType)
|
||||||
{
|
{
|
||||||
case VTYPE_STRING:
|
case VTYPE_STRING:
|
||||||
return mString;
|
return mString;
|
||||||
|
|
||||||
case VTYPE_INT:
|
case VTYPE_INT:
|
||||||
sprintf(buffer, "%d", mInt);
|
sprintf(buffer, "%d", mInt);
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
case VTYPE_INT64:
|
case VTYPE_INT64:
|
||||||
sprintf(buffer, "%lli", static_cast<long long int>(mInt64));
|
sprintf(buffer, "%lli", static_cast<long long int>(mInt64));
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
case VTYPE_BOOL:
|
case VTYPE_BOOL:
|
||||||
return mBool ? "true" : "false";
|
return mBool ? "true" : "false";
|
||||||
|
|
||||||
case VTYPE_FLOAT:
|
case VTYPE_FLOAT:
|
||||||
sprintf(buffer, "%f", mFloat);
|
sprintf(buffer, "%f", mFloat);
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Variant::asPointer() const
|
void* Variant::asPointer() const
|
||||||
{
|
{
|
||||||
switch (mType)
|
switch (mType)
|
||||||
{
|
{
|
||||||
case VTYPE_POINTER:
|
case VTYPE_POINTER:
|
||||||
return mPointer;
|
return mPointer;
|
||||||
default:
|
default:
|
||||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PVariantMap Variant::asVMap()
|
PVariantMap Variant::asVMap()
|
||||||
{
|
{
|
||||||
switch (mType)
|
switch (mType)
|
||||||
{
|
{
|
||||||
case VTYPE_VMAP:
|
case VTYPE_VMAP:
|
||||||
return mVMap;
|
return mVMap;
|
||||||
default:
|
default:
|
||||||
throw Exception(ERR_BAD_VARIANT_TYPE);
|
throw Exception(ERR_BAD_VARIANT_TYPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantType Variant::type() const
|
VariantType Variant::type() const
|
||||||
{
|
{
|
||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantMap::VariantMap()
|
VariantMap::VariantMap()
|
||||||
|
|
@ -351,25 +355,25 @@ VariantMap::~VariantMap()
|
||||||
|
|
||||||
bool VariantMap::empty() const
|
bool VariantMap::empty() const
|
||||||
{
|
{
|
||||||
return mData.empty();
|
return mData.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VariantMap::clear()
|
void VariantMap::clear()
|
||||||
{
|
{
|
||||||
mData.clear();
|
mData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VariantMap::exists(int itemId) const
|
bool VariantMap::exists(int itemId) const
|
||||||
{
|
{
|
||||||
return mData.find(itemId) != mData.end();
|
return mData.find(itemId) != mData.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& VariantMap::operator [](int itemId)
|
Variant& VariantMap::operator [](int itemId)
|
||||||
{
|
{
|
||||||
return mData[itemId];
|
return mData[itemId];
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant& VariantMap::at(int itemId)
|
Variant& VariantMap::at(int itemId)
|
||||||
{
|
{
|
||||||
return mData[itemId];
|
return mData[itemId];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ public:
|
||||||
int asInt() const;
|
int asInt() const;
|
||||||
int64_t asInt64() const;
|
int64_t asInt64() const;
|
||||||
bool asBool() const;
|
bool asBool() const;
|
||||||
float asFloat() const ;
|
float asFloat() const;
|
||||||
|
double asDouble() const;
|
||||||
std::string asStdString() const;
|
std::string asStdString() const;
|
||||||
//const char* asString();
|
//const char* asString();
|
||||||
void* asPointer() const;
|
void* asPointer() const;
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,6 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY)
|
|
||||||
# include "MT_SevanaMos.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// #define DUMP_DECODED
|
// #define DUMP_DECODED
|
||||||
|
|
||||||
namespace MT
|
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/rtptimeutilities.h"
|
||||||
#include "jrtplib/src/rtppacket.h"
|
#include "jrtplib/src/rtppacket.h"
|
||||||
|
|
||||||
#include "MT_SevanaMos.h"
|
|
||||||
|
|
||||||
using std::experimental::optional;
|
using std::experimental::optional;
|
||||||
|
|
||||||
namespace MT
|
namespace MT
|
||||||
|
|
@ -79,8 +77,8 @@ class JitterStatistics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void process(jrtplib::RTPPacket* packet, int samplerate);
|
void process(jrtplib::RTPPacket* packet, int samplerate);
|
||||||
ProbeStats<double> get() const { return mJitter; }
|
ProbeStats<float> get() const { return mJitter; }
|
||||||
double getMaxDelta() const { return mMaxDelta; }
|
float getMaxDelta() const { return mMaxDelta; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Jitter calculation
|
// Jitter calculation
|
||||||
|
|
@ -90,13 +88,13 @@ protected:
|
||||||
uint32_t mReceiveTimestamp = 0;
|
uint32_t mReceiveTimestamp = 0;
|
||||||
|
|
||||||
// It is classic jitter value in units
|
// It is classic jitter value in units
|
||||||
optional<double> mLastJitter;
|
optional<float> mLastJitter;
|
||||||
|
|
||||||
// Some statistics for jitter value in seconds
|
// Some statistics for jitter value in seconds
|
||||||
ProbeStats<double> mJitter;
|
ProbeStats<float> mJitter;
|
||||||
|
|
||||||
// Maximal delta in seconds
|
// Maximal delta in seconds
|
||||||
double mMaxDelta = 0.0;
|
float mMaxDelta = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Statistics
|
class Statistics
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,6 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include "../helper/HL_Optional.hpp"
|
#include "../helper/HL_Optional.hpp"
|
||||||
|
|
||||||
#if defined(USE_PVQA_LIBRARY)
|
|
||||||
# include "MT_SevanaMos.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using std::experimental::optional;
|
using std::experimental::optional;
|
||||||
|
|
||||||
namespace MT
|
namespace MT
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue