diff --git a/daemon/call.c b/daemon/call.c
index 49eb8c529..7bf77f850 100644
--- a/daemon/call.c
+++ b/daemon/call.c
@@ -546,22 +546,22 @@ static void callmaster_timer(void *ptr) {
 		/* XXX this only works if the kernel module actually gets to see the packets. */
 		if (sink) {
 			mutex_lock(&sink->out_lock);
-			if (sink->crypto.params.crypto_suite
-					&& ke->target.ssrc == sink->crypto.ssrc
-					&& ke->target.encrypt.last_index - sink->crypto.last_index > 0x4000)
+			if (sink->crypto.params.crypto_suite && sink->ssrc_out
+					&& ke->target.ssrc == sink->ssrc_out->parent->ssrc
+					&& ke->target.encrypt.last_index - sink->ssrc_out->srtp_index > 0x4000)
 			{
-				sink->crypto.last_index = ke->target.encrypt.last_index;
+				sink->ssrc_out->srtp_index = ke->target.encrypt.last_index;
 				update = 1;
 			}
 			mutex_unlock(&sink->out_lock);
 		}
 
 		mutex_lock(&ps->in_lock);
-		if (sfd->crypto.params.crypto_suite
-				&& ke->target.ssrc == sfd->crypto.ssrc
-				&& ke->target.decrypt.last_index - sfd->crypto.last_index > 0x4000)
+		if (sfd->crypto.params.crypto_suite && ps->ssrc_in
+				&& ke->target.ssrc == ps->ssrc_in->parent->ssrc
+				&& ke->target.decrypt.last_index - ps->ssrc_in->srtp_index > 0x4000)
 		{
-			sfd->crypto.last_index = ke->target.decrypt.last_index;
+			ps->ssrc_in->srtp_index = ke->target.decrypt.last_index;
 			update = 1;
 		}
 		mutex_unlock(&ps->in_lock);
@@ -1884,11 +1884,13 @@ void call_destroy(struct call *c) {
 					char *addr = sockaddr_print_buf(&ps->endpoint.address);
                                         char *local_addr = ps->selected_sfd ? sockaddr_print_buf(&ps->selected_sfd->socket.local.address) : "0.0.0.0";
 
-					ilog(LOG_INFO, "--------- Port %15s:%-5u <> %15s:%-5u%s, "
-							""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet", local_addr,
+					ilog(LOG_INFO, "--------- Port %15s:%-5u <> %15s:%-5u%s, SSRC %" PRIu32 ", "
+							""UINT64F" p, "UINT64F" b, "UINT64F" e, "UINT64F" last_packet",
+							local_addr,
 							(unsigned int) (ps->selected_sfd ? ps->selected_sfd->socket.local.port : 0),
 							addr, ps->endpoint.port,
 							(!PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP)) ? " (RTCP)" : "",
+							ps->ssrc_in ? ps->ssrc_in->parent->ssrc : 0,
 							atomic64_get(&ps->stats.packets),
 							atomic64_get(&ps->stats.bytes),
 							atomic64_get(&ps->stats.errors),
diff --git a/daemon/call.h b/daemon/call.h
index 01a02a11e..d1e7bf186 100644
--- a/daemon/call.h
+++ b/daemon/call.h
@@ -282,6 +282,8 @@ struct packet_stream {
 	struct endpoint		endpoint;	/* LOCK: out_lock */
 	struct endpoint		advertised_endpoint; /* RO */
 	struct crypto_context	crypto;		/* OUT direction, LOCK: out_lock */
+	struct ssrc_ctx		*ssrc_in,	/* LOCK: in_lock */
+				*ssrc_out;	/* LOCK: out_lock */
 
 	struct stats		stats;
 	struct stats		kernel_stats;
diff --git a/daemon/crypto.h b/daemon/crypto.h
index 16e6fc448..d0663fb83 100644
--- a/daemon/crypto.h
+++ b/daemon/crypto.h
@@ -82,8 +82,6 @@ struct crypto_context {
 	char session_salt[SRTP_MAX_SESSION_SALT_LEN]; /* k_s */
 	char session_auth_key[SRTP_MAX_SESSION_AUTH_LEN];
 
-	u_int32_t ssrc;
-	u_int64_t last_index;
 	/* XXX replay list */
 	/* <from, to>? */
 
@@ -145,9 +143,8 @@ INLINE void crypto_cleanup(struct crypto_context *c) {
 	c->params.crypto_suite = NULL;
 }
 INLINE void crypto_reset(struct crypto_context *c) {
+	// XXX reset details from ssrc_ctx?
 	crypto_cleanup(c);
-	c->last_index = 0;
-	c->ssrc = 0;
 }
 INLINE void crypto_params_copy(struct crypto_params *o, const struct crypto_params *i, int copy_sp) {
 	struct crypto_session_params sp;
diff --git a/daemon/media_socket.c b/daemon/media_socket.c
index 42242a1e9..e072b28c0 100644
--- a/daemon/media_socket.c
+++ b/daemon/media_socket.c
@@ -35,7 +35,7 @@
 
 
 typedef int (*rewrite_func)(str *, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 
 
 struct streamhandler_io {
@@ -56,20 +56,20 @@ static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *);
 static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *);
 
 static int call_noop_rtcp(str *s, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 static int call_avp2savp_rtp(str *s, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 static int call_savp2avp_rtp(str *s, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 static int call_avp2savp_rtcp(str *s, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 static int call_savp2avp_rtcp(str *s, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 static int call_avpf2avp_rtcp(str *s, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 //static int call_avpf2savp_rtcp(str *s, struct packet_stream *);
 static int call_savpf2avp_rtcp(str *s, struct packet_stream *, struct stream_fd *, const endpoint_t *,
-		const struct timeval *);
+		const struct timeval *, struct ssrc_ctx *);
 //static int call_savpf2savp_rtcp(str *s, struct packet_stream *);
 
 
@@ -805,45 +805,45 @@ static int rtcp_demux(str *s, struct call_media *media) {
 }
 
 static int call_noop_rtcp(str *s, struct packet_stream *stream, struct stream_fd *sfd, const endpoint_t *src,
-		const struct timeval *tv)
+		const struct timeval *tv, struct ssrc_ctx *ssrc_ctx)
 {
 	rtcp_parse(s, sfd, src, tv);
 	return 0;
 }
 static int call_avpf2avp_rtcp(str *s, struct packet_stream *stream, struct stream_fd *sfd, const endpoint_t *src,
-		const struct timeval *tv)
+		const struct timeval *tv, struct ssrc_ctx *ssrc_ctx)
 {
 	return rtcp_avpf2avp(s, sfd, src, tv); // also does rtcp_parse
 }
 static int call_avp2savp_rtp(str *s, struct packet_stream *stream, struct stream_fd *sfd, const endpoint_t *src,
-		const struct timeval *tv)
+		const struct timeval *tv, struct ssrc_ctx *ssrc_ctx)
 {
-	return rtp_avp2savp(s, &stream->crypto, stream->call->ssrc_hash, SSRC_DIR_OUTPUT);
+	return rtp_avp2savp(s, &stream->crypto, ssrc_ctx);
 }
 static int call_avp2savp_rtcp(str *s, struct packet_stream *stream, struct stream_fd *sfd, const endpoint_t *src,
-		const struct timeval *tv)
+		const struct timeval *tv, struct ssrc_ctx *ssrc_ctx)
 {
-	return rtcp_avp2savp(s, &stream->crypto);
+	return rtcp_avp2savp(s, &stream->crypto, ssrc_ctx);
 }
 static int call_savp2avp_rtp(str *s, struct packet_stream *stream, struct stream_fd *sfd, const endpoint_t *src,
-		const struct timeval *tv)
+		const struct timeval *tv, struct ssrc_ctx *ssrc_ctx)
 {
-	return rtp_savp2avp(s, &stream->selected_sfd->crypto, stream->call->ssrc_hash, SSRC_DIR_INPUT);
+	return rtp_savp2avp(s, &stream->selected_sfd->crypto, ssrc_ctx);
 }
 static int call_savp2avp_rtcp(str *s, struct packet_stream *stream, struct stream_fd *sfd, const endpoint_t *src,
-		const struct timeval *tv)
+		const struct timeval *tv, struct ssrc_ctx *ssrc_ctx)
 {
-	int ret = rtcp_savp2avp(s, &stream->selected_sfd->crypto);
+	int ret = rtcp_savp2avp(s, &stream->selected_sfd->crypto, ssrc_ctx);
 	if (ret < 0)
 		return ret;
 	rtcp_parse(s, sfd, src, tv);
 	return ret;
 }
 static int call_savpf2avp_rtcp(str *s, struct packet_stream *stream, struct stream_fd *sfd, const endpoint_t *src,
-		const struct timeval *tv)
+		const struct timeval *tv, struct ssrc_ctx *ssrc_ctx)
 {
 	int ret;
-	ret = rtcp_savp2avp(s, &stream->selected_sfd->crypto);
+	ret = rtcp_savp2avp(s, &stream->selected_sfd->crypto, ssrc_ctx);
 	if (ret < 0)
 		return ret;
 	return rtcp_avpf2avp(s, sfd, src, tv);
@@ -854,7 +854,7 @@ static int __k_null(struct rtpengine_srtp *s, struct packet_stream *stream) {
 	*s = __res_null;
 	return 0;
 }
-static int __k_srtp_crypt(struct rtpengine_srtp *s, struct crypto_context *c) {
+static int __k_srtp_crypt(struct rtpengine_srtp *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
 	if (!c->params.crypto_suite)
 		return -1;
 
@@ -862,7 +862,7 @@ static int __k_srtp_crypt(struct rtpengine_srtp *s, struct crypto_context *c) {
 		.cipher		= c->params.crypto_suite->kernel_cipher,
 		.hmac		= c->params.crypto_suite->kernel_hmac,
 		.mki_len	= c->params.mki_len,
-		.last_index	= c->last_index,
+		.last_index	= ssrc_ctx->srtp_index,
 		.auth_tag_len	= c->params.crypto_suite->srtp_auth_tag,
 	};
 	if (c->params.mki_len)
@@ -880,10 +880,10 @@ static int __k_srtp_crypt(struct rtpengine_srtp *s, struct crypto_context *c) {
 	return 0;
 }
 static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *stream) {
-	return __k_srtp_crypt(s, &stream->crypto);
+	return __k_srtp_crypt(s, &stream->crypto, stream->ssrc_out);
 }
 static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *stream) {
-	return __k_srtp_crypt(s, &stream->selected_sfd->crypto);
+	return __k_srtp_crypt(s, &stream->selected_sfd->crypto, stream->ssrc_in);
 }
 
 INLINE void __re_address_translate_ep(struct re_address *o, const endpoint_t *ep) {
@@ -962,7 +962,7 @@ void kernelize(struct packet_stream *stream) {
 
 	__re_address_translate_ep(&reti.dst_addr, &sink->endpoint);
 	__re_address_translate_ep(&reti.src_addr, &sink->selected_sfd->socket.local);
-	reti.ssrc = sink->crypto.ssrc;
+	reti.ssrc = sink->ssrc_in ? sink->ssrc_in->parent->ssrc : 0;
 
 	stream->handler->in->kernel(&reti.decrypt, stream);
 	stream->handler->out->kernel(&reti.encrypt, sink);
@@ -1095,6 +1095,38 @@ noop:
 }
 
 
+// check and update SSRC pointers
+static void __stream_ssrc(struct packet_stream *in_srtp, struct packet_stream *out_srtp, u_int32_t ssrc_bs,
+		struct ssrc_ctx **ssrc_in_p, struct ssrc_ctx **ssrc_out_p, struct ssrc_hash *ssrc_hash)
+{
+	u_int32_t ssrc = ntohl(ssrc_bs);
+
+	// input direction
+	mutex_lock(&in_srtp->in_lock);
+
+	(*ssrc_in_p) = in_srtp->ssrc_in;
+	if (G_UNLIKELY(!(*ssrc_in_p) || (*ssrc_in_p)->parent->ssrc != ssrc)) {
+		// SSRC mismatch - get the new entry
+		(*ssrc_in_p) = in_srtp->ssrc_in =
+			get_ssrc_ctx(ssrc, ssrc_hash, SSRC_DIR_INPUT);
+	}
+
+	mutex_unlock(&in_srtp->in_lock);
+
+	// out direction
+	mutex_lock(&out_srtp->out_lock);
+
+	(*ssrc_out_p) = out_srtp->ssrc_out;
+	if (G_UNLIKELY(!(*ssrc_out_p) || (*ssrc_out_p)->parent->ssrc != ssrc)) {
+		// SSRC mismatch - get the new entry
+		(*ssrc_out_p) = out_srtp->ssrc_out =
+			get_ssrc_ctx(ssrc, ssrc_hash, SSRC_DIR_OUTPUT);
+	}
+
+	mutex_unlock(&out_srtp->out_lock);
+}
+
+
 /* XXX split this function into pieces */
 /* called lock-free */
 static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, const struct timeval *tv) {
@@ -1132,7 +1164,9 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin,
 	rewrite_func rwf_in, rwf_out;
 	//struct local_intf *loc_addr;
 	struct rtp_header *rtp_h;
+	struct rtcp_packet *rtcp_h;
 	struct rtp_stats *rtp_s;
+	struct ssrc_ctx *ssrc_in = NULL, *ssrc_out = NULL;
 
 	call = sfd->call;
 	cm = call->callmaster;
@@ -1227,33 +1261,39 @@ loop_ok:
 		out_srtp = sink->rtcp_sibling;
 
 
-	/* stats per RTP payload type */
+	/* RTP/RTCP specifics */
 
-	if (media->protocol && media->protocol->rtp && !rtcp && !rtp_payload(&rtp_h, NULL, s)) {
-		i = (rtp_h->m_pt & 0x7f);
+	if (G_LIKELY(media->protocol && media->protocol->rtp)) {
+		if (G_LIKELY(!rtcp && !rtp_payload(&rtp_h, NULL, s))) {
+			__stream_ssrc(in_srtp, out_srtp, rtp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash);
 
-		// XXX two hash table lookups for each packet, not ideal
-		// XXX limit size of hash tables
-		rtp_s = g_hash_table_lookup(stream->rtp_stats, &i);
-		if (!rtp_s) {
-			ilog(LOG_WARNING | LOG_FLAG_LIMIT,
-					"RTP packet with unknown payload type %u received", i);
-			atomic64_inc(&stream->stats.errors);
-			atomic64_inc(&cm->statsps.errors);
-		}
+			// check the payload type
+			i = (rtp_h->m_pt & 0x7f);
+			ssrc_in->parent->payload_type = i;
 
-		else {
-			atomic64_inc(&rtp_s->packets);
-			atomic64_add(&rtp_s->bytes, s->len);
+			// XXX limit size of hash tables
+			// XXX convert to array? or keep last pointer?
+			rtp_s = g_hash_table_lookup(stream->rtp_stats, &i);
+			if (!rtp_s) {
+				ilog(LOG_WARNING | LOG_FLAG_LIMIT,
+						"RTP packet with unknown payload type %u received", i);
+				atomic64_inc(&stream->stats.errors);
+				atomic64_inc(&cm->statsps.errors);
+			}
 
-			struct ssrc_entry *se = get_ssrc(ntohl(rtp_h->ssrc), call->ssrc_hash);
-			se->payload_type = i;
+			else {
+				atomic64_inc(&rtp_s->packets);
+				atomic64_add(&rtp_s->bytes, s->len);
+			}
+		}
+		else if (rtcp && !rtcp_payload(&rtcp_h, NULL, s)) {
+			__stream_ssrc(in_srtp, out_srtp, rtcp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash);
 		}
 	}
 
 	/* do we have somewhere to forward it to? */
 
-	if (!sink || !sink->selected_sfd || !out_srtp->selected_sfd || !in_srtp->selected_sfd) {
+	if (G_UNLIKELY(!sink || !sink->selected_sfd || !out_srtp->selected_sfd || !in_srtp->selected_sfd)) {
 		ilog(LOG_WARNING, "RTP packet from %s discarded", endpoint_print_buf(fsin));
 		atomic64_inc(&stream->stats.errors);
 		atomic64_inc(&cm->statsps.errors);
@@ -1267,7 +1307,7 @@ loop_ok:
 
 	determine_handler(in_srtp, sink);
 
-	if (!rtcp) {
+	if (G_LIKELY(!rtcp)) {
 		rwf_in = in_srtp->handler->in->rtp;
 		rwf_out = in_srtp->handler->out->rtp;
 	}
@@ -1281,7 +1321,7 @@ loop_ok:
 	/* return values are: 0 = forward packet, -1 = error/dont forward,
 	 * 1 = forward and push update to redis */
 	if (rwf_in) {
-		handler_ret = rwf_in(s, in_srtp, sfd, fsin, tv);
+		handler_ret = rwf_in(s, in_srtp, sfd, fsin, tv, ssrc_in);
 	}
 
 	// If recording pcap dumper is set, then we record the call.
@@ -1289,9 +1329,9 @@ loop_ok:
 		dump_packet(call->recording, stream, s);
 	}
 
-	if (handler_ret >= 0) {
+	if (G_LIKELY(handler_ret >= 0)) {
 		if (rwf_out)
-			handler_ret += rwf_out(s, out_srtp, NULL, NULL, NULL);
+			handler_ret += rwf_out(s, out_srtp, NULL, NULL, NULL, ssrc_out);
 	}
 
 	if (handler_ret > 0) {
diff --git a/daemon/redis.c b/daemon/redis.c
index f932abcc0..9aa60c795 100644
--- a/daemon/redis.c
+++ b/daemon/redis.c
@@ -29,6 +29,7 @@
 #include "recording.h"
 #include "rtplib.h"
 #include "str.h"
+#include "ssrc.h"
 
 
 INLINE redisReply *redis_expect(int type, redisReply *r) {
@@ -801,7 +802,7 @@ define_get_int_type(time_t, time_t, strtoull);
 define_get_int_type(int, int, strtol);
 define_get_int_type(unsigned, unsigned int, strtol);
 //define_get_int_type(u16, u_int16_t, strtol);
-define_get_int_type(u64, u_int64_t, strtoull);
+//define_get_int_type(u64, u_int64_t, strtoull);
 define_get_int_type(a64, atomic64, strtoa64);
 
 define_get_type_format(str, str);
@@ -998,22 +999,6 @@ err:
 	rlog(LOG_ERR, "Crypto params error: %s", err);
 	return -1;
 }
-static int redis_hash_get_crypto_context(struct crypto_context *out, const struct redis_hash *h) {
-	int ret;
-
-	ret = redis_hash_get_crypto_params(&out->params, h, "");
-	if (ret == 1)
-		return 0;
-	else if (ret)
-		return -1;
-
-	if (redis_hash_get_u64(&out->last_index, h, "last_index"))
-		return -1;
-	// coverity[check_return : FALSE]
-	redis_hash_get_unsigned(&out->ssrc, h, "ssrc");
-
-	return 0;
-}
 
 static int redis_sfds(struct call *c, struct redis_list *sfds) {
 	unsigned int i;
@@ -1067,9 +1052,6 @@ static int redis_sfds(struct call *c, struct redis_list *sfds) {
 			goto err;
 		sfd = stream_fd_new(sock, c, loc);
 		// XXX tos
-		err = "failed to config crypto context";
-		if (redis_hash_get_crypto_context(&sfd->crypto, rh))
-			goto err;
 
 		sfds->ptrs[i] = sfd;
 	}
@@ -1103,8 +1085,6 @@ static int redis_streams(struct call *c, struct redis_list *streams) {
 			return -1;
 		if (redis_hash_get_stats(&ps->stats, rh, "stats"))
 			return -1;
-		if (redis_hash_get_crypto_context(&ps->crypto, rh))
-			return -1;
 
 		streams->ptrs[i] = ps;
 
@@ -1499,6 +1479,8 @@ static void json_restore_call(struct redis *r, struct callmaster *m, const str *
 	if (json_link_maps(c, &maps, &sfds, root_reader))
 		goto err8;
 
+	// XXX restore SSRC table
+
 	// presence of this key determines whether we were recording at all
 	if (!redis_hash_get_str(&s, &call, "recording_meta_prefix")) {
 		recording_start(c, s.s);
@@ -1678,20 +1660,6 @@ static int json_update_crypto_params(JsonBuilder *builder, const char *pref,
 	return 0;
 }
 
-static void json_update_crypto_context(JsonBuilder *builder, const char *pref,
-		unsigned int unique_id,
-		const struct crypto_context *c)
-{
-	char tmp[2048];
-
-	if (json_update_crypto_params(builder, pref, unique_id, "", &c->params))
-		return;
-
-	JSON_SET_SIMPLE("last_index","%" PRIu64, c->last_index);
-	JSON_SET_SIMPLE("ssrc","%u",(unsigned) c->ssrc);
-
-}
-
 static void json_update_dtls_fingerprint(JsonBuilder *builder, const char *pref,
 		unsigned int unique_id,
 		const struct dtls_fingerprint *f)
@@ -1766,8 +1734,6 @@ char* redis_encode_json(struct call *c) {
 				JSON_SET_SIMPLE("local_intf_uid","%u",sfd->local_intf->unique_id);
 				JSON_SET_SIMPLE("stream","%u",sfd->stream->unique_id);
 
-				json_update_crypto_context(builder, "sfd", sfd->unique_id, &sfd->crypto);
-
 			}
 			json_builder_end_object (builder);
 
@@ -1799,8 +1765,6 @@ char* redis_encode_json(struct call *c) {
 				JSON_SET_SIMPLE("stats-bytes","%" PRIu64, atomic64_get(&ps->stats.bytes));
 				JSON_SET_SIMPLE("stats-errors","%" PRIu64, atomic64_get(&ps->stats.errors));
 
-				json_update_crypto_context(builder, "stream", ps->unique_id, &ps->crypto);
-
 			}
 
 			json_builder_end_object (builder);
@@ -1951,7 +1915,6 @@ char* redis_encode_json(struct call *c) {
 			json_builder_end_array (builder);
 
 			g_list_free(k);
-
 		}
 
 		for (l = c->endpoint_maps.head; l; l = l->next) {
@@ -1990,6 +1953,29 @@ char* redis_encode_json(struct call *c) {
 			}
 			json_builder_end_array (builder);
 		}
+
+		// SSRC table dump
+		k = g_hash_table_get_values(c->ssrc_hash->ht);
+		json_builder_set_member_name(builder, "ssrc_table");
+		json_builder_begin_array (builder);
+		for (m = k; m; m = m->next) {
+			struct ssrc_entry *se = m->data;
+			json_builder_begin_object (builder);
+
+			JSON_SET_SIMPLE("ssrc","%" PRIu32, se->ssrc);
+			// XXX use function for in/out
+			JSON_SET_SIMPLE("in_srtp_index","%" PRIu64, se->input_ctx.srtp_index);
+			JSON_SET_SIMPLE("in_srtcp_index","%" PRIu64, se->input_ctx.srtcp_index);
+			JSON_SET_SIMPLE("out_srtp_index","%" PRIu64, se->output_ctx.srtp_index);
+			JSON_SET_SIMPLE("out_srtcp_index","%" PRIu64, se->output_ctx.srtcp_index);
+			JSON_SET_SIMPLE("payload_type","%i", se->payload_type);
+			// XXX add rest of info
+
+			json_builder_end_object (builder);
+		}
+		json_builder_end_array (builder);
+
+		g_list_free(k);
 	}
 	json_builder_end_object (builder);
 
diff --git a/daemon/rtcp.c b/daemon/rtcp.c
index da0c377d9..64b47e25f 100644
--- a/daemon/rtcp.c
+++ b/daemon/rtcp.c
@@ -768,7 +768,7 @@ error:
 	return -1;
 }
 
-static int rtcp_payload(struct rtcp_packet **out, str *p, const str *s) {
+int rtcp_payload(struct rtcp_packet **out, str *p, const str *s) {
 	struct rtcp_packet *rtcp;
 	const char *err;
 
@@ -786,10 +786,14 @@ static int rtcp_payload(struct rtcp_packet **out, str *p, const str *s) {
 			&& rtcp->header.pt != RTCP_PT_RR)
 		goto error;
 
+	if (!p)
+		goto done;
+
 	*p = *s;
 	str_shift(p, sizeof(*rtcp));
-	*out = rtcp;
 
+done:
+	*out = rtcp;
 	return 0;
 error:
 	ilog(LOG_WARNING | LOG_FLAG_LIMIT, "Error parsing RTCP header: %s", err);
@@ -797,21 +801,25 @@ error:
 }
 
 /* rfc 3711 section 3.4 */
-int rtcp_avp2savp(str *s, struct crypto_context *c) {
+int rtcp_avp2savp(str *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
 	struct rtcp_packet *rtcp;
 	u_int32_t *idx;
 	str to_auth, payload;
 
+	if (G_UNLIKELY(!ssrc_ctx))
+		return -1;
 	if (rtcp_payload(&rtcp, &payload, s))
 		return -1;
 	if (check_session_keys(c))
 		return -1;
 
-	if (!c->params.session_params.unencrypted_srtcp && crypto_encrypt_rtcp(c, rtcp, &payload, c->last_index))
+	if (!c->params.session_params.unencrypted_srtcp && crypto_encrypt_rtcp(c, rtcp, &payload,
+				ssrc_ctx->srtcp_index))
 		return -1;
 
 	idx = (void *) s->s + s->len;
-	*idx = htonl((c->params.session_params.unencrypted_srtcp ? 0ULL : 0x80000000ULL) | c->last_index++);
+	*idx = htonl((c->params.session_params.unencrypted_srtcp ? 0ULL : 0x80000000ULL) |
+			ssrc_ctx->srtcp_index++);
 	s->len += sizeof(*idx);
 
 	to_auth = *s;
@@ -826,13 +834,15 @@ int rtcp_avp2savp(str *s, struct crypto_context *c) {
 
 
 /* rfc 3711 section 3.4 */
-int rtcp_savp2avp(str *s, struct crypto_context *c) {
+int rtcp_savp2avp(str *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
 	struct rtcp_packet *rtcp;
 	str payload, to_auth, to_decrypt, auth_tag;
 	u_int32_t idx, *idx_p;
 	char hmac[20];
 	const char *err;
 
+	if (G_UNLIKELY(!ssrc_ctx))
+		return -1;
 	if (rtcp_payload(&rtcp, &payload, s))
 		return -1;
 	if (check_session_keys(c))
diff --git a/daemon/rtcp.h b/daemon/rtcp.h
index 3a41864dd..cf7e3d870 100644
--- a/daemon/rtcp.h
+++ b/daemon/rtcp.h
@@ -7,12 +7,16 @@
 
 
 struct crypto_context;
+struct rtcp_packet;
+struct ssrc_ctx;
 
 
 
 int rtcp_avpf2avp(str *, struct stream_fd *sfd, const endpoint_t *, const struct timeval *);
-int rtcp_avp2savp(str *, struct crypto_context *);
-int rtcp_savp2avp(str *, struct crypto_context *);
+int rtcp_avp2savp(str *, struct crypto_context *, struct ssrc_ctx *);
+int rtcp_savp2avp(str *, struct crypto_context *, struct ssrc_ctx *);
+
+int rtcp_payload(struct rtcp_packet **out, str *p, const str *s);
 
 //void parse_and_log_rtcp_report(struct stream_fd *sfd, const str *, const endpoint_t *, const struct timeval *);
 void rtcp_parse(const str *, struct stream_fd *sfd, const endpoint_t *, const struct timeval *);
diff --git a/daemon/rtp.c b/daemon/rtp.c
index 6fdb1f945..4ae11db16 100644
--- a/daemon/rtp.c
+++ b/daemon/rtp.c
@@ -18,7 +18,7 @@ INLINE int check_session_keys(struct crypto_context *c) {
 	str s;
 	const char *err;
 
-	if (c->have_session_key)
+	if (G_LIKELY(c->have_session_key))
 		return 0;
 	err = "SRTP output wanted, but no crypto suite was negotiated";
 	if (!c->params.crypto_suite)
@@ -45,17 +45,17 @@ error:
 	return -1;
 }
 
-static u_int64_t packet_index(struct crypto_context *c, struct rtp_header *rtp) {
+static u_int64_t packet_index(struct ssrc_ctx *ssrc_ctx, struct rtp_header *rtp) {
 	u_int16_t seq;
 
 	seq = ntohs(rtp->seq_num);
 	/* rfc 3711 section 3.3.1 */
-	if (G_UNLIKELY(!c->last_index))
-		c->last_index = seq;
+	if (G_UNLIKELY(!ssrc_ctx->srtp_index))
+		ssrc_ctx->srtp_index = seq;
 
 	/* rfc 3711 appendix A, modified, and sections 3.3 and 3.3.1 */
-	u_int16_t s_l = (c->last_index & 0x00000000ffffULL);
-	u_int32_t roc = (c->last_index & 0xffffffff0000ULL) >> 16;
+	u_int16_t s_l = (ssrc_ctx->srtp_index & 0x00000000ffffULL);
+	u_int32_t roc = (ssrc_ctx->srtp_index & 0xffffffff0000ULL) >> 16;
 	u_int32_t v = 0;
 
 	if (s_l < 0x8000) {
@@ -70,8 +70,8 @@ static u_int64_t packet_index(struct crypto_context *c, struct rtp_header *rtp)
 			v = roc;
 	}
 
-	c->last_index = (u_int64_t)(((v << 16) | seq) & 0xffffffffffffULL);
-	return c->last_index;
+	ssrc_ctx->srtp_index = (u_int64_t)(((v << 16) | seq) & 0xffffffffffffULL);
+	return ssrc_ctx->srtp_index;
 }
 
 void rtp_append_mki(str *s, struct crypto_context *c) {
@@ -86,48 +86,21 @@ void rtp_append_mki(str *s, struct crypto_context *c) {
 	s->len += c->params.mki_len;
 }
 
-static int rtp_ssrc_check(const struct rtp_header *rtp, struct crypto_context *c, struct ssrc_hash *ht,
-		enum ssrc_dir dir)
-{
-	struct ssrc_ctx *ssrc_ctx;
-
-	/* check last known SSRC */
-	if (G_LIKELY(rtp->ssrc == c->ssrc)) // XXX replace by pointer
-		return 0;
-	if (!c->ssrc) {
-		c->ssrc = rtp->ssrc;
-		return 1;
-	}
-
-	/* SSRC mismatch. stash away last know info */
-	ilog(LOG_DEBUG, "SSRC changed, updating SRTP crypto contexts");
-
-	// Find the entry for the last SSRC.
-	ssrc_ctx = get_ssrc_ctx(c->ssrc, ht, dir);
-	ssrc_ctx->srtp_index = c->last_index;
-
-	// New SSRC, set the crypto context.
-	c->ssrc = rtp->ssrc;
-	ssrc_ctx = get_ssrc_ctx(rtp->ssrc, ht, dir);
-	c->last_index = ssrc_ctx->srtp_index; // defaults to 0
-
-	return 1;
-}
-
 /* rfc 3711, section 3.3 */
-int rtp_avp2savp(str *s, struct crypto_context *c, struct ssrc_hash *ht, enum ssrc_dir dir) {
+int rtp_avp2savp(str *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
 	struct rtp_header *rtp;
 	str payload, to_auth;
 	u_int64_t index;
 	int ret = 0;
 
+	if (G_UNLIKELY(!ssrc_ctx))
+		return -1;
 	if (rtp_payload(&rtp, &payload, s))
 		return -1;
 	if (check_session_keys(c))
 		return -1;
 
-	ret = rtp_ssrc_check(rtp, c, ht, dir);
-	index = packet_index(c, rtp);
+	index = packet_index(ssrc_ctx, rtp);
 
 	/* rfc 3711 section 3.1 */
 	if (!c->params.session_params.unencrypted_srtp && crypto_encrypt_rtp(c, rtp, &payload, index))
@@ -146,20 +119,21 @@ int rtp_avp2savp(str *s, struct crypto_context *c, struct ssrc_hash *ht, enum ss
 }
 
 /* rfc 3711, section 3.3 */
-int rtp_savp2avp(str *s, struct crypto_context *c, struct ssrc_hash *ht, enum ssrc_dir dir) {
+int rtp_savp2avp(str *s, struct crypto_context *c, struct ssrc_ctx *ssrc_ctx) {
 	struct rtp_header *rtp;
 	u_int64_t index;
 	str payload, to_auth, to_decrypt, auth_tag;
 	char hmac[20];
 	int ret = 0;
 
+	if (G_UNLIKELY(!ssrc_ctx))
+		return -1;
 	if (rtp_payload(&rtp, &payload, s))
 		return -1;
 	if (check_session_keys(c))
 		return -1;
 
-	ret = rtp_ssrc_check(rtp, c, ht, dir);
-	index = packet_index(c, rtp);
+	index = packet_index(ssrc_ctx, rtp);
 	if (srtp_payloads(&to_auth, &to_decrypt, &auth_tag, NULL,
 			c->params.session_params.unauthenticated_srtp ? 0 : c->params.crypto_suite->srtp_auth_tag,
 			c->params.mki_len,
@@ -195,7 +169,7 @@ int rtp_savp2avp(str *s, struct crypto_context *c, struct ssrc_hash *ht, enum ss
 	goto error;
 
 decrypt_idx:
-	c->last_index = index;
+	ssrc_ctx->srtp_index = index;
 decrypt:
 	if (!c->params.session_params.unencrypted_srtp && crypto_decrypt_rtp(c, rtp, &to_decrypt, index))
 		return -1;
diff --git a/daemon/rtp.h b/daemon/rtp.h
index 6741551c9..123a3cb5f 100644
--- a/daemon/rtp.h
+++ b/daemon/rtp.h
@@ -12,6 +12,7 @@ struct crypto_context;
 struct rtp_header;
 struct ssrc_hash;
 enum ssrc_dir;
+struct ssrc_ctx;
 
 
 
@@ -19,8 +20,8 @@ enum ssrc_dir;
 
 const struct rtp_payload_type *rtp_payload_type(unsigned int, GHashTable *);
 
-int rtp_avp2savp(str *, struct crypto_context *, struct ssrc_hash *, enum ssrc_dir);
-int rtp_savp2avp(str *, struct crypto_context *, struct ssrc_hash *, enum ssrc_dir);
+int rtp_avp2savp(str *, struct crypto_context *, struct ssrc_ctx *);
+int rtp_savp2avp(str *, struct crypto_context *, struct ssrc_ctx *);
 
 void rtp_append_mki(str *s, struct crypto_context *c);
 int srtp_payloads(str *to_auth, str *to_decrypt, str *auth_tag, str *mki,
diff --git a/daemon/ssrc.c b/daemon/ssrc.c
index 82f84ecdb..a39ad11ec 100644
--- a/daemon/ssrc.c
+++ b/daemon/ssrc.c
@@ -6,12 +6,17 @@
 
 
 
+static void init_ssrc_ctx(struct ssrc_ctx *c, struct ssrc_entry *parent) {
+	c->parent = parent;
+}
 static struct ssrc_entry *create_ssrc_entry(u_int32_t ssrc) {
 	struct ssrc_entry *ent;
 	ent = g_slice_alloc0(sizeof(struct ssrc_entry));
 	ent->ssrc = ssrc;
 	mutex_init(&ent->lock);
 	ent->payload_type = -1;
+	init_ssrc_ctx(&ent->input_ctx, ent);
+	init_ssrc_ctx(&ent->output_ctx, ent);
 	return ent;
 }
 static void add_ssrc_entry(struct ssrc_entry *ent, struct ssrc_hash *ht) {
diff --git a/daemon/ssrc.h b/daemon/ssrc.h
index 0f29506ae..f289e6529 100644
--- a/daemon/ssrc.h
+++ b/daemon/ssrc.h
@@ -14,6 +14,8 @@ struct call;
 struct call_media;
 struct timeval;
 struct rtp_payload_type;
+struct ssrc_entry;
+enum ssrc_dir;
 
 
 
@@ -22,8 +24,10 @@ struct ssrc_hash {
 	rwlock_t lock;
 };
 struct ssrc_ctx {
+	struct ssrc_entry *parent;
 	// XXX lock this?
-	u_int64_t srtp_index;
+	u_int64_t srtp_index,
+		  srtcp_index;
 	// XXX move entire crypto context in here?
 };
 struct ssrc_entry {
@@ -36,7 +40,7 @@ struct ssrc_entry {
 	int payload_type; // to determine the clock rate for jitter calculations
 	unsigned int last_rtt; // last calculated raw rtt without rtt from opposide side
 };
-enum ssrc_dir {
+enum ssrc_dir { // these values must not be used externally
 	SSRC_DIR_INPUT  = G_STRUCT_OFFSET(struct ssrc_entry, input_ctx),
 	SSRC_DIR_OUTPUT = G_STRUCT_OFFSET(struct ssrc_entry, output_ctx),
 };
diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl
index e112bad9e..3191c2688 100755
--- a/tests/simulator-ng.pl
+++ b/tests/simulator-ng.pl
@@ -228,10 +228,11 @@ sub savp_sdp {
 }
 
 sub rtcp_sr {
+	my ($ssrc) = @_;
 	my @now = Time::HiRes::gettimeofday();
 	my $secs = $now[0] + 2208988800;
 	my $frac = $now[1] / 1000000 * 2**32;
-	my $sr = pack('CCnN NNN NN', (2 << 6) | 1, 200, 12, rand(2**32), $secs, $frac,
+	my $sr = pack('CCnN NNN NN', (2 << 6) | 1, 200, 12, $ssrc, $secs, $frac,
 		12345, rand(12345), rand(4321));
 	$sr .= pack('N CCCC NNNN', rand(2**32), rand(256), rand(256), rand(256), rand(256),
 		rand(2**32), rand(2**32), rand(2**32), rand(2**32));
@@ -262,7 +263,8 @@ sub rtcp_rtpfb {
 
 sub rtcp_avp {
 	my ($recv, $ctx, $ctx_o) = @_;
-	my $sr = rtcp_sr();
+	my $ssrc = $$ctx{ssrc} // ($$ctx{ssrc} = rand(2**32));
+	my $sr = rtcp_sr($ssrc);
 	my $exp = $sr;
 	$$recv{srtp} and $exp = rtcp_encrypt($exp, $ctx_o, 'in');
 	return ($sr, $exp);
@@ -270,7 +272,8 @@ sub rtcp_avp {
 
 sub rtcp_savp {
 	my ($recv, $ctx, $ctx_o) = @_;
-	my $sr = rtcp_sr();
+	my $ssrc = $$ctx{ssrc} // ($$ctx{ssrc} = rand(2**32));
+	my $sr = rtcp_sr($ssrc);
 	my $enc = rtcp_encrypt($sr, $ctx, 'out');
 	my $exp = $sr;
 	$$recv{srtp} and $exp = rtcp_encrypt($exp, $ctx_o, 'in');
@@ -279,7 +282,8 @@ sub rtcp_savp {
 
 sub rtcp_avpf {
 	my ($recv, $ctx, $ctx_o) = @_;
-	my $sr = rtcp_sr();
+	my $ssrc = $$ctx{ssrc} // ($$ctx{ssrc} = rand(2**32));
+	my $sr = rtcp_sr($ssrc);
 	my $fb = rtcp_rtpfb();
 	my $exp = $sr;
 	$$recv{avpf} and $exp .= $fb;
@@ -289,7 +293,8 @@ sub rtcp_avpf {
 
 sub rtcp_savpf {
 	my ($recv, $ctx, $ctx_o) = @_;
-	my $sr = rtcp_sr();
+	my $ssrc = $$ctx{ssrc} // ($$ctx{ssrc} = rand(2**32));
+	my $sr = rtcp_sr($ssrc);
 	my $fb = rtcp_rtpfb();
 	my $enc = rtcp_encrypt($sr . $fb, $ctx, 'out');
 	my $exp = $sr;