Merge branch 'master' into jb_disable

pull/1134/head^2
Balajee SV 4 years ago committed by GitHub
commit 9deedea1c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -54,6 +54,7 @@ the following additional features are available:
- Injection of DTMF events or PCM DTMF tones into running audio streams - Injection of DTMF events or PCM DTMF tones into running audio streams
- Playback of pre-recorded streams/announcements - Playback of pre-recorded streams/announcements
- Transcoding between T.38 and PCM (G.711 or other audio codecs) - Transcoding between T.38 and PCM (G.711 or other audio codecs)
- Silence detection and comfort noise (RFC 3389) payloads
*Rtpengine* does not (yet) support: *Rtpengine* does not (yet) support:
@ -811,6 +812,13 @@ Optionally included keys are:
injection via the `play DTMF` control message. See `play DTMF` below for additional injection via the `play DTMF` control message. See `play DTMF` below for additional
information. information.
- `generate RTCP`
With this flag set, received RTCP packets will not simply be passed through as
usual, but instead will be consumed, and instead *rtpengine* will generate its own
RTCP packets to send to the RTP peers. This flag will be effective for both
sides of a call.
* `replace` * `replace`
Similar to the `flags` list. Controls which parts of the SDP body should be rewritten. Similar to the `flags` list. Controls which parts of the SDP body should be rewritten.
@ -1226,6 +1234,19 @@ Optionally included keys are:
This option is only processed in `offer` messages and ignored otherwise. This option is only processed in `offer` messages and ignored otherwise.
* `consume`
Identical to `mask` but enables the transcoding engine even if no other transcoding
related options are given.
* `accept`
Similar to `mask` and `consume` but doesn't remove the codec from the list of
offered codecs. This means that a codec listed under `accept` will still be offered
to the remote peer, but if the remote peer rejects it, it will still be accepted
torwards the original offerer and then used for transcoding. It is a more selective
version of what the `always transcode` flag does.
* `set` * `set`
Contains a list of strings. This list makes it possible to set codec options Contains a list of strings. This list makes it possible to set codec options

@ -77,6 +77,8 @@ static struct timeval add_ongoing_calls_dur_in_interval(struct timeval *interval
struct timeval *interval_duration); struct timeval *interval_duration);
static void __call_free(void *p); static void __call_free(void *p);
static void __call_cleanup(struct call *c); static void __call_cleanup(struct call *c);
static void __monologue_stop(struct call_monologue *ml);
static void media_stop(struct call_media *m);
/* called with call->master_lock held in R */ /* called with call->master_lock held in R */
static int call_timer_delete_monologues(struct call *c) { static int call_timer_delete_monologues(struct call *c) {
@ -2122,6 +2124,11 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
ice_restart(other_media->ice_agent); ice_restart(other_media->ice_agent);
} }
if (flags && flags->generate_rtcp) {
MEDIA_SET(media, RTCP_GEN);
MEDIA_SET(other_media, RTCP_GEN);
}
__update_media_protocol(media, other_media, sp, flags); __update_media_protocol(media, other_media, sp, flags);
__update_media_id(media, other_media, sp, flags); __update_media_id(media, other_media, sp, flags);
__endpoint_loop_protect(sp, other_media); __endpoint_loop_protect(sp, other_media);
@ -2388,13 +2395,13 @@ static void __call_cleanup(struct call *c) {
for (GList *l = c->medias.head; l; l = l->next) { for (GList *l = c->medias.head; l; l = l->next) {
struct call_media *md = l->data; struct call_media *md = l->data;
ice_shutdown(&md->ice_agent); ice_shutdown(&md->ice_agent);
t38_gateway_stop(md->t38_gateway); media_stop(md);
t38_gateway_put(&md->t38_gateway); t38_gateway_put(&md->t38_gateway);
} }
for (GList *l = c->monologues.head; l; l = l->next) { for (GList *l = c->monologues.head; l; l = l->next) {
struct call_monologue *ml = l->data; struct call_monologue *ml = l->data;
media_player_stop(ml->player); __monologue_stop(ml);
media_player_put(&ml->player); media_player_put(&ml->player);
} }
@ -3042,13 +3049,18 @@ struct call_monologue *call_get_mono_dialogue(struct call *call, const str *from
static void monologue_stop(struct call_monologue *ml) { static void media_stop(struct call_media *m) {
t38_gateway_stop(m->t38_gateway);
codec_handlers_stop(&m->codec_handlers_store);
m->rtcp_timer.tv_sec = 0;
}
static void __monologue_stop(struct call_monologue *ml) {
media_player_stop(ml->player); media_player_stop(ml->player);
for (GList *l = ml->medias.head; l; l = l->next) { }
struct call_media *m = l->data; static void monologue_stop(struct call_monologue *ml) {
t38_gateway_stop(m->t38_gateway); __monologue_stop(ml);
codec_handlers_stop(&m->codec_handlers_store); for (GList *l = ml->medias.head; l; l = l->next)
} media_stop(l->data);
} }
@ -3115,6 +3127,8 @@ do_delete:
ng_call_stats(c, fromtag, totag, output, NULL); ng_call_stats(c, fromtag, totag, output, NULL);
monologue_stop(ml); monologue_stop(ml);
if (ml->active_dialogue && ml->active_dialogue->active_dialogue == ml)
monologue_stop(ml->active_dialogue);
if (delete_delay > 0) { if (delete_delay > 0) {
ilog(LOG_INFO, "Scheduling deletion of call branch '" STR_FORMAT_M "' " ilog(LOG_INFO, "Scheduling deletion of call branch '" STR_FORMAT_M "' "

@ -801,14 +801,18 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) {
case CSH_LOOKUP("full-rtcp-attribute"): case CSH_LOOKUP("full-rtcp-attribute"):
out->full_rtcp_attr = 1; out->full_rtcp_attr = 1;
break; break;
case CSH_LOOKUP("generate-RTCP"):
out->generate_rtcp = 1;
break;
case CSH_LOOKUP("loop-protect"): case CSH_LOOKUP("loop-protect"):
out->loop_protect = 1; out->loop_protect = 1;
break; break;
case CSH_LOOKUP("original-sendrecv"): case CSH_LOOKUP("original-sendrecv"):
out->original_sendrecv = 1; out->original_sendrecv = 1;
break; break;
case CSH_LOOKUP("always-transcode"): case CSH_LOOKUP("always-transcode"):;
out->always_transcode = 1; static const str str_all = STR_CONST_INIT("all");
call_ng_flags_str_ht(out, (str *) &str_all, &out->codec_accept);
break; break;
case CSH_LOOKUP("asymmetric-codecs"): case CSH_LOOKUP("asymmetric-codecs"):
out->asymmetric_codecs = 1; out->asymmetric_codecs = 1;
@ -862,14 +866,20 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) {
if (call_ng_flags_prefix(out, s, "codec-mask-", call_ng_flags_str_ht, if (call_ng_flags_prefix(out, s, "codec-mask-", call_ng_flags_str_ht,
&out->codec_mask)) &out->codec_mask))
return; return;
if (call_ng_flags_prefix(out, s, "codec-set-", call_ng_flags_str_ht_split,
&out->codec_set))
return;
if (call_ng_flags_prefix(out, s, "T38-", ng_t38_option, NULL)) if (call_ng_flags_prefix(out, s, "T38-", ng_t38_option, NULL))
return; return;
if (call_ng_flags_prefix(out, s, "T.38-", ng_t38_option, NULL)) if (call_ng_flags_prefix(out, s, "T.38-", ng_t38_option, NULL))
return; return;
} }
if (call_ng_flags_prefix(out, s, "codec-set-", call_ng_flags_str_ht_split,
&out->codec_set))
return;
if (call_ng_flags_prefix(out, s, "codec-accept-", call_ng_flags_str_ht,
&out->codec_accept))
return;
if (call_ng_flags_prefix(out, s, "codec-consume-", call_ng_flags_str_ht,
&out->codec_consume))
return;
#endif #endif
ilog(LOG_WARN, "Unknown flag encountered: '" STR_FORMAT "'", ilog(LOG_WARN, "Unknown flag encountered: '" STR_FORMAT "'",
@ -1082,6 +1092,8 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
call_ng_flags_list(out, dict, "transcode", call_ng_flags_codec_list, &out->codec_transcode); call_ng_flags_list(out, dict, "transcode", call_ng_flags_codec_list, &out->codec_transcode);
call_ng_flags_list(out, dict, "mask", call_ng_flags_str_ht, &out->codec_mask); call_ng_flags_list(out, dict, "mask", call_ng_flags_str_ht, &out->codec_mask);
call_ng_flags_list(out, dict, "set", call_ng_flags_str_ht_split, &out->codec_set); call_ng_flags_list(out, dict, "set", call_ng_flags_str_ht_split, &out->codec_set);
call_ng_flags_list(out, dict, "accept", call_ng_flags_str_ht, &out->codec_accept);
call_ng_flags_list(out, dict, "consume", call_ng_flags_str_ht, &out->codec_consume);
} }
#endif #endif
} }
@ -1095,6 +1107,10 @@ static void call_ng_free_flags(struct sdp_ng_flags *flags) {
g_hash_table_destroy(flags->codec_mask); g_hash_table_destroy(flags->codec_mask);
if (flags->codec_set) if (flags->codec_set)
g_hash_table_destroy(flags->codec_set); g_hash_table_destroy(flags->codec_set);
if (flags->codec_accept)
g_hash_table_destroy(flags->codec_accept);
if (flags->codec_consume)
g_hash_table_destroy(flags->codec_consume);
if (flags->sdes_no) if (flags->sdes_no)
g_hash_table_destroy(flags->sdes_no); g_hash_table_destroy(flags->sdes_no);
g_queue_clear_full(&flags->codec_offer, free); g_queue_clear_full(&flags->codec_offer, free);

@ -85,6 +85,11 @@ struct dtx_entry {
void *ssrc_ptr; // opaque pointer, doesn't hold a reference void *ssrc_ptr; // opaque pointer, doesn't hold a reference
}; };
struct silence_event {
uint64_t start;
uint64_t end;
};
struct codec_ssrc_handler { struct codec_ssrc_handler {
struct ssrc_entry h; // must be first struct ssrc_entry h; // must be first
struct codec_handler *handler; struct codec_handler *handler;
@ -109,6 +114,9 @@ struct codec_ssrc_handler {
GQueue dtmf_events; GQueue dtmf_events;
struct dtmf_event dtmf_event; struct dtmf_event dtmf_event;
// silence detection
GQueue silence_events;
uint64_t skip_pts; uint64_t skip_pts;
int rtp_mark:1; int rtp_mark:1;
@ -131,9 +139,19 @@ struct codec_tracker {
GHashTable *supp_codecs; // telephone-event etc => hash table of clock rates GHashTable *supp_codecs; // telephone-event etc => hash table of clock rates
}; };
struct rtcp_timer_queue {
struct timerthread_queue ttq;
};
struct rtcp_timer {
struct timerthread_queue_entry ttq_entry;
struct call *call;
struct call_media *media;
};
static struct timerthread codec_timers_thread; static struct timerthread codec_timers_thread;
static struct rtcp_timer_queue *rtcp_timer_queue;
static codec_handler_func handler_func_passthrough_ssrc; static codec_handler_func handler_func_passthrough_ssrc;
@ -176,6 +194,7 @@ static void __handler_shutdown(struct codec_handler *handler) {
handler->dtmf_scaler = 0; handler->dtmf_scaler = 0;
handler->output_handler = handler; // reset to default handler->output_handler = handler; // reset to default
handler->dtmf_payload_type = -1; handler->dtmf_payload_type = -1;
handler->cn_payload_type = -1;
handler->pcm_dtmf_detect = 0; handler->pcm_dtmf_detect = 0;
if (handler->stats_entry) { if (handler->stats_entry) {
@ -203,6 +222,7 @@ static struct codec_handler *__handler_new(const struct rtp_payload_type *pt, st
handler->source_pt = *pt; handler->source_pt = *pt;
handler->output_handler = handler; // default handler->output_handler = handler; // default
handler->dtmf_payload_type = -1; handler->dtmf_payload_type = -1;
handler->cn_payload_type = -1;
handler->packet_encoded = packet_encoded_rtp; handler->packet_encoded = packet_encoded_rtp;
handler->packet_decoded = packet_decoded_fifo; handler->packet_decoded = packet_decoded_fifo;
handler->media = media; handler->media = media;
@ -405,6 +425,7 @@ static struct rtp_payload_type *__check_dest_codecs(struct call_media *receiver,
const struct sdp_ng_flags *flags, GHashTable *supplemental_sinks, int *sink_transcoding) const struct sdp_ng_flags *flags, GHashTable *supplemental_sinks, int *sink_transcoding)
{ {
struct rtp_payload_type *pref_dest_codec = NULL; struct rtp_payload_type *pref_dest_codec = NULL;
struct rtp_payload_type *first_tc_codec = NULL;
for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) { for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data; struct rtp_payload_type *pt = l->data;
@ -418,28 +439,53 @@ static struct rtp_payload_type *__check_dest_codecs(struct call_media *receiver,
if (sink->ptime) if (sink->ptime)
pt->ptime = sink->ptime; pt->ptime = sink->ptime;
if (!pref_dest_codec && !pt->codec_def->supplemental) { if (!pref_dest_codec && !pt->codec_def->supplemental)
ilog(LOG_DEBUG, "Default sink codec is " STR_FORMAT, STR_FMT(&pt->encoding_with_params));
pref_dest_codec = pt; pref_dest_codec = pt;
}
// also check if this is a transcoding codec: if we can send a codec to the sink, // also check if this is a transcoding codec: if we can send a codec to the sink,
// but can't receive it on the receiver side, then it's transcoding. this is to check // but can't receive it on the receiver side, then it's transcoding. this is to check
// whether transcoding on the sink side is actually needed. if transcoding has been // whether transcoding on the sink side is actually needed. if transcoding has been
// previously enabled on the sink, but no transcoding codecs are actually present, // previously enabled on the sink, but no transcoding codecs are actually present,
// we can disable the transcoding engine. // we can disable the transcoding engine.
struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send,
&pt->payload_type);
if (recv_pt && rtp_payload_type_cmp(pt, recv_pt))
recv_pt = NULL;
//ilog(LOG_DEBUG, "XXXXXXXXXXXX old flag is %i", *sink_transcoding);
//ilog(LOG_DEBUG, "XXXXXXXXXXXX checking dest codec " STR_FORMAT " is %i",
//STR_FMT(&pt->encoding_with_params),
//pt->for_transcoding);
//if (recv_pt)
//ilog(LOG_DEBUG, "XXXXXXXXXXXX checking dest codec reverse " STR_FORMAT " is %i",
//STR_FMT(&recv_pt->encoding_with_params),
//recv_pt->for_transcoding);
if (MEDIA_ISSET(sink, TRANSCODE)) { if (MEDIA_ISSET(sink, TRANSCODE)) {
struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send, if (!recv_pt) {
&pt->payload_type); // can the sink receive codec but the receiver can't send it?
if (!recv_pt || rtp_payload_type_cmp(pt, recv_pt)) {
// can the sink receive supplemental codec but the receiver can't send it?
*sink_transcoding |= 0x3; *sink_transcoding |= 0x3;
} }
} }
if (pt->for_transcoding) {
// codec is explicitly marked for transcoding. enable transcoding engine
MEDIA_SET(receiver, TRANSCODE);
*sink_transcoding |= 0x3;
if (!first_tc_codec && !pt->codec_def->supplemental)
first_tc_codec = pt;
if (pt->codec_def->supplemental)
*sink_transcoding |= 0x4;
}
//ilog(LOG_DEBUG, "XXXXXXXXXXXX new flag is %i", *sink_transcoding);
__track_supp_codec(supplemental_sinks, pt); __track_supp_codec(supplemental_sinks, pt);
} }
if (first_tc_codec)
pref_dest_codec = first_tc_codec;
if (pref_dest_codec)
ilog(LOG_DEBUG, "Default sink codec is " STR_FORMAT,
STR_FMT(&pref_dest_codec->encoding_with_params));
return pref_dest_codec; return pref_dest_codec;
} }
@ -454,10 +500,21 @@ static void __check_send_codecs(struct call_media *receiver, struct call_media *
struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send, struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send,
&pt->payload_type); &pt->payload_type);
int tc_flag = 0; int tc_flag = 0;
//ilog(LOG_DEBUG, "XXXXXXXXXXXX old flag is %i", *sink_transcoding);
//ilog(LOG_DEBUG, "XXXXXXXXXXXX checking send codec " STR_FORMAT " is %i",
//STR_FMT(&pt->encoding_with_params),
//pt->for_transcoding);
//if (recv_pt)
//ilog(LOG_DEBUG, "XXXXXXXXXXXX checking send codec reverse " STR_FORMAT " is %i",
//STR_FMT(&recv_pt->encoding_with_params),
//recv_pt->for_transcoding);
if (!recv_pt || rtp_payload_type_cmp(pt, recv_pt)) if (!recv_pt || rtp_payload_type_cmp(pt, recv_pt))
tc_flag |= 0x3; tc_flag |= 0x3;
if (flags && flags->inject_dtmf) if (flags && flags->inject_dtmf)
tc_flag |= 0x1; tc_flag |= 0x1;
if (pt->for_transcoding)
tc_flag |= 0x3;
//ilog(LOG_DEBUG, "XXXXXXXXXXXX set flag is %i", *sink_transcoding);
if (tc_flag) { if (tc_flag) {
// can the sink receive codec but the receiver can't send it? // can the sink receive codec but the receiver can't send it?
*sink_transcoding |= tc_flag; *sink_transcoding |= tc_flag;
@ -466,11 +523,12 @@ static void __check_send_codecs(struct call_media *receiver, struct call_media *
// even if the receiver can receive the same codec that the sink can // even if the receiver can receive the same codec that the sink can
// send, we might still have it configured as a transcoder due to // send, we might still have it configured as a transcoder due to
// always-transcode in the offer // force accepted codec in the offer
struct codec_handler *ch_recv = struct codec_handler *ch_recv =
g_hash_table_lookup(sink->codec_handlers, GINT_TO_POINTER(recv_pt->payload_type)); g_hash_table_lookup(sink->codec_handlers, GINT_TO_POINTER(recv_pt->payload_type));
if (!ch_recv) if (!ch_recv)
continue; continue;
//ilog(LOG_DEBUG, "XXXXXXXXXXXX handler transcoder %i", ch_recv->transcoder);
if (ch_recv->transcoder) if (ch_recv->transcoder)
*sink_transcoding |= 0x3; *sink_transcoding |= 0x3;
} }
@ -571,8 +629,28 @@ static void __single_codec(struct call_media *media, const struct sdp_ng_flags *
} }
} }
static int __check_receiver_codecs(struct call_media *receiver) {
int ret = 0;
// if some codecs were explicitly marked for transcoding, then we accept only those.
// otherwise we accept all that we can.
for (GList *l = receiver->codecs_prefs_send.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data;
ensure_codec_def(pt, receiver);
if (!pt->codec_def)
continue;
//ilog(LOG_DEBUG, "XXXXXXXXXXXX checking recv send " STR_FORMAT " %i %i", STR_FMT(&pt->encoding_with_params), pt->for_transcoding, pt->codec_def->supplemental);
if (pt->for_transcoding) {
if (pt->codec_def->supplemental)
ret |= 0x2 | 0x4;
else
ret |= 0x1 | 0x2;
}
}
return ret;
}
static void __accept_transcode_codecs(struct call_media *receiver, struct call_media *sink, static void __accept_transcode_codecs(struct call_media *receiver, struct call_media *sink,
const struct sdp_ng_flags *flags) const struct sdp_ng_flags *flags, int accept_only_tc)
{ {
// if the other side is transcoding, we need to accept codecs that were // if the other side is transcoding, we need to accept codecs that were
// originally offered (recv->send) if we support them, even if the // originally offered (recv->send) if we support them, even if the
@ -583,6 +661,9 @@ static void __accept_transcode_codecs(struct call_media *receiver, struct call_m
ensure_codec_def(pt, receiver); ensure_codec_def(pt, receiver);
if (!pt->codec_def) if (!pt->codec_def)
continue; continue;
if (accept_only_tc && !pt->for_transcoding)
continue;
//ilog(LOG_DEBUG, "XXXXXXXXXXX accept codec " STR_FORMAT " flag %i", STR_FMT(&pt->encoding_with_params), pt->for_transcoding);
struct rtp_payload_type *existing_pt struct rtp_payload_type *existing_pt
= g_hash_table_lookup(receiver->codecs_recv, &pt->payload_type); = g_hash_table_lookup(receiver->codecs_recv, &pt->payload_type);
if (existing_pt && !rtp_payload_type_cmp_nf(existing_pt, pt)) { if (existing_pt && !rtp_payload_type_cmp_nf(existing_pt, pt)) {
@ -625,6 +706,7 @@ static void __accept_transcode_codecs(struct call_media *receiver, struct call_m
g_hash_table_insert(receiver->codecs_recv, &existing_pt->payload_type, existing_pt); g_hash_table_insert(receiver->codecs_recv, &existing_pt->payload_type, existing_pt);
} }
//ilog(LOG_DEBUG, "XXXXXXXXXXXXX offered codec %i", pt->for_transcoding);
ilog(LOG_DEBUG, "Accepting offered codec " STR_FORMAT " due to transcoding", ilog(LOG_DEBUG, "Accepting offered codec " STR_FORMAT " due to transcoding",
STR_FMT(&pt->encoding_with_params)); STR_FMT(&pt->encoding_with_params));
MEDIA_SET(receiver, TRANSCODE); MEDIA_SET(receiver, TRANSCODE);
@ -714,6 +796,7 @@ static GHashTable *__payload_type_queue_hash(GQueue *prefs, GQueue *order) {
static void __symmetric_codecs(struct call_media *receiver, struct call_media *sink, static void __symmetric_codecs(struct call_media *receiver, struct call_media *sink,
int *sink_transcoding) int *sink_transcoding)
{ {
//ilog(LOG_DEBUG, "XXXXXXXXXXXXXXX symm codec flags %i %i", MEDIA_ISSET(sink, TRANSCODE), *sink_transcoding);
if (!MEDIA_ISSET(sink, TRANSCODE)) if (!MEDIA_ISSET(sink, TRANSCODE))
return; return;
if (!*sink_transcoding) if (!*sink_transcoding)
@ -735,28 +818,62 @@ static void __symmetric_codecs(struct call_media *receiver, struct call_media *s
// reconstruct list based on other side's preference. // reconstruct list based on other side's preference.
int transcoding = 0; int transcoding = 0;
// keep track of our reconstruction order. there might be some codecs that have been force accepted
// that aren't present in sink->codecs_prefs_send. we must add them our output (receiver->send/recv)
// in order.
GList *prefix_pt_pos = prefs_send_order.head;
for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) { for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data; struct rtp_payload_type *pt = l->data;
//ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXX symm codec check " STR_FORMAT, STR_FMT(&pt->encoding_with_params));
// do we have a matching output? // do we have a matching output?
struct rtp_payload_type *out_pt = g_hash_table_lookup(prefs_recv, struct rtp_payload_type *out_pt = g_hash_table_lookup(prefs_recv,
GINT_TO_POINTER(pt->payload_type)); GINT_TO_POINTER(pt->payload_type));
if (out_pt && g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type))) { struct rtp_payload_type *send_pt;
if (!out_pt || !(send_pt = g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type)))) {
// we must transcode after all.
ilog(LOG_DEBUG, "RTP payload type %i is not symmetric and must be transcoded",
pt->payload_type);
transcoding = 1;
continue;
}
// seek forward in our prefix list and check any PTs to see if they're force accepted
while (prefix_pt_pos) {
void *ptype = prefix_pt_pos->data;
struct rtp_payload_type *prefix_pt = g_hash_table_lookup(prefs_send, ptype);
prefix_pt_pos = prefix_pt_pos->next;
if (!prefix_pt)
continue; // bug?
if (prefix_pt == send_pt)
break; // caught up
//ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXX prefix codec check " STR_FORMAT " %i", STR_FMT(&prefix_pt->encoding_with_params), prefix_pt->for_transcoding);
if (!prefix_pt->for_transcoding)
continue; // not interesting
// add it to the list // add it to the list
ilog(LOG_DEBUG, "Adding symmetric RTP payload type %i", pt->payload_type); ilog(LOG_DEBUG, "Adding force-accepted RTP payload type %i", prefix_pt->payload_type);
g_hash_table_steal(prefs_recv, GINT_TO_POINTER(pt->payload_type)); g_hash_table_steal(prefs_send, ptype);
__rtp_payload_type_add_recv(receiver, out_pt, 1); __rtp_payload_type_add_send(receiver, prefix_pt);
// and our send leg // and our receive leg
out_pt = g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type)); struct rtp_payload_type *in_pt = g_hash_table_lookup(prefs_recv, ptype);
if (out_pt) { if (in_pt) {
g_hash_table_steal(prefs_send, GINT_TO_POINTER(pt->payload_type)); g_hash_table_steal(prefs_recv, ptype);
__rtp_payload_type_add_send(receiver, out_pt); __rtp_payload_type_add_recv(receiver, in_pt, 1);
} }
continue; transcoding = 1;
}
// add it to the list
ilog(LOG_DEBUG, "Adding symmetric RTP payload type %i", pt->payload_type);
g_hash_table_steal(prefs_recv, GINT_TO_POINTER(pt->payload_type));
__rtp_payload_type_add_recv(receiver, out_pt, 1);
// and our send leg
out_pt = g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type));
if (out_pt) {
g_hash_table_steal(prefs_send, GINT_TO_POINTER(pt->payload_type));
__rtp_payload_type_add_send(receiver, out_pt);
} }
// we must transcode after all.
ilog(LOG_DEBUG, "RTP payload type %i is not symmetric and must be transcoded",
pt->payload_type);
transcoding = 1;
} }
if (!transcoding) if (!transcoding)
@ -983,6 +1100,111 @@ static int codec_handler_non_rtp_update(struct call_media *receiver, struct call
} }
static void __rtcp_timer_free(void *p) {
struct rtcp_timer *rt = p;
if (rt->call)
obj_put(rt->call);
g_slice_free1(sizeof(*rt), rt);
}
// master lock held in W
static void __codec_rtcp_timer_schedule(struct call_media *media) {
struct rtcp_timer *rt = g_slice_alloc0(sizeof(*rt));
rt->ttq_entry.when = media->rtcp_timer;
rt->call = obj_get(media->call);
rt->media = media;
timerthread_queue_push(&rtcp_timer_queue->ttq, &rt->ttq_entry);
}
// no lock held
static void __rtcp_timer_run(struct timerthread_queue *q, void *p) {
struct rtcp_timer *rt = p;
// check scheduling
rwlock_lock_w(&rt->call->master_lock);
struct call_media *media = rt->media;
struct timeval rtcp_timer = media->rtcp_timer;
log_info_call(rt->call);
if (!rtcp_timer.tv_sec || timeval_diff(&rtpe_now, &rtcp_timer) < 0 || !proto_is_rtp(media->protocol)) {
__rtcp_timer_free(rt);
rwlock_unlock_w(&rt->call->master_lock);
goto out;
}
timeval_add_usec(&rtcp_timer, 5000000 + (random() % 2000000));
media->rtcp_timer = rtcp_timer;
__codec_rtcp_timer_schedule(media);
// switch locks to be more graceful
rwlock_unlock_w(&rt->call->master_lock);
rwlock_lock_r(&rt->call->master_lock);
struct ssrc_ctx *ssrc_out = NULL;
if (media->streams.head) {
struct packet_stream *ps = media->streams.head->data;
mutex_lock(&ps->out_lock);
ssrc_out = ps->ssrc_out;
if (ssrc_out)
obj_hold(&ssrc_out->parent->h);
mutex_unlock(&ps->out_lock);
}
if (ssrc_out)
rtcp_send_report(media, ssrc_out);
rwlock_unlock_r(&rt->call->master_lock);
if (ssrc_out)
obj_put(&ssrc_out->parent->h);
__rtcp_timer_free(rt);
out:
log_info_clear();
}
// master lock held in W
static void __codec_rtcp_timer(struct call_media *receiver) {
if (receiver->rtcp_timer.tv_sec) // already scheduled
return;
receiver->rtcp_timer = rtpe_now;
timeval_add_usec(&receiver->rtcp_timer, 5000000 + (random() % 2000000));
__codec_rtcp_timer_schedule(receiver);
// XXX unify with media player into a generic RTCP player
}
// returns: 0 = supp codec not present; 1 = sink has codec but receiver does not, 2 = both have codec
int __supp_codec_match(struct call_media *receiver, struct call_media *sink, int pt,
struct rtp_payload_type **sink_pt, struct rtp_payload_type **recv_pt)
{
if (pt == -1)
return 0;
//ilog(LOG_DEBUG, "XXXXXXXXX checking supp PT match %i", pt);
struct rtp_payload_type *sink_pt_stor = NULL;
struct rtp_payload_type *recv_pt_stor = NULL;
if (!sink_pt)
sink_pt = &sink_pt_stor;
if (!recv_pt)
recv_pt = &recv_pt_stor;
// find a matching output payload type
*sink_pt = g_hash_table_lookup(sink->codecs_send, &pt);
if (!*sink_pt)
return 0;
//ilog(LOG_DEBUG, "XXXXXXXXX sink has supp PT %i", pt);
// XXX should go by codec name/params, not payload type number
*recv_pt = g_hash_table_lookup(receiver->codecs_recv, &pt);
if (!*recv_pt)
return 1;
//ilog(LOG_DEBUG, "XXXXXXXXX recv has supp PT %i", pt);
if (rtp_payload_type_cmp(*sink_pt, *recv_pt))
return 1;
//ilog(LOG_DEBUG, "XXXXXXXXX recv has matching supp PT %i", pt);
return 2;
}
// call must be locked in W // call must be locked in W
void codec_handlers_update(struct call_media *receiver, struct call_media *sink, void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
@ -1027,7 +1249,11 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// if we transcode, we transcode to the highest-preference supported codec // if we transcode, we transcode to the highest-preference supported codec
// that the sink specified. determine this first. // that the sink specified. determine this first.
struct rtp_payload_type *pref_dest_codec = NULL; struct rtp_payload_type *pref_dest_codec = NULL;
int sink_transcoding = 0; // 0x1 = any transcoder present, 0x2 = non pseudo transcoder present
// 0x1 = any transcoder present, 0x2 = non pseudo transcoder present,
// 0x4 = supplemental codec for transcoding
int sink_transcoding = 0;
// keep track of supplemental payload types. we hash them by clock rate // keep track of supplemental payload types. we hash them by clock rate
// in case there's several of them. the clock rates of the destination // in case there's several of them. the clock rates of the destination
// codec and the supplemental codec must match. // codec and the supplemental codec must match.
@ -1044,33 +1270,32 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// similarly, if the sink can receive a codec that the receiver can't send, it's also transcoding // similarly, if the sink can receive a codec that the receiver can't send, it's also transcoding
__check_send_codecs(receiver, sink, flags, supplemental_sinks, &sink_transcoding); __check_send_codecs(receiver, sink, flags, supplemental_sinks, &sink_transcoding);
// 0x1 = accept only codecs marked for transcoding, 0x2 = some codecs marked for transcoding
// present, 0x4 = supplemental codec for transcoding
int receiver_transcoding = __check_receiver_codecs(receiver);
if (flags && flags->opmode == OP_ANSWER && flags->symmetric_codecs) if (flags && flags->opmode == OP_ANSWER && flags->symmetric_codecs)
__symmetric_codecs(receiver, sink, &sink_transcoding); __symmetric_codecs(receiver, sink, &sink_transcoding);
int dtmf_payload_type = __dtmf_payload_type(supplemental_sinks, pref_dest_codec); int dtmf_payload_type = __dtmf_payload_type(supplemental_sinks, pref_dest_codec);
int cn_payload_type = __supp_payload_type(supplemental_sinks, pref_dest_codec, "CN");
g_hash_table_destroy(supplemental_sinks); g_hash_table_destroy(supplemental_sinks);
supplemental_sinks = NULL; supplemental_sinks = NULL;
struct rtp_payload_type *dtmf_pt = NULL; struct rtp_payload_type *dtmf_pt = NULL;
struct rtp_payload_type *reverse_dtmf_pt = NULL; struct rtp_payload_type *reverse_dtmf_pt = NULL;
int dtmf_pt_match = __supp_codec_match(receiver, sink, dtmf_payload_type, &dtmf_pt, &reverse_dtmf_pt);
if (dtmf_payload_type != -1) { int cn_pt_match = __supp_codec_match(receiver, sink, cn_payload_type, NULL, NULL);
// find a matching output DTMF payload type
dtmf_pt = g_hash_table_lookup(sink->codecs_send, &dtmf_payload_type);
reverse_dtmf_pt = g_hash_table_lookup(receiver->codecs_recv, &dtmf_payload_type);
if (reverse_dtmf_pt && dtmf_pt && rtp_payload_type_cmp(reverse_dtmf_pt, dtmf_pt))
reverse_dtmf_pt = NULL;
}
// stop transcoding if we've determined that we don't need it // stop transcoding if we've determined that we don't need it
if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding) { if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding && !(receiver_transcoding & 0x2)) {
ilog(LOG_DEBUG, "Disabling transcoding engine (not needed)"); ilog(LOG_DEBUG, "Disabling transcoding engine (not needed)");
MEDIA_CLEAR(sink, TRANSCODE); MEDIA_CLEAR(sink, TRANSCODE);
} }
if (MEDIA_ISSET(sink, TRANSCODE) && (sink_transcoding & 0x2)) if (MEDIA_ISSET(sink, TRANSCODE) && (sink_transcoding & 0x2))
__accept_transcode_codecs(receiver, sink, flags); __accept_transcode_codecs(receiver, sink, flags, (receiver_transcoding & 0x1));
else else
__eliminate_rejected_codecs(receiver, sink, flags); __eliminate_rejected_codecs(receiver, sink, flags);
@ -1080,11 +1305,12 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
GHashTable *output_transcoders = g_hash_table_new(g_direct_hash, g_direct_equal); GHashTable *output_transcoders = g_hash_table_new(g_direct_hash, g_direct_equal);
int transcode_supplemental = 0; // is one of our source codecs a supplemental one? int transcode_supplemental = 0; // is one of our source codecs a supplemental one?
if ((sink_transcoding & 0x4))
transcode_supplemental = 1;
// do we need to detect PCM DTMF tones? // do we need to detect PCM DTMF tones?
int pcm_dtmf_detect = 0; int pcm_dtmf_detect = 0;
if (reverse_dtmf_pt) if ((MEDIA_ISSET(sink, TRANSCODE) || (sink_transcoding & 0x2))
if ((MEDIA_ISSET(sink, TRANSCODE) || (flags && flags->always_transcode))
&& dtmf_payload_type != -1 && dtmf_payload_type != -1
&& dtmf_pt && (!reverse_dtmf_pt || reverse_dtmf_pt->for_transcoding || && dtmf_pt && (!reverse_dtmf_pt || reverse_dtmf_pt->for_transcoding ||
!g_hash_table_lookup(receiver->codecs_send, &dtmf_payload_type))) !g_hash_table_lookup(receiver->codecs_send, &dtmf_payload_type)))
@ -1130,23 +1356,27 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
goto next; goto next;
} }
//ilog(LOG_DEBUG, "XXXXXXXXXXXX pref dest codec " STR_FORMAT " is %i",
//STR_FMT(&pref_dest_codec->encoding_with_params),
//pref_dest_codec->for_transcoding);
struct rtp_payload_type *dest_pt; // transcode to this struct rtp_payload_type *dest_pt; // transcode to this
GQueue *dest_codecs = NULL; GQueue *dest_codecs = NULL;
if (!flags || !flags->always_transcode) { if (pref_dest_codec->for_transcoding) {
// we ignore output codec matches if we must transcode DTMF // with force accepted codec, we still accept DTMF payloads if possible
if (dtmf_pt && !reverse_dtmf_pt) if (pt->codec_def && pt->codec_def->supplemental)
dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding);
}
else {
// we ignore output codec matches if we must transcode supp codecs
if ((dtmf_pt_match == 1 || cn_pt_match == 1) && MEDIA_ISSET(sink, TRANSCODE))
; ;
else if (pcm_dtmf_detect) else if (pcm_dtmf_detect)
; ;
else else
dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding); dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding);
} }
else if (flags->always_transcode) {
// with always-transcode, we still accept DTMF payloads if possible
if (pt->codec_def && pt->codec_def->supplemental)
dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding);
}
if (dest_codecs) { if (dest_codecs) {
// the sink supports this codec - check offered formats // the sink supports this codec - check offered formats
dest_pt = NULL; dest_pt = NULL;
@ -1187,6 +1417,11 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
if (rtp_payload_type_cmp_nf(pt, dest_pt)) if (rtp_payload_type_cmp_nf(pt, dest_pt))
goto transcode; goto transcode;
// do we need silence detection?
if (cn_pt_match == 2 && MEDIA_ISSET(sink, TRANSCODE))
goto transcode;
// XXX check format parameters as well
ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding_with_params)); ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding_with_params));
__make_passthrough_gsl(handler, &passthrough_handlers); __make_passthrough_gsl(handler, &passthrough_handlers);
if (pt->codec_def && pt->codec_def->dtmf) if (pt->codec_def && pt->codec_def->dtmf)
@ -1214,6 +1449,7 @@ transcode:;
} }
MEDIA_SET(receiver, TRANSCODE); MEDIA_SET(receiver, TRANSCODE);
__make_transcoder(handler, dest_pt, output_transcoders, dtmf_payload_type, pcm_dtmf_detect); __make_transcoder(handler, dest_pt, output_transcoders, dtmf_payload_type, pcm_dtmf_detect);
handler->cn_payload_type = cn_payload_type;
next: next:
l = l->next; l = l->next;
@ -1253,16 +1489,24 @@ next:
// if the sink does not support DTMF but we can receive it, we must transcode // if the sink does not support DTMF but we can receive it, we must transcode
// DTMF event packets to PCM. this requires all codecs to be transcoded to the // DTMF event packets to PCM. this requires all codecs to be transcoded to the
// sink's preferred destination codec. // sink's preferred destination codec.
//ilog(LOG_DEBUG, "XXXXXXXXXXXXX tc supp %i DTMF PT %i DTMF PT match %i PCM detect %i",
//transcode_supplemental, dtmf_payload_type, dtmf_pt_match, pcm_dtmf_detect);
//ilog(LOG_DEBUG, "XXXXXXXXXXXXX tc supp %i CN PT %i CN PT match %i",
//transcode_supplemental, cn_payload_type, cn_pt_match);
//ilog(LOG_DEBUG, "XXXXXXXXXXXXX %p %p %p",
//pref_dest_codec, handler->source_pt.codec_def, pref_dest_codec->codec_def);
if (!transcode_supplemental && !pcm_dtmf_detect) if (!transcode_supplemental && !pcm_dtmf_detect)
__make_passthrough_ssrc(handler); __make_passthrough_ssrc(handler);
else if (dtmf_pt && reverse_dtmf_pt) else if (dtmf_pt_match == 2)
__make_passthrough_ssrc(handler); __make_passthrough_ssrc(handler);
else if (!pref_dest_codec else if (!pref_dest_codec
|| !handler->source_pt.codec_def || !pref_dest_codec->codec_def) || !handler->source_pt.codec_def || !pref_dest_codec->codec_def)
__make_passthrough_ssrc(handler); __make_passthrough_ssrc(handler);
else else {
__make_transcoder(handler, pref_dest_codec, output_transcoders, __make_transcoder(handler, pref_dest_codec, output_transcoders,
dtmf_payload_type, pcm_dtmf_detect); dtmf_payload_type, pcm_dtmf_detect);
handler->cn_payload_type = cn_payload_type;
}
passthrough_handlers = g_slist_delete_link(passthrough_handlers, passthrough_handlers); passthrough_handlers = g_slist_delete_link(passthrough_handlers, passthrough_handlers);
} }
@ -1272,6 +1516,11 @@ next:
} }
g_hash_table_destroy(output_transcoders); g_hash_table_destroy(output_transcoders);
if (MEDIA_ISSET(receiver, RTCP_GEN)) {
receiver->rtcp_handler = rtcp_sink_handler;
__codec_rtcp_timer(receiver);
}
} }
@ -2086,6 +2335,104 @@ void codec_handlers_stop(GQueue *q) {
} }
static void silence_event_free(void *p) {
g_slice_free1(sizeof(struct silence_event), p);
}
#define __silence_detect_type(type) \
static void __silence_detect_ ## type(struct codec_ssrc_handler *ch, AVFrame *frame, type thres) { \
type *s = (void *) frame->data[0]; \
struct silence_event *last = g_queue_peek_tail(&ch->silence_events); \
\
if (last && last->end) /* last event finished? */ \
last = NULL; \
\
for (unsigned int i = 0; i < frame->nb_samples; i++) { \
/* ilog(LOG_DEBUG, "XXXXXXXXXXXX checking %u %i vs %i", i, (int) s[i], (int) thres); */ \
if (s[i] <= thres && s[1] >= -thres) { \
/* silence */ \
if (!last) { \
/* new event */ \
last = g_slice_alloc0(sizeof(*last)); \
last->start = frame->pts + i; \
g_queue_push_tail(&ch->silence_events, last); \
} \
} \
else { \
/* not silence */ \
if (last && !last->end) { \
/* close off event */ \
last->end = frame->pts + i; \
last = NULL; \
} \
} \
} \
}
__silence_detect_type(double)
__silence_detect_type(float)
__silence_detect_type(int32_t)
__silence_detect_type(int16_t)
static void __silence_detect(struct codec_ssrc_handler *ch, AVFrame *frame) {
//ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXXXXXX silence detect %i %i", rtpe_config.silence_detect_int, ch->handler->cn_payload_type);
if (!rtpe_config.silence_detect_int)
return;
if (ch->handler->cn_payload_type < 0)
return;
switch (frame->format) {
case AV_SAMPLE_FMT_DBL:
__silence_detect_double(ch, frame, rtpe_config.silence_detect_double);
break;
case AV_SAMPLE_FMT_FLT:
__silence_detect_float(ch, frame, rtpe_config.silence_detect_double);
break;
case AV_SAMPLE_FMT_S32:
__silence_detect_int32_t(ch, frame, rtpe_config.silence_detect_int);
break;
case AV_SAMPLE_FMT_S16:
__silence_detect_int16_t(ch, frame, rtpe_config.silence_detect_int >> 16);
break;
default:
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Unsupported sample format %i for silence detection",
frame->format);
}
}
static int is_silence_event(str *inout, GQueue *events, uint64_t pts, uint64_t duration) {
uint64_t end = pts + duration;
while (events->length) {
struct silence_event *first = g_queue_peek_head(events);
if (first->start > pts) // future event
return 0;
if (!first->end) // ongoing event
goto silence;
if (first->end > end) // event finished with end in the future
goto silence;
// event has ended: remove it
g_queue_pop_head(events);
// does the event fill the entire span?
if (first->end == end) {
silence_event_free(first);
goto silence;
}
// keep going, there might be more
silence_event_free(first);
}
return 0;
silence:
// replace with CN payload
inout->len = rtpe_config.cn_payload.len;
memcpy(inout->s, rtpe_config.cn_payload.s, inout->len);
return 1;
}
static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) { static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) {
struct codec_handler *h = p; struct codec_handler *h = p;
@ -2184,6 +2531,7 @@ static void __free_ssrc_handler(void *chp) {
dtmf_rx_free(ch->dtmf_dsp); dtmf_rx_free(ch->dtmf_dsp);
resample_shutdown(&ch->dtmf_resampler); resample_shutdown(&ch->dtmf_resampler);
g_queue_clear_full(&ch->dtmf_events, dtmf_event_free); g_queue_clear_full(&ch->dtmf_events, dtmf_event_free);
g_queue_clear_full(&ch->silence_events, silence_event_free);
if (ch->dtx_buffer) if (ch->dtx_buffer)
obj_put(&ch->dtx_buffer->ttq.tt_obj); obj_put(&ch->dtx_buffer->ttq.tt_obj);
} }
@ -2224,13 +2572,26 @@ static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) {
ilog(LOG_DEBUG, "Received packet of %i bytes from packetizer", inout.len); ilog(LOG_DEBUG, "Received packet of %i bytes from packetizer", inout.len);
// check special payloads
unsigned int repeats = 0; unsigned int repeats = 0;
int payload_type = -1;
int is_dtmf = dtmf_event_payload(&inout, (uint64_t *) &enc->avpkt.pts, enc->avpkt.duration, int is_dtmf = dtmf_event_payload(&inout, (uint64_t *) &enc->avpkt.pts, enc->avpkt.duration,
&ch->dtmf_event, &ch->dtmf_events); &ch->dtmf_event, &ch->dtmf_events);
if (is_dtmf == 1) if (is_dtmf) {
ch->rtp_mark = 1; // DTMF start event payload_type = ch->handler->dtmf_payload_type;
else if (is_dtmf == 3) if (is_dtmf == 1)
repeats = 2; // DTMF end event ch->rtp_mark = 1; // DTMF start event
else if (is_dtmf == 3)
repeats = 2; // DTMF end event
}
else {
if (is_silence_event(&inout, &ch->silence_events, enc->avpkt.pts, enc->avpkt.duration))
payload_type = ch->handler->cn_payload_type;
}
// ready to send
do { do {
char *send_buf = buf; char *send_buf = buf;
@ -2242,7 +2603,7 @@ static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) {
__output_rtp(mp, ch, ch->handler, send_buf, inout.len, ch->first_ts __output_rtp(mp, ch, ch->handler, send_buf, inout.len, ch->first_ts
+ enc->avpkt.pts / enc->def->clockrate_mult, + enc->avpkt.pts / enc->def->clockrate_mult,
ch->rtp_mark ? 1 : 0, -1, 0, ch->rtp_mark ? 1 : 0, -1, 0,
is_dtmf ? ch->handler->dtmf_payload_type : -1); payload_type);
mp->ssrc_out->parent->seq_diff++; mp->ssrc_out->parent->seq_diff++;
//mp->iter_out++; //mp->iter_out++;
ch->rtp_mark = 0; ch->rtp_mark = 0;
@ -2349,6 +2710,7 @@ static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, v
} }
__dtmf_detect(ch, frame); __dtmf_detect(ch, frame);
__silence_detect(ch, frame);
// locking deliberately ignored // locking deliberately ignored
if (mp->media_out) if (mp->media_out)
@ -2414,6 +2776,27 @@ static int packet_decode(struct codec_ssrc_handler *ch, struct transcode_packet
return ret; return ret;
} }
static void codec_calc_jitter(struct media_packet *mp, unsigned int clockrate) {
if (!mp->ssrc_in)
return;
struct ssrc_entry_call *sec = mp->ssrc_in->parent;
// RFC 3550 A.8
uint32_t transit = (((timeval_us(&mp->tv) / 1000) * clockrate) / 1000)
- ntohl(mp->rtp->timestamp);
mutex_lock(&sec->h.lock);
int32_t d = 0;
if (sec->transit)
d = transit - sec->transit;
sec->transit = transit;
if (d < 0)
d = -d;
sec->jitter += d - ((sec->jitter + 8) >> 4);
mutex_unlock(&sec->h.lock);
}
static int handler_func_transcode(struct codec_handler *h, struct media_packet *mp) { static int handler_func_transcode(struct codec_handler *h, struct media_packet *mp) {
if (G_UNLIKELY(!mp->rtp)) if (G_UNLIKELY(!mp->rtp))
return handler_func_passthrough(h, mp); return handler_func_passthrough(h, mp);
@ -2426,6 +2809,8 @@ static int handler_func_transcode(struct codec_handler *h, struct media_packet *
ntohl(mp->rtp->ssrc), mp->rtp->m_pt, ntohs(mp->rtp->seq_num), ntohl(mp->rtp->ssrc), mp->rtp->m_pt, ntohs(mp->rtp->seq_num),
ntohl(mp->rtp->timestamp), mp->payload.len); ntohl(mp->rtp->timestamp), mp->payload.len);
codec_calc_jitter(mp, h->source_pt.clock_rate);
if (h->stats_entry) { if (h->stats_entry) {
unsigned int idx = rtpe_now.tv_sec & 1; unsigned int idx = rtpe_now.tv_sec & 1;
int last_tv_sec = g_atomic_int_get(&h->stats_entry->last_tv_sec[idx]); int last_tv_sec = g_atomic_int_get(&h->stats_entry->last_tv_sec[idx]);
@ -2586,9 +2971,7 @@ static void __insert_codec_tracker(struct call_media *media, GList *link) {
} }
} }
#endif #endif
static void __queue_insert_supp(GQueue *q, struct rtp_payload_type *pt, int supp_check, static void __queue_insert_supp(GQueue *q, struct rtp_payload_type *pt, int supp_check) {
struct codec_tracker *sct)
{
// do we care at all? // do we care at all?
if (!supp_check) { if (!supp_check) {
g_queue_push_tail(q, pt); g_queue_push_tail(q, pt);
@ -2632,7 +3015,7 @@ void __rtp_payload_type_add_recv(struct call_media *media, struct rtp_payload_ty
pt->ptime = media->ptime; pt->ptime = media->ptime;
g_hash_table_insert(media->codecs_recv, &pt->payload_type, pt); g_hash_table_insert(media->codecs_recv, &pt->payload_type, pt);
__rtp_payload_type_add_name(media->codec_names_recv, pt); __rtp_payload_type_add_name(media->codec_names_recv, pt);
__queue_insert_supp(&media->codecs_prefs_recv, pt, supp_check, media->codec_tracker); __queue_insert_supp(&media->codecs_prefs_recv, pt, supp_check);
} }
// consumes 'pt' // consumes 'pt'
void __rtp_payload_type_add_send(struct call_media *other_media, void __rtp_payload_type_add_send(struct call_media *other_media,
@ -2862,7 +3245,7 @@ int __codec_ht_except(int all_flag, GHashTable *yes_ht, GHashTable *no_ht, struc
else if (g_hash_table_lookup(yes_ht, &pt->encoding_with_params)) else if (g_hash_table_lookup(yes_ht, &pt->encoding_with_params))
do_this = 1; do_this = 1;
} }
if (no_ht) { if (no_ht && all_flag) {
if (g_hash_table_lookup(no_ht, &pt->encoding)) if (g_hash_table_lookup(no_ht, &pt->encoding))
do_this = 0; do_this = 0;
else if (g_hash_table_lookup(no_ht, &pt->encoding_with_params)) else if (g_hash_table_lookup(no_ht, &pt->encoding_with_params))
@ -2870,6 +3253,19 @@ int __codec_ht_except(int all_flag, GHashTable *yes_ht, GHashTable *no_ht, struc
} }
return do_this; return do_this;
} }
void __ht_merge(GHashTable **dst, GHashTable *src) {
if (!src)
return;
if (!*dst)
*dst = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
GHashTableIter iter;
g_hash_table_iter_init(&iter, src);
void *key;
while (g_hash_table_iter_next(&iter, &key, NULL)) {
str *dup = str_dup(key);
g_hash_table_replace(*dst, dup, dup);
}
}
void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media, void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media,
GQueue *types, struct sdp_ng_flags *flags) GQueue *types, struct sdp_ng_flags *flags)
{ {
@ -2883,7 +3279,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_
static const str str_full = STR_CONST_INIT("full"); static const str str_full = STR_CONST_INIT("full");
GHashTable *stripped = g_hash_table_new_full(str_case_hash, str_case_equal, free, __payload_queue_free); GHashTable *stripped = g_hash_table_new_full(str_case_hash, str_case_equal, free, __payload_queue_free);
GHashTable *masked = g_hash_table_new_full(str_case_hash, str_case_equal, free, __payload_queue_free); GHashTable *masked = g_hash_table_new_full(str_case_hash, str_case_equal, free, __payload_queue_free);
int strip_all = 0, mask_all = 0; int strip_all = 0, mask_all = 0, consume_all = 0, accept_all = 0;
// start fresh // start fresh
if (!proto_is_rtp(other_media->protocol) && proto_is_rtp(media->protocol) && flags->opmode == OP_OFFER) { if (!proto_is_rtp(other_media->protocol) && proto_is_rtp(media->protocol) && flags->opmode == OP_OFFER) {
@ -2906,12 +3302,23 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_
if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_all)) if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_all))
strip_all = 1; strip_all = 1;
if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_all)) else if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_full))
strip_all = 2; strip_all = 2;
if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_all)) if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_all))
mask_all = 1; mask_all = 1;
else if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_full)) else if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_full))
mask_all = 2; mask_all = 2;
if (flags->codec_consume && g_hash_table_lookup(flags->codec_consume, &str_all))
consume_all = 1;
else if (flags->codec_consume && g_hash_table_lookup(flags->codec_consume, &str_full))
consume_all = 2;
if (flags->codec_accept && g_hash_table_lookup(flags->codec_accept, &str_all))
accept_all = 1;
__ht_merge(&flags->codec_except, flags->codec_consume);
__ht_merge(&flags->codec_except, flags->codec_accept);
__ht_merge(&flags->codec_except, flags->codec_strip);
__ht_merge(&flags->codec_except, flags->codec_mask);
/* we steal the entire list to avoid duplicate allocs */ /* we steal the entire list to avoid duplicate allocs */
while ((pt = g_queue_pop_head(types))) { while ((pt = g_queue_pop_head(types))) {
@ -2940,12 +3347,39 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
codec_touched(pt, media); codec_touched(pt, media);
#endif #endif
// special case for handling of the legacy always-transcode flag (= accept-all)
// in combination with codec-mask
if (accept_all)
pt->for_transcoding = 1;
GQueue *q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding), free); GQueue *q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding), free);
g_queue_push_tail(q, __rtp_payload_type_copy(pt)); g_queue_push_tail(q, __rtp_payload_type_copy(pt));
q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding_with_params), free); q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding_with_params), free);
g_queue_push_tail(q, __rtp_payload_type_copy(pt)); g_queue_push_tail(q, __rtp_payload_type_copy(pt));
__rtp_payload_type_add_send(other_media, pt); __rtp_payload_type_add_send(other_media, pt);
} }
else if (__codec_ht_except(consume_all, flags->codec_consume, flags->codec_except, pt)) {
ilog(LOG_DEBUG, "Consuming codec '" STR_FORMAT "'",
STR_FMT(&pt->encoding_with_params));
#ifdef WITH_TRANSCODING
codec_touched(pt, media);
#endif
pt->for_transcoding = 1;
GQueue *q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding), free);
g_queue_push_tail(q, __rtp_payload_type_copy(pt));
q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding_with_params), free);
g_queue_push_tail(q, __rtp_payload_type_copy(pt));
__rtp_payload_type_add_send(other_media, pt);
}
else if (__codec_ht_except(accept_all, flags->codec_accept, NULL, pt)) {
ilog(LOG_DEBUG, "Accepting codec '" STR_FORMAT "'",
STR_FMT(&pt->encoding_with_params));
#ifdef WITH_TRANSCODING
codec_touched(pt, media);
#endif
pt->for_transcoding = 1;
__rtp_payload_type_add(media, other_media, pt);
}
else else
__rtp_payload_type_add(media, other_media, pt); __rtp_payload_type_add(media, other_media, pt);
} }
@ -3067,6 +3501,8 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_
void codecs_init(void) { void codecs_init(void) {
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
timerthread_init(&codec_timers_thread, timerthread_queue_run); timerthread_init(&codec_timers_thread, timerthread_queue_run);
rtcp_timer_queue = timerthread_queue_new("rtcp_timer_queue", sizeof(*rtcp_timer_queue),
&codec_timers_thread, NULL, __rtcp_timer_run, NULL, __rtcp_timer_free);
#endif #endif
} }
void codecs_cleanup(void) { void codecs_cleanup(void) {

@ -240,3 +240,27 @@ unsigned int kernel_add_intercept_stream(unsigned int call_idx, const char *id)
return UNINIT_IDX; return UNINIT_IDX;
return msg.u.stream.stream_idx; return msg.u.stream.stream_idx;
} }
int kernel_update_stats(const struct re_address *a, uint32_t ssrc, struct rtpengine_ssrc_stats *out) {
struct rtpengine_message msg;
int ret;
if (!kernel.is_open)
return -1;
ZERO(msg);
msg.cmd = REMG_GET_RESET_STATS;
msg.u.stats.local = *a;
ret = read(kernel.fd, &msg, sizeof(msg));
if (ret <= 0) {
ilog(LOG_ERROR, "Failed to get stream stats from kernel: %s", strerror(errno));
return -1;
}
if (msg.u.stats.ssrc != ssrc)
return -1;
*out = msg.u.stats.ssrc_stats;
return 0;
}

@ -380,6 +380,8 @@ static void options(int *argc, char ***argv) {
AUTO_CLEANUP_GBUF(dtmf_udp_ep); AUTO_CLEANUP_GBUF(dtmf_udp_ep);
AUTO_CLEANUP_GBUF(endpoint_learning); AUTO_CLEANUP_GBUF(endpoint_learning);
AUTO_CLEANUP_GBUF(dtls_sig); AUTO_CLEANUP_GBUF(dtls_sig);
double silence_detect = 0;
AUTO_CLEANUP_GVBUF(cn_payload);
GOptionEntry e[] = { GOptionEntry e[] = {
{ "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" }, { "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" },
@ -465,6 +467,8 @@ static void options(int *argc, char ***argv) {
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
{ "dtx-delay", 0,0, G_OPTION_ARG_INT, &rtpe_config.dtx_delay, "Delay in milliseconds to trigger DTX handling","INT"}, { "dtx-delay", 0,0, G_OPTION_ARG_INT, &rtpe_config.dtx_delay, "Delay in milliseconds to trigger DTX handling","INT"},
{ "max-dtx", 0,0, G_OPTION_ARG_INT, &rtpe_config.max_dtx, "Maximum duration of DTX handling", "INT"}, { "max-dtx", 0,0, G_OPTION_ARG_INT, &rtpe_config.max_dtx, "Maximum duration of DTX handling", "INT"},
{ "silence-detect",0,0, G_OPTION_ARG_DOUBLE, &silence_detect, "Audio level threshold in percent for silence detection","FLOAT"},
{ "cn-payload",0,0, G_OPTION_ARG_STRING_ARRAY,&cn_payload, "Comfort noise parameters to replace silence with","INT INT INT ..."},
#endif #endif
{ NULL, } { NULL, }
@ -684,6 +688,32 @@ static void options(int *argc, char ***argv) {
if (rtpe_config.jb_length < 0) if (rtpe_config.jb_length < 0)
die("Invalid negative jitter buffer size"); die("Invalid negative jitter buffer size");
if (silence_detect > 0) {
rtpe_config.silence_detect_double = silence_detect / 100.0;
rtpe_config.silence_detect_int = (int) ((silence_detect / 100.0) * UINT32_MAX);
}
if (!cn_payload)
str_init_dup(&rtpe_config.cn_payload, "\x20");
else {
int len = g_strv_length(cn_payload);
if (len < 1)
die("Invalid CN payload specified");
rtpe_config.cn_payload.s = malloc(len);
for (int i = 0; i < len; i++) {
char *endp;
long p = strtol(cn_payload[i], &endp, 0);
if (endp == cn_payload[i] || *endp != '\0')
die("Invalid CN payload specified");
if (p < 0 || p > 254)
die("Invalid CN payload specified");
if (i == 0 && p > 127)
die("Invalid CN payload specified");
rtpe_config.cn_payload.s[i] = p;
}
rtpe_config.cn_payload.len = len;
}
} }
void fill_initial_rtpe_cfg(struct rtpengine_config* ini_rtpe_cfg) { void fill_initial_rtpe_cfg(struct rtpengine_config* ini_rtpe_cfg) {

@ -163,51 +163,17 @@ struct send_timer *send_timer_new(struct packet_stream *ps) {
return st; return st;
} }
// call is locked in R
static void send_timer_send_rtcp(struct ssrc_ctx *ssrc_out, struct call *call, struct packet_stream *ps) {
GQueue rrs = G_QUEUE_INIT;
rtcp_receiver_reports(&rrs, call->ssrc_hash, ps->media->monologue);
ilog(LOG_DEBUG, "Generating and sending RTCP SR for %x and up to %i source(s)",
ssrc_out->parent->h.ssrc, rrs.length);
GString *sr = rtcp_sender_report(ssrc_out->parent->h.ssrc,
atomic64_get(&ssrc_out->last_ts),
atomic64_get(&ssrc_out->packets),
atomic64_get(&ssrc_out->octets),
&rrs);
socket_sendto(&ps->selected_sfd->socket, sr->str, sr->len, &ps->endpoint);
g_string_free(sr, TRUE);
}
// call is locked in R // call is locked in R
static void send_timer_rtcp(struct send_timer *st, struct ssrc_ctx *ssrc_out) { static void send_timer_rtcp(struct send_timer *st, struct ssrc_ctx *ssrc_out) {
struct call_media *media = st->sink ? st->sink->media : NULL; struct call_media *media = st->sink ? st->sink->media : NULL;
if (!media) if (!media)
return; return;
struct call *call = media->call;
// figure out where to send it
struct packet_stream *ps = media->streams.head->data;
if (MEDIA_ISSET(media, RTCP_MUX))
;
else if (!media->streams.head->next)
;
else {
struct packet_stream *next_ps = media->streams.head->next->data;
if (PS_ISSET(next_ps, RTCP))
ps = next_ps;
}
log_info_stream_fd(ps->selected_sfd);
send_timer_send_rtcp(ssrc_out, call, ps); rtcp_send_report(media, ssrc_out);
// XXX missing locking? // XXX missing locking?
ssrc_out->next_rtcp = rtpe_now; ssrc_out->next_rtcp = rtpe_now;
timeval_add_usec(&ssrc_out->next_rtcp, 5000000); timeval_add_usec(&ssrc_out->next_rtcp, 5000000 + (random() % 2000000));
} }

@ -1137,6 +1137,7 @@ void kernelize(struct packet_stream *stream) {
reti.dtls = MEDIA_ISSET(media, DTLS); reti.dtls = MEDIA_ISSET(media, DTLS);
reti.stun = media->ice_agent ? 1 : 0; reti.stun = media->ice_agent ? 1 : 0;
reti.non_forwarding = non_forwarding; reti.non_forwarding = non_forwarding;
reti.rtp_stats = MEDIA_ISSET(media, RTCP_GEN) ? 1 : 0;
__re_address_translate_ep(&reti.dst_addr, &sink->endpoint); __re_address_translate_ep(&reti.dst_addr, &sink->endpoint);
__re_address_translate_ep(&reti.src_addr, &sink->selected_sfd->socket.local); __re_address_translate_ep(&reti.src_addr, &sink->selected_sfd->socket.local);
@ -1179,7 +1180,9 @@ void kernelize(struct packet_stream *stream) {
struct codec_handler *ch = codec_handler_get(media, rs->payload_type); struct codec_handler *ch = codec_handler_get(media, rs->payload_type);
if (!ch->kernelize) if (!ch->kernelize)
continue; continue;
reti.payload_types[reti.num_payload_types++] = rs->payload_type; reti.payload_types[reti.num_payload_types] = rs->payload_type;
reti.clock_rates[reti.num_payload_types] = ch->source_pt.clock_rate;
reti.num_payload_types++;
} }
g_list_free(values); g_list_free(values);
} }
@ -1202,6 +1205,60 @@ no_kernel:
PS_SET(stream, NO_KERNEL_SUPPORT); PS_SET(stream, NO_KERNEL_SUPPORT);
} }
// must be called with appropriate locks (master lock and/or in_lock)
static void __stream_update_stats(struct packet_stream *ps, int have_in_lock) {
struct re_address local;
if (!have_in_lock)
mutex_lock(&ps->in_lock);
struct ssrc_ctx *ssrc_ctx = ps->ssrc_in;
struct ssrc_entry_call *parent = ssrc_ctx->parent;
__re_address_translate_ep(&local, &ps->selected_sfd->socket.local);
struct rtpengine_ssrc_stats stats;
if (kernel_update_stats(&local, htonl(parent->h.ssrc), &stats)) {
if (!have_in_lock)
mutex_unlock(&ps->in_lock);
return;
}
if (!stats.basic_stats.packets) {
// no change
if (!have_in_lock)
mutex_unlock(&ps->in_lock);
return;
}
atomic64_add(&ssrc_ctx->packets, stats.basic_stats.packets);
atomic64_add(&ssrc_ctx->octets, stats.basic_stats.bytes);
atomic64_add(&ssrc_ctx->packets_lost, stats.total_lost);
atomic64_set(&ssrc_ctx->last_seq, stats.ext_seq);
atomic64_set(&ssrc_ctx->last_ts, stats.timestamp);
parent->jitter = stats.jitter;
uint32_t ssrc_map_out = ssrc_ctx->ssrc_map_out;
if (!have_in_lock)
mutex_unlock(&ps->in_lock);
// update opposite outgoing SSRC
if (!have_in_lock)
mutex_lock(&ps->out_lock);
else {
if (mutex_trylock(&ps->out_lock))
return; // will have to skip this
}
ssrc_ctx = ps->ssrc_out;
parent = ssrc_ctx->parent;
if (parent->h.ssrc == ssrc_map_out) {
atomic64_add(&ssrc_ctx->packets, stats.basic_stats.packets);
atomic64_add(&ssrc_ctx->octets, stats.basic_stats.bytes);
}
mutex_unlock(&ps->out_lock);
}
/* must be called with in_lock held or call->master_lock held in W */ /* must be called with in_lock held or call->master_lock held in W */
void __unkernelize(struct packet_stream *p) { void __unkernelize(struct packet_stream *p) {
struct re_address rea; struct re_address rea;
@ -1212,6 +1269,7 @@ void __unkernelize(struct packet_stream *p) {
return; return;
if (kernel.is_open) { if (kernel.is_open) {
__stream_update_stats(p, 1);
__re_address_translate_ep(&rea, &p->selected_sfd->socket.local); __re_address_translate_ep(&rea, &p->selected_sfd->socket.local);
kernel_del_stream(&rea); kernel_del_stream(&rea);
} }
@ -1241,6 +1299,24 @@ void unkernelize(struct packet_stream *ps) {
mutex_unlock(&ps->in_lock); mutex_unlock(&ps->in_lock);
} }
// master lock held in R
void media_update_stats(struct call_media *m) {
if (!proto_is_rtp(m->protocol))
return;
if (!kernel.is_open)
return;
for (GList *l = m->streams.head; l; l = l->next) {
struct packet_stream *ps = l->data;
if (!PS_ISSET(ps, RTP))
continue;
if (!PS_ISSET(ps, KERNELIZED))
continue;
__stream_update_stats(ps, 0);
}
}
const struct streamhandler *determine_handler(const struct transport_protocol *in_proto, const struct streamhandler *determine_handler(const struct transport_protocol *in_proto,
@ -1764,16 +1840,19 @@ static int do_rtcp(struct packet_handler_ctx *phc) {
int ret = -1; int ret = -1;
GQueue rtcp_list = G_QUEUE_INIT; GQueue rtcp_list = G_QUEUE_INIT;
if (rtcp_parse(&rtcp_list, &phc->mp)) int rtcp_ret = rtcp_parse(&rtcp_list, &phc->mp);
if (rtcp_ret < 0)
goto out; goto out;
if (rtcp_ret == 1)
goto ok;
if (phc->rtcp_filter) if (phc->rtcp_filter)
if (phc->rtcp_filter(&phc->mp, &rtcp_list)) if (phc->rtcp_filter(&phc->mp, &rtcp_list))
goto out; goto out;
// queue for output // queue for output
codec_add_raw_packet(&phc->mp); codec_add_raw_packet(&phc->mp);
ok:
ret = 0; ret = 0;
out: out:
rtcp_list_free(&rtcp_list); rtcp_list_free(&rtcp_list);
return ret; return ret;

@ -16,6 +16,7 @@
#include "rtcplib.h" #include "rtcplib.h"
#include "ssrc.h" #include "ssrc.h"
#include "sdp.h" #include "sdp.h"
#include "log_funcs.h"
@ -246,6 +247,9 @@ struct rtcp_process_ctx {
// Homer stats // Homer stats
GString *json; GString *json;
int json_init_len; int json_init_len;
// verdict
int discard:1;
}; };
// all available methods // all available methods
struct rtcp_handler { struct rtcp_handler {
@ -307,6 +311,9 @@ static void transcode_common_wrap(struct rtcp_process_ctx *, struct rtcp_packet
static void transcode_rr_wrap(struct rtcp_process_ctx *, struct report_block *); static void transcode_rr_wrap(struct rtcp_process_ctx *, struct report_block *);
static void transcode_sr_wrap(struct rtcp_process_ctx *, struct sender_report_packet *); static void transcode_sr_wrap(struct rtcp_process_ctx *, struct sender_report_packet *);
// RTCP sinks for local RTCP generation
static void sink_common(struct rtcp_process_ctx *, struct rtcp_packet *);
// homer functions // homer functions
static void homer_init(struct rtcp_process_ctx *); static void homer_init(struct rtcp_process_ctx *);
static void homer_sr(struct rtcp_process_ctx *, struct sender_report_packet *); static void homer_sr(struct rtcp_process_ctx *, struct sender_report_packet *);
@ -358,6 +365,9 @@ static struct rtcp_handler transcode_handlers = {
.rr = transcode_rr, .rr = transcode_rr,
.sr = transcode_sr, .sr = transcode_sr,
}; };
static struct rtcp_handler sink_handlers = {
.common = sink_common,
};
static struct rtcp_handler transcode_handlers_wrap = { static struct rtcp_handler transcode_handlers_wrap = {
.common = transcode_common_wrap, .common = transcode_common_wrap,
.rr = transcode_rr_wrap, .rr = transcode_rr_wrap,
@ -472,6 +482,7 @@ static const int min_xr_packet_sizes[] = {
struct rtcp_handler *rtcp_transcode_handler = &transcode_handlers; struct rtcp_handler *rtcp_transcode_handler = &transcode_handlers;
struct rtcp_handler *rtcp_sink_handler = &sink_handlers;
@ -645,6 +656,7 @@ void rtcp_list_free(GQueue *q) {
// returns: 0 = ok, forward, -1 = error, drop, 1 = ok, but discard (no forward)
int rtcp_parse(GQueue *q, struct media_packet *mp) { int rtcp_parse(GQueue *q, struct media_packet *mp) {
struct rtcp_header *hdr; struct rtcp_header *hdr;
struct rtcp_chain_element *el; struct rtcp_chain_element *el;
@ -713,7 +725,7 @@ next:
CAH(finish, c, &mp->fsin, &mp->sfd->socket.local, &mp->tv); CAH(finish, c, &mp->fsin, &mp->sfd->socket.local, &mp->tv);
CAH(destroy); CAH(destroy);
return 0; return log_ctx->discard ? 1 : 0;
error: error:
CAH(finish, c, &mp->fsin, &mp->sfd->socket.local, &mp->tv); CAH(finish, c, &mp->fsin, &mp->sfd->socket.local, &mp->tv);
@ -1357,17 +1369,20 @@ static void transcode_sr(struct rtcp_process_ctx *ctx, struct sender_report_pack
static void transcode_common_wrap(struct rtcp_process_ctx *ctx, struct rtcp_packet *common) { static void transcode_common_wrap(struct rtcp_process_ctx *ctx, struct rtcp_packet *common) {
if (!ctx->mp->media->rtcp_handler) if (!ctx->mp->media->rtcp_handler)
return; return;
ctx->mp->media->rtcp_handler->common(ctx, common); if (ctx->mp->media->rtcp_handler->common)
ctx->mp->media->rtcp_handler->common(ctx, common);
} }
static void transcode_rr_wrap(struct rtcp_process_ctx *ctx, struct report_block *rr) { static void transcode_rr_wrap(struct rtcp_process_ctx *ctx, struct report_block *rr) {
if (!ctx->mp->media->rtcp_handler) if (!ctx->mp->media->rtcp_handler)
return; return;
ctx->mp->media->rtcp_handler->rr(ctx, rr); if (ctx->mp->media->rtcp_handler->rr)
ctx->mp->media->rtcp_handler->rr(ctx, rr);
} }
static void transcode_sr_wrap(struct rtcp_process_ctx *ctx, struct sender_report_packet *sr) { static void transcode_sr_wrap(struct rtcp_process_ctx *ctx, struct sender_report_packet *sr) {
if (!ctx->mp->media->rtcp_handler) if (!ctx->mp->media->rtcp_handler)
return; return;
ctx->mp->media->rtcp_handler->sr(ctx, sr); if (ctx->mp->media->rtcp_handler->sr)
ctx->mp->media->rtcp_handler->sr(ctx, sr);
} }
@ -1398,8 +1413,8 @@ GString *rtcp_sender_report(uint32_t ssrc, uint32_t ts, uint32_t packets, uint32
// receiver reports // receiver reports
int i = 0, n = 0; int i = 0, n = 0;
for (GList *l = rrs->head; l; l = l->next) { while (rrs->length) {
struct ssrc_ctx *s = l->data; struct ssrc_ctx *s = g_queue_pop_head(rrs);
if (i < 30) { if (i < 30) {
struct report_block *rr = (void *) ret->str + ret->len; struct report_block *rr = (void *) ret->str + ret->len;
g_string_set_size(ret, ret->len + sizeof(*rr)); g_string_set_size(ret, ret->len + sizeof(*rr));
@ -1416,6 +1431,7 @@ GString *rtcp_sender_report(uint32_t ssrc, uint32_t ts, uint32_t packets, uint32
tv_diff = timeval_diff(&rtpe_now, &si->received); tv_diff = timeval_diff(&rtpe_now, &si->received);
ntp_middle_bits = si->ntp_middle_bits; ntp_middle_bits = si->ntp_middle_bits;
} }
uint32_t jitter = se->jitter;
mutex_unlock(&se->h.lock); mutex_unlock(&se->h.lock);
uint64_t lost = atomic64_get(&s->packets_lost); uint64_t lost = atomic64_get(&s->packets_lost);
@ -1430,8 +1446,8 @@ GString *rtcp_sender_report(uint32_t ssrc, uint32_t ts, uint32_t packets, uint32
.high_seq_received = htonl(atomic64_get(&s->last_seq)), .high_seq_received = htonl(atomic64_get(&s->last_seq)),
.lsr = htonl(ntp_middle_bits), .lsr = htonl(ntp_middle_bits),
.dlsr = htonl(tv_diff * 65536 / 1000000), .dlsr = htonl(tv_diff * 65536 / 1000000),
.jitter = htonl(jitter >> 4),
}; };
// XXX jitter
n++; n++;
} }
ssrc_ctx_put(&s); ssrc_ctx_put(&s);
@ -1478,7 +1494,7 @@ void rtcp_receiver_reports(GQueue *out, struct ssrc_hash *hash, struct call_mono
rwlock_lock_r(&hash->lock); rwlock_lock_r(&hash->lock);
for (GList *l = hash->q.head; l; l = l->next) { for (GList *l = hash->q.head; l; l = l->next) {
struct ssrc_entry_call *e = l->data; struct ssrc_entry_call *e = l->data;
ilog(LOG_DEBUG, "xxxxx %x %i %i %p %p %p", e->h.ssrc, (int) atomic64_get(&e->input_ctx.packets), (int) atomic64_get(&e->output_ctx.packets), ml, e->input_ctx.ref, e->output_ctx.ref); //ilog(LOG_DEBUG, "xxxxx %x %i %i %p %p %p", e->h.ssrc, (int) atomic64_get(&e->input_ctx.packets), (int) atomic64_get(&e->output_ctx.packets), ml, e->input_ctx.ref, e->output_ctx.ref);
struct ssrc_ctx *i = &e->input_ctx; struct ssrc_ctx *i = &e->input_ctx;
if (i->ref != ml) if (i->ref != ml)
continue; continue;
@ -1489,3 +1505,44 @@ void rtcp_receiver_reports(GQueue *out, struct ssrc_hash *hash, struct call_mono
} }
rwlock_unlock_r(&hash->lock); rwlock_unlock_r(&hash->lock);
} }
// call must be locked in R
void rtcp_send_report(struct call_media *media, struct ssrc_ctx *ssrc_out) {
struct call *call = media->call;
// figure out where to send it
struct packet_stream *ps = media->streams.head->data;
if (MEDIA_ISSET(media, RTCP_MUX))
;
else if (!media->streams.head->next)
;
else {
struct packet_stream *next_ps = media->streams.head->next->data;
if (PS_ISSET(next_ps, RTCP))
ps = next_ps;
}
log_info_stream_fd(ps->selected_sfd);
GQueue rrs = G_QUEUE_INIT;
rtcp_receiver_reports(&rrs, call->ssrc_hash, ps->media->monologue);
ilog(LOG_DEBUG, "Generating and sending RTCP SR for %x and up to %i source(s)",
ssrc_out->parent->h.ssrc, rrs.length);
GString *sr = rtcp_sender_report(ssrc_out->parent->h.ssrc,
atomic64_get(&ssrc_out->last_ts),
atomic64_get(&ssrc_out->packets),
atomic64_get(&ssrc_out->octets),
&rrs);
socket_sendto(&ps->selected_sfd->socket, sr->str, sr->len, &ps->endpoint);
g_string_free(sr, TRUE);
}
static void sink_common(struct rtcp_process_ctx *ctx, struct rtcp_packet *common) {
ctx->discard = 1;
}

@ -745,6 +745,51 @@ received within this time frame, then DTX processing will stop. Can be set to
zero or negative to disable and keep DTX processing on indefinitely. Defaults zero or negative to disable and keep DTX processing on indefinitely. Defaults
to 30 seconds. to 30 seconds.
=item B<--silence-detect=>I<FLOAT>
Enable silence detection and specify threshold in percent. This option is
applicable to transcoded stream only and defaults to zero (disabled).
When enabled, silence detection will be performed on all transcoded audio
streams. The threshold specified here is the sensitivity for detecting silence:
higher thresholds result in more audio to be detected as silence, while lower
thresholds result in less audio to be detected as silence. The threshold is
specified as percent between zero and 100. If set to 100, then all audio would
be detected as silence; if set to 50, then any audio that is quieter than 50%
of the maximum volume would be detected as silence; and so on. Setting it to
zero disables silence detection. To only detect silence that is very near or
equal to absolute silence, set this value to a low number such as 0.01. (For
certain codecs such as PCMA, a higher minimum threshold is required to detect
complete silence, as their compressed payloads don't decode to actual silence
but instead have a residual DC offset. For PCMA the minimum value is 0.013.)
Audio that is detected as silence will be replaced by comfort noise as
specified by the B<cn-payload> option (see below). Currently this is applicable
only to RTP peers that have advertised support for the B<CN> RTP payload type,
in which case the silence audio frames will be replaced by B<CN> RTP frames.
=item B<--cn-payload=>I<INT>
Specify one comfort noise parameter. This option can be given multiple times
and the format follows RFC 3389. When specified at the command line, list the
B<--cn-payload=> option multiple times, each one specifying a single CN
parameter. When used in the config file, list the option only a single time and
list multiple CN parameters separated by semicolons (e.g.
I<cn-payload = 20;40;60>).
The first CN payload value given is the noise level, specified as -dBov as per
RFC 3389. This means that a noise level of zero corresponds to maximum volume,
while higher numbers correspond to lower volumes. The highest allowable number
is 127, corresponding to -127 dBov, which is near silence.
Subsequent CN payload values carry spectral information (reflection
coefficients) as per RFC 3389. Allowable values for each coefficient are
between 0 and 254. Specifying spectral information is optional and the number
of coefficients listed (model order) is variable.
The default values are 32 (-32 dBov) for the noise level and no spectral
information.
=back =back
=head1 INTERFACES =head1 INTERFACES

@ -630,11 +630,13 @@ int stun(const str *b, struct stream_fd *sfd, const endpoint_t *sin) {
bad_req: bad_req:
ilog(LOG_NOTICE | LOG_FLAG_LIMIT, "Received invalid STUN packet" SLF ": %s", SLP, err); ilog(LOG_NOTICE | LOG_FLAG_LIMIT, "Received invalid STUN packet" SLF ": %s", SLP, err);
stun_error(sfd, sin, req, 400, "Bad request"); if (class == STUN_CLASS_REQUEST)
stun_error(sfd, sin, req, 400, "Bad request");
return 0; return 0;
unauth: unauth:
ilog(LOG_NOTICE | LOG_FLAG_LIMIT, "STUN authentication mismatch" SLF, SLP); ilog(LOG_NOTICE | LOG_FLAG_LIMIT, "STUN authentication mismatch" SLF, SLP);
stun_error(sfd, sin, req, 401, "Unauthorized"); if (class == STUN_CLASS_REQUEST)
stun_error(sfd, sin, req, 401, "Unauthorized");
return 0; return 0;
ignore: ignore:
ilog(LOG_NOTICE | LOG_FLAG_LIMIT, "Not handling potential STUN packet" SLF ": %s", SLP, err); ilog(LOG_NOTICE | LOG_FLAG_LIMIT, "Not handling potential STUN packet" SLF ": %s", SLP, err);

@ -149,6 +149,7 @@ enum call_stream_state {
#define MEDIA_FLAG_RTCP_FB SHARED_FLAG_RTCP_FB #define MEDIA_FLAG_RTCP_FB SHARED_FLAG_RTCP_FB
#define MEDIA_FLAG_GENERATOR 0x02000000 #define MEDIA_FLAG_GENERATOR 0x02000000
#define MEDIA_FLAG_ICE_LITE_SELF 0x04000000 #define MEDIA_FLAG_ICE_LITE_SELF 0x04000000
#define MEDIA_FLAG_RTCP_GEN 0x08000000
/* access macros */ /* access macros */
#define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f) #define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f)
@ -331,6 +332,7 @@ struct call_media {
GQueue codec_handlers_store; // storage for struct codec_handler GQueue codec_handlers_store; // storage for struct codec_handler
struct codec_handler *codec_handler_cache; struct codec_handler *codec_handler_cache;
struct rtcp_handler *rtcp_handler; struct rtcp_handler *rtcp_handler;
struct timeval rtcp_timer; // master lock for scheduling purposes
struct codec_handler *dtmf_injector; struct codec_handler *dtmf_injector;
struct t38_gateway *t38_gateway; struct t38_gateway *t38_gateway;
struct codec_handler *t38_handler; struct codec_handler *t38_handler;

@ -42,6 +42,8 @@ struct sdp_ng_flags {
GQueue codec_offer; GQueue codec_offer;
GQueue codec_transcode; GQueue codec_transcode;
GHashTable *codec_mask; GHashTable *codec_mask;
GHashTable *codec_accept;
GHashTable *codec_consume;
GHashTable *codec_set; GHashTable *codec_set;
int ptime, int ptime,
rev_ptime; rev_ptime;
@ -75,6 +77,7 @@ struct sdp_ng_flags {
rtcp_mux_reject:1, rtcp_mux_reject:1,
no_rtcp_attr:1, no_rtcp_attr:1,
full_rtcp_attr:1, full_rtcp_attr:1,
generate_rtcp:1,
generate_mid:1, generate_mid:1,
strict_source:1, strict_source:1,
media_handover:1, media_handover:1,
@ -88,7 +91,6 @@ struct sdp_ng_flags {
record_call:1, record_call:1,
loop_protect:1, loop_protect:1,
original_sendrecv:1, original_sendrecv:1,
always_transcode:1,
asymmetric_codecs:1, asymmetric_codecs:1,
symmetric_codecs:1, symmetric_codecs:1,
single_codec:1, single_codec:1,

@ -29,6 +29,7 @@ struct codec_handler {
struct rtp_payload_type source_pt; // source_pt.payload_type = hashtable index struct rtp_payload_type source_pt; // source_pt.payload_type = hashtable index
struct rtp_payload_type dest_pt; struct rtp_payload_type dest_pt;
int dtmf_payload_type; int dtmf_payload_type;
int cn_payload_type;
codec_handler_func *func; codec_handler_func *func;
int kernelize:1; int kernelize:1;
int transcoder:1; int transcoder:1;

@ -17,6 +17,7 @@
struct rtpengine_target_info; struct rtpengine_target_info;
struct re_address; struct re_address;
struct rtpengine_ssrc_stats;
@ -35,6 +36,7 @@ int kernel_setup_table(unsigned int);
int kernel_add_stream(struct rtpengine_target_info *, int); int kernel_add_stream(struct rtpengine_target_info *, int);
int kernel_del_stream(const struct re_address *); int kernel_del_stream(const struct re_address *);
GList *kernel_list(void); GList *kernel_list(void);
int kernel_update_stats(const struct re_address *a, uint32_t ssrc, struct rtpengine_ssrc_stats *out);
unsigned int kernel_add_call(const char *id); unsigned int kernel_add_call(const char *id);
int kernel_del_call(unsigned int); int kernel_del_call(unsigned int);

@ -108,6 +108,9 @@ struct rtpengine_config {
int http_threads; int http_threads;
int dtx_delay; int dtx_delay;
int max_dtx; int max_dtx;
double silence_detect_double;
uint32_t silence_detect_int;
str cn_payload;
}; };

@ -176,6 +176,8 @@ void __unkernelize(struct packet_stream *);
void unkernelize(struct packet_stream *); void unkernelize(struct packet_stream *);
void __stream_unconfirm(struct packet_stream *); void __stream_unconfirm(struct packet_stream *);
void media_update_stats(struct call_media *m);
void media_packet_copy(struct media_packet *, const struct media_packet *); void media_packet_copy(struct media_packet *, const struct media_packet *);
void media_packet_release(struct media_packet *); void media_packet_release(struct media_packet *);
int media_socket_dequeue(struct media_packet *mp, struct packet_stream *sink); int media_socket_dequeue(struct media_packet *mp, struct packet_stream *sink);

@ -22,6 +22,7 @@ struct rtcp_parse_ctx {
extern struct rtcp_handler *rtcp_transcode_handler; extern struct rtcp_handler *rtcp_transcode_handler;
extern struct rtcp_handler *rtcp_sink_handler;
int rtcp_avp2savp(str *, struct crypto_context *, struct ssrc_ctx *); int rtcp_avp2savp(str *, struct crypto_context *, struct ssrc_ctx *);
@ -39,5 +40,6 @@ void rtcp_init(void);
GString *rtcp_sender_report(uint32_t ssrc, uint32_t ts, uint32_t packets, uint32_t octets, GQueue *rrs); GString *rtcp_sender_report(uint32_t ssrc, uint32_t ts, uint32_t packets, uint32_t octets, GQueue *rrs);
void rtcp_receiver_reports(GQueue *out, struct ssrc_hash *hash, struct call_monologue *ml); void rtcp_receiver_reports(GQueue *out, struct ssrc_hash *hash, struct call_monologue *ml);
void rtcp_send_report(struct call_media *media, struct ssrc_ctx *ssrc_out);
#endif #endif

@ -98,6 +98,7 @@ struct ssrc_entry_call {
// for transcoding // for transcoding
// input only // input only
packet_sequencer_t sequencer; packet_sequencer_t sequencer;
uint32_t jitter, transit;
// output only // output only
uint16_t seq_diff; uint16_t seq_diff;
}; };

@ -277,6 +277,8 @@ struct rtpengine_target {
struct rtpengine_stats_a stats; struct rtpengine_stats_a stats;
struct rtpengine_rtp_stats_a rtp_stats[NUM_PAYLOAD_TYPES]; struct rtpengine_rtp_stats_a rtp_stats[NUM_PAYLOAD_TYPES];
spinlock_t ssrc_stats_lock;
struct rtpengine_ssrc_stats ssrc_stats;
struct re_crypto_context decrypt; struct re_crypto_context decrypt;
struct re_crypto_context encrypt; struct re_crypto_context encrypt;
@ -1573,6 +1575,8 @@ static int proc_list_show(struct seq_file *f, void *v) {
seq_printf(f, " option: transcoding\n"); seq_printf(f, " option: transcoding\n");
if (g->target.non_forwarding) if (g->target.non_forwarding)
seq_printf(f, " option: non forwarding\n"); seq_printf(f, " option: non forwarding\n");
if (g->target.rtp_stats)
seq_printf(f, " option: RTP stats\n");
target_put(g); target_put(g);
@ -1658,6 +1662,32 @@ static struct re_dest_addr *find_dest_addr(const struct re_dest_addr_hash *h, co
static int table_get_target_stats(struct rtpengine_table *t, struct rtpengine_stats_info *i, int reset) {
struct rtpengine_target *g;
g = get_target(t, &i->local);
if (!g)
return -ENOENT;
i->ssrc = g->target.ssrc;
spin_lock(&g->ssrc_stats_lock);
i->ssrc_stats = g->ssrc_stats;
if (reset) {
g->ssrc_stats.basic_stats.packets = 0;
g->ssrc_stats.basic_stats.bytes = 0;
g->ssrc_stats.total_lost = 0;
}
spin_unlock(&g->ssrc_stats_lock);
target_put(g);
return 0;
}
static int table_del_target(struct rtpengine_table *t, const struct re_address *local) { static int table_del_target(struct rtpengine_table *t, const struct re_address *local) {
unsigned char hi, lo; unsigned char hi, lo;
struct re_dest_addr *rda; struct re_dest_addr *rda;
@ -2131,6 +2161,8 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i
memcpy(&g->target, i, sizeof(*i)); memcpy(&g->target, i, sizeof(*i));
crypto_context_init(&g->decrypt, &g->target.decrypt); crypto_context_init(&g->decrypt, &g->target.decrypt);
crypto_context_init(&g->encrypt, &g->target.encrypt); crypto_context_init(&g->encrypt, &g->target.encrypt);
spin_lock_init(&g->ssrc_stats_lock);
g->ssrc_stats.lost_bits = -1;
err = gen_session_keys(&g->decrypt, &g->target.decrypt); err = gen_session_keys(&g->decrypt, &g->target.decrypt);
if (err) if (err)
@ -3244,6 +3276,20 @@ static inline ssize_t proc_control_read_write(struct file *file, char __user *ub
err = table_new_target(t, &msg->u.target, 1); err = table_new_target(t, &msg->u.target, 1);
break; break;
case REMG_GET_STATS:
err = -EINVAL;
if (!writeable)
goto err;
err = table_get_target_stats(t, &msg->u.stats, 0);
break;
case REMG_GET_RESET_STATS:
err = -EINVAL;
if (!writeable)
goto err;
err = table_get_target_stats(t, &msg->u.stats, 1);
break;
case REMG_ADD_CALL: case REMG_ADD_CALL:
err = -EINVAL; err = -EINVAL;
if (!writeable) if (!writeable)
@ -3925,6 +3971,88 @@ static struct sk_buff *intercept_skb_copy(struct sk_buff *oskb, const struct re_
static void rtp_stats(struct rtpengine_target *g, struct rtp_parsed *rtp, s64 arrival_time, int pt_idx) {
unsigned long flags;
struct rtpengine_ssrc_stats *s = &g->ssrc_stats;
u_int16_t old_seq_trunc;
u_int32_t last_seq;
u_int16_t seq_diff;
u_int32_t clockrate;
u_int32_t transit;
int32_t d;
u_int16_t seq = ntohs(rtp->header->seq_num);
u_int32_t ts = ntohl(rtp->header->timestamp);
spin_lock_irqsave(&g->ssrc_stats_lock, flags);
s->basic_stats.packets++;
s->basic_stats.bytes += rtp->payload_len;
s->timestamp = ts;
// track sequence numbers and lost frames
last_seq = s->ext_seq;
// old seq or seq reset?
old_seq_trunc = last_seq & 0xffff;
seq_diff = seq - old_seq_trunc;
if (seq_diff == 0 || seq_diff >= 0xfeff) // old/dup seq - ignore
;
else if (seq_diff > 0x100) {
// reset seq and loss tracker
s->ext_seq = seq;
s->lost_bits = -1;
}
else {
// seq wrap?
u_int32_t new_seq = (last_seq & 0xffff0000) | seq;
while (new_seq < last_seq) {
new_seq += 0x10000;
if ((new_seq & 0xffff0000) == 0) // ext seq wrapped
break;
}
seq_diff = new_seq - s->ext_seq;
s->ext_seq = new_seq;
// shift loss tracker bit field and count losses
if (seq_diff >= (sizeof(s->lost_bits) * 8)) {
// complete loss
s->total_lost += sizeof(s->lost_bits) * 8;
s->lost_bits = -1;
}
else {
while (seq_diff) {
// shift out one bit and see if we lost it
if ((s->lost_bits & 0x80000000) == 0)
s->total_lost++;
s->lost_bits <<= 1;
seq_diff--;
}
}
}
// track this frame as being seen
seq_diff = (s->ext_seq & 0xffff) - seq;
if (seq_diff < (sizeof(s->lost_bits) * 8))
s->lost_bits |= (1 << seq_diff);
// jitter
// RFC 3550 A.8
clockrate = g->target.clock_rates[pt_idx];
transit = (((arrival_time / 1000) * clockrate) / 1000) - ts;
d = 0;
if (s->transit)
d = transit - s->transit;
s->transit = transit;
if (d < 0)
d = -d;
s->jitter += d - ((s->jitter + 8) >> 4);
spin_unlock_irqrestore(&g->ssrc_stats_lock, flags);
}
static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src, static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, struct re_address *src,
struct re_address *dst, u_int8_t in_tos, const struct xt_action_param *par) struct re_address *dst, u_int8_t in_tos, const struct xt_action_param *par)
{ {
@ -4040,6 +4168,9 @@ src_check_ok:
skb_trim(skb, rtp.header_len + rtp.payload_len); skb_trim(skb, rtp.header_len + rtp.payload_len);
if (g->target.rtp_stats)
rtp_stats(g, &rtp, ktime_to_us(skb->tstamp), rtp_pt_idx);
DBG("packet payload decrypted as %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x...\n", DBG("packet payload decrypted as %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x...\n",
rtp.payload[0], rtp.payload[1], rtp.payload[2], rtp.payload[3], rtp.payload[0], rtp.payload[1], rtp.payload[2], rtp.payload[3],
rtp.payload[4], rtp.payload[5], rtp.payload[6], rtp.payload[7], rtp.payload[4], rtp.payload[5], rtp.payload[6], rtp.payload[7],

@ -24,6 +24,15 @@ struct rtpengine_rtp_stats {
u_int64_t packets; u_int64_t packets;
u_int64_t bytes; u_int64_t bytes;
}; };
struct rtpengine_ssrc_stats {
struct rtpengine_rtp_stats basic_stats;
u_int32_t timestamp;
u_int32_t ext_seq;
u_int32_t lost_bits; // sliding bitfield, [0] = ext_seq
u_int32_t total_lost;
u_int32_t transit;
u_int32_t jitter;
};
struct re_address { struct re_address {
int family; int family;
@ -95,6 +104,7 @@ struct rtpengine_target_info {
u_int32_t ssrc_out; // Rewrite SSRC u_int32_t ssrc_out; // Rewrite SSRC
unsigned char payload_types[NUM_PAYLOAD_TYPES]; /* must be sorted */ unsigned char payload_types[NUM_PAYLOAD_TYPES]; /* must be sorted */
u_int32_t clock_rates[NUM_PAYLOAD_TYPES];
unsigned int num_payload_types; unsigned int num_payload_types;
unsigned char tos; unsigned char tos;
@ -105,7 +115,8 @@ struct rtpengine_target_info {
rtp_only:1, rtp_only:1,
do_intercept:1, do_intercept:1,
transcoding:1, // SSRC subst and RTP PT filtering transcoding:1, // SSRC subst and RTP PT filtering
non_forwarding:1; // empty src/dst addr non_forwarding:1, // empty src/dst addr
rtp_stats:1; // requires SSRC and clock_rates to be set
}; };
struct rtpengine_call_info { struct rtpengine_call_info {
@ -125,6 +136,12 @@ struct rtpengine_packet_info {
unsigned int stream_idx; unsigned int stream_idx;
}; };
struct rtpengine_stats_info {
struct re_address local; // input
u_int32_t ssrc; // output
struct rtpengine_ssrc_stats ssrc_stats; // output
};
struct rtpengine_message { struct rtpengine_message {
enum { enum {
REMG_NOOP = 1, REMG_NOOP = 1,
@ -145,6 +162,10 @@ struct rtpengine_message {
/* packet_info: */ /* packet_info: */
REMG_PACKET, REMG_PACKET,
/* stats_info: */
REMG_GET_STATS,
REMG_GET_RESET_STATS,
__REMG_LAST __REMG_LAST
} cmd; } cmd;
@ -153,6 +174,7 @@ struct rtpengine_message {
struct rtpengine_call_info call; struct rtpengine_call_info call;
struct rtpengine_stream_info stream; struct rtpengine_stream_info stream;
struct rtpengine_packet_info packet; struct rtpengine_packet_info packet;
struct rtpengine_stats_info stats;
} u; } u;
unsigned char data[]; unsigned char data[];

@ -63,6 +63,8 @@ static int ilbc_decoder_input(decoder_t *dec, const str *data, GQueue *out);
static const char *dtmf_decoder_init(decoder_t *, const str *, const str *); static const char *dtmf_decoder_init(decoder_t *, const str *, const str *);
static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out); static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out);
static int cn_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 format_cmp_ignore(const struct rtp_payload_type *, const struct rtp_payload_type *);
static int amr_packet_lost(decoder_t *, GQueue *); static int amr_packet_lost(decoder_t *, GQueue *);
@ -102,6 +104,12 @@ static const codec_type_t codec_type_dtmf = {
.decoder_init = dtmf_decoder_init, .decoder_init = dtmf_decoder_init,
.decoder_input = dtmf_decoder_input, .decoder_input = dtmf_decoder_input,
}; };
static const codec_type_t codec_type_cn = {
.def_init = avc_def_init,
.decoder_init = avc_decoder_init,
.decoder_input = cn_decoder_input,
.decoder_close = avc_decoder_close,
};
#ifdef HAVE_BCG729 #ifdef HAVE_BCG729
static packetizer_f packetizer_g729; // aggregate some frames into packets static packetizer_f packetizer_g729; // aggregate some frames into packets
@ -407,6 +415,20 @@ static codec_def_t __codec_defs[] = {
.support_encoding = 1, .support_encoding = 1,
.support_decoding = 1, .support_decoding = 1,
}, },
{
.rtpname = "CN",
.avcodec_id = AV_CODEC_ID_COMFORT_NOISE,
.avcodec_name_enc = "comfortnoise",
.avcodec_name_dec = "comfortnoise",
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.supplemental = 1,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.format_cmp = format_cmp_ignore,
.codec_type = &codec_type_cn,
},
// for file reading and writing // for file reading and writing
{ {
.rtpname = "PCM-S16LE", .rtpname = "PCM-S16LE",
@ -2314,3 +2336,20 @@ static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
static int format_cmp_ignore(const struct rtp_payload_type *a, const struct rtp_payload_type *b) { static int format_cmp_ignore(const struct rtp_payload_type *a, const struct rtp_payload_type *b) {
return 0; return 0;
} }
static int cn_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
// generate one set of ptime worth of samples
int ptime = dec->ptime;
if (!ptime)
ptime = 20; // ?
int samples = dec->in_format.clockrate * ptime / 1000;
dec->u.avc.avcctx->frame_size = samples;
int ret = avc_decoder_input(dec, data, out);
if (ret)
return ret;
if (!out->length)
return -1;
return 0;
}

File diff suppressed because it is too large Load Diff

@ -39,10 +39,20 @@ static struct call_media *media_A;
static struct call_media *media_B; static struct call_media *media_B;
struct call_monologue ml_A; struct call_monologue ml_A;
struct call_monologue ml_B; struct call_monologue ml_B;
static GQueue rtp_types; static GQueue rtp_types = G_QUEUE_INIT;
#define start() __start(__FILE__, __LINE__) #define start() __start(__FILE__, __LINE__)
static void __init(void) {
g_queue_clear(&rtp_types);
memset(&flags, 0, sizeof(flags));
flags.codec_strip = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
flags.codec_mask = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
flags.codec_except = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
flags.codec_set = g_hash_table_new_full(str_case_hash, str_case_equal, free, free);
flags.codec_consume = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
flags.codec_accept = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
}
static void __start(const char *file, int line) { static void __start(const char *file, int line) {
printf("running test %s:%i\n", file, line); printf("running test %s:%i\n", file, line);
rtp_ts_ht = g_hash_table_new(g_direct_hash, g_direct_equal); rtp_ts_ht = g_hash_table_new(g_direct_hash, g_direct_equal);
@ -53,7 +63,6 @@ static void __start(const char *file, int line) {
call.ssrc_hash = create_ssrc_hash_call(); call.ssrc_hash = create_ssrc_hash_call();
call.tags = g_hash_table_new(g_str_hash, g_str_equal); call.tags = g_hash_table_new(g_str_hash, g_str_equal);
str_init(&call.callid, "test-call"); str_init(&call.callid, "test-call");
flags = (struct sdp_ng_flags) {0,};
bencode_buffer_init(&call.buffer); bencode_buffer_init(&call.buffer);
media_A = call_media_new(&call); // originator media_A = call_media_new(&call); // originator
media_B = call_media_new(&call); // output destination media_B = call_media_new(&call); // output destination
@ -65,11 +74,7 @@ static void __start(const char *file, int line) {
str_init(&ml_B.tag, "tag_B"); str_init(&ml_B.tag, "tag_B");
media_B->monologue = &ml_B; media_B->monologue = &ml_B;
media_B->protocol = &transport_protocols[PROTO_RTP_AVP]; media_B->protocol = &transport_protocols[PROTO_RTP_AVP];
g_queue_init(&rtp_types); // parsed from received SDP __init();
flags.codec_strip = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
flags.codec_mask = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
flags.codec_except = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL);
flags.codec_set = g_hash_table_new_full(str_case_hash, str_case_equal, free, free);
} }
#define transcode(codec) g_queue_push_tail(&flags.codec_transcode, sdup(#codec)) #define transcode(codec) g_queue_push_tail(&flags.codec_transcode, sdup(#codec))
@ -92,6 +97,12 @@ static void codec_set(char *c) {
} }
#endif #endif
static void __ht_set(GHashTable *h, char *x) {
str *d = sdup(x);
g_hash_table_insert(h, d, d);
}
#define ht_set(ht, s) __ht_set(flags.ht, #s)
#define sdp_pt_fmt(num, codec, clockrate, fmt) \ #define sdp_pt_fmt(num, codec, clockrate, fmt) \
__sdp_pt_fmt(num, (str) STR_CONST_INIT(#codec), clockrate, (str) STR_CONST_INIT(#codec "/" #clockrate), \ __sdp_pt_fmt(num, (str) STR_CONST_INIT(#codec), clockrate, (str) STR_CONST_INIT(#codec "/" #clockrate), \
(str) STR_CONST_INIT(fmt)) (str) STR_CONST_INIT(fmt))
@ -112,8 +123,7 @@ static void offer(void) {
codec_rtp_payload_types(media_B, media_A, &rtp_types, &flags); codec_rtp_payload_types(media_B, media_A, &rtp_types, &flags);
codec_handlers_update(media_B, media_A, &flags, NULL); codec_handlers_update(media_B, media_A, &flags, NULL);
codec_tracker_finish(media_B); codec_tracker_finish(media_B);
g_queue_clear(&rtp_types); __init();
memset(&flags, 0, sizeof(flags));
} }
static void answer(void) { static void answer(void) {
@ -123,8 +133,7 @@ static void answer(void) {
codec_rtp_payload_types(media_A, media_B, &rtp_types, &flags); codec_rtp_payload_types(media_A, media_B, &rtp_types, &flags);
codec_handlers_update(media_A, media_B, &flags, NULL); codec_handlers_update(media_A, media_B, &flags, NULL);
codec_tracker_finish(media_A); codec_tracker_finish(media_A);
g_queue_clear(&rtp_types); __init();
memset(&flags, 0, sizeof(flags));
} }
#define expect(side, dir, codecs) \ #define expect(side, dir, codecs) \
@ -318,6 +327,7 @@ static void dtmf(const char *s) {
#define PCMU_payload "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00" #define PCMU_payload "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00"
#define PCMA_payload "\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a" #define PCMA_payload "\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a\x2b\x2a"
#define PCMA_silence "\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\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\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\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\xd5\xd5\xd5\xd5"
#define G722_payload "\x23\x84\x20\x84\x20\x84\x04\x84\x04\x04\x84\x04\x84\x04\x84\x05\x85\x46\x87\x48\xc8\x48\x88\x48\xc8\x49\x8a\x4b\xcc\x4c\x8c\x4c\xcc\x4c\x8c\x4d\xce\x50\xcf\x51\x90\x50\xcf\x12\xd1\x52\xd2\x54\x91\x52\xd2\x54\x92\x54\xd3\x56\x93\xd6\x94\xd4\x93\xd7\xd5\x55\x94\x55\xd5\x55\xd4\x56\xd5\x17\xd7\x5a\x95\xd7\x97\xd9\xd4\x16\x58\x57\x98\xd5\xd7\x5b\x96\xda\xd6\x1b\x57\x5a\xd6\x1a\x57\x5b\x98\xd6\xd8\x56\x98\xd7\xd9\x5a\x95\xdb\xd6\x1c\x52\x5e\xd7\x5c\x93\xdf\x99\xd5\xd7\x5f\xd9\x14\x56\x7f\x92\xda\xd9\x5c\x92\xdd\xd7\x5d\x92\xff\xd6\x5a\x96\xdc\xd5\x18\x56\x7e\xd2\x5e\x96\xde\x94\xd8\xd8\x58\xd3\x79\x93\xfb\x90\xdc\xd6\x5b\xdd\x58\x96\xff" #define G722_payload "\x23\x84\x20\x84\x20\x84\x04\x84\x04\x04\x84\x04\x84\x04\x84\x05\x85\x46\x87\x48\xc8\x48\x88\x48\xc8\x49\x8a\x4b\xcc\x4c\x8c\x4c\xcc\x4c\x8c\x4d\xce\x50\xcf\x51\x90\x50\xcf\x12\xd1\x52\xd2\x54\x91\x52\xd2\x54\x92\x54\xd3\x56\x93\xd6\x94\xd4\x93\xd7\xd5\x55\x94\x55\xd5\x55\xd4\x56\xd5\x17\xd7\x5a\x95\xd7\x97\xd9\xd4\x16\x58\x57\x98\xd5\xd7\x5b\x96\xda\xd6\x1b\x57\x5a\xd6\x1a\x57\x5b\x98\xd6\xd8\x56\x98\xd7\xd9\x5a\x95\xdb\xd6\x1c\x52\x5e\xd7\x5c\x93\xdf\x99\xd5\xd7\x5f\xd9\x14\x56\x7f\x92\xda\xd9\x5c\x92\xdd\xd7\x5d\x92\xff\xd6\x5a\x96\xdc\xd5\x18\x56\x7e\xd2\x5e\x96\xde\x94\xd8\xd8\x58\xd3\x79\x93\xfb\x90\xdc\xd6\x5b\xdd\x58\x96\xff"
#define AMR_WB_payload "\xf0\x1c\xf3\x06\x08\x10\x77\x32\x23\x20\xd3\x50\x62\x12\xc7\x7c\xe2\xea\x84\x0e\x6e\xf4\x4d\xe4\x7f\xc9\x4c\xcc\x58\x5d\xed\xcc\x5d\x7c\x6c\x14\x7d\xc0" // octet aligned #define AMR_WB_payload "\xf0\x1c\xf3\x06\x08\x10\x77\x32\x23\x20\xd3\x50\x62\x12\xc7\x7c\xe2\xea\x84\x0e\x6e\xf4\x4d\xe4\x7f\xc9\x4c\xcc\x58\x5d\xed\xcc\x5d\x7c\x6c\x14\x7d\xc0" // octet aligned
#define AMR_WB_payload_noe "\xf1\xfc\xc1\x82\x04\x1d\xcc\x88\xc8\x34\xd4\x18\x84\xb1\xdf\x38\xba\xa1\x03\x9b\xbd\x13\x79\x1f\xf2\x53\x33\x16\x17\x7b\x73\x17\x5f\x1b\x05\x1f\x70" // bandwidth efficient #define AMR_WB_payload_noe "\xf1\xfc\xc1\x82\x04\x1d\xcc\x88\xc8\x34\xd4\x18\x84\xb1\xdf\x38\xba\xa1\x03\x9b\xbd\x13\x79\x1f\xf2\x53\x33\x16\x17\x7b\x73\x17\x5f\x1b\x05\x1f\x70" // bandwidth efficient
@ -410,7 +420,7 @@ int main(void) {
// plain with two offered and two answered + always-transcode one way // plain with two offered and two answered + always-transcode one way
start(); start();
flags.always_transcode = 1; ht_set(codec_accept, all);
sdp_pt(0, PCMU, 8000); sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000); sdp_pt(8, PCMA, 8000);
offer(); offer();
@ -433,7 +443,7 @@ int main(void) {
// plain with two offered and two answered + always-transcode both ways // plain with two offered and two answered + always-transcode both ways
start(); start();
flags.always_transcode = 1; ht_set(codec_accept, all);
sdp_pt(0, PCMU, 8000); sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000); sdp_pt(8, PCMA, 8000);
offer(); offer();
@ -441,7 +451,7 @@ int main(void) {
expect(A, send, "0/PCMU/8000 8/PCMA/8000"); expect(A, send, "0/PCMU/8000 8/PCMA/8000");
expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); expect(B, recv, "0/PCMU/8000 8/PCMA/8000");
expect(B, send, ""); expect(B, send, "");
flags.always_transcode = 1; ht_set(codec_accept, all);
sdp_pt(0, PCMU, 8000); sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000); sdp_pt(8, PCMA, 8000);
answer(); answer();
@ -1199,5 +1209,484 @@ int main(void) {
packet_seq(A, 0, PCMU_payload, 1002240, 220, 0, PCMU_payload); packet_seq(A, 0, PCMU_payload, 1002240, 220, 0, PCMU_payload);
end(); end();
// codec-mask/accept/consume tests
// control - plain in/out
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "8/PCMA/8000 101/telephone-event/8000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-mask only
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_mask, PCMU);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "104/SILK/16000 9/G722/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "8/PCMA/8000 101/telephone-event/8000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-mask + transcode + reject transcoded codec
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_mask, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "8/PCMA/8000 101/telephone-event/8000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-mask + transcode + accept transcoded codec
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_mask, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(3, GSM, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
// G.722 > PCMA
packet_seq(A, 9, G722_payload, 0, 0, -1, ""); // nothing due to resampling
packet_seq_nf(A, 9, G722_payload, 160, 1, 8, PCMA_payload);
packet_seq_ts(A, 9, G722_payload, 320, 2, 8, PCMA_payload, 160, 0);
// asymmetric codec
packet(B, 8, PCMA_payload, 8, PCMA_payload); // nothing due to resampling
end();
// codec-mask + transcode + accept transcoded codec + symmetric codecs
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_mask, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(3, GSM, 8000);
sdp_pt(101, telephone-event, 8000);
flags.symmetric_codecs = 1;
answer();
expect(A, recv, "8/PCMA/8000 9/G722/8000 0/PCMU/8000 13/CN/8000 101/telephone-event/8000");
expect(A, send, "8/PCMA/8000 101/telephone-event/8000 104/SILK/16000 9/G722/8000 0/PCMU/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
end();
// codec-consume only
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_consume, PCMU);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-consume w symmetric codecs
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_consume, PCMU);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
flags.symmetric_codecs = 1;
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 104/SILK/16000 9/G722/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-consume + transcode + reject transcoded codec
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_consume, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-consume + transcode + accept transcoded codec
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_consume, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(3, GSM, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
end();
// codec-consume + transcode + accept transcoded codec + symmetric codecs
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_consume, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(3, GSM, 8000);
sdp_pt(101, telephone-event, 8000);
flags.symmetric_codecs = 1;
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 104/SILK/16000 9/G722/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
end();
// codec-accept only
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_accept, PCMU);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-accept w symmetric codecs
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_accept, PCMU);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
flags.symmetric_codecs = 1;
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 104/SILK/16000 9/G722/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-accept + transcode + reject transcoded codec
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_accept, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// codec-accept + transcode + accept transcoded codec
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_accept, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(3, GSM, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
end();
// codec-accept + transcode + accept transcoded codec + symmetric codecs
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_accept, PCMU);
transcode(GSM);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(3, GSM, 8000);
sdp_pt(101, telephone-event, 8000);
flags.symmetric_codecs = 1;
answer();
expect(A, recv, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 104/SILK/16000 9/G722/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000");
end();
// codec-accept first codec
start();
sdp_pt(104, SILK, 16000);
sdp_pt(9, G722, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
sdp_pt(13, CN, 8000);
sdp_pt(118, CN, 16000);
ht_set(codec_accept, G722);
offer();
expect(A, recv, "");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000");
expect(B, send, "");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "9/G722/8000 8/PCMA/8000 101/telephone-event/8000");
expect(A, send, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000");
expect(B, recv, "8/PCMA/8000 101/telephone-event/8000");
expect(B, send, "8/PCMA/8000 101/telephone-event/8000");
end();
// gh 664 codec masking a/t
start();
sdp_pt(120, opus, 48000);
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(101, telephone-event, 8000);
ht_set(codec_mask, opus);
ht_set(codec_mask, G722);
ht_set(codec_mask, G7221);
ht_set(codec_accept, all);
offer();
expect(B, recv, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "120/opus/48000 8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000 96/telephone-event/48000/0-15");
// gh 664 codec masking accept=all
start();
sdp_pt(120, opus, 48000);
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(101, telephone-event, 8000);
ht_set(codec_mask, opus);
ht_set(codec_mask, G722);
ht_set(codec_mask, G7221);
ht_set(codec_accept, all);
offer();
expect(B, recv, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000");
sdp_pt(8, PCMA, 8000);
sdp_pt(101, telephone-event, 8000);
answer();
expect(A, recv, "120/opus/48000 8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000 96/telephone-event/48000/0-15");
// CN transcoding
rtpe_config.silence_detect_int = 10 << 16;
str_init_len(&rtpe_config.cn_payload, "\x40", 1);
// CN transcoding - forward
start();
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
transcode(CN);
offer();
expect(B, recv, "8/PCMA/8000 0/PCMU/8000 13/CN/8000");
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(13, CN, 8000);
answer();
expect(A, recv, "8/PCMA/8000 0/PCMU/8000");
packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload);
packet_seq(B, 8, PCMA_payload, 160, 1, 8, PCMA_payload);
packet_seq(B, 13, "\x20", 320, 2, 8, "\xf5\x5c\x4b\xc2\xde\xf4\x5e\xd4\x47\x70\x5d\x77\x45\x51\xc5\xcd\xd7\x77\x5a\xf5\xcf\x4a\x4c\x40\xc3\x47\x74\x49\x59\xc4\x76\x57\x71\x57\x40\xc5\xf4\x5a\x47\xd6\xc4\xf6\xc7\xf3\x40\x58\x74\x54\x4b\xd7\x5c\xc7\x41\x49\xf5\x5b\x53\xd9\x70\x44\xcd\xc4\xce\xcb\xc7\x58\xcd\x45\xc6\x71\xf5\x70\x43\xca\x43\xd5\x52\x5c\x75\x74\xc6\xc3\x4f\xda\x56\xc3\x46\xf5\x49\xdf\x56\x4f\x71\x5b\x52\xc6\x4e\xd0\x43\xc2\xcd\xd5\xdf\x40\x43\x4a\xf7\xf6\xd9\xdf\xde\x45\xc9\xd9\xc2\xf0\xc1\x4a\x40\x52\xd1\x5b\xd0\x54\xc9\x5e\xde\xd5\x74\x5c\x5d\x59\x71\xc1\xc1\x71\xd2\xcb\x50\x50\x54\x53\x75\xdc\x4b\xcf\xc2\xd7\x4a\xcc\x58\xc7\xdb\xd8\x48\x4a\xd6\x58\xf0\x46");
packet_seq(A, 8, PCMA_silence, 320, 2, 13, "\x40");
// CN transcoding - reverse 1
start();
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(13, CN, 8000);
ht_set(codec_consume, CN);
offer();
expect(B, recv, "8/PCMA/8000 0/PCMU/8000");
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
answer();
expect(A, recv, "8/PCMA/8000 0/PCMU/8000 13/CN/8000");
packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload);
packet_seq(B, 8, PCMA_payload, 160, 1, 8, PCMA_payload);
packet_seq(A, 13, "\x20", 320, 2, 8, "\xf5\x5c\x4b\xc2\xde\xf4\x5e\xd4\x47\x70\x5d\x77\x45\x51\xc5\xcd\xd7\x77\x5a\xf5\xcf\x4a\x4c\x40\xc3\x47\x74\x49\x59\xc4\x76\x57\x71\x57\x40\xc5\xf4\x5a\x47\xd6\xc4\xf6\xc7\xf3\x40\x58\x74\x54\x4b\xd7\x5c\xc7\x41\x49\xf5\x5b\x53\xd9\x70\x44\xcd\xc4\xce\xcb\xc7\x58\xcd\x45\xc6\x71\xf5\x70\x43\xca\x43\xd5\x52\x5c\x75\x74\xc6\xc3\x4f\xda\x56\xc3\x46\xf5\x49\xdf\x56\x4f\x71\x5b\x52\xc6\x4e\xd0\x43\xc2\xcd\xd5\xdf\x40\x43\x4a\xf7\xf6\xd9\xdf\xde\x45\xc9\xd9\xc2\xf0\xc1\x4a\x40\x52\xd1\x5b\xd0\x54\xc9\x5e\xde\xd5\x74\x5c\x5d\x59\x71\xc1\xc1\x71\xd2\xcb\x50\x50\x54\x53\x75\xdc\x4b\xcf\xc2\xd7\x4a\xcc\x58\xc7\xdb\xd8\x48\x4a\xd6\x58\xf0\x46");
packet_seq(B, 8, PCMA_silence, 320, 2, 13, "\x40");
// CN transcoding - reverse 2
start();
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
sdp_pt(13, CN, 8000);
ht_set(codec_accept, CN);
offer();
expect(B, recv, "8/PCMA/8000 0/PCMU/8000 13/CN/8000");
sdp_pt(8, PCMA, 8000);
sdp_pt(0, PCMU, 8000);
answer();
expect(A, recv, "8/PCMA/8000 0/PCMU/8000 13/CN/8000");
packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload);
packet_seq(B, 8, PCMA_payload, 160, 1, 8, PCMA_payload);
packet_seq(A, 13, "\x20", 320, 2, 8, "\xf5\x5c\x4b\xc2\xde\xf4\x5e\xd4\x47\x70\x5d\x77\x45\x51\xc5\xcd\xd7\x77\x5a\xf5\xcf\x4a\x4c\x40\xc3\x47\x74\x49\x59\xc4\x76\x57\x71\x57\x40\xc5\xf4\x5a\x47\xd6\xc4\xf6\xc7\xf3\x40\x58\x74\x54\x4b\xd7\x5c\xc7\x41\x49\xf5\x5b\x53\xd9\x70\x44\xcd\xc4\xce\xcb\xc7\x58\xcd\x45\xc6\x71\xf5\x70\x43\xca\x43\xd5\x52\x5c\x75\x74\xc6\xc3\x4f\xda\x56\xc3\x46\xf5\x49\xdf\x56\x4f\x71\x5b\x52\xc6\x4e\xd0\x43\xc2\xcd\xd5\xdf\x40\x43\x4a\xf7\xf6\xd9\xdf\xde\x45\xc9\xd9\xc2\xf0\xc1\x4a\x40\x52\xd1\x5b\xd0\x54\xc9\x5e\xde\xd5\x74\x5c\x5d\x59\x71\xc1\xc1\x71\xd2\xcb\x50\x50\x54\x53\x75\xdc\x4b\xcf\xc2\xd7\x4a\xcc\x58\xc7\xdb\xd8\x48\x4a\xd6\x58\xf0\x46");
packet_seq(B, 8, PCMA_silence, 320, 2, 13, "\x40");
return 0; return 0;
} }

@ -52,6 +52,8 @@ GetOptions(
'codec-offer=s@' => \$options{'codec-offer'}, 'codec-offer=s@' => \$options{'codec-offer'},
'codec-transcode=s@' => \$options{'codec-transcode'}, 'codec-transcode=s@' => \$options{'codec-transcode'},
'codec-mask=s@' => \$options{'codec-mask'}, 'codec-mask=s@' => \$options{'codec-mask'},
'codec-consume=s@' => \$options{'codec-consume'},
'codec-accept=s@' => \$options{'codec-accept'},
'codec-set=s@' => \$options{'codec-set'}, 'codec-set=s@' => \$options{'codec-set'},
'ptime=i' => \$options{'ptime'}, 'ptime=i' => \$options{'ptime'},
'flags=s@' => \$options{'flags'}, 'flags=s@' => \$options{'flags'},
@ -79,14 +81,15 @@ GetOptions(
'inject-DTMF' => \$options{'inject DTMF'}, 'inject-DTMF' => \$options{'inject DTMF'},
'DTLS-fingerprint=s' => \$options{'DTLS-fingerprint'}, 'DTLS-fingerprint=s' => \$options{'DTLS-fingerprint'},
'ICE-lite=s' => \$options{'ICE-lite'}, 'ICE-lite=s' => \$options{'ICE-lite'},
'no-jitter-buffer' => \$options{'no jitter buffer'}, 'no-jitter-buffer' => \$options{'no jitter buffer'},
'generate-RTCP' => \$options{'generate RTCP'},
) or die; ) or die;
my $cmd = shift(@ARGV) or die; my $cmd = shift(@ARGV) or die;
my %packet = (command => $cmd); my %packet = (command => $cmd);
for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id,code,DTLS-fingerprint,ICE-lite')) { for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id,code,DTLS-fingerprint,ICE-lite,generate RTCP')) {
defined($options{$x}) and $packet{$x} = \$options{$x}; defined($options{$x}) and $packet{$x} = \$options{$x};
} }
for my $x (split(/,/, 'TOS,delete-delay')) { for my $x (split(/,/, 'TOS,delete-delay')) {
@ -106,7 +109,7 @@ if (defined($options{direction})) {
$options{direction} =~ /(.*),(.*)/ or die; $options{direction} =~ /(.*),(.*)/ or die;
$packet{direction} = [$1,$2]; $packet{direction} = [$1,$2];
} }
for my $x (qw(strip offer transcode mask set)) { for my $x (qw(strip offer transcode mask set consume accept)) {
if ($options{'codec-'.$x} && @{$options{'codec-'.$x}}) { if ($options{'codec-'.$x} && @{$options{'codec-'.$x}}) {
if (!$options{'codec options flag'}) { if (!$options{'codec options flag'}) {
$packet{codec}{$x} = $options{'codec-'.$x}; $packet{codec}{$x} = $options{'codec-'.$x};

Loading…
Cancel
Save