|
|
|
@ -328,12 +328,12 @@ str *call_query_udp(char **out, struct callmaster *m) { |
|
|
|
goto err; |
|
|
|
} |
|
|
|
|
|
|
|
stats_query(c, &fromtag, &totag, &stats, NULL, NULL); |
|
|
|
ng_call_stats(c, &fromtag, &totag, NULL, &stats); |
|
|
|
|
|
|
|
rwlock_unlock_w(&c->master_lock); |
|
|
|
|
|
|
|
ret = str_sprintf("%s %lld "UINT64F" "UINT64F" "UINT64F" "UINT64F"\n", out[RE_UDP_COOKIE], |
|
|
|
(long long int) m->conf.silent_timeout - (poller_now - stats.newest), |
|
|
|
(long long int) m->conf.silent_timeout - (poller_now - stats.last_packet), |
|
|
|
stats.totals[0].packets, stats.totals[1].packets, |
|
|
|
stats.totals[2].packets, stats.totals[3].packets); |
|
|
|
goto out; |
|
|
|
@ -408,6 +408,20 @@ void calls_status_tcp(struct callmaster *m, struct control_stream *s) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void call_release_ref(void *p) { |
|
|
|
struct call *c = p; |
|
|
|
obj_put(c); |
|
|
|
} |
|
|
|
INLINE void call_bencode_hold_ref(struct call *c, bencode_item_t *bi) { |
|
|
|
/* We cannot guarantee that the "call" structures are still around at the time |
|
|
|
* when the bencode reply is finally read and sent out. Since we use scatter/gather |
|
|
|
* to avoid duplication of strings and stuff, we reserve a reference to the call |
|
|
|
* structs and have it released when the bencode buffer is destroyed. This is |
|
|
|
* necessary every time the bencode response may reference strings contained |
|
|
|
* within the call structs. */ |
|
|
|
bencode_buffer_destroy_add(bi->buffer, call_release_ref, obj_get(c)); |
|
|
|
} |
|
|
|
|
|
|
|
static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) { |
|
|
|
bencode_item_t *list, *it; |
|
|
|
int diridx; |
|
|
|
@ -528,6 +542,10 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster |
|
|
|
if (!call) |
|
|
|
goto out; |
|
|
|
|
|
|
|
/* At least the random ICE strings are contained within the call struct, so we |
|
|
|
* need to hold a ref until we're done sending the reply */ |
|
|
|
call_bencode_hold_ref(call, output); |
|
|
|
|
|
|
|
monologue = call_get_mono_dialogue(call, &fromtag, &totag); |
|
|
|
|
|
|
|
chopper = sdp_chopper_new(&sdp); |
|
|
|
@ -594,114 +612,189 @@ const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_ |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
#if 0 |
|
|
|
static bencode_item_t *peer_address(bencode_buffer_t *b, struct stream *s) { |
|
|
|
bencode_item_t *d; |
|
|
|
static void ng_stats(bencode_item_t *d, const struct stats *s, struct stats *totals) { |
|
|
|
bencode_dictionary_add_integer(d, "packets", s->packets); |
|
|
|
bencode_dictionary_add_integer(d, "bytes", s->bytes); |
|
|
|
bencode_dictionary_add_integer(d, "errors", s->errors); |
|
|
|
if (!totals) |
|
|
|
return; |
|
|
|
totals->packets += s->packets; |
|
|
|
totals->bytes += s->bytes; |
|
|
|
totals->errors += s->errors; |
|
|
|
} |
|
|
|
|
|
|
|
static void ng_stats_endpoint(bencode_item_t *dict, const struct endpoint *ep) { |
|
|
|
char buf[64]; |
|
|
|
|
|
|
|
d = bencode_dictionary(b); |
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&s->ip46)) { |
|
|
|
bencode_dictionary_add_string(d, "family", "IPv4"); |
|
|
|
inet_ntop(AF_INET, &(s->ip46.s6_addr32[3]), buf, sizeof(buf)); |
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&ep->ip46)) { |
|
|
|
bencode_dictionary_add_string(dict, "family", "IPv4"); |
|
|
|
inet_ntop(AF_INET, &(ep->ip46.s6_addr32[3]), buf, sizeof(buf)); |
|
|
|
} |
|
|
|
else { |
|
|
|
bencode_dictionary_add_string(d, "family", "IPv6"); |
|
|
|
inet_ntop(AF_INET6, &s->ip46, buf, sizeof(buf)); |
|
|
|
bencode_dictionary_add_string(dict, "family", "IPv6"); |
|
|
|
inet_ntop(AF_INET6, &ep->ip46, buf, sizeof(buf)); |
|
|
|
} |
|
|
|
bencode_dictionary_add_string_dup(d, "address", buf); |
|
|
|
bencode_dictionary_add_integer(d, "port", s->port); |
|
|
|
|
|
|
|
return d; |
|
|
|
bencode_dictionary_add_string_dup(dict, "address", buf); |
|
|
|
bencode_dictionary_add_integer(dict, "port", ep->port); |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
#if 0 |
|
|
|
static bencode_item_t *stats_encode(bencode_buffer_t *b, struct stats *s) { |
|
|
|
bencode_item_t *d; |
|
|
|
#define BF_PS(k, f) if (PS_ISSET(ps, f)) bencode_list_add_string(flags, k) |
|
|
|
|
|
|
|
d = bencode_dictionary(b); |
|
|
|
bencode_dictionary_add_integer(d, "packets", s->packets); |
|
|
|
bencode_dictionary_add_integer(d, "bytes", s->bytes); |
|
|
|
bencode_dictionary_add_integer(d, "errors", s->errors); |
|
|
|
return d; |
|
|
|
} |
|
|
|
#endif |
|
|
|
static void ng_stats_stream(bencode_item_t *list, const struct packet_stream *ps, |
|
|
|
struct call_stats *totals) |
|
|
|
{ |
|
|
|
bencode_item_t *dict = NULL, *flags; |
|
|
|
struct stats *s; |
|
|
|
|
|
|
|
#if 0 |
|
|
|
static bencode_item_t *streamrelay_stats(bencode_buffer_t *b, struct packet_stream *ps) { |
|
|
|
bencode_item_t *d; |
|
|
|
if (!list) |
|
|
|
goto stats; |
|
|
|
|
|
|
|
d = bencode_dictionary(b); |
|
|
|
dict = bencode_list_add_dictionary(list); |
|
|
|
|
|
|
|
// XXX |
|
|
|
//bencode_dictionary_add(d, "counters", stats_encode(b, &r->stats)); |
|
|
|
//bencode_dictionary_add(d, "peer address", peer_address(b, &r->peer)); |
|
|
|
//bencode_dictionary_add(d, "advertised peer address", peer_address(b, &r->peer_advertised)); |
|
|
|
if (ps->sfd) |
|
|
|
bencode_dictionary_add_integer(dict, "local port", ps->sfd->fd.localport); |
|
|
|
ng_stats_endpoint(bencode_dictionary_add_dictionary(dict, "endpoint"), &ps->endpoint); |
|
|
|
ng_stats_endpoint(bencode_dictionary_add_dictionary(dict, "advertised endpoint"), |
|
|
|
&ps->advertised_endpoint); |
|
|
|
if (ps->crypto.params.crypto_suite) |
|
|
|
bencode_dictionary_add_string(dict, "crypto suite", |
|
|
|
ps->crypto.params.crypto_suite->name); |
|
|
|
bencode_dictionary_add_integer(dict, "last packet", ps->last_packet); |
|
|
|
|
|
|
|
bencode_dictionary_add_integer(d, "local port", ps->fd.localport); |
|
|
|
flags = bencode_dictionary_add_list(dict, "flags"); |
|
|
|
|
|
|
|
return d; |
|
|
|
} |
|
|
|
#endif |
|
|
|
BF_PS("RTP", RTP); |
|
|
|
BF_PS("RTCP", RTCP); |
|
|
|
BF_PS("fallback RTCP", FALLBACK_RTCP); |
|
|
|
BF_PS("filled", FILLED); |
|
|
|
BF_PS("confirmed", CONFIRMED); |
|
|
|
BF_PS("kernelized", KERNELIZED); |
|
|
|
BF_PS("no kernel support", NO_KERNEL_SUPPORT); |
|
|
|
|
|
|
|
stats: |
|
|
|
if (totals->last_packet < ps->last_packet) |
|
|
|
totals->last_packet = ps->last_packet; |
|
|
|
|
|
|
|
#if 0 |
|
|
|
static bencode_item_t *rtp_rtcp_stats(bencode_buffer_t *b, struct stats *rtp, struct stats *rtcp) { |
|
|
|
bencode_item_t *s; |
|
|
|
s = bencode_dictionary(b); |
|
|
|
bencode_dictionary_add(s, "rtp", stats_encode(b, rtp)); |
|
|
|
bencode_dictionary_add(s, "rtcp", stats_encode(b, rtcp)); |
|
|
|
return s; |
|
|
|
/* XXX distinguish between input and output */ |
|
|
|
s = &totals->totals[0]; |
|
|
|
if (!PS_ISSET(ps, RTP)) |
|
|
|
s = &totals->totals[1]; |
|
|
|
ng_stats(bencode_dictionary_add_dictionary(dict, "stats"), &ps->stats, s); |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
#if 0 |
|
|
|
XXX |
|
|
|
static bencode_item_t *peer_stats(bencode_buffer_t *b, struct peer *p) { |
|
|
|
bencode_item_t *d, *s; |
|
|
|
#define BF_M(k, f) if (MEDIA_ISSET(m, f)) bencode_list_add_string(flags, k) |
|
|
|
|
|
|
|
d = bencode_dictionary(b); |
|
|
|
static void ng_stats_media(bencode_item_t *list, const struct call_media *m, |
|
|
|
struct call_stats *totals) |
|
|
|
{ |
|
|
|
bencode_item_t *dict, *streams = NULL, *flags; |
|
|
|
GList *l; |
|
|
|
struct packet_stream *ps; |
|
|
|
|
|
|
|
bencode_dictionary_add_str_dup(d, "tag", &p->tag); |
|
|
|
if (p->codec) |
|
|
|
bencode_dictionary_add_string(d, "codec", p->codec); |
|
|
|
if (p->kernelized) |
|
|
|
bencode_dictionary_add_string(d, "status", "in kernel"); |
|
|
|
else if (p->confirmed) |
|
|
|
bencode_dictionary_add_string(d, "status", "confirmed peer address"); |
|
|
|
else if (p->filled) |
|
|
|
bencode_dictionary_add_string(d, "status", "known but unconfirmed peer address"); |
|
|
|
else |
|
|
|
bencode_dictionary_add_string(d, "status", "unknown peer address"); |
|
|
|
if (!list) |
|
|
|
goto stats; |
|
|
|
|
|
|
|
dict = bencode_list_add_dictionary(list); |
|
|
|
|
|
|
|
s = bencode_dictionary_add_dictionary(d, "stats"); |
|
|
|
bencode_dictionary_add(s, "rtp", streamrelay_stats(b, &p->rtps[0])); |
|
|
|
bencode_dictionary_add(s, "rtcp", streamrelay_stats(b, &p->rtps[1])); |
|
|
|
bencode_dictionary_add_integer(dict, "index", m->index); |
|
|
|
bencode_dictionary_add_str(dict, "type", &m->type); |
|
|
|
if (m->protocol) |
|
|
|
bencode_dictionary_add_string(dict, "protocol", m->protocol->name); |
|
|
|
|
|
|
|
return d; |
|
|
|
streams = bencode_dictionary_add_list(dict, "streams"); |
|
|
|
|
|
|
|
flags = bencode_dictionary_add_list(dict, "flags"); |
|
|
|
|
|
|
|
BF_M("initialized", INITIALIZED); |
|
|
|
BF_M("rtcp-mux", RTCP_MUX); |
|
|
|
BF_M("DTLS-SRTP", DTLS); |
|
|
|
BF_M("SDES", SDES); |
|
|
|
BF_M("passthrough", PASSTHRU); |
|
|
|
BF_M("ICE", ICE); |
|
|
|
|
|
|
|
stats: |
|
|
|
for (l = m->streams.head; l; l = l->next) { |
|
|
|
ps = l->data; |
|
|
|
ng_stats_stream(streams, ps, totals); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void ng_stats_cb(struct peer *p, struct peer *px, void *streams) { |
|
|
|
bencode_item_t *stream; |
|
|
|
static void ng_stats_monologue(bencode_item_t *dict, const struct call_monologue *ml, |
|
|
|
struct call_stats *totals) |
|
|
|
{ |
|
|
|
bencode_item_t *sub, *medias = NULL; |
|
|
|
GList *l; |
|
|
|
struct call_media *m; |
|
|
|
|
|
|
|
if (!ml) |
|
|
|
return; |
|
|
|
|
|
|
|
stream = bencode_list_add_list(streams); |
|
|
|
bencode_list_add(stream, peer_stats(stream->buffer, p)); |
|
|
|
bencode_list_add(stream, peer_stats(stream->buffer, px)); |
|
|
|
if (!dict) |
|
|
|
goto stats; |
|
|
|
|
|
|
|
sub = bencode_dictionary_add_dictionary(dict, ml->tag.s); |
|
|
|
|
|
|
|
bencode_dictionary_add_str(sub, "tag", &ml->tag); |
|
|
|
bencode_dictionary_add_integer(sub, "created", ml->created); |
|
|
|
if (ml->active_dialogue) |
|
|
|
bencode_dictionary_add_str(sub, "in dialogue with", &ml->active_dialogue->tag); |
|
|
|
|
|
|
|
medias = bencode_dictionary_add_list(sub, "medias"); |
|
|
|
|
|
|
|
stats: |
|
|
|
for (l = ml->medias.head; l; l = l->next) { |
|
|
|
m = l->data; |
|
|
|
ng_stats_media(medias, m, totals); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
/* call must be locked */ |
|
|
|
void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output) { |
|
|
|
//bencode_item_t *streams, *dict; |
|
|
|
// struct call_stats stats; |
|
|
|
void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output, |
|
|
|
struct call_stats *totals) |
|
|
|
{ |
|
|
|
bencode_item_t *tags = NULL, *dict; |
|
|
|
const str *match_tag; |
|
|
|
GSList *l; |
|
|
|
struct call_monologue *ml; |
|
|
|
struct call_stats t_b; |
|
|
|
|
|
|
|
// bencode_dictionary_add_integer(output, "created", call->created); |
|
|
|
if (!totals) |
|
|
|
totals = &t_b; |
|
|
|
ZERO(*totals); |
|
|
|
|
|
|
|
//streams = bencode_dictionary_add_list(output, "streams"); |
|
|
|
//stats_query(call, fromtag, totag, &stats, ng_stats_cb, streams); XXX |
|
|
|
if (!output) |
|
|
|
goto stats; |
|
|
|
|
|
|
|
// dict = bencode_dictionary_add_dictionary(output, "totals"); |
|
|
|
// bencode_dictionary_add(dict, "input", rtp_rtcp_stats(output->buffer, &stats.totals[0], &stats.totals[1])); |
|
|
|
// bencode_dictionary_add(dict, "output", rtp_rtcp_stats(output->buffer, &stats.totals[2], &stats.totals[3])); |
|
|
|
call_bencode_hold_ref(call, output); |
|
|
|
|
|
|
|
bencode_dictionary_add_integer(output, "created", call->created); |
|
|
|
bencode_dictionary_add_integer(output, "last_signal", call->last_signal); |
|
|
|
|
|
|
|
tags = bencode_dictionary_add_dictionary(output, "tags"); |
|
|
|
|
|
|
|
stats: |
|
|
|
match_tag = (totag && totag->s && totag->len) ? totag : fromtag; |
|
|
|
|
|
|
|
if (!match_tag) { |
|
|
|
for (l = call->monologues; l; l = l->next) { |
|
|
|
ml = l->data; |
|
|
|
ng_stats_monologue(tags, ml, totals); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
ml = g_hash_table_lookup(call->tags, match_tag); |
|
|
|
if (ml) { |
|
|
|
ng_stats_monologue(tags, ml, totals); |
|
|
|
ng_stats_monologue(tags, ml->active_dialogue, totals); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!output) |
|
|
|
return; |
|
|
|
|
|
|
|
dict = bencode_dictionary_add_dictionary(output, "totals"); |
|
|
|
ng_stats(bencode_dictionary_add_dictionary(dict, "RTP"), &totals->totals[0], NULL); |
|
|
|
ng_stats(bencode_dictionary_add_dictionary(dict, "RTCP"), &totals->totals[1], NULL); |
|
|
|
} |
|
|
|
|
|
|
|
const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { |
|
|
|
@ -717,8 +810,9 @@ const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_i |
|
|
|
bencode_dictionary_get_str(input, "to-tag", &totag); |
|
|
|
|
|
|
|
bencode_dictionary_add_string(output, "result", "ok"); |
|
|
|
ng_call_stats(call, &fromtag, &totag, output); |
|
|
|
ng_call_stats(call, &fromtag, &totag, output, NULL); |
|
|
|
rwlock_unlock_w(&call->master_lock); |
|
|
|
obj_put(call); |
|
|
|
|
|
|
|
return NULL; |
|
|
|
} |