mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-02 11:06:31 +00:00
res_rtp_asterisk: Asterisk Media Experience Score (MES)
----------------- This commit reinstates MES with some casting fixes to the functions in time.h that convert between doubles and timeval structures. The casting issues were causing incorrect timestamps to be calculated which caused transcoding from/to G722 to produce bad or no audio. ASTERISK-30391 ----------------- This module has been updated to provide additional quality statistics in the form of an Asterisk Media Experience Score. The score is avilable using the same mechanisms you'd use to retrieve jitter, loss, and rtt statistics. For more information about the score and how to retrieve it, see https://wiki.asterisk.org/wiki/display/AST/Media+Experience+Score * Updated chan_pjsip to set quality channel variables when a call ends. * Updated channels/pjsip/dialplan_functions.c to add the ability to retrieve the MES along with the existing rtcp stats when using the CHANNEL dialplan function. * Added the ast_debug_rtp_is_allowed and ast_debug_rtcp_is_allowed checks for debugging purposes. * Added several function to time.h for manipulating time-in-samples and times represented as double seconds. * Updated rtp_engine.c to pass through the MES when stats are requested. Also debug output that dumps the stats when an rtp instance is destroyed. * Updated res_rtp_asterisk.c to implement the calculation of the MES. In the process, also had to update the calculation of jitter. Many debugging statements were also changed to be more informative. * Added a unit test for internal testing. The test should not be run during normal operation and is disabled by default. Change-Id: I4fce265965e68c3fdfeca55e614371ee69c65038
This commit is contained in:
@@ -36,11 +36,14 @@
|
||||
#include "asterisk/rtp_engine.h"
|
||||
#include "asterisk/data_buffer.h"
|
||||
#include "asterisk/format_cache.h"
|
||||
#include <assert.h>
|
||||
#include <sched.h>
|
||||
|
||||
enum test_type {
|
||||
TEST_TYPE_NONE = 0, /* No special setup required */
|
||||
TEST_TYPE_NACK, /* Enable NACK */
|
||||
TEST_TYPE_REMB, /* Enable REMB */
|
||||
TEST_TYPE_STD_RTCP, /* Let the stack do RTCP */
|
||||
};
|
||||
|
||||
static void ast_sched_context_destroy_wrapper(struct ast_sched_context *sched)
|
||||
@@ -54,18 +57,30 @@ static int test_init_rtp_instances(struct ast_rtp_instance **instance1,
|
||||
struct ast_rtp_instance **instance2, struct ast_sched_context *test_sched,
|
||||
enum test_type type)
|
||||
{
|
||||
struct ast_sockaddr addr;
|
||||
struct ast_sockaddr addr1;
|
||||
struct ast_sockaddr addr2;
|
||||
enum ast_rtp_instance_rtcp rtcp_type = AST_RTP_INSTANCE_RTCP_MUX;
|
||||
|
||||
ast_sockaddr_parse(&addr, "127.0.0.1", 0);
|
||||
ast_sockaddr_parse(&addr1, "127.0.0.1", 0);
|
||||
ast_sockaddr_parse(&addr2, "127.0.0.1", 0);
|
||||
|
||||
*instance1 = ast_rtp_instance_new("asterisk", test_sched, &addr, NULL);
|
||||
*instance2 = ast_rtp_instance_new("asterisk", test_sched, &addr, NULL);
|
||||
*instance1 = ast_rtp_instance_new("asterisk", test_sched, &addr1, "instance1");
|
||||
*instance2 = ast_rtp_instance_new("asterisk", test_sched, &addr2, "instance2");
|
||||
if (!instance1 || !instance2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_rtp_instance_set_prop(*instance1, AST_RTP_PROPERTY_RTCP, AST_RTP_INSTANCE_RTCP_MUX);
|
||||
ast_rtp_instance_set_prop(*instance2, AST_RTP_PROPERTY_RTCP, AST_RTP_INSTANCE_RTCP_MUX);
|
||||
ast_rtp_instance_set_channel_id(*instance1, "instance1");
|
||||
ast_rtp_instance_set_channel_id(*instance2, "instance2");
|
||||
|
||||
if (type == TEST_TYPE_STD_RTCP) {
|
||||
rtcp_type = AST_RTP_INSTANCE_RTCP_STANDARD;
|
||||
}
|
||||
|
||||
ast_rtp_instance_set_prop(*instance1,
|
||||
AST_RTP_PROPERTY_RTCP, rtcp_type);
|
||||
ast_rtp_instance_set_prop(*instance2,
|
||||
AST_RTP_PROPERTY_RTCP, rtcp_type);
|
||||
|
||||
if (type == TEST_TYPE_NACK) {
|
||||
ast_rtp_instance_set_prop(*instance1, AST_RTP_PROPERTY_RETRANS_RECV, 1);
|
||||
@@ -77,11 +92,11 @@ static int test_init_rtp_instances(struct ast_rtp_instance **instance1,
|
||||
ast_rtp_instance_set_prop(*instance2, AST_RTP_PROPERTY_REMB, 1);
|
||||
}
|
||||
|
||||
ast_rtp_instance_get_local_address(*instance1, &addr);
|
||||
ast_rtp_instance_set_remote_address(*instance2, &addr);
|
||||
ast_rtp_instance_get_local_address(*instance1, &addr1);
|
||||
ast_rtp_instance_set_remote_address(*instance2, &addr1);
|
||||
|
||||
ast_rtp_instance_get_local_address(*instance2, &addr);
|
||||
ast_rtp_instance_set_remote_address(*instance1, &addr);
|
||||
ast_rtp_instance_get_local_address(*instance2, &addr2);
|
||||
ast_rtp_instance_set_remote_address(*instance1, &addr2);
|
||||
|
||||
ast_rtp_instance_reset_test_engine(*instance1);
|
||||
|
||||
@@ -130,6 +145,120 @@ static void test_write_and_read_frames(struct ast_rtp_instance *instance1,
|
||||
test_read_frames(instance2, num);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Unfortunately, we can't use usleep() to create
|
||||
* packet spacing because there are signals in use
|
||||
* which cause usleep to immediately return. Instead
|
||||
* we have to spin. :(
|
||||
*/
|
||||
static void SLEEP_SPINNER(int ms)
|
||||
{
|
||||
struct timeval a = ast_tvnow();
|
||||
|
||||
while(1) {
|
||||
sched_yield();
|
||||
if (ast_remaining_ms(a, ms) <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is NOT really a reliable implementation.
|
||||
* Its purpose is only to aid in code development in res_rtp_asterisk.
|
||||
*/
|
||||
static void test_write_and_read_interleaved_frames(struct ast_rtp_instance *instance1,
|
||||
struct ast_rtp_instance *instance2, int howlong, int rtcp_interval)
|
||||
{
|
||||
char data[320] = "";
|
||||
int pktinterval = 20;
|
||||
|
||||
struct ast_frame frame_out1 = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.subclass.format = ast_format_ulaw,
|
||||
.seqno = 4556,
|
||||
.data.ptr = data,
|
||||
.datalen = 160,
|
||||
.samples = 1,
|
||||
.len = pktinterval,
|
||||
.ts = 4622295,
|
||||
};
|
||||
struct ast_frame frame_out2 = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.subclass.format = ast_format_ulaw,
|
||||
.seqno = 6554,
|
||||
.data.ptr = data,
|
||||
.datalen = 160,
|
||||
.samples = 1,
|
||||
.len = pktinterval,
|
||||
.ts = 8622295,
|
||||
};
|
||||
struct ast_frame *frame_in1;
|
||||
struct ast_frame *frame_in2;
|
||||
int index;
|
||||
int num;
|
||||
int rtcpnum;
|
||||
int reverse = 1;
|
||||
int send_rtcp = 0;
|
||||
|
||||
num = howlong / pktinterval;
|
||||
|
||||
rtcpnum = rtcp_interval / pktinterval;
|
||||
|
||||
ast_set_flag(&frame_out1, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
|
||||
ast_set_flag(&frame_out1, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
ast_set_flag(&frame_out2, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
|
||||
ast_set_flag(&frame_out2, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
|
||||
for (index = 0; index < num; index++) {
|
||||
struct timeval start = ast_tvnow();
|
||||
time_t ms;
|
||||
|
||||
if (index == 1) {
|
||||
ast_clear_flag(&frame_out1, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
|
||||
ast_clear_flag(&frame_out1, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
ast_clear_flag(&frame_out2, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
|
||||
ast_clear_flag(&frame_out2, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
}
|
||||
frame_out1.seqno += index;
|
||||
frame_out1.delivery = start;
|
||||
frame_out1.ts += frame_out1.len;
|
||||
ast_rtp_instance_write(instance1, &frame_out1);
|
||||
|
||||
if (send_rtcp && index && (index % rtcpnum == 0)) {
|
||||
ast_rtp_instance_queue_report(instance1);
|
||||
}
|
||||
|
||||
frame_in2 = ast_rtp_instance_read(instance2, 0);
|
||||
ast_frfree(frame_in2);
|
||||
frame_in2 = ast_rtp_instance_read(instance2, 1);
|
||||
ast_frfree(frame_in2);
|
||||
|
||||
if (reverse) {
|
||||
frame_out2.seqno += index;
|
||||
frame_out2.delivery = ast_tvnow();
|
||||
frame_out2.ts += frame_out2.len;
|
||||
ast_rtp_instance_write(instance2, &frame_out2);
|
||||
|
||||
if (send_rtcp && index && (index % rtcpnum == 0)) {
|
||||
ast_rtp_instance_queue_report(instance2);
|
||||
}
|
||||
|
||||
frame_in1 = ast_rtp_instance_read(instance1, 0);
|
||||
ast_frfree(frame_in1);
|
||||
frame_in1 = ast_rtp_instance_read(instance1, 1);
|
||||
ast_frfree(frame_in1);
|
||||
|
||||
}
|
||||
|
||||
ms = frame_out1.len - ast_tvdiff_ms(ast_tvnow(),start);
|
||||
ms += (index % 2 ? 5 : 12);
|
||||
ms += (index % 3 ? 2 : 30);
|
||||
SLEEP_SPINNER(ms);
|
||||
}
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(nack_no_packet_loss)
|
||||
{
|
||||
RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
|
||||
@@ -523,8 +652,47 @@ AST_TEST_DEFINE(fir_nominal)
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
/*
|
||||
* This test should not normally be run. Its only purpose is to
|
||||
* aid in code development.
|
||||
*/
|
||||
AST_TEST_DEFINE(mes)
|
||||
{
|
||||
RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
|
||||
RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
|
||||
RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "mes";
|
||||
info->category = "/res/res_rtp/";
|
||||
info->summary = "Media Experience Score";
|
||||
info->description =
|
||||
"Tests calculation of Media Experience Score (only run by explicit request)";
|
||||
info->explicit_only = 1;
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
test_sched = ast_sched_context_create();
|
||||
ast_sched_start_thread(test_sched);
|
||||
|
||||
if ((test_init_rtp_instances(&instance1, &instance2,
|
||||
test_sched, TEST_TYPE_NONE)) < 0) {
|
||||
ast_log(LOG_ERROR, "Failed to initialize test!\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
test_write_and_read_interleaved_frames(
|
||||
instance1, instance2, 1000, 5000);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(mes);
|
||||
AST_TEST_UNREGISTER(nack_no_packet_loss);
|
||||
AST_TEST_UNREGISTER(nack_nominal);
|
||||
AST_TEST_UNREGISTER(nack_overflow);
|
||||
@@ -544,6 +712,7 @@ static int load_module(void)
|
||||
AST_TEST_REGISTER(remb_nominal);
|
||||
AST_TEST_REGISTER(sr_rr_nominal);
|
||||
AST_TEST_REGISTER(fir_nominal);
|
||||
AST_TEST_REGISTER(mes);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user