How to enable payload QoE analysis in existing RTP monitoring system
In many cases requirements for software and traffic processing are so specific that it is quite difficult to record audio for post call analysis. This may be due to legal aspects of call recordings, hard drive capacity and CPU load. Installing a stand along probe for analysis may also be an issue due to corporate polices and existing environment. However, receiving non-intrusively calculated payload MOS and information on impairments that caused degradation can bring a great value for QoE analysis and problem root cause detection.
You might have already heard of Sevana Passive Voice Quality Analyzer (PVQA) and real-time call quality monitoring server that is using PVQA technology for payload waveform analysis and MOS prediction. Now we would like present a PVQA RTP Library that suits those who already have an RTP monitoring system, but would like to get more out of it.
Simple integration of PVQA RTP Library will enable your system to receive, analyze and process such metrics as several types of dead air, SNR, amplitude clipping, VAD clipping, clicking/crackling and echo, so besides network metrics as E-Model MOS/R-factor you will receive payload MOS and information on the reasons that caused call quality degradation.
PVQA RTP Library provides API to analyze RTP streams instead of plain audio streams and without making call recordings that may violate legislation. The library is developers friendly and does not limit them in selection of RTP streams to monitor, or implementation of traffic capturing and monitoring rules. PVQA RTP Library is provided as standard Linux “.a” and “.so” binaries making the integration as simple as possible.
The library API is typical with usual initialize / create stream context / get statistics / close stream context functions. API language is C, which is easy to translate into any other programming language. Here is a cut&paste from the library header file (contact us for further details):
// Result codes for PvqaRtp library
enum PvqaStatus
{
Pvqa_Ok,
Pvqa_Failed,
Pvqa_BadParameter
};
// RTP stream context type
typedef void* PvqaRtp_StreamContext;
// Logging callback – to ease integration with 3rd party log systems
typedef void (*PvqaRtp_LogCallback) (int level, const char* filename, int line, const char* subsystem, const char* msg);
// Settings to configure PVQA RTP library.
struct PvqaRtp_Settings
{
// Dynamic payload types
int mAmrNbPtype; // AMR narrowband
int mAmrWbPtype; // AMR wideband
int mIlbc30Ptype; // Ilbc 30ms
int mIlbc20Ptype; // Ilbc 20 ms
int mIsac32KPtype; // ISAC 32KHz
int mIsac16KPtype; // ISAC 16KHz
// Non zero to avoid decoding. Only network MOS + jitter + packet counters stats.
int mSrtpMode;
// Optional callback to integrate logging. Can be NULL.
PvqaRtp_LogCallback mLogCallback;
// Can be 0 (Error) / 1 (Info) / 2 (Debug) / 3 (Media)
int mLogLevel;
// PVQA license file path. Must be set except the cases when SRTP mode is active.
const char* mPvqaLicensePath;
// PVQA settings file path. Must be set except the cases when SRTP mode is active.
const char* mPvqaSettingsPath;
};
// Get build number of library
extern const char* PvqaRtp_GetVersion(void);
// Initialized library. Options is zero terminated string with command line options (used for standalone server).
extern PvqaStatus PvqaRtp_InitLibrary(PvqaRtp_Settings* settings);
// Releases library.
extern PvqaStatus PvqaRtp_FreeLibrary();
// Creates new stream decoder & analyzer context
extern PvqaStatus PvqaRtp_CreateStreamContext(PvqaRtp_StreamContext* ctx);
// Destroy stream context
extern PvqaStatus PvqaRtp_FreeStreamContext(PvqaRtp_StreamContext ctx);
// Updates stream with RTP packet
extern PvqaStatus PvqaRtp_UpdateStream(PvqaRtp_StreamContext ctx, const void* packetData, size_t packetLength);
// Obtained statistics
struct PvqaRtp_Statistics
{
// Used payload types
char* mPayloadTypes;
// Most used codec name
char* mCodecName;
// Network MOS / Sevana MOS / Jitter stats
double mNetworkMos,
mSevanaMos;
// Jitter and max delta in seconds
double mJitter;
double mMaxDelta;
// Optional interval report
char* mIntervalReport;
// Number of RTP packets in the stream
int mRtpPacketCounter;
// Number of lost packets in the stream
int mLostPacketCounter;
// Number of bad RTP packets (failed to decode) in the stream
int mRtpIllegalPacketCounter;
// UNIX timestamps to record stream start & finish
time_t mStartTime, mEndTime;
// AMR codec mode switches counters.
// Counters are increased every time when codec changes bitrate.
int mAmrNbSwitchCounter;
int mAmrWbSwitchCounter;
};
// Gets current statistics for stream. Resulting statistics must be finalized with freeRtpStreamStats.
extern PvqaStatus PvqaRtp_GetStreamStats(PvqaRtp_StreamContext ctx, PvqaRtp_Statistics* stats);
// Frees memory related to Statistics structure.
// Please do not forget to call it after PvqaRtp_GetStreamStats.
extern PvqaStatus PvqaRtp_FreeStreamStats(PvqaRtp_Statistics* stats);
Example of API usage is here (it is cut from real demo application) :
int parseSinglePcap(const std::string& path)
{
// Initialize library – no options, no log callbacks
PvqaRtp_Settings settings;
memset(&settings, 0, sizeof settings);
settings.mPvqaLicensePath = “pvqa.lic”;
settings.mPvqaSettingsPath = “pvqa.cfg”;
if (Pvqa_Ok != PvqaRtp_InitLibrary(&settings))
{
printf(“Failed to initialize PVQA libraries.”);
return EXIT_FAILURE;
}
PvqaRtp_StreamContext ctx = NULL;
if (Pvqa_Ok != PvqaRtp_CreateStreamContext(&ctx))
{
printf(“Failed to create PVQA RTP stream context.”);
PvqaRtp_FreeLibrary();
return EXIT_FAILURE;
}
// Read file and send to library
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* handle = pcap_open_offline(path.c_str(), errbuf);
// Throw exception if smth wrong
if (!handle)
{
printf(“Failed to open .pcap file. Error: %s\n”, errbuf);
return EXIT_FAILURE;
}
pcap_pkthdr* packetHeader = NULL;
const u_char* packetData = NULL;
int readCode = 0;
int linkType = pcap_datalink(handle);
NetworkFrame::PacketData p;
while ((readCode = pcap_next_ex(handle, &packetHeader, &packetData)) == 1)
{
// Increate processed packet counter
if (parsePacket(linkType, packetHeader, packetData, p))
PvqaRtp_UpdateStream(ctx, p.mData, p.mLength);
}
pcap_close(handle);
// Get statistics
PvqaRtp_Statistics stats;
if (Pvqa_Ok != PvqaRtp_GetStreamStats(ctx, &stats))
{
printf(“Failed to get stats for RTP stream. See log for details.”);
PvqaRtp_FreeStreamContext(ctx);
PvqaRtp_FreeLibrary();
return EXIT_FAILURE;
}
printf(“Sevana MOS: %f\nNetwork MOS: %f\nDetector report:\n%s\n”, stats.mSevanaMos, stats.mNetworkMos, stats.mIntervalReport);
PvqaRtp_FreeStreamStats(&stats);
PvqaRtp_FreeStreamContext(ctx);
PvqaRtp_FreeLibrary();
return EXIT_SUCCESS;
}