Browse Source

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
changes/32/25932/7
Richard Fuchs 7 years ago
parent
commit
497c2d6a8d
9 changed files with 376 additions and 107 deletions
  1. +64
    -36
      daemon/codec.c
  2. +4
    -0
      daemon/ssrc.c
  3. +7
    -0
      include/ssrc.h
  4. +8
    -3
      lib/codeclib.c
  5. +8
    -1
      lib/codeclib.h
  6. +1
    -1
      recording-daemon/packet.c
  7. +2
    -1
      t/Makefile
  8. +222
    -15
      t/auto-daemon-tests.pl
  9. +60
    -50
      t/transcode-test.c

+ 64
- 36
daemon/codec.c View File

@ -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);
}


+ 4
- 0
daemon/ssrc.c View File

@ -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;


+ 7
- 0
include/ssrc.h View File

@ -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),


+ 8
- 3
lib/codeclib.c View File

@ -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;
}


+ 8
- 1
lib/codeclib.h View File

@ -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;


+ 1
- 1
recording-daemon/packet.c View File

@ -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


+ 2
- 1
t/Makefile View File

@ -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)


+ 222
- 15
t/auto-daemon-tests.pl View File

@ -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'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2010 RTP/AVP 0 8
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 8
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendrecv
a=rtcp:PORT
SDP
my ($port_b) = answer('two codecs, no transcoding', { ICE => 'remove', replace => ['origin'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2012 RTP/AVP 0 8
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 8
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1001, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(0, 1010, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1010, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(8, 1000, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1000, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(8, 1000, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1000, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(8, 1001, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1001, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(8, 1010, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1010, 3000, 0x1234, "\00" x 160));
($sock_a, $sock_b) = new_call([qw(198.51.100.1 2010)], [qw(198.51.100.3 2012)]);
($port_a) = offer('one codec with one for transcoding', { ICE => 'remove', replace => ['origin'],
codec => { transcode => ['PCMA'] }}, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2010 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 8
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('one codec with one for transcoding', { replace => ['origin'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2012 RTP/AVP 0 8
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1001, 3160, 0x1234, "\00" x 160));
snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1010, 4600, 0x1234, "\00" x 160));
snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x5678, "\00" x 160));
my ($seq, $ssrc) = rcv($sock_a, $port_b, rtpm(0, -1, 4000, -1, "\00" x 160));
snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x5678, "\00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq, 4000, $ssrc, "\00" x 160));
snd($sock_b, $port_a, rtp(0, 2001, 4000+160, 0x5678, "\00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+1, 4000+160, $ssrc, "\00" x 160));
snd($sock_b, $port_a, rtp(0, 2010, 4000+1600, 0x5678, "\00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+10, 4000+1600, $ssrc, "\00" x 160));
snd($sock_b, $port_a, rtp(8, 2011, 4000+160*11, 0x5678, "\00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+11, 4000+160*11, $ssrc, ")" x 160));
# #664 seq reset
snd($sock_b, $port_a, rtp(8, 62011, 4000+160*12, 0x5678, "\00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+12, 4000+160*12, $ssrc, ")" x 160));
snd($sock_b, $port_a, rtp(8, 62012, 4000+160*13, 0x5678, "\00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+13, 4000+160*13, $ssrc, ")" x 160));
snd($sock_b, $port_a, rtp(0, 62013, 4000+160*14, 0x5678, "\00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+14, 4000+160*14, $ssrc, "\00" x 160));
END {
if ($rtpe_pid) {
kill('INT', $rtpe_pid) or die;


+ 60
- 50
t/transcode-test.c View File

@ -130,7 +130,7 @@ static void __expect(const char *file, int line, GQueue *dumper, const char *cod
printf("received: %s\n", s->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;


Loading…
Cancel
Save