|
|
@ -130,6 +130,117 @@ struct rtcp_chain_element { |
|
|
} u; |
|
|
} u; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// log handlers |
|
|
|
|
|
// struct defs |
|
|
|
|
|
// context to hold state variables |
|
|
|
|
|
struct rtcp_process_ctx { |
|
|
|
|
|
u_int32_t scratch; |
|
|
|
|
|
GString *log; |
|
|
|
|
|
GString *json; |
|
|
|
|
|
}; |
|
|
|
|
|
// all available methods |
|
|
|
|
|
struct rtcp_handler { |
|
|
|
|
|
void (*init)(struct rtcp_process_ctx *); |
|
|
|
|
|
void (*start)(struct rtcp_process_ctx *, struct call *); |
|
|
|
|
|
void (*common)(struct rtcp_process_ctx *, const pjmedia_rtcp_common *); |
|
|
|
|
|
void (*sr)(struct rtcp_process_ctx *, const pjmedia_rtcp_sr *); |
|
|
|
|
|
void (*rr_list_start)(struct rtcp_process_ctx *, const pjmedia_rtcp_common *); |
|
|
|
|
|
void (*rr)(struct rtcp_process_ctx *, const pjmedia_rtcp_rr *); |
|
|
|
|
|
void (*rr_list_end)(struct rtcp_process_ctx *); |
|
|
|
|
|
void (*xr)(struct rtcp_process_ctx *, const pjmedia_rtcp_common *, str *); |
|
|
|
|
|
void (*sdes_list_start)(struct rtcp_process_ctx *, const pjmedia_rtcp_common *); |
|
|
|
|
|
void (*sdes_item)(struct rtcp_process_ctx *, const rtcp_sdes_chunk_t *, const rtcp_sdes_item_t *, |
|
|
|
|
|
const char *); |
|
|
|
|
|
void (*sdes_list_end)(struct rtcp_process_ctx *); |
|
|
|
|
|
void (*finish)(struct rtcp_process_ctx *, struct call *, const endpoint_t *, const endpoint_t *, |
|
|
|
|
|
const struct timeval *); |
|
|
|
|
|
void (*destroy)(struct rtcp_process_ctx *); |
|
|
|
|
|
}; |
|
|
|
|
|
// collection of all handler types |
|
|
|
|
|
struct rtcp_handlers { |
|
|
|
|
|
const struct rtcp_handler |
|
|
|
|
|
*scratch, |
|
|
|
|
|
*logging, |
|
|
|
|
|
*homer; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// log handler function prototypes |
|
|
|
|
|
static void dummy_handler(); |
|
|
|
|
|
|
|
|
|
|
|
// scratch area (prepare/parse packet) |
|
|
|
|
|
static void scratch_rr(struct rtcp_process_ctx *, const pjmedia_rtcp_rr *); |
|
|
|
|
|
|
|
|
|
|
|
// homer functions |
|
|
|
|
|
static void homer_init(struct rtcp_process_ctx *); |
|
|
|
|
|
static void homer_sr(struct rtcp_process_ctx *, const pjmedia_rtcp_sr *); |
|
|
|
|
|
static void homer_rr_list_start(struct rtcp_process_ctx *, const pjmedia_rtcp_common *); |
|
|
|
|
|
static void homer_rr(struct rtcp_process_ctx *, const pjmedia_rtcp_rr *); |
|
|
|
|
|
static void homer_rr_list_end(struct rtcp_process_ctx *); |
|
|
|
|
|
static void homer_sdes_list_start(struct rtcp_process_ctx *, const pjmedia_rtcp_common *); |
|
|
|
|
|
static void homer_sdes_item(struct rtcp_process_ctx *, const rtcp_sdes_chunk_t *, const rtcp_sdes_item_t *, |
|
|
|
|
|
const char *); |
|
|
|
|
|
static void homer_sdes_list_end(struct rtcp_process_ctx *); |
|
|
|
|
|
static void homer_finish(struct rtcp_process_ctx *, struct call *, const endpoint_t *, const endpoint_t *, |
|
|
|
|
|
const struct timeval *); |
|
|
|
|
|
|
|
|
|
|
|
// syslog functions |
|
|
|
|
|
static void logging_init(struct rtcp_process_ctx *); |
|
|
|
|
|
static void logging_start(struct rtcp_process_ctx *, struct call *); |
|
|
|
|
|
static void logging_common(struct rtcp_process_ctx *, const pjmedia_rtcp_common *); |
|
|
|
|
|
static void logging_sr(struct rtcp_process_ctx *, const pjmedia_rtcp_sr *); |
|
|
|
|
|
static void logging_rr(struct rtcp_process_ctx *, const pjmedia_rtcp_rr *); |
|
|
|
|
|
static void logging_xr(struct rtcp_process_ctx *, const pjmedia_rtcp_common *, str *); |
|
|
|
|
|
static void logging_finish(struct rtcp_process_ctx *, struct call *, const endpoint_t *, const endpoint_t *, |
|
|
|
|
|
const struct timeval *); |
|
|
|
|
|
static void logging_destroy(struct rtcp_process_ctx *); |
|
|
|
|
|
|
|
|
|
|
|
// structs for each handler type |
|
|
|
|
|
static struct rtcp_handler dummy_handlers; |
|
|
|
|
|
static struct rtcp_handler scratch_handlers = { |
|
|
|
|
|
.rr = scratch_rr, |
|
|
|
|
|
}; |
|
|
|
|
|
static struct rtcp_handler log_handlers = { |
|
|
|
|
|
.init = logging_init, |
|
|
|
|
|
.start = logging_start, |
|
|
|
|
|
.common = logging_common, |
|
|
|
|
|
.sr = logging_sr, |
|
|
|
|
|
.rr = logging_rr, |
|
|
|
|
|
.xr = logging_xr, |
|
|
|
|
|
.finish = logging_finish, |
|
|
|
|
|
.destroy = logging_destroy, |
|
|
|
|
|
}; |
|
|
|
|
|
static struct rtcp_handler homer_handlers = { |
|
|
|
|
|
.init = homer_init, |
|
|
|
|
|
.sr = homer_sr, |
|
|
|
|
|
.rr_list_start = homer_rr_list_start, |
|
|
|
|
|
.rr = homer_rr, |
|
|
|
|
|
.rr_list_end = homer_rr_list_end, |
|
|
|
|
|
.sdes_list_start = homer_sdes_list_start, |
|
|
|
|
|
.sdes_item = homer_sdes_item, |
|
|
|
|
|
.sdes_list_end = homer_sdes_list_end, |
|
|
|
|
|
.finish = homer_finish, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// main var to hold references |
|
|
|
|
|
static struct rtcp_handlers rtcp_handlers = { |
|
|
|
|
|
.scratch = &scratch_handlers, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// list of all handlers |
|
|
|
|
|
static struct rtcp_handler *all_handlers[] = { |
|
|
|
|
|
&dummy_handlers, |
|
|
|
|
|
&scratch_handlers, |
|
|
|
|
|
&log_handlers, |
|
|
|
|
|
&homer_handlers, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// macro to call all function handlers in one go |
|
|
|
|
|
#define CAH(func, ...) do { \ |
|
|
|
|
|
rtcp_handlers.scratch->func(&log_ctx, ##__VA_ARGS__); \ |
|
|
|
|
|
rtcp_handlers.logging->func(&log_ctx, ##__VA_ARGS__); \ |
|
|
|
|
|
rtcp_handlers.homer->func(&log_ctx, ##__VA_ARGS__); \ |
|
|
|
|
|
} while (0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -255,6 +366,7 @@ static void rtcp_list_free(GQueue *q) { |
|
|
g_queue_clear_full(q, rtcp_ce_free); |
|
|
g_queue_clear_full(q, rtcp_ce_free); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// XXX do this only once for each RTCP packet and use the resulting list for everything |
|
|
static int rtcp_parse(GQueue *q, str *_s) { |
|
|
static int rtcp_parse(GQueue *q, str *_s) { |
|
|
struct rtcp_packet *hdr; |
|
|
struct rtcp_packet *hdr; |
|
|
struct rtcp_chain_element *el; |
|
|
struct rtcp_chain_element *el; |
|
|
@ -467,161 +579,28 @@ error: |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void print_rtcp_common(GString *log, const pjmedia_rtcp_common *common) { |
|
|
|
|
|
if (log) |
|
|
|
|
|
g_string_append_printf(log,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ", |
|
|
|
|
|
common->version, |
|
|
|
|
|
common->p, |
|
|
|
|
|
common->count, |
|
|
|
|
|
common->pt, |
|
|
|
|
|
ntohl(common->length), |
|
|
|
|
|
ntohl(common->ssrc)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void print_rtcp_sr(GString *log, const pjmedia_rtcp_sr* sr, GString *json) { |
|
|
|
|
|
if (log) |
|
|
|
|
|
g_string_append_printf(log,"ntp_sec=%u, ntp_fractions=%u, rtp_ts=%u, sender_packets=%u, sender_bytes=%u, ", |
|
|
|
|
|
ntohl(sr->ntp_sec), |
|
|
|
|
|
ntohl(sr->ntp_frac), |
|
|
|
|
|
ntohl(sr->rtp_ts), |
|
|
|
|
|
ntohl(sr->sender_pcount), |
|
|
|
|
|
ntohl(sr->sender_bcount)); |
|
|
|
|
|
|
|
|
|
|
|
if (json) |
|
|
|
|
|
g_string_append_printf(json, "\"sender_information\":{\"ntp_timestamp_sec\":%u," |
|
|
|
|
|
"\"ntp_timestamp_usec\":%u,\"octets\":%u,\"rtp_timestamp\":%u, \"packets\":%u},", |
|
|
|
|
|
ntohl(sr->ntp_sec), |
|
|
|
|
|
ntohl(sr->ntp_frac), |
|
|
|
|
|
ntohl(sr->sender_bcount), |
|
|
|
|
|
ntohl(sr->rtp_ts), |
|
|
|
|
|
ntohl(sr->sender_pcount)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void print_rtcp_rr_list_start(pjmedia_rtcp_common *common, GString *json) { |
|
|
|
|
|
if (json) |
|
|
|
|
|
g_string_append_printf(json, "\"ssrc\":%u,\"type\":%u,\"report_count\":%u,\"report_blocks\":[", |
|
|
|
|
|
ntohl(common->ssrc), |
|
|
|
|
|
common->pt, |
|
|
|
|
|
common->count); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void print_rtcp_sdes_list_start(pjmedia_rtcp_common *common, GString *json) { |
|
|
|
|
|
if (json) |
|
|
|
|
|
g_string_append_printf(json, "\"sdes_ssrc\":%u,\"sdes_report_count\":%u,\"sdes_information\": [ ", |
|
|
|
|
|
ntohl(common->ssrc), |
|
|
|
|
|
common->count); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void print_rtcp_rr(GString *log, const pjmedia_rtcp_rr* rr, GString *json) { |
|
|
|
|
|
/* Get packet loss */ |
|
|
|
|
|
u_int32_t packet_loss=0; |
|
|
|
|
|
packet_loss = (rr->total_lost_2 << 16) + |
|
|
|
|
|
(rr->total_lost_1 << 8) + |
|
|
|
|
|
rr->total_lost_0; |
|
|
|
|
|
|
|
|
|
|
|
if (log) |
|
|
|
|
|
g_string_append_printf(log,"ssrc=%u, fraction_lost=%u, packet_loss=%u, last_seq=%u, jitter=%u, last_sr=%u, delay_since_last_sr=%u, ", |
|
|
|
|
|
ntohl(rr->ssrc), |
|
|
|
|
|
rr->fract_lost, |
|
|
|
|
|
packet_loss, |
|
|
|
|
|
ntohl(rr->last_seq), |
|
|
|
|
|
ntohl(rr->jitter), |
|
|
|
|
|
ntohl(rr->lsr), |
|
|
|
|
|
ntohl(rr->dlsr)); |
|
|
|
|
|
|
|
|
|
|
|
if (json) |
|
|
|
|
|
g_string_append_printf(json, "{\"source_ssrc\":%u," |
|
|
|
|
|
"\"highest_seq_no\":%u,\"fraction_lost\":%u,\"ia_jitter\":%u," |
|
|
|
|
|
"\"packets_lost\":%u,\"lsr\":%u,\"dlsr\":%u},", |
|
|
|
|
|
ntohl(rr->ssrc), |
|
|
|
|
|
ntohl(rr->last_seq), |
|
|
|
|
|
rr->fract_lost, |
|
|
|
|
|
ntohl(rr->jitter), |
|
|
|
|
|
packet_loss, |
|
|
|
|
|
ntohl(rr->lsr), |
|
|
|
|
|
ntohl(rr->dlsr)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void print_rtcp_sdes_item(GString *json, const rtcp_sdes_chunk_t *chunk, const rtcp_sdes_item_t *item, |
|
|
|
|
|
const char *data) |
|
|
|
|
|
{ |
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
if (json) { |
|
|
|
|
|
g_string_append_printf(json, "{\"sdes_chunk_ssrc\":%u,\"type\":%u,\"text\":\"", |
|
|
|
|
|
htonl(chunk->csrc), |
|
|
|
|
|
item->type); |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < item->len; i++) { |
|
|
|
|
|
switch (data[i]) { |
|
|
|
|
|
case '"': |
|
|
|
|
|
g_string_append(json, "\\\""); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\\': |
|
|
|
|
|
g_string_append(json, "\\\\"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\b': |
|
|
|
|
|
g_string_append(json, "\\b"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\f': |
|
|
|
|
|
g_string_append(json, "\\f"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\n': |
|
|
|
|
|
g_string_append(json, "\\n"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\r': |
|
|
|
|
|
g_string_append(json, "\\r"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\t': |
|
|
|
|
|
g_string_append(json, "\\t"); |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
g_string_append_c(json, data[i]); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
g_string_append(json, "\"},"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void str_sanitize(GString *s) { |
|
|
static void str_sanitize(GString *s) { |
|
|
while (s->len > 0 && (s->str[s->len - 1] == ' ' || s->str[s->len - 1] == ',')) |
|
|
while (s->len > 0 && (s->str[s->len - 1] == ' ' || s->str[s->len - 1] == ',')) |
|
|
g_string_truncate(s, s->len - 1); |
|
|
g_string_truncate(s, s->len - 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static void print_rtcp_list_end(GString *json) { |
|
|
|
|
|
if (json) { |
|
|
|
|
|
str_sanitize(json); |
|
|
|
|
|
g_string_append_printf(json, "],"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *ori_s, const endpoint_t *src, |
|
|
void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *ori_s, const endpoint_t *src, |
|
|
const struct timeval *tv) |
|
|
const struct timeval *tv) |
|
|
{ |
|
|
{ |
|
|
|
|
|
|
|
|
GString *log; |
|
|
|
|
|
str iter_s, comp_s; |
|
|
str iter_s, comp_s; |
|
|
pjmedia_rtcp_common *common; |
|
|
pjmedia_rtcp_common *common; |
|
|
const pjmedia_rtcp_rr *rr; |
|
|
const pjmedia_rtcp_rr *rr; |
|
|
const pjmedia_rtcp_sr *sr; |
|
|
const pjmedia_rtcp_sr *sr; |
|
|
const rtcp_sdes_chunk_t *sdes_chunk; |
|
|
const rtcp_sdes_chunk_t *sdes_chunk; |
|
|
const rtcp_sdes_item_t *sdes_item; |
|
|
const rtcp_sdes_item_t *sdes_item; |
|
|
GString *json; |
|
|
|
|
|
struct call *c = sfd->call; |
|
|
struct call *c = sfd->call; |
|
|
struct callmaster *cm = c->callmaster; |
|
|
|
|
|
int i; |
|
|
int i; |
|
|
|
|
|
struct rtcp_process_ctx log_ctx; |
|
|
|
|
|
|
|
|
log = _log_facility_rtcp ? g_string_new(NULL) : NULL; |
|
|
|
|
|
json = cm->homer ? g_string_new("{ ") : NULL; |
|
|
|
|
|
|
|
|
|
|
|
// anything to do? |
|
|
|
|
|
if (!log && !json) |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
if (log) |
|
|
|
|
|
g_string_append_printf(log, "["STR_FORMAT"] ", STR_FMT(&sfd->stream->call->callid)); |
|
|
|
|
|
|
|
|
ZERO(log_ctx); |
|
|
|
|
|
CAH(init); |
|
|
|
|
|
CAH(start, c); |
|
|
|
|
|
|
|
|
iter_s = *ori_s; |
|
|
iter_s = *ori_s; |
|
|
|
|
|
|
|
|
@ -639,7 +618,7 @@ void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *ori_s, const en |
|
|
if (str_shift(&iter_s, (ntohs(common->length) + 1) << 2)) // puts iter_s on the next compound packet |
|
|
if (str_shift(&iter_s, (ntohs(common->length) + 1) << 2)) // puts iter_s on the next compound packet |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
print_rtcp_common(log, common); |
|
|
|
|
|
|
|
|
CAH(common, common); |
|
|
|
|
|
|
|
|
/* Parse RTCP */ |
|
|
/* Parse RTCP */ |
|
|
switch (common->pt) { |
|
|
switch (common->pt) { |
|
|
@ -648,28 +627,28 @@ void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *ori_s, const en |
|
|
if (str_shift(&comp_s, sizeof(*sr))) |
|
|
if (str_shift(&comp_s, sizeof(*sr))) |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
print_rtcp_sr(log, sr, json); |
|
|
|
|
|
|
|
|
CAH(sr, sr); |
|
|
// fall through to RTCP_PT_RR |
|
|
// fall through to RTCP_PT_RR |
|
|
|
|
|
|
|
|
case RTCP_PT_RR: |
|
|
case RTCP_PT_RR: |
|
|
print_rtcp_rr_list_start(common, json); |
|
|
|
|
|
|
|
|
CAH(rr_list_start, common); |
|
|
|
|
|
|
|
|
for (i = 0; i < common->count; i++) { |
|
|
for (i = 0; i < common->count; i++) { |
|
|
rr = (pjmedia_rtcp_rr*)((comp_s.s)); |
|
|
rr = (pjmedia_rtcp_rr*)((comp_s.s)); |
|
|
if (str_shift(&comp_s, sizeof(*rr))) |
|
|
if (str_shift(&comp_s, sizeof(*rr))) |
|
|
break; |
|
|
break; |
|
|
print_rtcp_rr(log, rr, json); |
|
|
|
|
|
|
|
|
CAH(rr, rr); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
print_rtcp_list_end(json); |
|
|
|
|
|
|
|
|
CAH(rr_list_end); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case RTCP_PT_XR: |
|
|
case RTCP_PT_XR: |
|
|
pjmedia_rtcp_xr_rx_rtcp_xr(log, common, &comp_s); |
|
|
|
|
|
|
|
|
CAH(xr, common, &comp_s); |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case RTCP_PT_SDES: |
|
|
case RTCP_PT_SDES: |
|
|
print_rtcp_sdes_list_start(common, json); |
|
|
|
|
|
|
|
|
CAH(sdes_list_start, common); |
|
|
|
|
|
|
|
|
// the "common" header actually includes the SDES |
|
|
// the "common" header actually includes the SDES |
|
|
// SSRC/CSRC chunk header, so we set our chunk header |
|
|
// SSRC/CSRC chunk header, so we set our chunk header |
|
|
@ -690,7 +669,7 @@ void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *ori_s, const en |
|
|
break; |
|
|
break; |
|
|
if (comp_s.len < sdes_item->len) |
|
|
if (comp_s.len < sdes_item->len) |
|
|
break; |
|
|
break; |
|
|
print_rtcp_sdes_item(json, sdes_chunk, sdes_item, comp_s.s); |
|
|
|
|
|
|
|
|
CAH(sdes_item, sdes_chunk, sdes_item, comp_s.s); |
|
|
str_shift(&comp_s, sdes_item->len); |
|
|
str_shift(&comp_s, sdes_item->len); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -708,24 +687,177 @@ void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *ori_s, const en |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
print_rtcp_list_end(json); |
|
|
|
|
|
|
|
|
CAH(sdes_list_end); |
|
|
|
|
|
|
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (log) { |
|
|
|
|
|
str_sanitize(log); |
|
|
|
|
|
rtcplog(log->str); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
CAH(finish, c, src, &sfd->socket.local, tv); |
|
|
|
|
|
CAH(destroy); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void dummy_handler() { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void scratch_rr(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_rr *rr) { |
|
|
|
|
|
ctx->scratch = (rr->total_lost_2 << 16) + (rr->total_lost_1 << 8) + rr->total_lost_0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (json) { |
|
|
|
|
|
str_sanitize(json); |
|
|
|
|
|
g_string_append(json, " }"); |
|
|
|
|
|
homer_send(cm->homer, json, &c->callid, src, &sfd->socket.local, tv); |
|
|
|
|
|
json = NULL; |
|
|
|
|
|
|
|
|
static void homer_init(struct rtcp_process_ctx *ctx) { |
|
|
|
|
|
ctx->json = g_string_new("{ "); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_sr(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_sr* sr) { |
|
|
|
|
|
g_string_append_printf(ctx->json, "\"sender_information\":{\"ntp_timestamp_sec\":%u," |
|
|
|
|
|
"\"ntp_timestamp_usec\":%u,\"octets\":%u,\"rtp_timestamp\":%u, \"packets\":%u},", |
|
|
|
|
|
ntohl(sr->ntp_sec), |
|
|
|
|
|
ntohl(sr->ntp_frac), |
|
|
|
|
|
ntohl(sr->sender_bcount), |
|
|
|
|
|
ntohl(sr->rtp_ts), |
|
|
|
|
|
ntohl(sr->sender_pcount)); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_rr_list_start(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_common *common) { |
|
|
|
|
|
g_string_append_printf(ctx->json, "\"ssrc\":%u,\"type\":%u,\"report_count\":%u,\"report_blocks\":[", |
|
|
|
|
|
ntohl(common->ssrc), |
|
|
|
|
|
common->pt, |
|
|
|
|
|
common->count); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_rr(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_rr *rr) { |
|
|
|
|
|
g_string_append_printf(ctx->json, "{\"source_ssrc\":%u," |
|
|
|
|
|
"\"highest_seq_no\":%u,\"fraction_lost\":%u,\"ia_jitter\":%u," |
|
|
|
|
|
"\"packets_lost\":%u,\"lsr\":%u,\"dlsr\":%u},", |
|
|
|
|
|
ntohl(rr->ssrc), |
|
|
|
|
|
ntohl(rr->last_seq), |
|
|
|
|
|
rr->fract_lost, |
|
|
|
|
|
ntohl(rr->jitter), |
|
|
|
|
|
ctx->scratch, |
|
|
|
|
|
ntohl(rr->lsr), |
|
|
|
|
|
ntohl(rr->dlsr)); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_rr_list_end(struct rtcp_process_ctx *ctx) { |
|
|
|
|
|
str_sanitize(ctx->json); |
|
|
|
|
|
g_string_append_printf(ctx->json, "],"); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_sdes_list_start(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_common *common) { |
|
|
|
|
|
g_string_append_printf(ctx->json, "\"sdes_ssrc\":%u,\"sdes_report_count\":%u,\"sdes_information\": [ ", |
|
|
|
|
|
ntohl(common->ssrc), |
|
|
|
|
|
common->count); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_sdes_item(struct rtcp_process_ctx *ctx, const rtcp_sdes_chunk_t *chunk, |
|
|
|
|
|
const rtcp_sdes_item_t *item, const char *data) |
|
|
|
|
|
{ |
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
g_string_append_printf(ctx->json, "{\"sdes_chunk_ssrc\":%u,\"type\":%u,\"text\":\"", |
|
|
|
|
|
htonl(chunk->csrc), |
|
|
|
|
|
item->type); |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < item->len; i++) { |
|
|
|
|
|
switch (data[i]) { |
|
|
|
|
|
case '"': |
|
|
|
|
|
g_string_append(ctx->json, "\\\""); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\\': |
|
|
|
|
|
g_string_append(ctx->json, "\\\\"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\b': |
|
|
|
|
|
g_string_append(ctx->json, "\\b"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\f': |
|
|
|
|
|
g_string_append(ctx->json, "\\f"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\n': |
|
|
|
|
|
g_string_append(ctx->json, "\\n"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\r': |
|
|
|
|
|
g_string_append(ctx->json, "\\r"); |
|
|
|
|
|
break; |
|
|
|
|
|
case '\t': |
|
|
|
|
|
g_string_append(ctx->json, "\\t"); |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
g_string_append_c(ctx->json, data[i]); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (log) |
|
|
|
|
|
g_string_free(log, TRUE); |
|
|
|
|
|
|
|
|
g_string_append(ctx->json, "\"},"); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_sdes_list_end(struct rtcp_process_ctx *ctx) { |
|
|
|
|
|
str_sanitize(ctx->json); |
|
|
|
|
|
g_string_append_printf(ctx->json, "],"); |
|
|
|
|
|
} |
|
|
|
|
|
static void homer_finish(struct rtcp_process_ctx *ctx, struct call *c, const endpoint_t *src, |
|
|
|
|
|
const endpoint_t *dst, const struct timeval *tv) |
|
|
|
|
|
{ |
|
|
|
|
|
str_sanitize(ctx->json); |
|
|
|
|
|
g_string_append(ctx->json, " }"); |
|
|
|
|
|
homer_send(ctx->json, &c->callid, src, dst, tv); |
|
|
|
|
|
ctx->json = NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void logging_init(struct rtcp_process_ctx *ctx) { |
|
|
|
|
|
ctx->log = g_string_new(NULL); |
|
|
|
|
|
} |
|
|
|
|
|
static void logging_start(struct rtcp_process_ctx *ctx, struct call *c) { |
|
|
|
|
|
g_string_append_printf(ctx->log, "["STR_FORMAT"] ", STR_FMT(&c->callid)); |
|
|
|
|
|
} |
|
|
|
|
|
static void logging_common(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_common *common) { |
|
|
|
|
|
g_string_append_printf(ctx->log,"version=%u, padding=%u, count=%u, payloadtype=%u, length=%u, ssrc=%u, ", |
|
|
|
|
|
common->version, |
|
|
|
|
|
common->p, |
|
|
|
|
|
common->count, |
|
|
|
|
|
common->pt, |
|
|
|
|
|
ntohl(common->length), |
|
|
|
|
|
ntohl(common->ssrc)); |
|
|
|
|
|
} |
|
|
|
|
|
static void logging_sr(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_sr* sr) { |
|
|
|
|
|
g_string_append_printf(ctx->log,"ntp_sec=%u, ntp_fractions=%u, rtp_ts=%u, sender_packets=%u, sender_bytes=%u, ", |
|
|
|
|
|
ntohl(sr->ntp_sec), |
|
|
|
|
|
ntohl(sr->ntp_frac), |
|
|
|
|
|
ntohl(sr->rtp_ts), |
|
|
|
|
|
ntohl(sr->sender_pcount), |
|
|
|
|
|
ntohl(sr->sender_bcount)); |
|
|
|
|
|
} |
|
|
|
|
|
static void logging_rr(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_rr *rr) { |
|
|
|
|
|
g_string_append_printf(ctx->log,"ssrc=%u, fraction_lost=%u, packet_loss=%u, last_seq=%u, jitter=%u, last_sr=%u, delay_since_last_sr=%u, ", |
|
|
|
|
|
ntohl(rr->ssrc), |
|
|
|
|
|
rr->fract_lost, |
|
|
|
|
|
ctx->scratch, |
|
|
|
|
|
ntohl(rr->last_seq), |
|
|
|
|
|
ntohl(rr->jitter), |
|
|
|
|
|
ntohl(rr->lsr), |
|
|
|
|
|
ntohl(rr->dlsr)); |
|
|
|
|
|
} |
|
|
|
|
|
static void logging_xr(struct rtcp_process_ctx *ctx, const pjmedia_rtcp_common *common, str *comp_s) { |
|
|
|
|
|
pjmedia_rtcp_xr_rx_rtcp_xr(ctx->log, common, comp_s); |
|
|
|
|
|
} |
|
|
|
|
|
static void logging_finish(struct rtcp_process_ctx *ctx, struct call *c, const endpoint_t *src, |
|
|
|
|
|
const endpoint_t *dst, const struct timeval *tv) |
|
|
|
|
|
{ |
|
|
|
|
|
str_sanitize(ctx->log); |
|
|
|
|
|
rtcplog(ctx->log->str); |
|
|
|
|
|
} |
|
|
|
|
|
static void logging_destroy(struct rtcp_process_ctx *ctx) { |
|
|
|
|
|
g_string_free(ctx->log, TRUE); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void rtcp_init() { |
|
|
|
|
|
rtcp_handlers.logging = _log_facility_rtcp ? &log_handlers : &dummy_handlers; |
|
|
|
|
|
rtcp_handlers.homer = has_homer() ? &homer_handlers : &dummy_handlers; |
|
|
|
|
|
|
|
|
|
|
|
// walk through list of handlers and fill missing entries to dummy handler |
|
|
|
|
|
void *dummy = dummy_handler; |
|
|
|
|
|
for (int i = 0; i < G_N_ELEMENTS(all_handlers); i++) { |
|
|
|
|
|
struct rtcp_handler *lh = all_handlers[i]; |
|
|
|
|
|
for (int j = 0; j < (sizeof(*lh) / sizeof(void *)); j++) { |
|
|
|
|
|
void **ptr = (void *) lh; |
|
|
|
|
|
ptr += j; |
|
|
|
|
|
if (*ptr == NULL) |
|
|
|
|
|
*ptr = dummy; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |