From 497c2d6a8da674cf323229290004355a6ba230d4 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 21 Dec 2018 10:50:30 -0500 Subject: [PATCH] TT#49104 move RTP sequencing into SSRC context Detect and handle RTP seq resets while transcoding and provide consistent output seq fixes #664 Change-Id: I063bd9432f253fde25e90cdeb4e3460a882f7778 --- daemon/codec.c | 100 ++++++++++------ daemon/ssrc.c | 4 + include/ssrc.h | 7 ++ lib/codeclib.c | 11 +- lib/codeclib.h | 9 +- recording-daemon/packet.c | 2 +- t/Makefile | 3 +- t/auto-daemon-tests.pl | 237 +++++++++++++++++++++++++++++++++++--- t/transcode-test.c | 110 ++++++++++-------- 9 files changed, 376 insertions(+), 107 deletions(-) diff --git a/daemon/codec.c b/daemon/codec.c index 09fcd36bb..e2f3637e9 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -54,17 +54,14 @@ static GList *__delete_receiver_codec(struct call_media *receiver, GList *link) struct codec_ssrc_handler { struct ssrc_entry h; // must be first struct codec_handler *handler; - mutex_t lock; - packet_sequencer_t sequencer; decoder_t *decoder; encoder_t *encoder; format_t encoder_format; int bitrate; int ptime; int bytes_per_packet; + unsigned long first_ts; // for output TS scaling unsigned long ts_in; // for DTMF dupe detection - unsigned long ts_out; - u_int16_t seq_out; GString *sample_buffer; }; struct transcode_packet { @@ -547,12 +544,17 @@ static int __handler_func_sequencer(struct codec_handler *h, struct media_packet /// fall through } - struct codec_ssrc_handler *ch = get_ssrc(mp->rtp->ssrc, h->ssrc_hash); + struct ssrc_ctx *ssrc_in = mp->ssrc_in; + struct ssrc_entry_call *ssrc_in_p = ssrc_in->parent; + struct ssrc_ctx *ssrc_out = mp->ssrc_out; + struct ssrc_entry_call *ssrc_out_p = ssrc_out->parent; + + struct codec_ssrc_handler *ch = get_ssrc(ssrc_in_p->h.ssrc, h->ssrc_hash); if (G_UNLIKELY(!ch)) return 0; - atomic64_inc(&mp->ssrc_in->packets); - atomic64_add(&mp->ssrc_in->octets, mp->payload.len); + atomic64_inc(&ssrc_in->packets); + atomic64_add(&ssrc_in->octets, mp->payload.len); packet->p.seq = ntohs(mp->rtp->seq_num); packet->payload = str_dup(&mp->payload); @@ -564,40 +566,65 @@ static int __handler_func_sequencer(struct codec_handler *h, struct media_packet if (packet->ignore_seq) seq_next_packet = packet_sequencer_force_next_packet; - mutex_lock(&ch->lock); + // we need a nested lock here - both input and output SSRC needs to be locked. + // we don't know the lock order, so try both, and keep trying until we succeed. + while (1) { + mutex_lock(&ssrc_in_p->h.lock); + if (ssrc_in_p == ssrc_out_p) + break; + if (!mutex_trylock(&ssrc_out_p->h.lock)) + break; + mutex_unlock(&ssrc_in_p->h.lock); + + mutex_lock(&ssrc_out_p->h.lock); + if (!mutex_trylock(&ssrc_in_p->h.lock)) + break; + mutex_unlock(&ssrc_out_p->h.lock); + } + + packet_sequencer_init(&ssrc_in_p->sequencer, (GDestroyNotify) __transcode_packet_free); - if (packet_sequencer_insert(&ch->sequencer, &packet->p)) { + u_int16_t seq_ori = ssrc_in_p->sequencer.seq; + int seq_ret = packet_sequencer_insert(&ssrc_in_p->sequencer, &packet->p); + if (seq_ret < 0) { // dupe if (packet->dup_func) packet->dup_func(ch, packet, mp); else ilog(LOG_DEBUG, "Ignoring duplicate RTP packet"); - mutex_unlock(&ch->lock); - obj_put(&ch->h); __transcode_packet_free(packet); - atomic64_inc(&mp->ssrc_in->duplicates); - return 0; + atomic64_inc(&ssrc_in->duplicates); + goto out; } // got a new packet, run decoder while (1) { - packet = seq_next_packet(&ch->sequencer); + packet = seq_next_packet(&ssrc_in_p->sequencer); if (G_UNLIKELY(!packet)) break; - atomic64_set(&mp->ssrc_in->packets_lost, ch->sequencer.lost_count); - atomic64_set(&mp->ssrc_in->last_seq, ch->sequencer.ext_seq); + atomic64_set(&ssrc_in->packets_lost, ssrc_in_p->sequencer.lost_count); + atomic64_set(&ssrc_in->last_seq, ssrc_in_p->sequencer.ext_seq); ilog(LOG_DEBUG, "Decoding RTP packet: seq %u, TS %lu", packet->p.seq, packet->ts); + if (seq_ret == 1) { + // seq reset - update output seq. we keep our output seq clean + ssrc_out_p->seq_diff -= packet->p.seq - seq_ori; + seq_ret = 0; + } + if (packet->func(ch, packet, mp)) ilog(LOG_WARN, "Decoder error while processing RTP packet"); __transcode_packet_free(packet); } - mutex_unlock(&ch->lock); +out: + mutex_unlock(&ssrc_in_p->h.lock); + if (ssrc_in_p != ssrc_out_p) + mutex_unlock(&ssrc_out_p->h.lock); obj_put(&ch->h); return 0; @@ -611,29 +638,31 @@ static void __output_rtp(struct media_packet *mp, struct codec_ssrc_handler *ch, int marker, int seq, int seq_inc) { struct rtp_header *rh = (void *) buf; + struct ssrc_ctx *ssrc_out = mp->ssrc_out; + struct ssrc_entry_call *ssrc_out_p = ssrc_out->parent; // reconstruct RTP header - unsigned int ts = payload_ts + ch->ts_out; + unsigned int ts = payload_ts; ZERO(*rh); rh->v_p_x_cc = 0x80; rh->m_pt = handler->dest_pt.payload_type | (marker ? 0x80 : 0); if (seq != -1) rh->seq_num = htons(seq); else - rh->seq_num = htons(ch->seq_out += seq_inc); + rh->seq_num = htons(ntohs(mp->rtp->seq_num) + (ssrc_out_p->seq_diff += seq_inc)); rh->timestamp = htonl(ts); - rh->ssrc = htonl(mp->ssrc_in->ssrc_map_out); + rh->ssrc = htonl(ssrc_out_p->h.ssrc); // add to output queue struct codec_packet *p = g_slice_alloc(sizeof(*p)); p->s.s = buf; p->s.len = payload_len + sizeof(struct rtp_header); - payload_tracker_add(&mp->ssrc_out->tracker, handler->dest_pt.payload_type); + payload_tracker_add(&ssrc_out->tracker, handler->dest_pt.payload_type); p->free_func = free; g_queue_push_tail(&mp->packets_out, p); - atomic64_inc(&mp->ssrc_out->packets); - atomic64_add(&mp->ssrc_out->octets, payload_len); - atomic64_set(&mp->ssrc_out->last_ts, ts); + atomic64_inc(&ssrc_out->packets); + atomic64_add(&ssrc_out->octets, payload_len); + atomic64_set(&ssrc_out->last_ts, ts); } static void packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct transcode_packet *packet, @@ -661,7 +690,7 @@ static int packet_dtmf(struct codec_ssrc_handler *ch, struct transcode_packet *p } if (!mp->call->block_dtmf && !mp->media->monologue->block_dtmf) - packet_dtmf_fwd(ch, packet, mp, 1); + packet_dtmf_fwd(ch, packet, mp, 0); return 0; } static void packet_dtmf_dup(struct codec_ssrc_handler *ch, struct transcode_packet *packet, @@ -823,8 +852,10 @@ static int handler_func_passthrough_ssrc(struct codec_handler *h, struct media_p if (mp->call->block_media || mp->media->monologue->block_media) return 0; - // substitute out SSRC + // substitute out SSRC etc mp->rtp->ssrc = htonl(mp->ssrc_in->ssrc_map_out); + //mp->rtp->timestamp = htonl(ntohl(mp->rtp->timestamp)); + mp->rtp->seq_num = htons(ntohs(mp->rtp->seq_num) + mp->ssrc_out->parent->seq_diff); // keep track of other stats here? @@ -843,9 +874,6 @@ static struct ssrc_entry *__ssrc_handler_new(void *p) { struct codec_handler *h = p; struct codec_ssrc_handler *ch = obj_alloc0("codec_ssrc_handler", sizeof(*ch), __free_ssrc_handler); ch->handler = h; - mutex_init(&ch->lock); - // needed for DTMF processing - packet_sequencer_init(&ch->sequencer, (GDestroyNotify) __transcode_packet_free); return &ch->h; } @@ -861,10 +889,6 @@ static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) { struct codec_ssrc_handler *ch = obj_alloc0("codec_ssrc_handler", sizeof(*ch), __free_ssrc_handler); ch->handler = h; - mutex_init(&ch->lock); - packet_sequencer_init(&ch->sequencer, (GDestroyNotify) __transcode_packet_free); - ch->seq_out = random(); - ch->ts_out = random(); ch->ptime = h->dest_pt.ptime; ch->sample_buffer = g_string_new(""); ch->bitrate = h->dest_pt.bitrate ? : h->dest_pt.codec_def->default_bitrate; @@ -911,7 +935,6 @@ static int __encoder_flush(encoder_t *enc, void *u1, void *u2) { static void __free_ssrc_handler(void *chp) { struct codec_ssrc_handler *ch = chp; ilog(LOG_DEBUG, "__free_ssrc_handler"); - packet_sequencer_destroy(&ch->sequencer); if (ch->decoder) decoder_close(ch->decoder); if (ch->encoder) { @@ -930,6 +953,7 @@ static void __free_ssrc_handler(void *chp) { static int __packet_encoded(encoder_t *enc, void *u1, void *u2) { struct codec_ssrc_handler *ch = u1; struct media_packet *mp = u2; + unsigned int seq_off = 0; ilog(LOG_DEBUG, "RTP media successfully encoded: TS %llu, len %i", (unsigned long long) enc->avpkt.pts, enc->avpkt.size); @@ -960,8 +984,9 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) { } ilog(LOG_DEBUG, "Received packet of %i bytes from packetizer", inout.len); - __output_rtp(mp, ch, ch->handler, buf, inout.len, enc->avpkt.pts / enc->def->clockrate_mult, - 0, -1, 1); + __output_rtp(mp, ch, ch->handler, buf, inout.len, ch->first_ts + + enc->avpkt.pts / enc->def->clockrate_mult, + 0, -1, seq_off); if (ret == 0) { // no more to go @@ -970,6 +995,7 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) { // loop around and get more in_pkt = NULL; + seq_off = 1; // next packet needs last seq + 1 XXX set unkernelize if used } return 0; @@ -989,6 +1015,8 @@ static int __packet_decoded(decoder_t *decoder, AVFrame *frame, void *u1, void * static int packet_decode(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp) { + if (!ch->first_ts) + ch->first_ts = packet->ts; return decoder_input_data(ch->decoder, packet->payload, packet->ts, __packet_decoded, ch, mp); } diff --git a/daemon/ssrc.c b/daemon/ssrc.c index 393358bc7..ea8e845f0 100644 --- a/daemon/ssrc.c +++ b/daemon/ssrc.c @@ -3,6 +3,7 @@ #include "aux.h" #include "call.h" #include "rtplib.h" +#include "codeclib.h" @@ -25,6 +26,8 @@ static struct ssrc_entry *create_ssrc_entry_call(void *uptr) { ent = obj_alloc0("ssrc_entry_call", sizeof(*ent), __free_ssrc_entry_call); init_ssrc_ctx(&ent->input_ctx, ent); init_ssrc_ctx(&ent->output_ctx, ent); + //ent->seq_out = random(); + //ent->ts_out = random(); return &ent->h; } static void add_ssrc_entry(u_int32_t ssrc, struct ssrc_entry *ent, struct ssrc_hash *ht) { @@ -48,6 +51,7 @@ static void __free_ssrc_entry_call(void *ep) { g_queue_clear_full(&e->sender_reports, (GDestroyNotify) free_sender_report); g_queue_clear_full(&e->rr_time_reports, (GDestroyNotify) free_rr_time); g_queue_clear_full(&e->stats_blocks, (GDestroyNotify) free_stats_block); + packet_sequencer_destroy(&e->sequencer); } static void ssrc_entry_put(void *ep) { struct ssrc_entry_call *e = ep; diff --git a/include/ssrc.h b/include/ssrc.h index 2d462e66a..59553358e 100644 --- a/include/ssrc.h +++ b/include/ssrc.h @@ -7,6 +7,7 @@ #include "compat.h" #include "aux.h" #include "obj.h" +#include "codeclib.h" @@ -89,6 +90,12 @@ struct ssrc_entry_call { *highest_mos, average_mos; // contains a running tally of all stats blocks unsigned int last_rtt; // last calculated raw rtt without rtt from opposide side + + // for transcoding + // input only + packet_sequencer_t sequencer; + // output only + uint16_t seq_diff; }; enum ssrc_dir { // these values must not be used externally SSRC_DIR_INPUT = G_STRUCT_OFFSET(struct ssrc_entry_call, input_ctx), diff --git a/lib/codeclib.c b/lib/codeclib.c index cab7ba0cb..55756333e 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -779,12 +779,14 @@ static int ptr_cmp(const void *a, const void *b, void *dummy) { return 0; } -void packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify ffunc) { +void __packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify ffunc) { ps->packets = g_tree_new_full(ptr_cmp, NULL, NULL, ffunc); ps->seq = -1; } void packet_sequencer_destroy(packet_sequencer_t *ps) { - g_tree_destroy(ps->packets); + if (ps->packets) + g_tree_destroy(ps->packets); + ps->packets = NULL; } struct tree_searcher { int find_seq, @@ -879,6 +881,8 @@ void *packet_sequencer_force_next_packet(packet_sequencer_t *ps) { } int packet_sequencer_insert(packet_sequencer_t *ps, seq_packet_t *p) { + int ret = 0; + // check seq for dupes if (G_UNLIKELY(ps->seq == -1)) { // first packet we see @@ -903,13 +907,14 @@ int packet_sequencer_insert(packet_sequencer_t *ps, seq_packet_t *p) { // everything else we consider a seq reset ilog(LOG_DEBUG, "Seq reset detected: expected seq %i, received seq %i", ps->seq, p->seq); ps->seq = p->seq; + ret = 1; // seq ok - fall thru seq_ok: if (g_tree_lookup(ps->packets, GINT_TO_POINTER(p->seq))) return -1; g_tree_insert(ps->packets, GINT_TO_POINTER(p->seq), p); - return 0; + return ret; } diff --git a/lib/codeclib.h b/lib/codeclib.h index 4920dcd3d..fc8fe3b1d 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -215,7 +215,8 @@ int encoder_input_fifo(encoder_t *enc, AVFrame *frame, int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2); -void packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify); +void __packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify); +INLINE 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); void *packet_sequencer_force_next_packet(packet_sequencer_t *ps); @@ -223,6 +224,12 @@ int packet_sequencer_insert(packet_sequencer_t *ps, seq_packet_t *); +// `ps` must be zero allocated +INLINE void packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify n) { + if (ps->packets) + return; + __packet_sequencer_init(ps, n); +} INLINE int format_eq(const format_t *a, const format_t *b) { if (G_UNLIKELY(a->clockrate != b->clockrate)) return 0; diff --git a/recording-daemon/packet.c b/recording-daemon/packet.c index ba60d2564..4729229ee 100644 --- a/recording-daemon/packet.c +++ b/recording-daemon/packet.c @@ -338,7 +338,7 @@ void packet_process(stream_t *stream, unsigned char *buf, unsigned len) { // insert into ssrc queue ssrc_t *ssrc = ssrc_get(stream, ssrc_num); - if (packet_sequencer_insert(&ssrc->sequencer, &packet->p)) + if (packet_sequencer_insert(&ssrc->sequencer, &packet->p) < 0) goto dupe; // got a new packet, run the decoder diff --git a/t/Makefile b/t/Makefile index f0f904f27..ed9b80f06 100644 --- a/t/Makefile +++ b/t/Makefile @@ -118,7 +118,8 @@ transcode-test: transcode-test.o $(COMMONOBJS) codeclib.o resample.o codec.o ssr control_ng.strhash.o \ streambuf.o cookie_cache.o udp_listener.o homer.o load.o cdr.o dtmf.o -payload-tracker-test: payload-tracker-test.o $(COMMONOBJS) ssrc.o aux.o auxlib.o rtp.o crypto.o +payload-tracker-test: payload-tracker-test.o $(COMMONOBJS) ssrc.o aux.o auxlib.o rtp.o crypto.o codeclib.o \ + resample.o const_str_hash-test.strhash: const_str_hash-test.strhash.o $(COMMONOBJS) diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index 4c2e826f2..fb0cdb3ab 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -8,17 +8,25 @@ use File::Temp; use IPC::Open3; use Time::HiRes; use POSIX ":sys_wait_h"; +use IO::Socket; like $ENV{LD_PRELOAD}, qr/tests-preload/, 'LD_PRELOAD present'; is $ENV{RTPE_PRELOAD_TEST_ACTIVE}, '1', 'preload library is active'; -ok -x $ENV{RTPE_BIN}, 'RTPE_BIN points to executable'; +SKIP: { + skip 'daemon is running externally', 1 if $ENV{RTPE_TEST_NO_LAUNCH}; + ok -x $ENV{RTPE_BIN}, 'RTPE_BIN points to executable'; +} my $rtpe_stdout = File::Temp::tempfile() or die; my $rtpe_stderr = File::Temp::tempfile() or die; -my $rtpe_pid = open3(undef, '>&'.fileno($rtpe_stdout), '>&'.fileno($rtpe_stderr), - $ENV{RTPE_BIN}, qw(--config-file=none -t -1 -i 203.0.113.1 -i 2001:db8:4321::1 - -n 2223 -c 12345 -f -L 7 -E -u 2222)); -ok $rtpe_pid, 'daemon launched in background'; +my $rtpe_pid; +SKIP: { + skip 'daemon is running externally', 1 if $ENV{RTPE_TEST_NO_LAUNCH}; + $rtpe_pid = open3(undef, '>&'.fileno($rtpe_stdout), '>&'.fileno($rtpe_stderr), + $ENV{RTPE_BIN}, qw(--config-file=none -t -1 -i 203.0.113.1 -i 2001:db8:4321::1 + -n 2223 -c 12345 -f -L 7 -E -u 2222)); + ok $rtpe_pid, 'daemon launched in background'; +} # keep trying to connect to the control socket while daemon is starting up my $c; @@ -31,14 +39,26 @@ for (1 .. 300) { 1; $c->{socket} or die; -my ($cid, $ft, $tt, $r); +my ($cid, $ft, $tt, @sockets); +my ($tag_iter) = (0); sub new_call { - undef($r); - $cid = rand(); - $ft = rand(); - $tt = rand(); - return; + my @ports = @_; + for my $s (@sockets) { + $s->close(); + } + @sockets = (); + $cid = $tag_iter++ . "-test-callID"; + $ft = $tag_iter++ . "-test-fromtag"; + $tt = $tag_iter++ . "-test-totag"; + for my $p (@ports) { + my ($addr, $port) = @{$p}; + my $s = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp', + LocalHost => $addr, LocalPort => $port) + or die; + push(@sockets, $s); + } + return @sockets; } sub crlf { my ($s) = @_; @@ -67,8 +87,10 @@ sub offer_answer { $regexp =~ s/CRYPTO128/([0-9a-zA-Z\/+]{40})/gs; $regexp =~ s/CRYPTO192/([0-9a-zA-Z\/+]{51})/gs; $regexp =~ s/CRYPTO256/([0-9a-zA-Z\/+]{62})/gs; - like crlf($resp->{sdp}), qr/$regexp/s, "$name - output $cmd SDP"; - return; + my $crlf = crlf($resp->{sdp}); + like $crlf, qr/$regexp/s, "$name - output $cmd SDP"; + my @matches = $crlf =~ qr/$regexp/s; + return @matches; } sub offer { return offer_answer('offer', @_); @@ -78,9 +100,55 @@ sub answer { $req->{'to-tag'} = $tt; return offer_answer('answer', $name, $req, $sdps); } +sub snd { + my ($sock, $dest, $packet) = @_; + $sock->send($packet, 0, pack_sockaddr_in($dest, inet_aton('203.0.113.1'))) or die; +} +sub rtp { + my ($pt, $seq, $ts, $ssrc, $payload) = @_; + print("rtp in $pt $seq $ts $ssrc\n"); + return pack('CCnNN a*', 0x80, $pt, $seq, $ts, $ssrc, $payload); +} +sub rcv { + my ($sock, $port, $match) = @_; + my $p = ''; + alarm(1); + my $addr = $sock->recv($p, 65535, 0) or die; + alarm(0); + my ($hdr_mark, $pt, $seq, $ts, $ssrc, $payload) = unpack('CCnNN a*', $p); + print("rtp recv $pt $seq $ts $ssrc\n"); + like $p, $match, 'received packet matches'; + my @matches = $p =~ $match; + for my $m (@matches) { + if (length($m) == 2) { + ($m) = unpack('n', $m); + } + elsif (length($m) == 4) { + ($m) = unpack('N', $m); + } + } + return @matches; +} +sub escape { + return "\Q$_[0]\E"; +} +sub rtpm { + my ($pt, $seq, $ts, $ssrc, $payload) = @_; + print("rtp matcher $pt $seq $ts $ssrc\n"); + my $re = ''; + $re .= escape(pack('C', 0x80)); + $re .= escape(pack('C', $pt)); + $re .= $seq >= 0 ? escape(pack('n', $seq)) : '(..)'; + $re .= $ts >= 0 ? escape(pack('N', $ts)) : '(....)'; + $re .= $ssrc >= 0 ? escape(pack('N', $ssrc)) : '(....)'; + $re .= escape($payload); + return qr/^$re$/s; +} -$r = $c->req({command => 'ping'}); -ok $r->{result} eq 'pong', 'ping works, daemon operational'; +{ + my $r = $c->req({command => 'ping'}); + ok $r->{result} eq 'pong', 'ping works, daemon operational'; +} # SDP in/out tests, various ICE options @@ -1034,6 +1102,145 @@ SDP +# RTP sequencing tests + +my ($sock_a, $sock_b) = new_call([qw(198.51.100.1 2010)], [qw(198.51.100.3 2012)]); + +my ($port_a) = offer('two codecs, no transcoding', { ICE => 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], + codec => { transcode => ['PCMA'] }}, < ['origin'] }, <str); abort(); } - printf("test ok: %s:%i\n", file, line); + printf("test ok: %s:%i\n\n", file, line); g_string_free(s, TRUE); } @@ -200,7 +200,17 @@ static void __packet_seq_ts(const char *file, int line, struct call_media *media memcpy(mp.payload.s, pl.s, pl.len); mp.raw.s = packet; mp.raw.len = packet_len; + printf("send RTP SSRC %x seq %u TS %u PT %u\n", (unsigned int) ssrc, + (unsigned int) rtp_seq, (unsigned int) rtp_ts, (unsigned int) pt_in); + printf("send packet contents: "); + for (int i = sizeof(struct rtp_header); i < mp.raw.len; i++) { + unsigned char cc = mp.raw.s[i]; + printf("\\x%02x", cc); + } + printf("\n"); + h->func(h, &mp); + if (pt_out == -1) { if (mp.packets_out.length != 0) { printf("test failed: %s:%i\n", file, line); @@ -222,7 +232,7 @@ static void __packet_seq_ts(const char *file, int line, struct call_media *media printf("received: %i\n", rtp->m_pt); abort(); } - printf("packet contents: "); + printf("recv packet contents: "); for (int i = sizeof(struct rtp_header); i < cp->s.len; i++) { unsigned char cc = cp->s.s[i]; printf("\\x%02x", cc); @@ -231,15 +241,14 @@ static void __packet_seq_ts(const char *file, int line, struct call_media *media uint32_t ts = ntohl(rtp->timestamp); uint16_t seq = ntohs(rtp->seq_num); uint32_t ssrc = ntohl(rtp->ssrc); - uint32_t ssrc_pt = ssrc ^ (pt_out & 0x7f); - ssrc_pt ^= (pt_in & 0x7f) << 8; /* XXX this is actually wrong and should be removed. it's a workaround for a bug */ - printf("RTP SSRC %x seq %u TS %u PT %u\n", (unsigned int) ssrc, + uint32_t ssrc_pt = ssrc; + printf("recv RTP SSRC %x seq %u TS %u PT %u\n", (unsigned int) ssrc, (unsigned int) seq, (unsigned int) ts, (unsigned int) rtp->m_pt); if (g_hash_table_contains(rtp_ts_ht, GUINT_TO_POINTER(ssrc_pt))) { uint32_t old_ts = GPOINTER_TO_UINT(g_hash_table_lookup(rtp_ts_ht, GUINT_TO_POINTER(ssrc_pt))); uint32_t diff = ts - old_ts; - printf("RTP TS diff: %u\n", (unsigned int) diff); + printf("recv RTP TS diff: %u\n", (unsigned int) diff); if (ts_exp != -1) assert(ts_exp == diff); } @@ -248,7 +257,8 @@ static void __packet_seq_ts(const char *file, int line, struct call_media *media uint32_t old_seq = GPOINTER_TO_UINT(g_hash_table_lookup(rtp_seq_ht, GUINT_TO_POINTER(ssrc_pt))); uint16_t diff = seq - old_seq; - printf("RTP seq diff: %u\n", (unsigned int) diff); + printf("recv RTP seq diff: %u (exp %u)\n", (unsigned int) diff, + (unsigned int) seq_diff_exp); assert(diff == seq_diff_exp); } g_hash_table_insert(rtp_seq_ht, GUINT_TO_POINTER(ssrc_pt), GUINT_TO_POINTER(seq)); @@ -259,7 +269,7 @@ static void __packet_seq_ts(const char *file, int line, struct call_media *media if (fatal && memcmp(pl_exp.s, cp->s.s, pl_exp.len)) abort(); } - printf("test ok: %s:%i\n", file, line); + printf("test ok: %s:%i\n\n", file, line); free(packet); } @@ -340,10 +350,10 @@ int main() { expect(A, send, "0/PCMU/8000 8/PCMA/8000"); expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); expect(B, send, "0/PCMU/8000 8/PCMA/8000"); - packet(A, 0, PCMU_payload, 0, PCMU_payload); - packet(B, 0, PCMU_payload, 0, PCMU_payload); - packet(A, 8, PCMA_payload, 8, PCMA_payload); - packet(B, 8, PCMA_payload, 8, PCMA_payload); + packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload); + packet_seq(B, 8, PCMA_payload, 160, 1, 8, PCMA_payload); end(); // plain with two offered and one answered @@ -381,8 +391,8 @@ int main() { expect(A, send, "0/PCMU/8000 8/PCMA/8000"); expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); expect(B, send, "8/PCMA/8000"); - packet(A, 0, PCMU_payload, 0, PCMU_payload); - packet(A, 8, PCMA_payload, 8, PCMA_payload); + packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload); packet(B, 8, PCMA_payload, 8, PCMA_payload); end(); @@ -403,10 +413,10 @@ int main() { expect(A, send, "0/PCMU/8000 8/PCMA/8000"); expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); expect(B, send, "0/PCMU/8000 8/PCMA/8000"); - packet(A, 0, PCMU_payload, 0, PCMU_payload); - packet(B, 0, PCMU_payload, 0, PCMU_payload); - packet(A, 8, PCMA_payload, 8, PCMA_payload); - packet(B, 8, PCMA_payload, 0, PCMU_payload); + packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload); + packet_seq(B, 8, PCMA_payload, 160, 1, 0, PCMU_payload); end(); // plain with two offered and two answered + always-transcode both ways @@ -427,10 +437,10 @@ int main() { expect(A, send, "0/PCMU/8000 8/PCMA/8000"); expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); expect(B, send, "0/PCMU/8000 8/PCMA/8000"); - packet(A, 0, PCMU_payload, 0, PCMU_payload); - packet(B, 0, PCMU_payload, 0, PCMU_payload); - packet(A, 8, PCMA_payload, 0, PCMU_payload); - packet(B, 8, PCMA_payload, 0, PCMU_payload); + packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(A, 8, PCMA_payload, 160, 1, 0, PCMU_payload); + packet_seq(B, 8, PCMA_payload, 160, 1, 0, PCMU_payload); end(); // add one codec to transcode @@ -450,8 +460,8 @@ int main() { expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); expect(B, send, "0/PCMU/8000 8/PCMA/8000"); packet(A, 0, PCMU_payload, 0, PCMU_payload); - packet(B, 0, PCMU_payload, 0, PCMU_payload); - packet(B, 8, PCMA_payload, 0, PCMU_payload); + packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); + packet_seq(B, 8, PCMA_payload, 160, 1, 0, PCMU_payload); end(); // add one codec to transcode, don't accept original offered codec @@ -490,8 +500,8 @@ int main() { expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); expect(B, send, "8/PCMA/8000"); packet(A, 0, PCMU_payload, 8, PCMA_payload); - packet(B, 8, PCMA_payload, 0, PCMU_payload); - packet(B, 0, PCMU_payload, 0, PCMU_payload); + packet_seq(B, 8, PCMA_payload, 0, 0, 0, PCMU_payload); + packet_seq(B, 0, PCMU_payload, 160, 1, 0, PCMU_payload); end(); #ifdef WITH_AMR_TESTS @@ -848,10 +858,10 @@ int main() { packet_seq_exp(A, 101, "\x08\x8a\x03\x20", 1000160, 205, 101, "\x08\x8a\x03\x20", 0); dtmf(""); // send some more audio - packet_seq_exp(A, 8, PCMA_payload, 1000960, 206, 8, PCMA_payload, 6); // expected seq is 200+6 for PT 8 + packet_seq(A, 8, PCMA_payload, 1000960, 206, 8, PCMA_payload); packet_seq(A, 8, PCMA_payload, 1001120, 207, 8, PCMA_payload); // start with marker - packet_seq_exp(A, 101 | 0x80, "\x05\x0a\x00\xa0", 1001280, 208, 101 | 0x80, "\x05\x0a\x00\xa0", 3); // expected seq is 205+3 for PT 101 + packet_seq(A, 101 | 0x80, "\x05\x0a\x00\xa0", 1001280, 208, 101 | 0x80, "\x05\x0a\x00\xa0"); dtmf(""); // continuous event with increasing length packet_seq(A, 101, "\x05\x0a\x01\x40", 1001280, 209, 101, "\x05\x0a\x01\x40"); @@ -864,7 +874,7 @@ int main() { packet_seq_exp(A, 101, "\x05\x8a\x02\x80", 1001280, 211, 101, "\x05\x8a\x02\x80", 0); dtmf(""); // final audio RTP test - packet_seq_exp(A, 8, PCMA_payload, 1000960, 212, 8, PCMA_payload, 5); // expected seq is 207+5 for PT 8 + packet_seq(A, 8, PCMA_payload, 1000960, 212, 8, PCMA_payload); end(); // DTMF passthrough w/ transcoding @@ -901,10 +911,10 @@ int main() { packet_seq_exp(A, 101, "\x08\x8a\x03\x20", 1000160, 205, 101, "\x08\x8a\x03\x20", 0); dtmf(""); // send some more audio - packet_seq_exp(A, 8, PCMA_payload, 1000960, 206, 0, PCMU_payload, 6); // expected seq is 200+6 for PT 8 + packet_seq(A, 8, PCMA_payload, 1000960, 206, 0, PCMU_payload); packet_seq(A, 8, PCMA_payload, 1001120, 207, 0, PCMU_payload); // start with marker - packet_seq_exp(A, 101 | 0x80, "\x05\x0a\x00\xa0", 1001280, 208, 101 | 0x80, "\x05\x0a\x00\xa0", 3); // expected seq is 205+3 for PT 101 + packet_seq(A, 101 | 0x80, "\x05\x0a\x00\xa0", 1001280, 208, 101 | 0x80, "\x05\x0a\x00\xa0"); dtmf(""); // continuous event with increasing length packet_seq(A, 101, "\x05\x0a\x01\x40", 1001280, 209, 101, "\x05\x0a\x01\x40"); @@ -917,7 +927,7 @@ int main() { packet_seq_exp(A, 101, "\x05\x8a\x02\x80", 1001280, 211, 101, "\x05\x8a\x02\x80", 0); dtmf(""); // final audio RTP test - packet_seq_exp(A, 8, PCMA_payload, 1000960, 212, 0, PCMU_payload, 5); // expected seq is 207+5 for PT 8 + packet_seq(A, 8, PCMA_payload, 1000960, 212, 0, PCMU_payload); end(); // plain DTMF passthrough w/o transcoding w/ implicit primary payload type @@ -953,10 +963,10 @@ int main() { packet_seq_exp(A, 101, "\x08\x8a\x03\x20", 1000160, 205, 101, "\x08\x8a\x03\x20", 0); dtmf(""); // send some more audio - packet_seq_exp(A, 0, PCMU_payload, 1000960, 206, 0, PCMU_payload, 6); // expected seq is 200+6 for PT 8 + packet_seq(A, 0, PCMU_payload, 1000960, 206, 0, PCMU_payload); packet_seq(A, 0, PCMU_payload, 1001120, 207, 0, PCMU_payload); // start with marker - packet_seq_exp(A, 101 | 0x80, "\x05\x0a\x00\xa0", 1001280, 208, 101 | 0x80, "\x05\x0a\x00\xa0", 3); // expected seq is 205+3 for PT 101 + packet_seq(A, 101 | 0x80, "\x05\x0a\x00\xa0", 1001280, 208, 101 | 0x80, "\x05\x0a\x00\xa0"); dtmf(""); // continuous event with increasing length packet_seq(A, 101, "\x05\x0a\x01\x40", 1001280, 209, 101, "\x05\x0a\x01\x40"); @@ -969,7 +979,7 @@ int main() { packet_seq_exp(A, 101, "\x05\x8a\x02\x80", 1001280, 211, 101, "\x05\x8a\x02\x80", 0); dtmf(""); // final audio RTP test - packet_seq_exp(A, 0, PCMU_payload, 1000960, 212, 0, PCMU_payload, 5); // expected seq is 207+5 for PT 8 + packet_seq(A, 0, PCMU_payload, 1000960, 212, 0, PCMU_payload); end(); // plain DTMF passthrough w/o transcoding - blocking @@ -1005,7 +1015,7 @@ int main() { packet_seq_exp(A, 101, "\x08\x8a\x03\x20", 1000160, 205, 101, "\x08\x8a\x03\x20", 0); dtmf(""); // send some more audio - packet_seq_exp(A, 8, PCMA_payload, 1000960, 206, 8, PCMA_payload, 6); // expected seq is 200+6 for PT 8 + packet_seq(A, 8, PCMA_payload, 1000960, 206, 8, PCMA_payload); packet_seq(A, 8, PCMA_payload, 1001120, 207, 8, PCMA_payload); // enable blocking call.block_dtmf = 1; @@ -1023,21 +1033,21 @@ int main() { packet_seq_exp(A, 101, "\x05\x8a\x02\x80", 1001280, 211, -1, "", 0); dtmf(""); // final audio RTP test - packet_seq_exp(A, 8, PCMA_payload, 1000960, 212, 8, PCMA_payload, 5); // expected seq is 207+5 for PT 8 - packet_seq_exp(A, 8, PCMA_payload, 1001120, 213, 8, PCMA_payload, 1); + packet_seq_exp(A, 8, PCMA_payload, 1000960, 212, 8, PCMA_payload, 5); // DTMF packets appear lost + packet_seq(A, 8, PCMA_payload, 1001120, 213, 8, PCMA_payload); // media blocking ml_A.block_media = 1; packet_seq_exp(A, 8, PCMA_payload, 1001280, 214, -1, "", 0); packet_seq_exp(A, 8, PCMA_payload, 1001440, 215, -1, "", 0); ml_A.block_media = 0; - packet_seq_exp(A, 8, PCMA_payload, 1001600, 216, 8, PCMA_payload, 3); + packet_seq_exp(A, 8, PCMA_payload, 1001600, 216, 8, PCMA_payload, 3); // media packets appear lost call.block_media = 1; packet_seq_exp(A, 8, PCMA_payload, 1001760, 217, -1, "", 0); packet_seq_exp(A, 8, PCMA_payload, 1001920, 218, -1, "", 0); call.block_media = 0; - packet_seq_exp(A, 8, PCMA_payload, 1002080, 219, 8, PCMA_payload, 3); + packet_seq_exp(A, 8, PCMA_payload, 1002080, 219, 8, PCMA_payload, 3); // media packets appear lost ml_B.block_media = 1; - packet_seq_exp(A, 8, PCMA_payload, 1002240, 220, 8, PCMA_payload, 1); + packet_seq(A, 8, PCMA_payload, 1002240, 220, 8, PCMA_payload); end(); // DTMF passthrough w/ transcoding - blocking @@ -1074,7 +1084,7 @@ int main() { packet_seq_exp(A, 101, "\x08\x8a\x03\x20", 1000160, 205, 101, "\x08\x8a\x03\x20", 0); dtmf(""); // send some more audio - packet_seq_exp(A, 8, PCMA_payload, 1000960, 206, 0, PCMU_payload, 6); // expected seq is 200+6 for PT 8 + packet_seq(A, 8, PCMA_payload, 1000960, 206, 0, PCMU_payload); packet_seq(A, 8, PCMA_payload, 1001120, 207, 0, PCMU_payload); // enable blocking call.block_dtmf = 1; @@ -1092,8 +1102,8 @@ int main() { packet_seq_exp(A, 101, "\x05\x8a\x02\x80", 1001280, 211, -1, "", 0); dtmf(""); // final audio RTP test - packet_seq_exp(A, 8, PCMA_payload, 1000960, 212, 0, PCMU_payload, 1); // expected seq is 207+1 for PT 8 - packet_seq_exp(A, 8, PCMA_payload, 1001120, 213, 0, PCMU_payload, 1); + packet_seq_exp(A, 8, PCMA_payload, 1000960, 212, 0, PCMU_payload, 5); // DTMF packets appear lost + packet_seq(A, 8, PCMA_payload, 1001120, 213, 0, PCMU_payload); // media blocking ml_A.block_media = 1; packet_seq_exp(A, 8, PCMA_payload, 1001280, 214, -1, "", 0); @@ -1142,7 +1152,7 @@ int main() { packet_seq_exp(A, 101, "\x08\x8a\x03\x20", 1000160, 205, 101, "\x08\x8a\x03\x20", 0); dtmf(""); // send some more audio - packet_seq_exp(A, 0, PCMU_payload, 1000960, 206, 0, PCMU_payload, 6); // expected seq is 200+6 for PT 8 + packet_seq(A, 0, PCMU_payload, 1000960, 206, 0, PCMU_payload); packet_seq(A, 0, PCMU_payload, 1001120, 207, 0, PCMU_payload); // enable blocking call.block_dtmf = 1; @@ -1160,21 +1170,21 @@ int main() { packet_seq_exp(A, 101, "\x05\x8a\x02\x80", 1001280, 211, -1, "", 0); dtmf(""); // final audio RTP test - packet_seq_exp(A, 0, PCMU_payload, 1000960, 212, 0, PCMU_payload, 5); // expected seq is 207+5 for PT 8 - packet_seq_exp(A, 0, PCMU_payload, 1001120, 213, 0, PCMU_payload, 1); + packet_seq_exp(A, 0, PCMU_payload, 1000960, 212, 0, PCMU_payload, 5); // DTMF packets appear lost + packet_seq(A, 0, PCMU_payload, 1001120, 213, 0, PCMU_payload); // media blocking ml_A.block_media = 1; packet_seq_exp(A, 0, PCMU_payload, 1001280, 214, -1, "", 0); packet_seq_exp(A, 0, PCMU_payload, 1001440, 215, -1, "", 0); ml_A.block_media = 0; - packet_seq_exp(A, 0, PCMU_payload, 1001600, 216, 0, PCMU_payload, 3); + packet_seq_exp(A, 0, PCMU_payload, 1001600, 216, 0, PCMU_payload, 3); // media packets appear lost call.block_media = 1; packet_seq_exp(A, 0, PCMU_payload, 1001760, 217, -1, "", 0); packet_seq_exp(A, 0, PCMU_payload, 1001920, 218, -1, "", 0); call.block_media = 0; - packet_seq_exp(A, 0, PCMU_payload, 1002080, 219, 0, PCMU_payload, 3); + packet_seq_exp(A, 0, PCMU_payload, 1002080, 219, 0, PCMU_payload, 3); // media packets appear lost ml_B.block_media = 1; - packet_seq_exp(A, 0, PCMU_payload, 1002240, 220, 0, PCMU_payload, 1); + packet_seq(A, 0, PCMU_payload, 1002240, 220, 0, PCMU_payload); end(); return 0;