TT#98901 handle AMR SID DTX

Change-Id: I418c43e0cef2a70143010235988523f195a3bf5a
pull/1116/head
Richard Fuchs 5 years ago
parent fa64e2261e
commit bae79d3fef

@ -3014,6 +3014,7 @@ static void monologue_stop(struct call_monologue *ml) {
for (GList *l = ml->medias.head; l; l = l->next) {
struct call_media *m = l->data;
t38_gateway_stop(m->t38_gateway);
codec_handlers_stop(&m->codec_handlers_store);
}
}

@ -15,6 +15,7 @@
#include "t38.h"
#include "media_player.h"
#include "timerthread.h"
#include "log_funcs.h"
@ -63,6 +64,24 @@ static GList *__delete_receiver_codec(struct call_media *receiver, GList *link)
struct codec_ssrc_handler;
struct dtx_buffer {
struct timerthread_queue ttq;
mutex_t lock;
struct codec_ssrc_handler *csh;
int ptime; // ms per packet
int tspp; // timestamp increment per packet
struct call *call;
unsigned long ts;
};
struct dtx_entry {
struct timerthread_queue_entry ttq_entry;
struct transcode_packet *packet;
struct media_packet mp;
unsigned long ts;
};
struct codec_ssrc_handler {
struct ssrc_entry h; // must be first
struct codec_handler *handler;
@ -77,6 +96,7 @@ struct codec_ssrc_handler {
struct timeval first_send;
unsigned long first_send_ts;
GString *sample_buffer;
struct dtx_buffer *dtx_buffer;
// DTMF DSP stuff
dtmf_rx_state_t *dtmf_dsp;
@ -1380,7 +1400,7 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa
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",
ilog(LOG_DEBUG, "Processing RTP packet: seq %u, TS %lu",
packet->p.seq, packet->ts);
if (seq_ret == 1) {
@ -1834,6 +1854,153 @@ static int codec_decoder_event(enum codec_event event, void *ptr, void *data) {
return 0;
}
static void __dtx_add_callback(struct dtx_buffer *dtxb, const struct timeval *base, unsigned int offset,
const struct media_packet *mp, unsigned long ts, int seq_add)
{
struct dtx_entry *dtxe = g_slice_alloc0(sizeof(*dtxe));
dtxe->ttq_entry.when = *base;
timeval_add_usec(&dtxe->ttq_entry.when, offset);
dtxe->ts = ts;
media_packet_copy(&dtxe->mp, mp);
dtxe->mp.rtp->seq_num += htons(seq_add);
timerthread_queue_push(&dtxb->ttq, &dtxe->ttq_entry);
}
static void __dtx_entry_free(void *p) {
struct dtx_entry *dtxe = p;
if (dtxe->packet)
__transcode_packet_free(dtxe->packet);
media_packet_release(&dtxe->mp);
g_slice_free1(sizeof(*dtxe), dtxe);
}
static void __dtx_send_later(struct timerthread_queue *ttq, void *p) {
struct dtx_buffer *dtxb = (void *) ttq;
struct dtx_entry *dtxe = p;
struct transcode_packet *packet = dtxe->packet;
struct media_packet *mp = &dtxe->mp;
struct packet_stream *ps = mp->stream;
struct packet_stream *sink = ps->rtp_sink;
int ret = 0;
mutex_lock(&dtxb->lock);
struct codec_ssrc_handler *ch = dtxb->csh ? obj_get(&dtxb->csh->h) : NULL;
struct call *call = dtxb->call ? obj_get(dtxb->call) : NULL;
mutex_unlock(&dtxb->lock);
if (!call)
goto out; // shut down
log_info_stream_fd(mp->sfd);
rwlock_lock_r(&call->master_lock);
__ssrc_lock_both(mp);
if (packet) {
ilog(LOG_DEBUG, "Decoding DTX-buffered RTP packet (TS %lu) now", packet->ts);
ret = decoder_input_data(ch->decoder, packet->payload, packet->ts,
ch->handler->packet_decoded, ch, &dtxe->mp);
mp->ssrc_out->parent->seq_diff--;
if (ret)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Decoder error while processing buffered RTP packet");
}
else {
unsigned long dtxe_ts = dtxe->ts;
mutex_lock(&dtxb->lock);
unsigned long dtxb_ts = dtxb->ts;
if (dtxe_ts == dtxb_ts) {
ilog(LOG_DEBUG, "RTP media for TS %lu+ missing, triggering DTX",
dtxe_ts);
dtxb_ts += dtxb->tspp;
dtxe_ts = dtxb_ts;
dtxb->ts = dtxb_ts;
mutex_unlock(&dtxb->lock);
ret = decoder_lost_packet(ch->decoder, dtxe_ts,
ch->handler->packet_decoded, ch, &dtxe->mp);
if (ret)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Decoder error handling DTX/lost packet");
__dtx_add_callback(dtxb, &dtxe->ttq_entry.when, dtxb->ptime * 1000, mp, dtxe_ts, 0);
}
else
mutex_unlock(&dtxb->lock);
}
__ssrc_unlock_both(mp);
if (mp->packets_out.length && ret == 0) {
if (ps->handler && media_packet_encrypt(ps->handler->out->rtp_crypt, sink, mp))
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error encrypting buffered RTP media");
mutex_lock(&sink->out_lock);
if (media_socket_dequeue(mp, sink))
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error sending buffered media to RTP sink");
mutex_unlock(&sink->out_lock);
}
rwlock_unlock_r(&call->master_lock);
obj_put(call);
obj_put(&ch->h);
out:
__dtx_entry_free(dtxe);
log_info_clear();
}
static void __dtx_shutdown(struct dtx_buffer *dtxb) {
if (dtxb->csh)
obj_put(&dtxb->csh->h);
dtxb->csh = NULL;
if (dtxb->call)
obj_put(dtxb->call);
dtxb->call = NULL;
}
static void __dtx_free(void *p) {
struct dtx_buffer *dtxb = p;
ilog(LOG_DEBUG, "__dtx_free");
__dtx_shutdown(dtxb);
mutex_destroy(&dtxb->lock);
}
static void __dtx_setup(struct codec_ssrc_handler *ch) {
if (!ch->handler->source_pt.codec_def->packet_lost || ch->dtx_buffer)
return;
if (!rtpe_config.dtx_delay)
return;
struct dtx_buffer *dtx =
ch->dtx_buffer = timerthread_queue_new("dtx_buffer", sizeof(*ch->dtx_buffer),
&codec_timers_thread, NULL, __dtx_send_later, __dtx_free, __dtx_entry_free);
dtx->csh = obj_get(&ch->h);
dtx->call = obj_get(ch->handler->media->call);
mutex_init(&dtx->lock);
dtx->ptime = ch->ptime;
if (!dtx->ptime)
dtx->ptime = 20; // XXX ?
dtx->tspp = dtx->ptime * ch->handler->source_pt.clock_rate / 1000;
}
void __ssrc_handler_stop(void *p) {
struct codec_ssrc_handler *ch = p;
if (ch->dtx_buffer) {
mutex_lock(&ch->dtx_buffer->lock);
__dtx_shutdown(ch->dtx_buffer);
mutex_unlock(&ch->dtx_buffer->lock);
obj_put(&ch->dtx_buffer->ttq.tt_obj);
ch->dtx_buffer = NULL;
}
}
void codec_handlers_stop(GQueue *q) {
for (GList *l = q->head; l; l = l->next) {
struct codec_handler *h = l->data;
ssrc_hash_foreach(h->ssrc_hash, __ssrc_handler_stop);
}
}
static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) {
struct codec_handler *h = p;
@ -1894,6 +2061,8 @@ static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) {
ch->bytes_per_packet = (ch->encoder->samples_per_packet ? : ch->encoder->samples_per_frame)
* h->dest_pt.codec_def->bits_per_sample / 8;
__dtx_setup(ch);
ilog(LOG_DEBUG, "Encoder created with clockrate %i, %i channels, using sample format %i "
"(ptime %i for %i samples per frame and %i samples (%i bytes) per packet, bitrate %i)",
ch->encoder_format.clockrate, ch->encoder_format.channels, ch->encoder_format.format,
@ -1930,6 +2099,8 @@ static void __free_ssrc_handler(void *chp) {
dtmf_rx_free(ch->dtmf_dsp);
resample_shutdown(&ch->dtmf_resampler);
g_queue_clear_full(&ch->dtmf_events, dtmf_event_free);
if (ch->dtx_buffer)
obj_put(&ch->dtx_buffer->ttq.tt_obj);
}
static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) {
@ -2116,11 +2287,43 @@ static int packet_decoded_direct(decoder_t *decoder, AVFrame *frame, void *u1, v
static int packet_decode(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp)
{
int ret = 0;
if (!ch->first_ts)
ch->first_ts = packet->ts;
int ret = decoder_input_data(ch->decoder, packet->payload, packet->ts, ch->handler->packet_decoded, ch, mp);
//mp->iter_in++;
mp->ssrc_out->parent->seq_diff--;
if (ch->dtx_buffer && mp->sfd && mp->ssrc_in && mp->ssrc_out) {
ilog(LOG_DEBUG, "Adding packet to DTX buffer");
struct dtx_buffer *dtxb = ch->dtx_buffer;
unsigned long ts = packet->ts;
mutex_lock(&dtxb->lock);
if (ts != dtxb->ts)
dtxb->ts = ts;
mutex_unlock(&dtxb->lock);
struct dtx_entry *dtxe = g_slice_alloc0(sizeof(*dtxe));
dtxe->ttq_entry.when = rtpe_now;
timeval_add_usec(&dtxe->ttq_entry.when, rtpe_config.dtx_delay * 1000);
dtxe->packet = packet;
media_packet_copy(&dtxe->mp, mp);
timerthread_queue_push(&dtxb->ttq, &dtxe->ttq_entry);
// packet now consumed
packet = NULL;
__dtx_add_callback(dtxb, &rtpe_now, (rtpe_config.dtx_delay + dtxb->ptime) * 1000, mp, ts, 1);
ret = 1;
}
else {
ilog(LOG_DEBUG, "Decoding RTP packet now");
ret = decoder_input_data(ch->decoder, packet->payload, packet->ts, ch->handler->packet_decoded,
ch, mp);
ret = ret ? -1 : 0;
mp->ssrc_out->parent->seq_diff--;
}
return ret;
}

@ -459,6 +459,9 @@ static void options(int *argc, char ***argv) {
{ "https-cert", 0,0, G_OPTION_ARG_STRING, &rtpe_config.https_cert,"Certificate for HTTPS and WSS","FILE"},
{ "https-key", 0,0, G_OPTION_ARG_STRING, &rtpe_config.https_key, "Private key for HTTPS and WSS","FILE"},
{ "http-threads", 0,0, G_OPTION_ARG_INT, &rtpe_config.http_threads,"Number of worker threads for HTTP and WS","INT"},
#ifdef WITH_TRANSCODING
{ "dtx-delay", 0,0, G_OPTION_ARG_INT, &rtpe_config.dtx_delay, "Delay in milliseconds to trigger DTX handling","INT"},
#endif
{ NULL, }
};

@ -726,6 +726,18 @@ Number of worker threads for HTTP/HTTPS/WS/WSS. If not specified, then the same
number as given under B<num-threads> will be used. If no HTTP listeners are
enabled, then no threads are created.
=item B<--dtx-delay=>I<INT>
Processing delay in milliseconds to handle discontinuous transmission (DTX) or
other transmission gaps for codecs that support it (currently only AMR and
AMR-WB). Defaults to zero (disabled) and applicable to transcoded audio streams
only. When enabled, delays processing of received packets for the specified
time (much like a jitter buffer) in order to trigger DTX handling when a
transmission gap occurs. The decoder is then instructed to fill in the missing
time during a transmission gap, for example by generating comfort noise. The
delay should be configured to just slightly more than the expected incoming
jitter.
=back
=head1 INTERFACES

@ -100,6 +100,7 @@ void codec_decoder_skip_pts(struct codec_ssrc_handler *ch, uint64_t);
uint64_t codec_decoder_unskip_pts(struct codec_ssrc_handler *ch);
void codec_tracker_init(struct call_media *);
void codec_tracker_finish(struct call_media *);
void codec_handlers_stop(GQueue *);
#else
@ -108,6 +109,7 @@ INLINE void codec_handlers_update(struct call_media *receiver, struct call_media
INLINE void codec_handler_free(struct codec_handler **handler) { }
INLINE void codec_tracker_init(struct call_media *m) { }
INLINE void codec_tracker_finish(struct call_media *m) { }
INLINE void codec_handlers_stop(GQueue *q) { }
#endif

@ -106,6 +106,7 @@ struct rtpengine_config {
char *https_cert;
char *https_key;
int http_threads;
int dtx_delay;
};

@ -65,6 +65,8 @@ static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out);
static int format_cmp_ignore(const struct rtp_payload_type *, const struct rtp_payload_type *);
static int amr_packet_lost(decoder_t *, GQueue *);
@ -370,6 +372,7 @@ static codec_def_t __codec_defs[] = {
.codec_type = &codec_type_amr,
.set_enc_options = amr_set_enc_options,
.set_dec_options = amr_set_dec_options,
.packet_lost = amr_packet_lost,
},
{
.rtpname = "AMR-WB",
@ -387,6 +390,7 @@ static codec_def_t __codec_defs[] = {
.codec_type = &codec_type_amr,
.set_enc_options = amr_set_enc_options,
.set_dec_options = amr_set_dec_options,
.packet_lost = amr_packet_lost,
},
{
.rtpname = "telephone-event",
@ -1887,7 +1891,7 @@ static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
unsigned int bits = dec->codec_options.amr.bits_per_frame[ft];
// AMR encoder expects an octet aligned TOC byte plus the payload
// AMR decoder expects an octet aligned TOC byte plus the payload
unsigned char frame_buf[(bits + 7) / 8 + 1 + 1];
str frame = STR_CONST_INIT_BUF(frame_buf);
str_shift(&frame, 1);
@ -2081,6 +2085,15 @@ static int packetizer_amr(AVPacket *pkt, GString *buf, str *output, encoder_t *e
return 0;
}
static int amr_packet_lost(decoder_t *dec, GQueue *out) {
ilog(LOG_DEBUG, "pushing empty/lost frame to AMR decoder");
unsigned char frame_buf[1];
frame_buf[0] = 0xf << 3; // no data
str frame = STR_CONST_INIT_BUF(frame_buf);
if (avc_decoder_input(dec, &frame, out))
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Error while writing 'no data' frame to AMR decoder");
return 0;
}

@ -36,6 +36,168 @@ my ($sock_a, $sock_b, $sock_c, $sock_d, $port_a, $port_b, $ssrc, $resp,
if (0) {
# AMR SID, needs --cn-delay=..
($sock_a, $sock_b) = new_call([qw(198.51.100.10 4024)], [qw(198.51.100.10 4026)]);
($port_a) = offer('AMR SID',
{ ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'],
'set' => ['AMR-WB/16000/1/23850'] } }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.10
s=tester
t=0 0
m=audio 4024 RTP/AVP 96
c=IN IP4 198.51.100.10
a=rtpmap:96 AMR-WB/16000
a=fmtp:96 octet-align=1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 96 8
c=IN IP4 203.0.113.1
a=rtpmap:96 AMR-WB/16000
a=rtpmap:8 PCMA/8000
a=fmtp:96 octet-align=1
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('AMR-WB -> PCM CMR',
{ ICE => 'remove', replace => ['origin'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.10
s=tester
t=0 0
m=audio 4026 RTP/AVP 8
c=IN IP4 198.51.100.10
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 96
c=IN IP4 203.0.113.1
a=rtpmap:96 AMR-WB/16000
a=fmtp:96 octet-align=1
a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(96, 2000, 4000, 0x5678, "\xf0\x1c\xd0\x46\x09\xa1\xf1\x73\x02\x71\x71\x00\x0a\x16\x87\x74\xea\x6a\x8c\x06\x67\x66\xec\xf5\x67\x6c\x54\x6d\x45\x4c\x7c\x59\x8d\x7c\x55\xc4\x6c\x50"));
Time::HiRes::usleep(20000); # 20 ms
snd($sock_a, $port_b, rtp(96, 2001, 4240, 0x5678, "\xf0\x1c\xe0\x92\x30\xf3\xf4\xff\x3d\x23\xdb\x6b\x59\x4f\xd5\x12\xad\xff\x5b\xf8\x88\x53\x85\x74\x19\x6d\x65\x63\x6e\x94\xbb\x5b\x9f\x7d\x97\x3c\x28\xe8"));
($ssrc) = rcv($sock_b, $port_a, rtpm(8, 2000, 4000, -1, "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\xd5\x55\xd4\xd4\xd7\xd4\xd7\xd5\xd0\x52\x72\x64\x66\x66\x66\x66\x67\x67\x67\x65\x7a\x79\x7c\x72\x70\x77\x75\x4e\x43\x47\x5b\x5c\x5d\x55\x5d\x71\x7e\x7b\x79\x7e\x7c\x7d\x73\x70\x77\x4b\x4c\x45\xc1\xf0\xf1\xfd\xf8\xfa\xe5\xe7\xe6\xe6\xe1\xe6\xe0\xe0\xe1\xe0\xe3\xe2\xe2\xe2\xed\xec\xef\xec\xec\xec\xed\xe2\xe3\xe3\xe1\xe1\xe7\xe7\xe7\xe5\xe5\xfa\xfa\xfb\xfb\xfb\xff\xfc\xf2\xf3\xf1\xf6\xf7\xf4\xf5\xcb\xce\xcd\xc3\xc1\xc7\xd8\xd9\xdf\xdc\xd3\xd3\xd7\x54\x51\x53\x5c\x5c\x5f\x5e\x58\x5a\x44"));
snd($sock_a, $port_b, rtp(96, 2002, 4560, 0x5678, "\xf0\x1c\x41\x42\x00\xd9\xd7\x64\x3c\xb0\x51\xe7\x1f\x95\x56\x3b\x34\x76\x35\x73\x46\x32\x16\x72\x67\xc4\x54\x16\x02\x64\x30\x36\x34\x18\xba\x14\xce\xd8"));
rcv($sock_b, $port_a, rtpm(8, 2001, 4160, $ssrc, "\x47\x47\x40\x43\x4c\x4f\x48\x49\x4f\x4d\x42\x42\x42\x43\x41\x41\x41\x40\x40\x40\x43\x4d\x4f\x49\x48\x75\x74\x74\x77\x77\x76\x76\x76\x76\x76\x71\x71\x70\x70\x70\x70\x70\x73\x72\x73\x72\x72\x72\x72\x73\x70\x70\x71\x71\x71\x71\x76\x76\x76\x77\x77\x77\x74\x74\x74\x75\x75\x4a\x4b\x48\x48\x4b\x4b\x4b\x48\x48\x48\x49\x49\x49\x49\x4f\x4f\x4c\x4c\x4d\x43\x40\x41\x46\x47\x45\x5a\x5b\x58\x59\x5e\x5f\x5c\x5d\x52\x52\x53\x53\x53\x53\x53\x53\x53\x50\x53\x50\x50\x51\x51\x51\x56\x56\x57\x54\x54\xd5\xd5\xd4\xd7\xd7\xd6\xd1\xd0\xd0\xd3\xd2\xdd\xdd\xdc\xdf\xdf\xde\xde\xd9\xd9\xd9\xd9\xd9\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8"));
snd($sock_a, $port_b, rtp(96, 2003, 4880, 0x5678, "\xf0\x1c\x41\x46\x30\xff\xf7\xfc\x31\x15\x57\x3b\x0a\x1e\x44\xcd\x5e\x0e\xa7\xe4\x3a\x1b\xb5\x7b\x38\x2a\x90\x13\x08\xf3\x5f\xaa\xba\x57\xb0\x30\xd3\xe8"));
rcv($sock_b, $port_a, rtpm(8, 2002, 4320, $ssrc, "\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xde\xde\xde\xde\xde\xde\xde\xdf\xdf\xdf\xdf\xdf\xdf\xdc\xdc\xdc\xdc\xdd\xdd\xdd\xdd\xd2\xd2\xd2\xd2\xd2\xd2\xd3\xd3\xd3\xd3\xd3\xd0\xd0\xd0\xd0\xd0\xd0\xd1\xd1\xd1\xd1\xd1\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd7\xd7\xd7\xd7\xd7\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54"));
# no snd, trigger DTX
rcv($sock_b, $port_a, rtpm(8, 2003, 4480, $ssrc, "\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x57\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x55\x55\x55\x55\x55\x55\x54\x54\x54\x54\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\xd5"));
rcv($sock_b, $port_a, rtpm(8, 2004, 4640, $ssrc, "\xd5\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\xd5\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\xd5\xd5\xd5\x55\xd5\x55\x55\xd5\xd5\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55"));
rcv($sock_b, $port_a, rtpm(8, 2005, 4800, $ssrc, "\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55"));
# continue AMR
snd($sock_a, $port_b, rtp(96, 2004, 6160, 0x5678, "\xf0\x1c\x41\x46\x30\xff\xf7\xfc\x31\x15\x57\x3b\x0a\x1e\x44\xcd\x5e\x0e\xa7\xe4\x3a\x1b\xb5\x7b\x38\x2a\x90\x13\x08\xf3\x5f\xaa\xba\x57\xb0\x30\xd3\xe8"));
rcv($sock_b, $port_a, rtpm(8, 2006, 4960, $ssrc, "\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55"));
snd($sock_a, $port_b, rtp(96, 2005, 6480, 0x5678, "\xf0\x1c\x41\x46\x30\xff\xf7\xfc\x31\x15\x57\x3b\x0a\x1e\x44\xcd\x5e\x0e\xa7\xe4\x3a\x1b\xb5\x7b\x38\x2a\x90\x13\x08\xf3\x5f\xaa\xba\x57\xb0\x30\xd3\xe8"));
rcv($sock_b, $port_a, rtpm(8, 2007, 5120, $ssrc, "\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\x55\xd5\xd5\x55\xd5\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\xd5\xd5\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\x55\x55"));
rtpe_req('delete', 'AMR SID', { 'from-tag' => ft() });
($sock_a, $sock_b) = new_call([qw(198.51.100.10 4026)], [qw(198.51.100.10 4028)]);
($port_a) = offer('AMR SID TS gap',
{ ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'],
'set' => ['AMR-WB/16000/1/23850'] } }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.10
s=tester
t=0 0
m=audio 4026 RTP/AVP 96
c=IN IP4 198.51.100.10
a=rtpmap:96 AMR-WB/16000
a=fmtp:96 octet-align=1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 96 8
c=IN IP4 203.0.113.1
a=rtpmap:96 AMR-WB/16000
a=rtpmap:8 PCMA/8000
a=fmtp:96 octet-align=1
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('AMR-WB -> PCM CMR',
{ ICE => 'remove', replace => ['origin'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.10
s=tester
t=0 0
m=audio 4028 RTP/AVP 8
c=IN IP4 198.51.100.10
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 96
c=IN IP4 203.0.113.1
a=rtpmap:96 AMR-WB/16000
a=fmtp:96 octet-align=1
a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(96, 2000, 4000, 0x5678, "\xf0\x1c\xd0\x46\x09\xa1\xf1\x73\x02\x71\x71\x00\x0a\x16\x87\x74\xea\x6a\x8c\x06\x67\x66\xec\xf5\x67\x6c\x54\x6d\x45\x4c\x7c\x59\x8d\x7c\x55\xc4\x6c\x50"));
Time::HiRes::usleep(20000); # 20 ms
snd($sock_a, $port_b, rtp(96, 2001, 4240, 0x5678, "\xf0\x1c\xe0\x92\x30\xf3\xf4\xff\x3d\x23\xdb\x6b\x59\x4f\xd5\x12\xad\xff\x5b\xf8\x88\x53\x85\x74\x19\x6d\x65\x63\x6e\x94\xbb\x5b\x9f\x7d\x97\x3c\x28\xe8"));
($ssrc) = rcv($sock_b, $port_a, rtpm(8, 2000, 4000, -1, "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\xd5\x55\xd4\xd4\xd7\xd4\xd7\xd5\xd0\x52\x72\x64\x66\x66\x66\x66\x67\x67\x67\x65\x7a\x79\x7c\x72\x70\x77\x75\x4e\x43\x47\x5b\x5c\x5d\x55\x5d\x71\x7e\x7b\x79\x7e\x7c\x7d\x73\x70\x77\x4b\x4c\x45\xc1\xf0\xf1\xfd\xf8\xfa\xe5\xe7\xe6\xe6\xe1\xe6\xe0\xe0\xe1\xe0\xe3\xe2\xe2\xe2\xed\xec\xef\xec\xec\xec\xed\xe2\xe3\xe3\xe1\xe1\xe7\xe7\xe7\xe5\xe5\xfa\xfa\xfb\xfb\xfb\xff\xfc\xf2\xf3\xf1\xf6\xf7\xf4\xf5\xcb\xce\xcd\xc3\xc1\xc7\xd8\xd9\xdf\xdc\xd3\xd3\xd7\x54\x51\x53\x5c\x5c\x5f\x5e\x58\x5a\x44"));
snd($sock_a, $port_b, rtp(96, 2002, 4560, 0x5678, "\xf0\x1c\x41\x42\x00\xd9\xd7\x64\x3c\xb0\x51\xe7\x1f\x95\x56\x3b\x34\x76\x35\x73\x46\x32\x16\x72\x67\xc4\x54\x16\x02\x64\x30\x36\x34\x18\xba\x14\xce\xd8"));
rcv($sock_b, $port_a, rtpm(8, 2001, 4160, $ssrc, "\x47\x47\x40\x43\x4c\x4f\x48\x49\x4f\x4d\x42\x42\x42\x43\x41\x41\x41\x40\x40\x40\x43\x4d\x4f\x49\x48\x75\x74\x74\x77\x77\x76\x76\x76\x76\x76\x71\x71\x70\x70\x70\x70\x70\x73\x72\x73\x72\x72\x72\x72\x73\x70\x70\x71\x71\x71\x71\x76\x76\x76\x77\x77\x77\x74\x74\x74\x75\x75\x4a\x4b\x48\x48\x4b\x4b\x4b\x48\x48\x48\x49\x49\x49\x49\x4f\x4f\x4c\x4c\x4d\x43\x40\x41\x46\x47\x45\x5a\x5b\x58\x59\x5e\x5f\x5c\x5d\x52\x52\x53\x53\x53\x53\x53\x53\x53\x50\x53\x50\x50\x51\x51\x51\x56\x56\x57\x54\x54\xd5\xd5\xd4\xd7\xd7\xd6\xd1\xd0\xd0\xd3\xd2\xdd\xdd\xdc\xdf\xdf\xde\xde\xd9\xd9\xd9\xd9\xd9\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8"));
snd($sock_a, $port_b, rtp(96, 2003, 4880, 0x5678, "\xf0\x1c\x41\x46\x30\xff\xf7\xfc\x31\x15\x57\x3b\x0a\x1e\x44\xcd\x5e\x0e\xa7\xe4\x3a\x1b\xb5\x7b\x38\x2a\x90\x13\x08\xf3\x5f\xaa\xba\x57\xb0\x30\xd3\xe8"));
rcv($sock_b, $port_a, rtpm(8, 2002, 4320, $ssrc, "\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xde\xde\xde\xde\xde\xde\xde\xdf\xdf\xdf\xdf\xdf\xdf\xdc\xdc\xdc\xdc\xdd\xdd\xdd\xdd\xd2\xd2\xd2\xd2\xd2\xd2\xd3\xd3\xd3\xd3\xd3\xd0\xd0\xd0\xd0\xd0\xd0\xd1\xd1\xd1\xd1\xd1\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd7\xd7\xd7\xd7\xd7\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54"));
# no snd, trigger DTX
rcv($sock_b, $port_a, rtpm(8, 2003, 4480, $ssrc, "\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x57\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x54\x55\x55\x55\x55\x55\x55\x54\x54\x54\x54\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\xd5"));
rcv($sock_b, $port_a, rtpm(8, 2004, 4640, $ssrc, "\xd5\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\xd5\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\xd5\xd5\xd5\x55\xd5\x55\x55\xd5\xd5\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55"));
rcv($sock_b, $port_a, rtpm(8, 2005, 4800, $ssrc, "\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55"));
# continue AMR
snd($sock_a, $port_b, rtp(96, 2004, 5200, 0x5678, "\xf0\x1c\x41\x46\x30\xff\xf7\xfc\x31\x15\x57\x3b\x0a\x1e\x44\xcd\x5e\x0e\xa7\xe4\x3a\x1b\xb5\x7b\x38\x2a\x90\x13\x08\xf3\x5f\xaa\xba\x57\xb0\x30\xd3\xe8"));
rcv($sock_b, $port_a, rtpm(8, 2006, 4960, $ssrc, "\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\xd5\xd5\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55"));
snd($sock_a, $port_b, rtp(96, 2005, 5520, 0x5678, "\xf0\x1c\x41\x46\x30\xff\xf7\xfc\x31\x15\x57\x3b\x0a\x1e\x44\xcd\x5e\x0e\xa7\xe4\x3a\x1b\xb5\x7b\x38\x2a\x90\x13\x08\xf3\x5f\xaa\xba\x57\xb0\x30\xd3\xe8"));
rcv($sock_b, $port_a, rtpm(8, 2007, 5120, $ssrc, "\xd5\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\x55\xd5\xd5\x55\xd5\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\xd5\xd5\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\x55\x55"));
rtpe_req('delete', 'AMR SID', { 'from-tag' => ft() });
done_testing;
exit;
}
if (0) {
# GH 1098
@ -1359,6 +1521,8 @@ rcv($sock_a, $port_b, rtpm(96, 1004, 4200, $ssrc, "\xf0\x14\x41\x00\x30\x44\x41\
}
new_call;
offer('DTMF-inject w tp-e', {

@ -318,6 +318,7 @@ int main(void) {
codeclib_init(0);
srandom(time(NULL));
statistics_init();
codecs_init();
// plain
start();

Loading…
Cancel
Save