diff --git a/daemon/codec.c b/daemon/codec.c index 67c461824..b65f13b53 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -499,11 +499,12 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) { ilog(LOG_DEBUG, "Received packet of %i bytes from packetizer", inout.len); // reconstruct RTP header + unsigned int ts = enc->avpkt.pts + ch->ts_out; ZERO(*rh); rh->v_p_x_cc = 0x80; rh->m_pt = ch->handler->dest_pt.payload_type; rh->seq_num = htons(ch->seq_out++); - rh->timestamp = htonl(enc->avpkt.pts + ch->ts_out); + rh->timestamp = htonl(ts); rh->ssrc = htonl(mp->ssrc_in->ssrc_map_out); // add to output queue @@ -515,6 +516,7 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) { atomic64_inc(&mp->ssrc_out->packets); atomic64_add(&mp->ssrc_out->octets, inout.len); + atomic64_set(&mp->ssrc_out->last_ts, ts); if (ret == 0) { // no more to go @@ -573,18 +575,19 @@ static int handler_func_transcode(struct codec_handler *h, struct call_media *me mutex_unlock(&ch->lock); __transcode_packet_free(packet); ilog(LOG_DEBUG, "Ignoring duplicate RTP packet"); + atomic64_inc(&mp->ssrc_in->duplicates); return 0; } // got a new packet, run decoder while (1) { - unsigned int lost; - packet = packet_sequencer_next_packet(&ch->sequencer, &lost); + packet = packet_sequencer_next_packet(&ch->sequencer); if (G_UNLIKELY(!packet)) break; - atomic64_add(&mp->ssrc_in->packets_lost, lost); + atomic64_set(&mp->ssrc_in->packets_lost, ch->sequencer.lost_count); + atomic64_set(&mp->ssrc_in->last_seq, ch->sequencer.ext_seq); ilog(LOG_DEBUG, "Decoding RTP packet: seq %u, TS %lu", packet->p.seq, packet->ts); diff --git a/daemon/rtcp.c b/daemon/rtcp.c index 00381b68b..625b75c0d 100644 --- a/daemon/rtcp.c +++ b/daemon/rtcp.c @@ -1222,7 +1222,10 @@ static void mos_xr_voip_metrics(struct rtcp_process_ctx *ctx, const struct xr_rb static void transcode_common(struct rtcp_process_ctx *ctx, struct rtcp_packet *common) { assert(ctx->scratch_common_ssrc == ctx->mp->ssrc_in->parent->h.ssrc); + // forward SSRC mapping common->ssrc = htonl(ctx->mp->ssrc_in->ssrc_map_out); + ilog(LOG_DEBUG, "Substituting RTCP header SSRC from %x to %x", + ctx->scratch_common_ssrc, ctx->mp->ssrc_in->ssrc_map_out); } static void transcode_rr(struct rtcp_process_ctx *ctx, struct report_block *rr) { assert(ctx->scratch.rr.from == ctx->mp->ssrc_in->parent->h.ssrc); @@ -1231,32 +1234,47 @@ static void transcode_rr(struct rtcp_process_ctx *ctx, struct report_block *rr) struct ssrc_ctx *map_ctx = get_ssrc_ctx(ctx->scratch.rr.ssrc, ctx->mp->call->ssrc_hash, SSRC_DIR_OUTPUT); rr->ssrc = htonl(map_ctx->ssrc_map_out); + // for reception stats + struct ssrc_ctx *input_ctx = get_ssrc_ctx(map_ctx->ssrc_map_out, ctx->mp->call->ssrc_hash, + SSRC_DIR_INPUT); + + // substitute our own values + + unsigned int packets = atomic64_get(&input_ctx->packets); + unsigned int lost = atomic64_get(&input_ctx->packets_lost); + unsigned int dupes = atomic64_get(&input_ctx->duplicates); + unsigned int tot_lost = lost - dupes; // can be negative/rollover + + ilog(LOG_DEBUG, "Substituting RTCP RR SSRC from %x to %x: %u packets, %u lost, %u duplicates", + ctx->scratch.rr.ssrc, map_ctx->ssrc_map_out, + packets, lost, dupes); + + if (G_UNLIKELY(tot_lost > 0xffffff)) + memset(rr->number_lost, 0xff, sizeof(rr->number_lost)); + else { + rr->number_lost[0] = (tot_lost & 0xff0000) >> 16; + rr->number_lost[1] = (tot_lost & 0x00ff00) >> 8; + rr->number_lost[2] = (tot_lost & 0x0000ff) >> 0; + } + + unsigned int exp_packets = packets + lost; + + if (dupes > lost || exp_packets == 0) // negative + rr->fraction_lost = 0; + else + rr->fraction_lost = tot_lost * 256 / (packets + lost); -// ilog(LOG_DEBUG, "transcode_rr: from ssrc %x about %x " -// "ssrc_in %x ssrc_in-map %x ssrc_out %x ssrc_out-map %x " -// "map_ctx %x map_ctx-map %x", -// ctx->scratch.rr.from, -// ctx->scratch.rr.ssrc, -// ctx->mp->ssrc_in->parent->h.ssrc, -// ctx->mp->ssrc_in->ssrc_map_out, -// ctx->mp->ssrc_out->parent->h.ssrc, -// ctx->mp->ssrc_out->ssrc_map_out, -// map_ctx->parent->h.ssrc, -// map_ctx->ssrc_map_out); - - // translate ctx->scratch.rr.from to ctx->mp->ssrc_in->ssrc_map_out - done by transcode_common + rr->high_seq_received = htonl(atomic64_get(&input_ctx->last_seq)); + // XXX jitter, last SR } static void transcode_sr(struct rtcp_process_ctx *ctx, struct sender_report_packet *sr) { assert(ctx->scratch.sr.ssrc == ctx->mp->ssrc_in->parent->h.ssrc); -// ilog(LOG_DEBUG, "transcode_sr: ssrc %x ssrc_in %x ssrc_in-map %x ssrc_out %x ssrc_out-map %x", -// ctx->scratch.sr.ssrc, -// ctx->mp->ssrc_in->parent->h.ssrc, -// ctx->mp->ssrc_in->ssrc_map_out, -// ctx->mp->ssrc_out->parent->h.ssrc, -// ctx->mp->ssrc_out->ssrc_map_out); -// - // translate ctx->scratch.sr.ssrc to ctx->mp->ssrc_in->ssrc_map_out - done by transcode_common + // substitute our own values + sr->octet_count = htonl(atomic64_get(&ctx->mp->ssrc_out->octets)); + sr->packet_count = htonl(atomic64_get(&ctx->mp->ssrc_out->packets)); + sr->timestamp = htonl(atomic64_get(&ctx->mp->ssrc_out->last_ts)); + // XXX NTP timestamp } diff --git a/daemon/ssrc.h b/daemon/ssrc.h index 2cd040e14..88532c401 100644 --- a/daemon/ssrc.h +++ b/daemon/ssrc.h @@ -44,9 +44,13 @@ struct ssrc_ctx { // for transcoding u_int32_t ssrc_map_out; + // RTCP stats atomic64 packets, octets, - packets_lost; + packets_lost, + duplicates, + last_seq, // XXX dup with srtp_index? + last_ts; }; struct ssrc_stats_block { diff --git a/lib/codeclib.c b/lib/codeclib.c index 5eeedd42c..dd9901193 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -631,7 +631,7 @@ static int packet_tree_search(const void *testseq_p, const void *ts_p) { return -1; } // caller must take care of locking -void *packet_sequencer_next_packet(packet_sequencer_t *ps, unsigned int *lost) { +void *packet_sequencer_next_packet(packet_sequencer_t *ps) { // see if we have a packet with the correct seq nr in the queue seq_packet_t *packet = g_tree_lookup(ps->packets, GINT_TO_POINTER(ps->seq)); if (G_LIKELY(packet != NULL)) { @@ -679,12 +679,17 @@ void *packet_sequencer_next_packet(packet_sequencer_t *ps, unsigned int *lost) { dbg("lost multiple packets - returning packet with next highest seq %i", packet->seq); out: - if (lost) { - u_int16_t l = packet->seq - ps->seq; - *lost = l; - } + ; + u_int16_t l = packet->seq - ps->seq; + ps->lost_count += l; + g_tree_steal(ps->packets, GINT_TO_POINTER(packet->seq)); ps->seq = (packet->seq + 1) & 0xffff; + + if (packet->seq < ps->ext_seq) + ps->roc++; + ps->ext_seq = ps->roc << 16 | packet->seq; + return packet; } diff --git a/lib/codeclib.h b/lib/codeclib.h index 5ffdada7f..10bd4b543 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -111,7 +111,10 @@ struct seq_packet_s { }; struct packet_sequencer_s { GTree *packets; - int seq; + unsigned int lost_count; + int seq; // next expected + unsigned int ext_seq; // last received + int roc; // rollover counter XXX duplicate with SRTP encryption context }; @@ -142,7 +145,7 @@ int encoder_input_fifo(encoder_t *enc, AVFrame *frame, void packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify); void packet_sequencer_destroy(packet_sequencer_t *ps); -void *packet_sequencer_next_packet(packet_sequencer_t *ps, unsigned int *); +void *packet_sequencer_next_packet(packet_sequencer_t *ps); int packet_sequencer_insert(packet_sequencer_t *ps, seq_packet_t *); diff --git a/recording-daemon/packet.c b/recording-daemon/packet.c index 28a727d0e..91b1e0d12 100644 --- a/recording-daemon/packet.c +++ b/recording-daemon/packet.c @@ -105,7 +105,7 @@ static void packet_decode(ssrc_t *ssrc, packet_t *packet) { static void ssrc_run(ssrc_t *ssrc) { while (1) { // see if we have a packet with the correct seq nr in the queue - packet_t *packet = packet_sequencer_next_packet(&ssrc->sequencer, NULL); + packet_t *packet = packet_sequencer_next_packet(&ssrc->sequencer); if (G_UNLIKELY(packet == NULL)) break;