diff --git a/README.md b/README.md index ecab89ed4..4e6acd671 100644 --- a/README.md +++ b/README.md @@ -734,69 +734,7 @@ Optionally included keys are: - `always transcode` - When transcoding is in use, *rtpengine* will normally match up the codecs offered with - one side with the codecs offered by the other side, and engage the transcoding engine - only for codec pairs that are not supported by both sides. With this flag present, - *rtpengine* will skip the codec match-up routine and always trancode any received media - to the first (highest priority) codec offered by the other side that is supported for - transcoding. Using this flag engages the transcoding engine even if no other - `transcoding` flags are present. Unlike other transcoding options, this one is directional, - which means that it's applied only to the one side doing the signalling that is being - handled (i.e. the side doing the `offer` or the `answer`). - - - `asymmetric codecs` - - This flag is relevant to transcoding scenarios. By default, if an RTP client rejects a - codec that was offered to it (by not including it in the answer SDP), *rtpengine* will - assume that this client will also not send this codec (in addition to not wishing to - receive it). With this flag given, *rtpengine* will not make this assumption, meaning - that *rtpengine* will expect to potentially receive a codec from an RTP client even if - that RTP client rejected this codec in its answer SDP. - - The effective difference is that when *rtpengine* is instructed to offer a new codec for - transcoding to an RTP client, and then this RTP client rejects this codec, by default - *rtpengine* is then able to shut down its transcoding engine and revert to non-transcoding - operation for this call. With this flag given however, *rtpengine* would not be able - to shut down its transcoding engine in this case, resulting in potentially different media - flow, and potentially transcoding media when it otherwise would not have to. - - This flag should be given as part of the `answer` message. - - - `symmetric codecs` - - This flag instructs *rtpengine* to honour the list of codecs accepted by answer, including - their order, and match them up with the list of codecs that *rtpengine* itself produces - when transcoding. It must be using in an `answer` message and is ignored in an `offer`. - - By default, any supported codec that was originally offered will be accepted by - *rtpengine* when transcoding, and the first codec listed will be used as output codec, - even if neither this codec nor its transcoded counterpart was accepted by the answer. - With this flag given, *rtpengine* will prefer the codecs listed in the answer over - the codecs listed in the offer and re-order the answer accordingly. This can lead to - a high-priority codec given in the offer to be listed as low-priority codec in the - answer, and vice versa. On the other hand, it can lead to the transcoding engine to be - disabled when it isn't needed. - - For example: The original offer lists codecs `PCMA` and `opus`. *Rtpengine* is instructed - to add `G722` as a transcoded codec in the offer, and so the offer produced by - *rtpengine* lists `PCMA`, `opus`, and `G722`. If *rtpengine* were to receive any - G.722 media, it would transcode it to PCMA as this is the codec preferred by the - offer. The answer now accepts `opus` and rejects the other two codecs. Without this - flag, the answer produced by *rtpengine* would contain both `PCMA` and `opus`, because - receiving G.722 would still be a possibility and so would have to be transcoded to - PCMA. With this flag however, *rtpengine* honours the single accepted codec from the - answer and so is able to eliminate PCMA from its own answer as it's not needed. - - - `reorder codecs` - - This flag adds an additional stage in the processing of the `answer` codecs. Instead of - accepting codecs in the same order that they were offered, reorder the list of codecs - to match the codecs on the opposite (answer) side. This can avoid asymmetric codec flow - in certain cases, at the cost of the answer message possibly listing codecs in a different - order from the offer message (which then could be suppressed using `single codec`). - - The config option `reorder-codecs` can be set to make this the default behaviour for - all answer messages. + Legacy flag, synonymous to `codec-accept=all`. - `single codec` diff --git a/daemon/call.c b/daemon/call.c index 38c48acc7..ebae04356 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -745,21 +745,11 @@ void call_free(void) { -void payload_type_free(struct rtp_payload_type *p) { - g_queue_clear(&p->rtcp_fb); - g_slice_free1(sizeof(*p), p); -} - struct call_media *call_media_new(struct call *call) { struct call_media *med; med = uid_slice_alloc0(med, &call->medias); med->call = call; - med->codecs_recv = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); - med->codecs_send = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); - med->codec_names_recv = g_hash_table_new_full(str_case_hash, str_case_equal, free, - (void (*)(void*)) g_queue_free); - med->codec_names_send = g_hash_table_new_full(str_case_hash, str_case_equal, free, - (void (*)(void*)) g_queue_free); + codec_store_init(&med->codecs, med); return med; } @@ -804,7 +794,7 @@ static struct call_media *__get_media(struct call_monologue *ml, GList **it, con med->monologue = ml; med->index = sp->index; call_str_cpy(ml->call, &med->type, &sp->type); - med->type_id = codec_get_type(&med->type); + med->type_id = sp->type_id; g_queue_push_tail(&ml->medias, med); @@ -1160,10 +1150,11 @@ int __init_stream(struct packet_stream *ps) { return 0; } -void __rtp_stats_update(GHashTable *dst, GHashTable *src) { +void __rtp_stats_update(GHashTable *dst, struct codec_store *cs) { struct rtp_stats *rs; struct rtp_payload_type *pt; GList *values, *l; + GHashTable *src = cs->codecs; /* "src" is a call_media->codecs table, while "dst" is a * packet_stream->rtp_stats table */ @@ -1208,7 +1199,7 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru a->rtp_sink = a; PS_SET(a, RTP); /* XXX technically not correct, could be udptl too */ - __rtp_stats_update(a->rtp_stats, A->codecs_recv); + __rtp_stats_update(a->rtp_stats, &A->codecs); if (sp) { __fill_stream(a, &sp->rtp_endpoint, port_off, sp, flags); @@ -2135,6 +2126,89 @@ static void __update_media_protocol(struct call_media *media, struct call_media } } +void codecs_offer_answer(struct call_media *media, struct call_media *other_media, + struct stream_params *sp, struct sdp_ng_flags *flags) +{ + if (!flags || flags->opmode != OP_ANSWER) { + // offer + ilogs(codec, LOG_DEBUG, "Updating receiver side codecs for offerer " STR_FORMAT " #%u", + STR_FMT(&other_media->monologue->tag), + other_media->index); + codec_store_populate(&other_media->codecs, &sp->codecs, flags->codec_set); + codec_store_strip(&other_media->codecs, &flags->codec_strip, flags->codec_except); + codec_store_offer(&other_media->codecs, &flags->codec_offer, &sp->codecs); + if (!other_media->codecs.strip_full) + codec_store_offer(&other_media->codecs, &flags->codec_transcode, &sp->codecs); + codec_store_accept(&other_media->codecs, &flags->codec_accept, NULL); + codec_store_accept(&other_media->codecs, &flags->codec_consume, &sp->codecs); + codec_store_track(&other_media->codecs, &flags->codec_mask); + + // we don't update the answerer side if the offer is not RTP but is going + // to RTP (i.e. T.38 transcoding) - instead we leave the existing codec list + // intact + int update_answerer = 1; + if (proto_is_rtp(media->protocol) && !proto_is_rtp(other_media->protocol)) + update_answerer = 0; + + if (update_answerer) { + // update/create answer/receiver side + ilogs(codec, LOG_DEBUG, "Updating receiver side codecs for answerer " STR_FORMAT " #%u", + STR_FMT(&media->monologue->tag), + media->index); + codec_store_populate(&media->codecs, &sp->codecs, NULL); + } + codec_store_strip(&media->codecs, &flags->codec_strip, flags->codec_except); + codec_store_strip(&media->codecs, &flags->codec_consume, flags->codec_except); + codec_store_strip(&media->codecs, &flags->codec_mask, flags->codec_except); + codec_store_offer(&media->codecs, &flags->codec_offer, &sp->codecs); + codec_store_transcode(&media->codecs, &flags->codec_transcode, &sp->codecs); + codec_store_synthesise(&media->codecs, &other_media->codecs); + + // update supp codecs based on actions so far + codec_tracker_update(&media->codecs); + + // set up handlers + codec_handlers_update(media, other_media, flags, sp); + + // updating the handlers may have removed some codecs, so run update the supp codecs again + codec_tracker_update(&media->codecs); + + // finally set up handlers again based on final results + codec_handlers_update(media, other_media, flags, sp); + } + else { + // answer + ilogs(codec, LOG_DEBUG, "Updating receiver side codecs for answerer " STR_FORMAT " #%u", + STR_FMT(&other_media->monologue->tag), + other_media->index); + codec_store_populate(&other_media->codecs, &sp->codecs, flags->codec_set); + codec_store_strip(&other_media->codecs, &flags->codec_strip, flags->codec_except); + codec_store_offer(&other_media->codecs, &flags->codec_offer, &sp->codecs); + + // update callee side codec handlers again (second pass after the offer) as we + // might need to update some handlers, e.g. when supplemental codecs have been + // rejected + codec_handlers_update(other_media, media, NULL, NULL); + + // finally set up our caller side codecs + ilogs(codec, LOG_DEBUG, "Codec answer for " STR_FORMAT " #%u", + STR_FMT(&other_media->monologue->tag), + other_media->index); + codec_store_answer(&media->codecs, &other_media->codecs, flags); + + // set up handlers + codec_handlers_update(media, other_media, flags, sp); + + // updating the handlers may have removed some codecs, so run update the supp codecs again + codec_tracker_update(&media->codecs); + codec_tracker_update(&other_media->codecs); + + // finally set up handlers again based on final results + codec_handlers_update(media, other_media, flags, sp); + codec_handlers_update(other_media, media, NULL, NULL); + } +} + /* called with call->master_lock held in W */ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, struct sdp_ng_flags *flags) @@ -2305,10 +2379,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, call_str_cpy(call, &media->format_str, &sp->format_str); } - codec_tracker_init(media); - codec_rtp_payload_types(media, other_media, &sp->rtp_payload_types, flags); - codec_handlers_update(media, other_media, flags, sp); - codec_tracker_finish(media, other_media); + codecs_offer_answer(media, other_media, sp, flags); /* send and recv are from our POV */ bf_copy_same(&media->media_flags, &sp->sp_flags, @@ -2483,7 +2554,7 @@ const struct rtp_payload_type *__rtp_stats_codec(struct call_media *m) { if (atomic64_get(&rtp_s->packets) == 0) goto out; - rtp_pt = rtp_payload_type(rtp_s->payload_type, m->codecs_recv); + rtp_pt = rtp_payload_type(rtp_s->payload_type, &m->codecs); out: g_list_free(values); @@ -2793,12 +2864,7 @@ void call_media_free(struct call_media **mdp) { crypto_params_sdes_queue_clear(&md->sdes_out); g_queue_clear(&md->streams); g_queue_clear(&md->endpoint_maps); - g_hash_table_destroy(md->codecs_recv); - g_hash_table_destroy(md->codecs_send); - g_hash_table_destroy(md->codec_names_recv); - g_hash_table_destroy(md->codec_names_send); - g_queue_clear_full(&md->codecs_prefs_recv, (GDestroyNotify) payload_type_free); - g_queue_clear_full(&md->codecs_prefs_send, (GDestroyNotify) payload_type_free); + codec_store_cleanup(&md->codecs); codec_handlers_free(md); codec_handler_free(&md->t38_handler); t38_gateway_put(&md->t38_gateway); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 21ba8755e..ea868bd83 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -303,22 +303,6 @@ static void streams_parse(const char *s, GQueue *q) { pcre_multi_match(streams_re, streams_ree, s, 3, streams_parse_func, &i, q); } -/* XXX move these somewhere else */ -static void rtp_pt_free(void *p) { - g_slice_free1(sizeof(struct rtp_payload_type), p); -} -static void sp_free(void *p) { - struct stream_params *s = p; - - g_queue_clear_full(&s->rtp_payload_types, rtp_pt_free); - ice_candidates_free(&s->ice_candidates); - crypto_params_sdes_queue_clear(&s->sdes_params); - g_slice_free1(sizeof(*s), s); -} -static void streams_free(GQueue *q) { - g_queue_clear_full(q, sp_free); -} - static str *call_request_lookup_tcp(char **out, enum call_opmode opmode) { @@ -364,7 +348,7 @@ static str *call_request_lookup_tcp(char **out, enum call_opmode opmode) { out2: rwlock_unlock_w(&c->master_lock); - streams_free(&s); + sdp_streams_free(&s); redis_update_onekey(c, rtpe_redis_write); @@ -855,16 +839,16 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { break; case CSH_LOOKUP("always-transcode"):; static const str str_all = STR_CONST_INIT("all"); - call_ng_flags_str_ht(out, (str *) &str_all, &out->codec_accept); + call_ng_flags_codec_list(out, (str *) &str_all, &out->codec_accept); break; case CSH_LOOKUP("asymmetric-codecs"): - out->asymmetric_codecs = 1; + ilog(LOG_INFO, "Ignoring obsolete flag `asymmetric-codecs`"); break; case CSH_LOOKUP("symmetric-codecs"): - out->symmetric_codecs = 1; + ilog(LOG_INFO, "Ignoring obsolete flag `symmetric-codecs`"); break; case CSH_LOOKUP("reorder-codecs"): - out->reorder_codecs = 1; + ilog(LOG_INFO, "Ignoring obsolete flag `reorder-codecs`"); break; case CSH_LOOKUP("single-codec"): out->single_codec = 1; @@ -895,7 +879,7 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { return; if (call_ng_flags_prefix(out, s, "OSRTP-", ng_osrtp_option, NULL)) return; - if (call_ng_flags_prefix(out, s, "codec-strip-", call_ng_flags_str_ht, + if (call_ng_flags_prefix(out, s, "codec-strip-", call_ng_flags_codec_list, &out->codec_strip)) return; if (call_ng_flags_prefix(out, s, "codec-offer-", call_ng_flags_codec_list, @@ -912,7 +896,7 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { if (call_ng_flags_prefix(out, s, "codec-transcode-", call_ng_flags_codec_list, &out->codec_transcode)) return; - 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_codec_list, &out->codec_mask)) return; if (call_ng_flags_prefix(out, s, "T38-", ng_t38_option, NULL)) @@ -923,10 +907,10 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { 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, + if (call_ng_flags_prefix(out, s, "codec-accept-", call_ng_flags_codec_list, &out->codec_accept)) return; - if (call_ng_flags_prefix(out, s, "codec-consume-", call_ng_flags_str_ht, + if (call_ng_flags_prefix(out, s, "codec-consume-", call_ng_flags_codec_list, &out->codec_consume)) return; #endif @@ -1133,16 +1117,16 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu } if ((dict = bencode_dictionary_get_expect(input, "codec", BENCODE_DICTIONARY))) { - call_ng_flags_list(out, dict, "strip", call_ng_flags_str_ht, &out->codec_strip); + call_ng_flags_list(out, dict, "strip", call_ng_flags_codec_list, &out->codec_strip); call_ng_flags_list(out, dict, "offer", call_ng_flags_codec_list, &out->codec_offer); call_ng_flags_list(out, dict, "except", call_ng_flags_str_ht, &out->codec_except); #ifdef WITH_TRANSCODING if (opmode == OP_OFFER) { 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_codec_list, &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, "accept", call_ng_flags_str_ht, &out->codec_accept); - call_ng_flags_list(out, dict, "consume", call_ng_flags_str_ht, &out->codec_consume); + call_ng_flags_list(out, dict, "accept", call_ng_flags_codec_list, &out->codec_accept); + call_ng_flags_list(out, dict, "consume", call_ng_flags_codec_list, &out->codec_consume); } #endif } @@ -1180,22 +1164,18 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu } } static void call_ng_free_flags(struct sdp_ng_flags *flags) { - if (flags->codec_strip) - g_hash_table_destroy(flags->codec_strip); if (flags->codec_except) g_hash_table_destroy(flags->codec_except); - if (flags->codec_mask) - g_hash_table_destroy(flags->codec_mask); if (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) g_hash_table_destroy(flags->sdes_no); g_queue_clear_full(&flags->codec_offer, free); g_queue_clear_full(&flags->codec_transcode, free); + g_queue_clear_full(&flags->codec_strip, free); + g_queue_clear_full(&flags->codec_accept, free); + g_queue_clear_full(&flags->codec_consume, free); + g_queue_clear_full(&flags->codec_mask, free); } static enum load_limit_reasons call_offer_session_limit(void) { @@ -1251,7 +1231,7 @@ static enum load_limit_reasons call_offer_session_limit(void) { } static void fragment_free(struct sdp_fragment *frag) { - streams_free(&frag->streams); + sdp_streams_free(&frag->streams); call_ng_free_flags(&frag->flags); obj_put(frag->ngbuf); g_slice_free1(sizeof(*frag), frag); @@ -1518,7 +1498,7 @@ static const char *call_offer_answer_ng(struct ng_buffer *ngbuf, bencode_item_t errstr = NULL; out: sdp_free(&parsed); - streams_free(&streams); + sdp_streams_free(&streams); call_ng_free_flags(&flags); return errstr; @@ -2394,8 +2374,8 @@ const char *call_play_dtmf_ng(bencode_item_t *input, bencode_item_t *output) { media = l->data; if (media->type_id != MT_AUDIO) continue; - if (!media->dtmf_injector) - continue; +// if (!media->dtmf_injector) +// continue; goto found; } diff --git a/daemon/codec.c b/daemon/codec.c index 4fe82a74b..858279147 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -37,37 +37,43 @@ struct mqtt_timer { static codec_handler_func handler_func_passthrough; static struct timerthread codec_timers_thread; -static struct rtp_payload_type *__rtp_payload_type_copy(const struct rtp_payload_type *pt); -static void __rtp_payload_type_dup(struct call *call, struct rtp_payload_type *pt); +static void rtp_payload_type_copy(struct rtp_payload_type *dst, const struct rtp_payload_type *src); +static void codec_store_add_raw_order(struct codec_store *cs, struct rtp_payload_type *pt); static void __rtp_payload_type_add_name(GHashTable *, struct rtp_payload_type *pt); static struct codec_handler codec_handler_stub = { .source_pt.payload_type = -1, + .dest_pt.payload_type = -1, .func = handler_func_passthrough, .kernelize = 1, + .passthrough = 1, }; -static GList *__delete_x_codec(GList *link, GHashTable *codecs, GHashTable *codec_names, GQueue *codecs_prefs) { +static void __ht_queue_del(GHashTable *ht, const void *key, const void *val) { + GQueue *q = g_hash_table_lookup(ht, key); + if (!q) + return; + g_queue_remove_all(q, val); +} + +static GList *__codec_store_delete_link(GList *link, struct codec_store *cs) { struct rtp_payload_type *pt = link->data; - g_hash_table_remove(codecs, GINT_TO_POINTER(pt->payload_type)); - g_hash_table_remove(codec_names, &pt->encoding); - g_hash_table_remove(codec_names, &pt->encoding_with_params); - g_hash_table_remove(codec_names, &pt->encoding_with_full_params); + g_hash_table_remove(cs->codecs, GINT_TO_POINTER(pt->payload_type)); + __ht_queue_del(cs->codec_names, &pt->encoding, GINT_TO_POINTER(pt->payload_type)); + __ht_queue_del(cs->codec_names, &pt->encoding_with_params, GINT_TO_POINTER(pt->payload_type)); + __ht_queue_del(cs->codec_names, &pt->encoding_with_full_params, GINT_TO_POINTER(pt->payload_type)); GList *next = link->next; - g_queue_delete_link(codecs_prefs, link); + if (cs->supp_link == link) + cs->supp_link = next; + g_queue_delete_link(&cs->codec_prefs, link); payload_type_free(pt); return next; } -static GList *__delete_receiver_codec(struct call_media *receiver, GList *link) { - return __delete_x_codec(link, receiver->codecs_recv, receiver->codec_names_recv, - &receiver->codecs_prefs_recv); -} - #ifdef WITH_TRANSCODING @@ -101,7 +107,9 @@ struct dtx_packet { struct transcode_packet *packet; struct media_packet mp; struct codec_ssrc_handler *decoder_handler; // holds reference - int (*func)(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp); + struct codec_ssrc_handler *input_handler; // holds reference + int (*func)(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp); }; struct silence_event { @@ -153,16 +161,16 @@ struct transcode_packet { str *payload; struct codec_handler *handler; unsigned int marker:1, - ignore_seq:1; - int (*func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *); - int (*dup_func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *); + bypass_seq:1; + int (*func)(struct codec_ssrc_handler *, struct codec_ssrc_handler *, struct transcode_packet *, + struct media_packet *); + int (*dup_func)(struct codec_ssrc_handler *, struct codec_ssrc_handler *, struct transcode_packet *, + struct media_packet *); struct rtp_header rtp; }; struct codec_tracker { - GHashTable *clockrates; // 8000, 16000, etc, for each real audio codec that is present GHashTable *touched; // 8000, 16000, etc, for each audio codec that was touched (added, removed, etc) int all_touched; - GHashTable *supp_codecs; // telephone-event etc => hash table of clock rates }; @@ -178,7 +186,6 @@ static codec_handler_func handler_func_passthrough_ssrc; static codec_handler_func handler_func_transcode; static codec_handler_func handler_func_playback; static codec_handler_func handler_func_inject_dtmf; -//static codec_handler_func handler_func_supplemental; static codec_handler_func handler_func_dtmf; static codec_handler_func handler_func_t38; @@ -189,25 +196,30 @@ static void __free_ssrc_handler(void *); static void __transcode_packet_free(struct transcode_packet *); -static int packet_decode(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *); +static int packet_decode(struct codec_ssrc_handler *, struct codec_ssrc_handler *, + struct transcode_packet *, struct media_packet *); static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2); static int packet_decoded_fifo(decoder_t *decoder, AVFrame *frame, void *u1, void *u2); static int packet_decoded_direct(decoder_t *decoder, AVFrame *frame, void *u1, void *u2); -static void codec_touched(struct rtp_payload_type *pt, struct call_media *media); +static void codec_touched(struct codec_store *cs, struct rtp_payload_type *pt); static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *ch, + struct codec_ssrc_handler *input_handler, struct transcode_packet *packet, struct media_packet *mp, - int (*func)(struct codec_ssrc_handler *ch, struct transcode_packet *packet, + int (*func)(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp)); static void __dtx_shutdown(struct dtx_buffer *dtxb); -static struct codec_handler *__decoder_handler(struct codec_handler *h, struct media_packet *mp); +static struct codec_handler *__input_handler(struct codec_handler *h, struct media_packet *mp); static struct codec_handler codec_handler_stub_ssrc = { .source_pt.payload_type = -1, + .dest_pt.payload_type = -1, .func = handler_func_passthrough_ssrc, .kernelize = 1, + .passthrough = 1, }; @@ -222,11 +234,13 @@ static void __handler_shutdown(struct codec_handler *handler) { handler->ssrc_handler = NULL; handler->kernelize = 0; handler->transcoder = 0; - handler->dtmf_scaler = 0; handler->output_handler = handler; // reset to default handler->dtmf_payload_type = -1; handler->cn_payload_type = -1; handler->pcm_dtmf_detect = 0; + handler->passthrough = 0; + + codec_handler_free(&handler->dtmf_injector); if (handler->stats_entry) { g_atomic_int_add(&handler->stats_entry->num_transcoders, -1); @@ -238,6 +252,8 @@ static void __handler_shutdown(struct codec_handler *handler) { static void __codec_handler_free(void *pp) { struct codec_handler *h = pp; __handler_shutdown(h); + payload_type_clear(&h->source_pt); + payload_type_clear(&h->dest_pt); g_slice_free1(sizeof(*h), h); } void codec_handler_free(struct codec_handler **handler) { @@ -249,8 +265,10 @@ void codec_handler_free(struct codec_handler **handler) { static struct codec_handler *__handler_new(const struct rtp_payload_type *pt, struct call_media *media) { struct codec_handler *handler = g_slice_alloc0(sizeof(*handler)); + handler->source_pt.payload_type = -1; if (pt) - handler->source_pt = *pt; + rtp_payload_type_copy(&handler->source_pt, pt); + handler->dest_pt.payload_type = -1; handler->output_handler = handler; // default handler->dtmf_payload_type = -1; handler->cn_payload_type = -1; @@ -260,20 +278,26 @@ static struct codec_handler *__handler_new(const struct rtp_payload_type *pt, st return handler; } -static void __make_passthrough(struct codec_handler *handler) { +static void __make_passthrough(struct codec_handler *handler, int dtmf_pt, int cn_pt) { __handler_shutdown(handler); - ilogs(codec, LOG_DEBUG, "Using passthrough handler for " STR_FORMAT, - STR_FMT(&handler->source_pt.encoding_with_params)); + ilogs(codec, LOG_DEBUG, "Using passthrough handler for " STR_FORMAT " with DTMF %i, CN %i", + STR_FMT(&handler->source_pt.encoding_with_params), dtmf_pt, cn_pt); if (handler->source_pt.codec_def && handler->source_pt.codec_def->dtmf) handler->func = handler_func_dtmf; else { handler->func = handler_func_passthrough; handler->kernelize = 1; } - handler->dest_pt = handler->source_pt; + rtp_payload_type_copy(&handler->dest_pt, &handler->source_pt); handler->ssrc_hash = create_ssrc_hash_full(__ssrc_handler_new, handler); + handler->dtmf_payload_type = dtmf_pt; + handler->cn_payload_type = cn_pt; + handler->passthrough = 1; } static void __make_passthrough_ssrc(struct codec_handler *handler) { + int dtmf_pt = handler->dtmf_payload_type; + int cn_pt = handler->cn_payload_type; + __handler_shutdown(handler); ilogs(codec, LOG_DEBUG, "Using passthrough handler with new SSRC for " STR_FORMAT, STR_FMT(&handler->source_pt.encoding_with_params)); @@ -283,19 +307,20 @@ static void __make_passthrough_ssrc(struct codec_handler *handler) { handler->func = handler_func_passthrough_ssrc; handler->kernelize = 1; } - handler->dest_pt = handler->source_pt; + rtp_payload_type_copy(&handler->dest_pt, &handler->source_pt); handler->ssrc_hash = create_ssrc_hash_full(__ssrc_handler_new, handler); + handler->dtmf_payload_type = dtmf_pt; + handler->cn_payload_type = cn_pt; + handler->passthrough = 1; } static void __make_transcoder(struct codec_handler *handler, struct rtp_payload_type *dest, - GHashTable *output_transcoders, int dtmf_payload_type, int pcm_dtmf_detect) + GHashTable *output_transcoders, int dtmf_payload_type, int pcm_dtmf_detect, + int cn_payload_type) { assert(handler->source_pt.codec_def != NULL); - assert(dest->codec_def != NULL); - // if we're just repacketising: - if (dtmf_payload_type == -1 && dest->codec_def && dest->codec_def->dtmf) - dtmf_payload_type = dest->payload_type; + assert(dest->codec_def != NULL); // don't reset handler if it already matches what we want if (!handler->transcoder) @@ -304,35 +329,40 @@ static void __make_transcoder(struct codec_handler *handler, struct rtp_payload_ goto reset; if (handler->func != handler_func_transcode) goto reset; + if (handler->cn_payload_type != cn_payload_type) + goto reset; + if (handler->dtmf_payload_type != dtmf_payload_type) + goto reset; - ilogs(codec, LOG_DEBUG, "Leaving transcode context for " STR_FORMAT " -> " STR_FORMAT " intact", + ilogs(codec, LOG_DEBUG, "Leaving transcode context for " STR_FORMAT " (%i) -> " STR_FORMAT " (%i) intact", STR_FMT(&handler->source_pt.encoding_with_params), - STR_FMT(&dest->encoding_with_params)); + handler->source_pt.payload_type, + STR_FMT(&dest->encoding_with_params), + dest->payload_type); goto check_output; reset: __handler_shutdown(handler); - handler->dest_pt = *dest; + rtp_payload_type_copy(&handler->dest_pt, dest); handler->func = handler_func_transcode; handler->transcoder = 1; - if (dtmf_payload_type != -1) - handler->dtmf_payload_type = dtmf_payload_type; + handler->dtmf_payload_type = dtmf_payload_type; + handler->cn_payload_type = cn_payload_type; handler->pcm_dtmf_detect = pcm_dtmf_detect ? 1 : 0; - // is this DTMF to DTMF? - if (dtmf_payload_type != -1 && handler->source_pt.codec_def->dtmf) { - ilogs(codec, LOG_DEBUG, "Created DTMF transcode context for " STR_FORMAT " -> PT %i", - STR_FMT(&handler->source_pt.encoding_with_params), - dtmf_payload_type); - handler->dtmf_scaler = 1; - } - else - ilogs(codec, LOG_DEBUG, "Created transcode context for " STR_FORMAT " -> " STR_FORMAT - " with DTMF output %i", - STR_FMT(&handler->source_pt.encoding_with_params), - STR_FMT(&dest->encoding_with_params), dtmf_payload_type); + // DTMF transcoder/scaler? + if (handler->source_pt.codec_def && handler->source_pt.codec_def->dtmf) + handler->func = handler_func_dtmf; + + ilogs(codec, LOG_DEBUG, "Created transcode context for " STR_FORMAT " (%i) -> " STR_FORMAT + " (%i) with DTMF output %i and CN output %i", + STR_FMT(&handler->source_pt.encoding_with_params), + handler->source_pt.payload_type, + STR_FMT(&dest->encoding_with_params), + dest->payload_type, + dtmf_payload_type, cn_payload_type); handler->ssrc_hash = create_ssrc_hash_full(__ssrc_handler_transcode_new, handler); @@ -378,7 +408,7 @@ struct codec_handler *codec_handler_make_playback(const struct rtp_payload_type const struct rtp_payload_type *dst_pt, unsigned long last_ts, struct call_media *media) { struct codec_handler *handler = __handler_new(src_pt, media); - handler->dest_pt = *dst_pt; + rtp_payload_type_copy(&handler->dest_pt, dst_pt); handler->func = handler_func_playback; handler->ssrc_handler = (void *) __ssrc_handler_transcode_new(handler); handler->ssrc_handler->first_ts = last_ts; @@ -393,52 +423,33 @@ struct codec_handler *codec_handler_make_playback(const struct rtp_payload_type return handler; } -void ensure_codec_def(struct rtp_payload_type *pt, struct call_media *media) { +static void ensure_codec_def_type(struct rtp_payload_type *pt, enum media_type type) { if (pt->codec_def) return; - pt->codec_def = codec_find(&pt->encoding, media->type_id); + pt->codec_def = codec_find(&pt->encoding, type); if (!pt->codec_def) return; if (!pt->codec_def->support_encoding || !pt->codec_def->support_decoding) pt->codec_def = NULL; } - -static GList *__delete_send_codec(struct call_media *sender, GList *link) { - return __delete_x_codec(link, sender->codecs_send, sender->codec_names_send, - &sender->codecs_prefs_send); +void ensure_codec_def(struct rtp_payload_type *pt, struct call_media *media) { + if (!media) + return; + ensure_codec_def_type(pt, media->type_id); } // only called from codec_handlers_update() -static void __make_passthrough_gsl(struct codec_handler *handler, GSList **handlers) { +static void __make_passthrough_gsl(struct codec_handler *handler, GSList **handlers, + struct rtp_payload_type *dtmf_pt, struct rtp_payload_type *cn_pt) +{ + __make_passthrough(handler, dtmf_pt ? dtmf_pt->payload_type : -1, + cn_pt ? cn_pt->payload_type : -1); if (MEDIA_ISSET(handler->media, ECHO)) __make_passthrough_ssrc(handler); - else - __make_passthrough(handler); *handlers = g_slist_prepend(*handlers, handler); } -// only called from codec_handlers_update() -static void __dtmf_dsp_shutdown(struct call_media *sink, int payload_type) { - if (!sink->codec_handlers) - return; - - for (GList *l = sink->codec_handlers_store.head; l; l = l->next) { - struct codec_handler *handler = l->data; - if (!handler->transcoder) - continue; - if (handler->dtmf_payload_type != payload_type) - continue; - if (handler->dtmf_scaler) - continue; - - ilogs(codec, LOG_DEBUG, "Shutting down DTMF DSP for '" STR_FORMAT "' -> %i (not needed)", - STR_FMT(&handler->source_pt.encoding_with_params), - payload_type); - handler->dtmf_payload_type = -1; - } -} - static void __track_supp_codec(GHashTable *supplemental_sinks, struct rtp_payload_type *pt) { if (!pt->codec_def || !pt->codec_def->supplemental) @@ -451,13 +462,22 @@ static void __track_supp_codec(GHashTable *supplemental_sinks, struct rtp_payloa g_hash_table_insert(supp_sinks, GUINT_TO_POINTER(pt->clock_rate), pt); } -static struct rtp_payload_type *__check_dest_codecs(struct call_media *receiver, struct call_media *sink, - const struct sdp_ng_flags *flags, GHashTable *supplemental_sinks, int *sink_transcoding) +static void __check_codec_list(GHashTable **supplemental_sinks, struct rtp_payload_type **pref_dest_codec, + struct call_media *sink, GQueue *sink_list) { - struct rtp_payload_type *pref_dest_codec = NULL; + // first initialise and populate the list of supp sinks + GHashTable *ss = *supplemental_sinks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + (GDestroyNotify) g_hash_table_destroy); + for (GList *l = codec_supplemental_codecs->head; l; l = l->next) { + codec_def_t *def = l->data; + g_hash_table_replace(ss, (void *) def->rtpname, + g_hash_table_new(g_direct_hash, g_direct_equal)); + } + + struct rtp_payload_type *pdc = 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.codec_prefs.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; ensure_codec_def(pt, sink); if (!pt->codec_def) // not supported, next @@ -469,138 +489,43 @@ static struct rtp_payload_type *__check_dest_codecs(struct call_media *receiver, if (sink->ptime > 0) pt->ptime = sink->ptime; - if (!pref_dest_codec && !pt->codec_def->supplemental) - pref_dest_codec = pt; - - - // 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 - // 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, - // we can disable the transcoding engine. - struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send, - GINT_TO_POINTER(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 (!recv_pt) { - // can the sink receive codec but the receiver can't send it? - *sink_transcoding |= 0x3; - } - } - if (pt->for_transcoding) { - // codec is explicitly marked for transcoding. enable transcoding engine - MEDIA_SET(receiver, TRANSCODE); - *sink_transcoding |= 0x3; + if (!pdc && !pt->codec_def->supplemental) + pdc = pt; + if (pt->accepted) { + // codec is explicitly marked as accepted 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(ss, pt); } if (first_tc_codec) - pref_dest_codec = first_tc_codec; - if (pref_dest_codec) + pdc = first_tc_codec; + if (pdc && pref_dest_codec) { + *pref_dest_codec = pdc; ilogs(codec, LOG_DEBUG, "Default sink codec is " STR_FORMAT, - STR_FMT(&pref_dest_codec->encoding_with_params)); - - return pref_dest_codec; -} - -static void __check_send_codecs(struct call_media *receiver, struct call_media *sink, - const struct sdp_ng_flags *flags, GHashTable *supplemental_sinks, int *sink_transcoding) -{ - if (!MEDIA_ISSET(sink, TRANSCODE)) - return; - - for (GList *l = sink->codecs_prefs_recv.head; l; l = l->next) { - struct rtp_payload_type *pt = l->data; - struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send, - GINT_TO_POINTER(pt->payload_type)); - 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)) - tc_flag |= 0x3; - if (flags && flags->inject_dtmf) - tc_flag |= 0x1; - if (pt->for_transcoding) - tc_flag |= 0x3; - //ilog(LOG_DEBUG, "XXXXXXXXXXXX set flag is %i", *sink_transcoding); - if (tc_flag) { - // can the sink receive codec but the receiver can't send it? - *sink_transcoding |= tc_flag; - continue; - } - - // 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 - // force accepted codec in the offer - struct codec_handler *ch_recv = - g_hash_table_lookup(sink->codec_handlers, GINT_TO_POINTER(recv_pt->payload_type)); - if (!ch_recv) - continue; - //ilog(LOG_DEBUG, "XXXXXXXXXXXX handler transcoder %i", ch_recv->transcoder); - if (ch_recv->transcoder) - *sink_transcoding |= 0x3; + STR_FMT(&(*pref_dest_codec)->encoding_with_params)); } } -static int __supp_payload_type(GHashTable *supplemental_sinks, struct rtp_payload_type *pref_dest_codec, +static struct rtp_payload_type *__supp_payload_type(GHashTable *supplemental_sinks, int clockrate, const char *codec) { GHashTable *supp_sinks = g_hash_table_lookup(supplemental_sinks, codec); if (!supp_sinks) - return -1; - if (!g_hash_table_size(supp_sinks) || !pref_dest_codec) - return -1; + return NULL; + if (!g_hash_table_size(supp_sinks)) + return NULL; // find the codec entry with a matching clock rate struct rtp_payload_type *pt = g_hash_table_lookup(supp_sinks, - GUINT_TO_POINTER(pref_dest_codec->clock_rate)); - if (!pt) - return -1; - return pt->payload_type; -} - -static int __dtmf_payload_type(GHashTable *supplemental_sinks, struct rtp_payload_type *pref_dest_codec) { - GHashTable *dtmf_sinks = g_hash_table_lookup(supplemental_sinks, "telephone-event"); - if (!dtmf_sinks) - return -1; - if (!g_hash_table_size(dtmf_sinks) || !pref_dest_codec) - return -1; - - int dtmf_payload_type = __supp_payload_type(supplemental_sinks, pref_dest_codec, "telephone-event"); - - if (dtmf_payload_type == -1) - ilogs(codec, LOG_INFO, "Not transcoding PCM DTMF tones to telephone-event packets as " - "no payload type with a matching clock rate for '" STR_FORMAT - "' was found", STR_FMT(&pref_dest_codec->encoding_with_params)); - else - ilogs(codec, LOG_DEBUG, "Output DTMF payload type is %i", dtmf_payload_type); - - return dtmf_payload_type; + GUINT_TO_POINTER(clockrate)); + return pt; } static int __unused_pt_number(struct call_media *media, struct call_media *other_media, + struct codec_store *extra_cs, struct rtp_payload_type *pt) { int num = pt ? pt->payload_type : -1; @@ -609,16 +534,18 @@ static int __unused_pt_number(struct call_media *media, struct call_media *other if (num < 0) num = 96; // default first dynamic payload type number while (1) { - if ((pt_match = g_hash_table_lookup(media->codecs_recv, GINT_TO_POINTER(num)))) - goto next; - if ((pt_match = g_hash_table_lookup(media->codecs_send, GINT_TO_POINTER(num)))) + if ((pt_match = g_hash_table_lookup(media->codecs.codecs, GINT_TO_POINTER(num)))) goto next; if (other_media) { - if ((pt_match = g_hash_table_lookup(other_media->codecs_recv, GINT_TO_POINTER(num)))) + if ((pt_match = g_hash_table_lookup(other_media->codecs.codecs, + GINT_TO_POINTER(num)))) goto next; - if ((pt_match = g_hash_table_lookup(other_media->codecs_send, GINT_TO_POINTER(num)))) + } + if (extra_cs) { + if ((pt_match = g_hash_table_lookup(extra_cs->codecs, + GINT_TO_POINTER(num)))) goto next; - } + } // OK break; @@ -635,389 +562,38 @@ next: return num; } -static void __single_codec(struct call_media *media, const struct sdp_ng_flags *flags) { - if (!flags || flags->opmode != OP_ANSWER || !flags->single_codec) - return; - int have_codec = 0; - for (GList *l = media->codecs_prefs_recv.head; l;) { - struct rtp_payload_type *pt = l->data; - ensure_codec_def(pt, media); - if (pt->codec_def && pt->codec_def->supplemental) { - // leave these alone - l = l->next; - continue; - } - if (!have_codec) { - have_codec = 1; - l = l->next; - continue; - } - ilogs(codec, LOG_DEBUG, "Removing codec '" STR_FORMAT "' due to 'single codec' flag", - STR_FMT(&pt->encoding_with_params)); - codec_touched(pt, media); - l = __delete_receiver_codec(media, l); - } -} - -static int __check_receiver_codecs(struct call_media *receiver, struct call_media *sink) { - 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); - struct rtp_payload_type *sink_pt = g_hash_table_lookup(sink->codecs_recv, - GINT_TO_POINTER(pt->payload_type)); - if (sink_pt && !rtp_payload_type_cmp(pt, sink_pt)) - continue; - if (pt->for_transcoding) { - if (pt->codec_def->supplemental) - ret |= 0x2 | 0x4; - else - ret |= 0x1 | 0x2; - } - } - return ret; -} - -static void __accept_pt(struct call_media *receiver, struct call_media *sink, int payload_type, - int fallback_type, int accept_only_tc, GList **insert_pos) -{ - struct rtp_payload_type *pt = g_hash_table_lookup(receiver->codecs_send, GINT_TO_POINTER(payload_type)); - - // fallback PT in case the codec handler is a dummy or a passthrough - // or some other internal problem - if (!pt && fallback_type != -1) - pt = g_hash_table_lookup(receiver->codecs_send, GINT_TO_POINTER(fallback_type)); - if (!pt) - return; - - ensure_codec_def(pt, receiver); - if (!pt->codec_def) - return; - if (accept_only_tc && !pt->for_transcoding) - return; - //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 - = g_hash_table_lookup(receiver->codecs_recv, GINT_TO_POINTER(pt->payload_type)); - if (existing_pt && !rtp_payload_type_cmp_nf(existing_pt, pt)) { - // already present. - // to keep the order intact, we seek the list for the position - // of this codec entry. all newly added codecs must come after - // this entry. - if (!*insert_pos) - *insert_pos = receiver->codecs_prefs_recv.head; - while (*insert_pos) { - if (!(*insert_pos)->next) - break; // end of list - we insert everything after - struct rtp_payload_type *test_pt = (*insert_pos)->data; - if (test_pt->payload_type == pt->payload_type) - break; - *insert_pos = (*insert_pos)->next; - } - return; - } - - if (existing_pt) { - // PT collision. We must renumber one of the entries. `pt` is taken - // from the send list, so the PT should remain the same. Renumber - // the existing entry. - int new_pt = __unused_pt_number(receiver, sink, existing_pt); - if (new_pt < 0) { - ilogs(codec, LOG_WARN, "Ran out of RTP payload type numbers while accepting '" - STR_FORMAT "' due to '" STR_FORMAT "'", - STR_FMT(&pt->encoding_with_params), - STR_FMT(&existing_pt->encoding_with_params)); - return; - } - ilogs(codec, LOG_DEBUG, "Renumbering '" STR_FORMAT "' from PT %i to %i due to '" STR_FORMAT "'", - STR_FMT(&existing_pt->encoding_with_params), - existing_pt->payload_type, - new_pt, - STR_FMT(&pt->encoding_with_params)); - g_hash_table_steal(receiver->codecs_recv, GINT_TO_POINTER(existing_pt->payload_type)); - existing_pt->payload_type = new_pt; - g_hash_table_insert(receiver->codecs_recv, GINT_TO_POINTER(existing_pt->payload_type), - existing_pt); - } - - //ilog(LOG_DEBUG, "XXXXXXXXXXXXX offered codec %i", pt->for_transcoding); - ilogs(codec, LOG_DEBUG, "Accepting offered codec " STR_FORMAT " due to transcoding", - STR_FMT(&pt->encoding_with_params)); - MEDIA_SET(receiver, TRANSCODE); - - // we need a new pt entry - pt = __rtp_payload_type_copy(pt); - pt->for_transcoding = 1; - codec_touched(pt, receiver); - // this somewhat duplicates __rtp_payload_type_add_recv - g_hash_table_insert(receiver->codecs_recv, GINT_TO_POINTER(pt->payload_type), pt); - __rtp_payload_type_add_name(receiver->codec_names_recv, pt); - - // keep supplemental codecs last - ensure_codec_def(pt, receiver); - if (!pt->codec_def || !pt->codec_def->supplemental) { - while (*insert_pos) { - struct rtp_payload_type *ipt = (*insert_pos)->data; - ensure_codec_def(ipt, receiver); - if (!ipt->codec_def || !ipt->codec_def->supplemental) - break; - *insert_pos = (*insert_pos)->prev; - } - } - else { - if (!*insert_pos) - *insert_pos = receiver->codecs_prefs_recv.tail; - } - - if (!*insert_pos) { - g_queue_push_head(&receiver->codecs_prefs_recv, pt); - *insert_pos = receiver->codecs_prefs_recv.head; - } - else { - g_queue_insert_after(&receiver->codecs_prefs_recv, *insert_pos, pt); - *insert_pos = (*insert_pos)->next; - } -} -static void __reorder_transcode_codecs(struct call_media *receiver, struct call_media *sink, - const struct sdp_ng_flags *flags, int accept_only_tc) -{ - // if the other side is transcoding, we need to accept codecs that were - // originally offered (recv->send) if we support them, even if the - // response (sink->send) doesn't include them - GList *insert_pos = NULL; - for (GList *l = sink->codecs_prefs_recv.head; l; l = l->next) { - // take the PT that we can receive on the sink side and get the appropriate - // output PT on the receiver side to ensure codec symmetry. - struct rtp_payload_type *sink_pt = l->data; - // determine output PT from the codec handler - struct codec_handler *ch = codec_handler_get(sink, sink_pt->payload_type); - if (ch && ch->source_pt.payload_type != -1 && ch->dest_pt.payload_type != -1) { - __accept_pt(receiver, sink, ch->dest_pt.payload_type, sink_pt->payload_type, - accept_only_tc, &insert_pos); - if (ch->dtmf_payload_type != -1) - __accept_pt(receiver, sink, ch->dtmf_payload_type, -1, - accept_only_tc, &insert_pos); - if (ch->cn_payload_type != -1) - __accept_pt(receiver, sink, ch->cn_payload_type, -1, - accept_only_tc, &insert_pos); - } - else - __accept_pt(receiver, sink, sink_pt->payload_type, -1, accept_only_tc, &insert_pos); - } - - __single_codec(receiver, flags); -} -static void __accept_transcode_codecs(struct call_media *receiver, struct call_media *sink, - const struct sdp_ng_flags *flags, int accept_only_tc) -{ - // if the other side is transcoding, we need to accept codecs that were - // originally offered (recv->send) if we support them, even if the - // response (sink->send) doesn't include them - GList *insert_pos = NULL; - for (GList *l = receiver->codecs_prefs_send.head; l; l = l->next) { - struct rtp_payload_type *pt = l->data; - __accept_pt(receiver, sink, pt->payload_type, -1, accept_only_tc, &insert_pos); - } - - __single_codec(receiver, flags); -} - -static void __eliminate_rejected_codecs(struct call_media *receiver, struct call_media *sink, - const struct sdp_ng_flags *flags) +static void __check_dtmf_injector(struct call_media *receiver, struct call_media *sink, + struct codec_handler *parent, + GHashTable *output_transcoders) { - if (flags && flags->asymmetric_codecs) + if (!sink->monologue->inject_dtmf) return; - - // in the other case (not transcoding), we can eliminate rejected codecs from our - // `send` list if the receiver cannot receive it. - for (GList *l = receiver->codecs_prefs_send.head; l;) { - struct rtp_payload_type *pt = l->data; - if (g_hash_table_lookup(receiver->codec_names_recv, &pt->encoding)) { - l = l->next; - continue; - } - ilogs(codec, LOG_DEBUG, "Eliminating asymmetric outbound codec " STR_FORMAT, - STR_FMT(&pt->encoding_with_params)); - l = __delete_send_codec(receiver, l); - } -} - -// transfers ownership of payload type objects from a queue to a hash table. -// duplicates are removed. -static GHashTable *__payload_type_queue_hash(GQueue *prefs, GQueue *order) { - GHashTable *ret = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, - (GDestroyNotify) payload_type_free); - g_queue_init(order); - for (GList *l = prefs->head; l; l = l->next) { - struct rtp_payload_type *pt = l->data; - if (g_hash_table_lookup(ret, GINT_TO_POINTER(pt->payload_type))) { - ilogs(codec, LOG_DEBUG, "Removing duplicate RTP payload type %i", pt->payload_type); - payload_type_free(pt); - continue; - } - g_hash_table_insert(ret, GINT_TO_POINTER(pt->payload_type), pt); - g_queue_push_tail(order, GINT_TO_POINTER(pt->payload_type)); - } - - // ownership has been transferred - g_queue_clear(prefs); - - return ret; -} - -static void __symmetric_codecs(struct call_media *receiver, struct call_media *sink, - int *sink_transcoding) -{ - //ilog(LOG_DEBUG, "XXXXXXXXXXXXXXX symm codec flags %i %i", MEDIA_ISSET(sink, TRANSCODE), *sink_transcoding); - if (!MEDIA_ISSET(sink, TRANSCODE)) + if (parent->dtmf_payload_type != -1) return; - if (!*sink_transcoding) + if (parent->dtmf_injector) return; - - // sink still looks like it's transcoding. reconstruct our answer to the receiver - // (receiver->prefs_recv) based on the codecs accepted by the sink (sink->prefs_send). - - GQueue prefs_recv_order, prefs_send_order; - GHashTable *prefs_recv = __payload_type_queue_hash(&receiver->codecs_prefs_recv, &prefs_recv_order); - GHashTable *prefs_send = __payload_type_queue_hash(&receiver->codecs_prefs_send, &prefs_send_order); - - // ownership of the objects has been transferred. clear out old structures. - g_hash_table_remove_all(receiver->codecs_recv); - g_hash_table_remove_all(receiver->codec_names_recv); - g_hash_table_remove_all(receiver->codecs_send); - g_hash_table_remove_all(receiver->codec_names_send); - - // reconstruct list based on other side's preference. - 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) { - 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? - struct rtp_payload_type *out_pt = g_hash_table_lookup(prefs_recv, - 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. - ilogs(codec, 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 - ilogs(codec, LOG_DEBUG, "Adding force-accepted RTP payload type %i", prefix_pt->payload_type); - g_hash_table_steal(prefs_send, ptype); - __rtp_payload_type_add_send(receiver, prefix_pt); - // and our receive leg - struct rtp_payload_type *in_pt = g_hash_table_lookup(prefs_recv, ptype); - if (in_pt) { - g_hash_table_steal(prefs_recv, ptype); - __rtp_payload_type_add_recv(receiver, in_pt, 1); - } - transcoding = 1; - } - - // add it to the list - ilogs(codec, 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); - } - } - - if (!transcoding) - *sink_transcoding = 0; - else { - // append any leftover codecs - while (prefs_recv_order.length) { - void *ptype = g_queue_pop_head(&prefs_recv_order); - struct rtp_payload_type *out_pt = g_hash_table_lookup(prefs_recv, ptype); - if (!out_pt) - continue; - //ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXX appending recv codec " STR_FORMAT, STR_FMT(&out_pt->encoding_with_params)); - g_hash_table_steal(prefs_recv, ptype); - __rtp_payload_type_add_recv(receiver, out_pt, 1); - } - while (prefs_send_order.length) { - void *ptype = g_queue_pop_head(&prefs_send_order); - struct rtp_payload_type *out_pt = g_hash_table_lookup(prefs_send, ptype); - if (!out_pt) - continue; - //ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXX appending send codec " STR_FORMAT, STR_FMT(&out_pt->encoding_with_params)); - g_hash_table_steal(prefs_send, ptype); - __rtp_payload_type_add_send(receiver, out_pt); - } - } - - g_hash_table_destroy(prefs_recv); - g_queue_clear(&prefs_recv_order); - g_hash_table_destroy(prefs_send); - g_queue_clear(&prefs_send_order); -} - -static void __check_dtmf_injector(const struct sdp_ng_flags *flags, struct call_media *receiver, - struct rtp_payload_type *pref_dest_codec, GHashTable *output_transcoders, - int dtmf_payload_type) -{ - if (!flags || !flags->inject_dtmf) + if (parent->source_pt.codec_def->supplemental) return; - if (receiver->dtmf_injector) { - // is this still valid? - if (!rtp_payload_type_cmp(pref_dest_codec, &receiver->dtmf_injector->dest_pt)) - return; - - receiver->dtmf_injector = NULL; - } // synthesise input rtp payload type struct rtp_payload_type src_pt = { .payload_type = -1, - .clock_rate = pref_dest_codec->clock_rate, - .channels = pref_dest_codec->channels, + .clock_rate = parent->source_pt.clock_rate, + .channels = parent->source_pt.channels, }; str_init(&src_pt.encoding, "DTMF injector"); str_init(&src_pt.encoding_with_params, "DTMF injector"); str_init(&src_pt.encoding_with_full_params, "DTMF injector"); - const str tp_event = STR_CONST_INIT("telephone-event"); + static const str tp_event = STR_CONST_INIT("telephone-event"); src_pt.codec_def = codec_find(&tp_event, MT_AUDIO); if (!src_pt.codec_def) { ilogs(codec, LOG_ERR, "RTP payload type 'telephone-event' is not defined"); return; } - //receiver->dtmf_injector = codec_handler_make_playback(&src_pt, pref_dest_codec, 0); - //receiver->dtmf_injector->dtmf_payload_type = dtmf_payload_type; - receiver->dtmf_injector = __handler_new(&src_pt, receiver); - __make_transcoder(receiver->dtmf_injector, pref_dest_codec, output_transcoders, dtmf_payload_type, 0); - receiver->dtmf_injector->func = handler_func_inject_dtmf; - g_queue_push_tail(&receiver->codec_handlers_store, receiver->dtmf_injector); + parent->dtmf_injector = __handler_new(&src_pt, receiver); + __make_transcoder(parent->dtmf_injector, &parent->dest_pt, output_transcoders, -1, 0, -1); + parent->dtmf_injector->func = handler_func_inject_dtmf; } @@ -1030,7 +606,7 @@ static struct codec_handler *__get_pt_handler(struct call_media *receiver, struc if (handler) { // make sure existing handler matches this PT if (rtp_payload_type_cmp(pt, &handler->source_pt)) { - ilogs(codec, LOG_DEBUG, "Resetting codec handler for PT %u", pt->payload_type); + ilogs(codec, LOG_DEBUG, "Resetting codec handler for PT %i", pt->payload_type); __handler_shutdown(handler); handler = NULL; g_atomic_pointer_set(&receiver->codec_handler_cache, NULL); @@ -1038,8 +614,9 @@ static struct codec_handler *__get_pt_handler(struct call_media *receiver, struc } } if (!handler) { - ilogs(codec, LOG_DEBUG, "Creating codec handler for " STR_FORMAT, - STR_FMT(&pt->encoding_with_params)); + ilogs(codec, LOG_DEBUG, "Creating codec handler for " STR_FORMAT " (%i)", + STR_FMT(&pt->encoding_with_params), + pt->payload_type); handler = __handler_new(pt, receiver); g_hash_table_insert(receiver->codec_handlers, GINT_TO_POINTER(handler->source_pt.payload_type), @@ -1123,7 +700,7 @@ static void __check_t38_gateway(struct call_media *pcm_media, struct call_media // for each codec type supported by the pcm_media, we create a codec handler that // links to the T.38 encoder - for (GList *l = pcm_media->codecs_prefs_recv.head; l; l = l->next) { + for (GList *l = pcm_media->codecs.codec_prefs.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; struct codec_handler *handler = __get_pt_handler(pcm_media, pt); if (!pt->codec_def) { @@ -1135,7 +712,7 @@ static void __check_t38_gateway(struct call_media *pcm_media, struct call_media ilogs(codec, LOG_DEBUG, "Creating T.38 encoder for " STR_FORMAT, STR_FMT(&pt->encoding_with_params)); - __make_transcoder(handler, &pcm_media->t38_gateway->pcm_pt, NULL, -1, 0); + __make_transcoder(handler, &pcm_media->t38_gateway->pcm_pt, NULL, -1, 0, -1); handler->packet_decoded = packet_decoded_direct; handler->packet_encoded = packet_encoded_t38; @@ -1254,43 +831,14 @@ static void __codec_rtcp_timer(struct call_media *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, GINT_TO_POINTER(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, GINT_TO_POINTER(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 void codec_handlers_update(struct call_media *receiver, struct call_media *sink, const struct sdp_ng_flags *flags, const struct stream_params *sp) { + ilogs(codec, LOG_DEBUG, "Setting up codec handlers for " STR_FORMAT_M " -> " STR_FORMAT_M " (media #%u)", + STR_FMT_M(&receiver->monologue->tag), STR_FMT_M(&sink->monologue->tag), + receiver->index); + MEDIA_CLEAR(receiver, GENERATOR); MEDIA_CLEAR(sink, GENERATOR); @@ -1323,280 +871,259 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, receiver->rtcp_handler = NULL; GSList *passthrough_handlers = NULL; - // we go through the list of codecs that the receiver supports and compare it - // with the list of codecs supported by the sink. if the receiver supports - // a codec that the sink doesn't support, we must transcode. - // - // if we transcode, we transcode to the highest-preference supported codec - // that the sink specified. determine this first. + // first gather info about what we can send + AUTO_CLEANUP_NULL(GHashTable *supplemental_sinks, __g_hash_table_destroy); struct rtp_payload_type *pref_dest_codec = NULL; + __check_codec_list(&supplemental_sinks, &pref_dest_codec, sink, &sink->codecs.codec_prefs); - // 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 - // in case there's several of them. the clock rates of the destination - // codec and the supplemental codec must match. - GHashTable *supplemental_sinks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, - (GDestroyNotify) g_hash_table_destroy); - for (GList *l = codec_supplemental_codecs->head; l; l = l->next) { - codec_def_t *def = l->data; - g_hash_table_replace(supplemental_sinks, (void *) def->rtpname, - g_hash_table_new(g_direct_hash, g_direct_equal)); - } - - pref_dest_codec = __check_dest_codecs(receiver, sink, flags, supplemental_sinks, &sink_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); - - // 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, sink); - - if (flags && flags->opmode == OP_ANSWER && flags->symmetric_codecs) - __symmetric_codecs(receiver, sink, &sink_transcoding); - - 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); - supplemental_sinks = NULL; - - struct rtp_payload_type *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); - int cn_pt_match = __supp_codec_match(receiver, sink, cn_payload_type, NULL, NULL); - - // stop transcoding if we've determined that we don't need it - if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding && !(receiver_transcoding & 0x2)) { - ilogs(codec, LOG_DEBUG, "Disabling transcoding engine (not needed)"); - MEDIA_CLEAR(sink, TRANSCODE); - } - - if (MEDIA_ISSET(sink, TRANSCODE) && (sink_transcoding & 0x2)) { - if (flags && flags->opmode == OP_ANSWER && - (rtpe_config.reorder_codecs || flags->reorder_codecs)) - __reorder_transcode_codecs(receiver, sink, flags, (receiver_transcoding & 0x1)); - else - __accept_transcode_codecs(receiver, sink, flags, (receiver_transcoding & 0x1)); - } - else - __eliminate_rejected_codecs(receiver, sink, flags); + // then do the same with what we can receive + AUTO_CLEANUP_NULL(GHashTable *supplemental_recvs, __g_hash_table_destroy); + __check_codec_list(&supplemental_recvs, NULL, receiver, &receiver->codecs.codec_prefs); // if multiple input codecs transcode to the same output codec, we want to make sure // that all the decoders output their media to the same encoder. we use the destination // payload type to keep track of this. - 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? - if ((sink_transcoding & 0x4)) - transcode_supplemental = 1; - - // do we need to detect PCM DTMF tones? - int pcm_dtmf_detect = 0; - if ((MEDIA_ISSET(sink, TRANSCODE) || (sink_transcoding & 0x2)) - && dtmf_payload_type != -1 - && dtmf_pt && (!reverse_dtmf_pt || reverse_dtmf_pt->for_transcoding || - !g_hash_table_lookup(receiver->codecs_send, GINT_TO_POINTER(dtmf_payload_type)))) - pcm_dtmf_detect = 1; + AUTO_CLEANUP(GHashTable *output_transcoders, __g_hash_table_destroy) + = g_hash_table_new(g_direct_hash, g_direct_equal); - for (GList *l = receiver->codecs_prefs_recv.head; l; ) { + for (GList *l = receiver->codecs.codec_prefs.head; l; ) { struct rtp_payload_type *pt = l->data; + struct rtp_payload_type *sink_pt = NULL; - ilogs(internals, LOG_DEBUG, "checking recv codec " STR_FORMAT, - STR_FMT(&pt->encoding)); - - if (MEDIA_ISSET(sink, TRANSCODE) && flags && flags->opmode == OP_ANSWER) { - // if the other side is transcoding, we may come across a receiver entry - // (recv->recv) that wasn't originally offered (recv->send). we must eliminate - // those, unless we added them ourselves for transcoding. - struct rtp_payload_type *recv_pt = - g_hash_table_lookup(receiver->codecs_send, GINT_TO_POINTER(pt->payload_type)); - if (!recv_pt && !pt->for_transcoding) { - ilogs(codec, LOG_DEBUG, "Eliminating transcoded codec " STR_FORMAT, - STR_FMT(&pt->encoding_with_params)); - - codec_touched(pt, receiver); - l = __delete_receiver_codec(receiver, l); - continue; - } - } + ilogs(codec, LOG_DEBUG, "Checking receiver codec " STR_FORMAT " (%i)", + STR_FMT(&pt->encoding_with_full_params), pt->payload_type); struct codec_handler *handler = __get_pt_handler(receiver, pt); // check our own support for this codec if (!pt->codec_def) { // not supported - __make_passthrough_gsl(handler, &passthrough_handlers); + ilogs(codec, LOG_DEBUG, "No codec support for " STR_FORMAT, + STR_FMT(&pt->encoding_with_params)); + __make_passthrough_gsl(handler, &passthrough_handlers, NULL, NULL); goto next; } - // if the sink's codec preferences are unknown (empty), or there are - // no supported codecs to transcode to, then we have nothing - // to do. most likely this is an initial offer without a received answer. - // we default to forwarding without transcoding. - if (!pref_dest_codec) { - ilogs(codec, LOG_DEBUG, "No known/supported sink codec for " STR_FORMAT, + // fill matching supp codecs + struct rtp_payload_type *recv_dtmf_pt = __supp_payload_type(supplemental_recvs, pt->clock_rate, + "telephone-event"); + struct rtp_payload_type *recv_cn_pt = __supp_payload_type(supplemental_recvs, pt->clock_rate, + "CN"); + int pcm_dtmf_detect = 0; + + // find the matching sink codec + + if (!sink_pt) { + // can we send the same codec that we want to receive? + sink_pt = g_hash_table_lookup(sink->codecs.codecs, + GINT_TO_POINTER(pt->payload_type)); + // is it actually the same? + if (sink_pt && rtp_payload_type_cmp(pt, sink_pt)) + sink_pt = NULL; + } + + if (!sink_pt) { + // no matching/identical output codec. maybe we have the same output codec, + // but with a different payload type or a different format? + GQueue *dest_codecs = g_hash_table_lookup(sink->codecs.codec_names, &pt->encoding); + if (dest_codecs) { + // the sink supports this codec - check offered formats + for (GList *k = dest_codecs->head; k; k = k->next) { + unsigned int dest_ptype = GPOINTER_TO_UINT(k->data); + sink_pt = g_hash_table_lookup(sink->codecs.codecs, + GINT_TO_POINTER(dest_ptype)); + if (!sink_pt) + continue; + if (sink_pt->clock_rate != pt->clock_rate || + sink_pt->channels != pt->channels) { + sink_pt = NULL; + continue; + } + break; + } + } + } + + if (sink_pt && !pt->codec_def->supplemental) { + // we have a matching output codec. do we actually want to use it, or + // do we want to transcode to something else? + // ignore the preference here - for now, all `for_transcoding` codecs + // take preference + if (pref_dest_codec && pref_dest_codec->for_transcoding) + sink_pt = pref_dest_codec; + } + + // still no output? pick the preferred sink codec + if (!sink_pt) + sink_pt = pref_dest_codec; + + if (!sink_pt) { + ilogs(codec, LOG_DEBUG, "No suitable output codec for " STR_FORMAT, STR_FMT(&pt->encoding_with_params)); - __make_passthrough_gsl(handler, &passthrough_handlers); + __make_passthrough_gsl(handler, &passthrough_handlers, recv_dtmf_pt, recv_cn_pt); goto next; } - ilogs(internals, LOG_DEBUG, "pref dest codec " STR_FORMAT " is %i, CN match %i DTMF match %i " - "sink TC %i/%i recv TC %i TC supp %i DTMF DSP %i", - STR_FMT(&pref_dest_codec->encoding_with_params), - pref_dest_codec->for_transcoding, - cn_pt_match, dtmf_pt_match, - MEDIA_ISSET(sink, TRANSCODE), sink_transcoding, - receiver_transcoding, - transcode_supplemental, pcm_dtmf_detect); + // sink_pt has been determined here now. + + ilogs(codec, LOG_DEBUG, "Sink codec is " STR_FORMAT " (%i)", + STR_FMT(&sink_pt->encoding_with_full_params), sink_pt->payload_type); - struct rtp_payload_type *dest_pt = NULL; // transcode to this + // we have found a usable output codec. gather matching output supp codecs + struct rtp_payload_type *sink_dtmf_pt = __supp_payload_type(supplemental_sinks, + sink_pt->clock_rate, "telephone-event"); + struct rtp_payload_type *sink_cn_pt = __supp_payload_type(supplemental_sinks, + sink_pt->clock_rate, "CN"); - if (pref_dest_codec->for_transcoding) { - // with force accepted codec, we still accept DTMF payloads if possible - if (pt->codec_def && pt->codec_def->supplemental) - dest_pt = g_hash_table_lookup(sink->codecs_send, GINT_TO_POINTER(pt->payload_type)); + // XXX synthesise missing supp codecs according to codec tracker XXX needed? + + if (!flags) { + // second pass going through the offerer codecs during an answer: + // if an answer rejected a supplemental codec that isn't marked for transcoding, + // reject it on the sink side as well + if (sink_dtmf_pt && !recv_dtmf_pt && !sink_dtmf_pt->for_transcoding) + sink_dtmf_pt = NULL; + if (sink_cn_pt && !recv_cn_pt && !sink_cn_pt->for_transcoding) + sink_cn_pt = NULL; } 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 ((receiver_transcoding & 0x4)) - ; - else if (pcm_dtmf_detect) - ; - else - dest_pt = g_hash_table_lookup(sink->codecs_send, GINT_TO_POINTER(pt->payload_type)); + if (flags->inject_dtmf) + sink->monologue->inject_dtmf = 1; } - if (!dest_pt) - goto unsupported; - // in case of ptime mismatch, we transcode, but between the same codecs - if (dest_pt->ptime && pt->ptime - && dest_pt->ptime != pt->ptime) + // do we need DTMF detection? + if (!pt->codec_def->supplemental && !recv_dtmf_pt && sink_dtmf_pt + && sink_dtmf_pt->for_transcoding) { - ilogs(codec, LOG_DEBUG, "Mismatched ptime between source and sink (%i <> %i), " - "enabling transcoding", - dest_pt->ptime, pt->ptime); - goto transcode; + pcm_dtmf_detect = 1; + ilogs(codec, LOG_DEBUG, "Enabling PCM DTMF detection from " STR_FORMAT + " to " STR_FORMAT + "/" STR_FORMAT, + STR_FMT(&pt->encoding_with_params), + STR_FMT(&sink_pt->encoding_with_params), + STR_FMT(&sink_dtmf_pt->encoding_with_params)); } - if (flags && flags->inject_dtmf) { - // we have a matching output codec, but we were told that we might - // want to inject DTMF, so we must still go through our transcoding - // engine, despite input and output codecs being the same. - goto transcode; - } + // we can now decide whether we can do passthrough, or transcode + // different codecs? // XXX needs more intelligent fmtp matching - if (rtp_payload_type_cmp_nf(pt, dest_pt)) + if (rtp_payload_type_cmp_nf(pt, sink_pt)) goto transcode; - // do we need silence detection? - if (cn_pt_match == 2 && MEDIA_ISSET(sink, TRANSCODE)) + // different ptime? + if (sink_pt->ptime && pt->ptime && sink_pt->ptime != pt->ptime) { + if (MEDIA_ISSET(sink, PTIME_OVERRIDE) || MEDIA_ISSET(receiver, PTIME_OVERRIDE)) { + ilogs(codec, LOG_DEBUG, "Mismatched ptime between source and sink (%i <> %i), " + "enabling transcoding", + sink_pt->ptime, pt->ptime); + goto transcode; + } + ilogs(codec, LOG_DEBUG, "Mismatched ptime between source and sink (%i <> %i), " + "but no override requested", + sink_pt->ptime, pt->ptime); + } + + if (sink->monologue->inject_dtmf) { + // we have a matching output codec, but we were told that we might + // want to inject DTMF, so we must still go through our transcoding + // engine, despite input and output codecs being the same. + goto transcode; + } + + // compare supplemental codecs + // DTMF + if (pcm_dtmf_detect) + goto transcode; + if (recv_dtmf_pt && recv_dtmf_pt->for_transcoding && !sink_dtmf_pt) { + ilogs(codec, LOG_DEBUG, "Transcoding DTMF events to PCM from " STR_FORMAT + " to " STR_FORMAT, + STR_FMT(&pt->encoding_with_params), + STR_FMT(&sink_pt->encoding_with_params)); + goto transcode; + } + // CN + if (!recv_cn_pt && sink_cn_pt && sink_cn_pt->for_transcoding) { + ilogs(codec, LOG_DEBUG, "Enabling CN silence detection from " STR_FORMAT + " to " STR_FORMAT + "/" STR_FORMAT, + STR_FMT(&pt->encoding_with_params), + STR_FMT(&sink_pt->encoding_with_params), + STR_FMT(&sink_cn_pt->encoding_with_params)); goto transcode; + } + if (recv_cn_pt && recv_cn_pt->for_transcoding && !sink_cn_pt) { + ilogs(codec, LOG_DEBUG, "Transcoding CN packets to PCM from " STR_FORMAT + " to " STR_FORMAT, + STR_FMT(&pt->encoding_with_params), + STR_FMT(&sink_pt->encoding_with_params)); + goto transcode; + } - // XXX check format parameters as well - ilogs(codec, LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding_with_params)); - __make_passthrough_gsl(handler, &passthrough_handlers); - if (pt->codec_def && pt->codec_def->dtmf) - __dtmf_dsp_shutdown(sink, pt->payload_type); + // everything matches - we can do passthrough + ilogs(codec, LOG_DEBUG, "Sink supports codec " STR_FORMAT " for passthrough", + STR_FMT(&pt->encoding_with_params)); + __make_passthrough_gsl(handler, &passthrough_handlers, sink_dtmf_pt, sink_cn_pt); goto next; -unsupported: - // the sink does not support this codec -> transcode - ilogs(codec, LOG_DEBUG, "Sink does not support codec " STR_FORMAT, STR_FMT(&pt->encoding_with_params)); - dest_pt = pref_dest_codec; - if (pt->codec_def->supplemental) - transcode_supplemental = 1; transcode:; // look up the reverse side of this payload type, which is the decoder to our // encoder. if any codec options such as bitrate were set during an offer, - // they're in the decoder // PT. copy them to the encoder PT. - struct rtp_payload_type *reverse_pt = g_hash_table_lookup(sink->codecs_recv, - GINT_TO_POINTER(dest_pt->payload_type)); + // they're in the decoder PT. copy them to the encoder PT. + struct rtp_payload_type *reverse_pt = g_hash_table_lookup(sink->codecs.codecs, + GINT_TO_POINTER(sink_pt->payload_type)); if (reverse_pt) { - if (!dest_pt->bitrate) - dest_pt->bitrate = reverse_pt->bitrate; - if (!dest_pt->codec_opts.len) - call_str_cpy(sink->call, &dest_pt->codec_opts, &reverse_pt->codec_opts); + if (!sink_pt->bitrate) + sink_pt->bitrate = reverse_pt->bitrate; + if (!sink_pt->codec_opts.len) { + str_free_dup(&sink_pt->codec_opts); + str_init_dup_str(&sink_pt->codec_opts, &reverse_pt->codec_opts); + } } MEDIA_SET(receiver, TRANSCODE); - __make_transcoder(handler, dest_pt, output_transcoders, dtmf_payload_type, pcm_dtmf_detect); - handler->cn_payload_type = cn_payload_type; + __make_transcoder(handler, sink_pt, output_transcoders, + sink_dtmf_pt ? sink_dtmf_pt->payload_type : -1, + pcm_dtmf_detect, sink_cn_pt ? sink_cn_pt->payload_type : -1); + __check_dtmf_injector(receiver, sink, handler, output_transcoders); next: l = l->next; } - // if we've determined that we transcode, we must remove all unsupported codecs from - // the list, as we must expect to potentially receive media in that codec, which we - // then could not transcode. if (MEDIA_ISSET(receiver, TRANSCODE)) { - ilogs(codec, LOG_INFO, "Enabling transcoding engine"); + // we have to translate RTCP packets + receiver->rtcp_handler = rtcp_transcode_handler; - for (GList *l = receiver->codecs_prefs_recv.head; l; ) { + for (GList *l = receiver->codecs.codec_prefs.head; l; ) { struct rtp_payload_type *pt = l->data; - ilogs(internals, LOG_DEBUG, "checking recv codec " STR_FORMAT, - STR_FMT(&pt->encoding)); - if (pt->codec_def) { // supported l = l->next; continue; } - ilogs(codec, LOG_DEBUG, "Stripping unsupported codec " STR_FORMAT " due to active transcoding", + ilogs(codec, LOG_DEBUG, "Stripping unsupported codec " STR_FORMAT + " due to active transcoding", STR_FMT(&pt->encoding)); - codec_touched(pt, receiver); - l = __delete_receiver_codec(receiver, l); + codec_touched(&receiver->codecs, pt); + l = __codec_store_delete_link(l, &receiver->codecs); } - // we have to translate RTCP packets - receiver->rtcp_handler = rtcp_transcode_handler; - - __check_dtmf_injector(flags, receiver, pref_dest_codec, output_transcoders, dtmf_payload_type); // at least some payload types will be transcoded, which will result in SSRC // change. for payload types which we don't actually transcode, we still // must substitute the SSRC while (passthrough_handlers) { struct codec_handler *handler = passthrough_handlers->data; - // 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 - // 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) - __make_passthrough_ssrc(handler); - else if (dtmf_pt_match == 2) - __make_passthrough_ssrc(handler); - else if (!pref_dest_codec - || !handler->source_pt.codec_def || !pref_dest_codec->codec_def) - __make_passthrough_ssrc(handler); - else { - __make_transcoder(handler, pref_dest_codec, output_transcoders, - dtmf_payload_type, pcm_dtmf_detect); - handler->cn_payload_type = cn_payload_type; - } + __make_passthrough_ssrc(handler); passthrough_handlers = g_slist_delete_link(passthrough_handlers, passthrough_handlers); } } - while (passthrough_handlers) { + while (passthrough_handlers) passthrough_handlers = g_slist_delete_link(passthrough_handlers, passthrough_handlers); - } - - g_hash_table_destroy(output_transcoders); if (MEDIA_ISSET(receiver, RTCP_GEN)) { receiver->rtcp_handler = rtcp_sink_handler; @@ -1734,7 +1261,6 @@ void codec_handlers_free(struct call_media *m) { m->codec_handler_cache = NULL; #ifdef WITH_TRANSCODING g_queue_clear_full(&m->codec_handlers_store, __codec_handler_free); - m->dtmf_injector = NULL; #endif } @@ -1800,8 +1326,9 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa struct codec_handler *h = packet->handler; if (G_UNLIKELY(!h->ssrc_hash)) { - if (!packet->func || !packet->handler->ssrc_hash) { + if (!packet->func || !h->input_handler->ssrc_hash) { h->func(h, mp); + __transcode_packet_free(packet); return 0; } } @@ -1812,11 +1339,10 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa struct ssrc_entry_call *ssrc_out_p = ssrc_out->parent; struct codec_ssrc_handler *ch = get_ssrc(ssrc_in_p->h.ssrc, h->ssrc_hash); - if (G_UNLIKELY(!ch)) + if (G_UNLIKELY(!ch)) { + __transcode_packet_free(packet); return 0; - - atomic64_inc(&ssrc_in->packets); - atomic64_add(&ssrc_in->octets, mp->payload.len); + } packet->p.seq = ntohs(mp->rtp->seq_num); packet->payload = str_dup(&mp->payload); @@ -1824,10 +1350,22 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa packet->ts = packet_ts; packet->marker = (mp->rtp->m_pt & 0x80) ? 1 : 0; - // how should we retrieve packets from the sequencer? - void *(*seq_next_packet)(packet_sequencer_t *) = packet_sequencer_next_packet; - if (packet->ignore_seq) - seq_next_packet = packet_sequencer_force_next_packet; + atomic64_inc(&ssrc_in->packets); + atomic64_add(&ssrc_in->octets, mp->payload.len); + + if (packet->bypass_seq) { + // bypass sequencer + int ret = packet->func(ch, ch, packet, mp); + if (ret != 1) + __transcode_packet_free(packet); + goto out_ch; + } + + struct codec_ssrc_handler *input_ch = get_ssrc(ssrc_in_p->h.ssrc, h->input_handler->ssrc_hash); + if (G_UNLIKELY(!input_ch)) { + __transcode_packet_free(packet); + goto out_ch; + } __ssrc_lock_both(mp); @@ -1838,7 +1376,7 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa if (seq_ret < 0) { // dupe if (packet->dup_func) - packet->dup_func(ch, packet, mp); + packet->dup_func(ch, input_ch, packet, mp); else ilogs(transcoding, LOG_DEBUG, "Ignoring duplicate RTP packet"); __transcode_packet_free(packet); @@ -1851,9 +1389,10 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa while (1) { int func_ret = 0; - packet = seq_next_packet(&ssrc_in_p->sequencer); + packet = packet_sequencer_next_packet(&ssrc_in_p->sequencer); if (G_UNLIKELY(!packet)) { - if (!ch->encoder_format.clockrate || !ch->handler || !ch->handler->dest_pt.codec_def) + if (!ch || !ch->encoder_format.clockrate || !ch->handler + || !ch->handler->dest_pt.codec_def) break; uint32_t ts_diff = packet_ts - ch->last_ts; @@ -1871,13 +1410,22 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa break; } + // new packet might have different handlers h = packet->handler; - struct codec_ssrc_handler *next_ch = get_ssrc(ssrc_in_p->h.ssrc, h->ssrc_hash); - if (G_UNLIKELY(!next_ch)) + if (ch) + obj_put(&ch->h); + if (input_ch) + obj_put(&input_ch->h); + input_ch = NULL; + ch = get_ssrc(ssrc_in_p->h.ssrc, h->ssrc_hash); + if (G_UNLIKELY(!ch)) goto next; - - obj_put(&ch->h); - ch = next_ch; + input_ch = get_ssrc(ssrc_in_p->h.ssrc, h->input_handler->ssrc_hash); + if (G_UNLIKELY(!input_ch)) { + obj_put(&ch->h); + ch = NULL; + goto next; + } atomic64_set(&ssrc_in->packets_lost, ssrc_in_p->sequencer.lost_count); atomic64_set(&ssrc_in->last_seq, ssrc_in_p->sequencer.ext_seq); @@ -1894,7 +1442,7 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa // we might be working with a different packet now mp->rtp = &packet->rtp; - func_ret = packet->func(ch, packet, mp); + func_ret = packet->func(ch, input_ch, packet, mp); if (func_ret < 0) ilogs(transcoding, LOG_WARN | LOG_FLAG_LIMIT, "Decoder error while processing RTP packet"); next: @@ -1904,7 +1452,11 @@ next: out: __ssrc_unlock_both(mp); - obj_put(&ch->h); + if (input_ch) + obj_put(&input_ch->h); +out_ch: + if (ch) + obj_put(&ch->h); return 0; } @@ -2027,82 +1579,77 @@ static struct codec_ssrc_handler *__output_ssrc_handler(struct codec_ssrc_handle return new_ch; } -static int packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct transcode_packet *packet, +// forwards DTMF input to DTMF output, plus rescaling duration +static int packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp) { int payload_type = -1; // take from handler's output config unsigned long ts_delay = 0; - if (ch->handler->dtmf_scaler) { - struct codec_ssrc_handler *output_ch = NULL; - struct codec_ssrc_handler *decoder_ch = NULL; - - // this is actually a DTMF -> PCM handler - // grab our underlying PCM transcoder - struct codec_handler *decoder_handler = __decoder_handler(ch->handler, mp); - decoder_ch = get_ssrc(mp->ssrc_in->parent->h.ssrc, - decoder_handler->ssrc_hash); - output_ch = __output_ssrc_handler(decoder_ch, mp); - if (G_UNLIKELY(!ch->encoder || !output_ch->encoder)) - goto skip; - - // init some vars - ch->first_ts = output_ch->first_ts; - ch->first_send_ts = output_ch->first_send_ts; - ch->output_skew = output_ch->output_skew; - ch->first_send = output_ch->first_send; - - - // the correct output TS is the encoder's FIFO PTS at the start of the DTMF - // event. however, we must shift the FIFO PTS forward as the DTMF event goes on - // as the DTMF event replaces the audio samples. therefore we must remember - // the TS at the start of the event and the last seen event duration. - if (ch->dtmf_ts != packet->ts) { - // this is a new event - ch->dtmf_ts = packet->ts; // start TS - ch->last_dtmf_event_ts = 0; // last DTMF event duration - } - - unsigned long ts = output_ch->encoder->next_pts / output_ch->encoder->def->clockrate_mult; - // roll back TS to start of event - ts -= ch->last_dtmf_event_ts; - // adjust to output RTP TS - unsigned long packet_ts = ts + output_ch->first_ts; - - ilogs(transcoding, LOG_DEBUG, "Scaling DTMF packet timestamp and duration: TS %lu -> %lu " - "(%u -> %u)", - packet->ts, packet_ts, - ch->handler->source_pt.clock_rate, ch->handler->dest_pt.clock_rate); - packet->ts = packet_ts; - - if (packet->payload->len >= sizeof(struct telephone_event_payload)) { - struct telephone_event_payload *dtmf = (void *) packet->payload->s; - unsigned int duration = av_rescale(ntohs(dtmf->duration), - ch->handler->dest_pt.clock_rate, ch->handler->source_pt.clock_rate); - dtmf->duration = htons(duration); - - // we can't directly use the RTP TS to schedule the send, as we have to adjust it - // by the duration - if (ch->dtmf_first_duration == 0 || duration < ch->dtmf_first_duration) - ch->dtmf_first_duration = duration; - ts_delay = duration - ch->dtmf_first_duration; - - // shift forward our output RTP TS - output_ch->encoder->next_pts = (ts + duration) * output_ch->encoder->def->clockrate_mult; - output_ch->encoder->packet_pts += (duration - ch->last_dtmf_event_ts) * output_ch->encoder->def->clockrate_mult; - ch->last_dtmf_event_ts = duration; - } - payload_type = ch->handler->dtmf_payload_type; + struct codec_ssrc_handler *output_ch = NULL; + + // this is actually a DTMF -> PCM handler + // grab our underlying PCM transcoder + output_ch = __output_ssrc_handler(input_ch, mp); + if (G_UNLIKELY(!ch->encoder || !output_ch->encoder)) + goto skip; + + // init some vars + ch->first_ts = output_ch->first_ts; + ch->first_send_ts = output_ch->first_send_ts; + ch->output_skew = output_ch->output_skew; + ch->first_send = output_ch->first_send; + + + // the correct output TS is the encoder's FIFO PTS at the start of the DTMF + // event. however, we must shift the FIFO PTS forward as the DTMF event goes on + // as the DTMF event replaces the audio samples. therefore we must remember + // the TS at the start of the event and the last seen event duration. + if (ch->dtmf_ts != packet->ts) { + // this is a new event + ch->dtmf_ts = packet->ts; // start TS + ch->last_dtmf_event_ts = 0; // last DTMF event duration + } + + unsigned long ts = output_ch->encoder->next_pts / output_ch->encoder->def->clockrate_mult; + // roll back TS to start of event + ts -= ch->last_dtmf_event_ts; + // adjust to output RTP TS + unsigned long packet_ts = ts + output_ch->first_ts; + + ilogs(transcoding, LOG_DEBUG, "Scaling DTMF packet timestamp and duration: TS %lu -> %lu " + "(%u -> %u)", + packet->ts, packet_ts, + ch->handler->source_pt.clock_rate, ch->handler->dest_pt.clock_rate); + packet->ts = packet_ts; + + if (packet->payload->len >= sizeof(struct telephone_event_payload)) { + struct telephone_event_payload *dtmf = (void *) packet->payload->s; + unsigned int duration = av_rescale(ntohs(dtmf->duration), + ch->handler->dest_pt.clock_rate, ch->handler->source_pt.clock_rate); + dtmf->duration = htons(duration); -skip: - if (output_ch) - obj_put(&output_ch->h); - obj_put(&decoder_ch->h); + // we can't directly use the RTP TS to schedule the send, as we have to adjust it + // by the duration + if (ch->dtmf_first_duration == 0 || duration < ch->dtmf_first_duration) + ch->dtmf_first_duration = duration; + ts_delay = duration - ch->dtmf_first_duration; + + // shift forward our output RTP TS + output_ch->encoder->next_pts = (ts + duration) * output_ch->encoder->def->clockrate_mult; + output_ch->encoder->packet_pts += (duration - ch->last_dtmf_event_ts) * output_ch->encoder->def->clockrate_mult; + ch->last_dtmf_event_ts = duration; } + payload_type = ch->handler->dtmf_payload_type; + +skip: + if (output_ch) + obj_put(&output_ch->h); char *buf = malloc(packet->payload->len + sizeof(struct rtp_header) + RTP_BUFFER_TAIL_ROOM); memcpy(buf + sizeof(struct rtp_header), packet->payload->s, packet->payload->len); - if (packet->ignore_seq) // inject original seq + if (packet->bypass_seq) // inject original seq __output_rtp(mp, ch, packet->handler ? : ch->handler, buf, packet->payload->len, packet->ts, packet->marker, packet->p.seq, -1, payload_type, ts_delay); else // use our own sequencing @@ -2113,7 +1660,7 @@ skip: } // returns the codec handler for the primary payload type - mostly determined by guessing -static struct codec_handler *__decoder_handler(struct codec_handler *h, struct media_packet *mp) { +static struct codec_handler *__input_handler(struct codec_handler *h, struct media_packet *mp) { if (!mp->ssrc_in) return h; @@ -2135,18 +1682,9 @@ static struct codec_handler *__decoder_handler(struct codec_handler *h, struct m return h; } -static int packet_dtmf(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp) +static int packet_dtmf(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp) { - struct codec_ssrc_handler *decoder_ch = NULL; - - if (mp->ssrc_in) { - // find our decoder handler for the appropriate DTX buffer - struct codec_handler *handler = ch->handler; - struct codec_handler *decoder_handler = __decoder_handler(handler, mp); - decoder_ch = get_ssrc(mp->ssrc_in->parent->h.ssrc, - decoder_handler->ssrc_hash); - } - if (ch->ts_in != packet->ts) { // ignore already processed events int ret = dtmf_event(mp, packet->payload, ch->encoder_format.clockrate); if (G_UNLIKELY(ret == -1)) // error @@ -2154,40 +1692,37 @@ static int packet_dtmf(struct codec_ssrc_handler *ch, struct transcode_packet *p if (ret == 1) { // END event ch->ts_in = packet->ts; - if (decoder_ch) - decoder_ch->dtmf_start_ts = 0; - } - else { - if (decoder_ch) - decoder_ch->dtmf_start_ts = packet->ts ? packet->ts : 1; + input_ch->dtmf_start_ts = 0; } + else + input_ch->dtmf_start_ts = packet->ts ? packet->ts : 1; } int ret = 0; if (!mp->call->block_dtmf && !mp->media->monologue->block_dtmf) { - if (decoder_ch && __buffer_dtx(decoder_ch->dtx_buffer, ch, packet, mp, packet_dtmf_fwd)) + if (__buffer_dtx(input_ch->dtx_buffer, ch, input_ch, packet, mp, packet_dtmf_fwd)) ret = 1; // consumed else - packet_dtmf_fwd(ch, packet, mp); + packet_dtmf_fwd(ch, input_ch, packet, mp); } - if (decoder_ch) - obj_put(&decoder_ch->h); - return ret; } -static int packet_dtmf_dup(struct codec_ssrc_handler *ch, struct transcode_packet *packet, +static int packet_dtmf_dup(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp) { if (!mp->call->block_dtmf && !mp->media->monologue->block_dtmf) - packet_dtmf_fwd(ch, packet, mp); + packet_dtmf_fwd(ch, input_ch, packet, mp); return 0; } static int __handler_func_supplemental(struct codec_handler *h, struct media_packet *mp, - int (*func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *), - int (*dup_func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *)) + int (*func)(struct codec_ssrc_handler *, struct codec_ssrc_handler *, + struct transcode_packet *, struct media_packet *), + int (*dup_func)(struct codec_ssrc_handler *, struct codec_ssrc_handler *, + struct transcode_packet *, struct media_packet *)) { if (G_UNLIKELY(!mp->rtp)) return handler_func_passthrough(h, mp); @@ -2196,7 +1731,8 @@ static int __handler_func_supplemental(struct codec_handler *h, struct media_pac // create new packet and insert it into sequencer queue - ilogs(transcoding, LOG_DEBUG, "Received %s RTP packet: SSRC %" PRIx32 ", PT %u, seq %u, TS %u, len %zu", + ilogs(transcoding, LOG_DEBUG, "Received %s supplemental RTP packet: SSRC %" PRIx32 + ", PT %u, seq %u, TS %u, len %zu", h->source_pt.codec_def->rtpname, ntohl(mp->rtp->ssrc), mp->rtp->m_pt, ntohs(mp->rtp->seq_num), ntohl(mp->rtp->timestamp), mp->payload.len); @@ -2204,9 +1740,10 @@ static int __handler_func_supplemental(struct codec_handler *h, struct media_pac // determine the primary audio codec used by this SSRC, as the sequence numbers // and timing info is shared with it. we'll need to use the same sequencer - struct codec_handler *sequencer_h = __decoder_handler(h, mp); + struct codec_handler *sequencer_h = __input_handler(h, mp); - // XXX ? h->output_handler = sequencer_h->output_handler; // XXX locking? + h->input_handler = sequencer_h; + h->output_handler = sequencer_h; struct transcode_packet *packet = g_slice_alloc0(sizeof(*packet)); packet->func = func; @@ -2214,19 +1751,18 @@ static int __handler_func_supplemental(struct codec_handler *h, struct media_pac packet->handler = h; packet->rtp = *mp->rtp; - if (sequencer_h->kernelize) { - // this sequencer doesn't actually keep track of RTP seq properly. instruct - // the sequencer not to wait for the next in-seq packet but always return - // them immediately - packet->ignore_seq = 1; + if (sequencer_h->passthrough || sequencer_h->kernelize) { + // bypass sequencer, directly pass it to forwarding function + packet->bypass_seq = 1; } return __handler_func_sequencer(mp, packet); } -//static int handler_func_supplemental(struct codec_handler *h, struct media_packet *mp) { - //return __handler_func_supplemental(h, mp, packet_decode, NULL); -//} static int handler_func_dtmf(struct codec_handler *h, struct media_packet *mp) { + // DTMF input - can we do DTMF output? + if (h->dtmf_payload_type == -1) + return handler_func_transcode(h, mp); + return __handler_func_supplemental(h, mp, packet_dtmf, packet_dtmf_dup); } @@ -2250,7 +1786,8 @@ void codec_packet_free(void *pp) { -struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct call_media *media) { +struct rtp_payload_type *codec_make_payload_type(const str *codec_str, enum media_type type) { + str codec_fmt = *codec_str; str codec, parms, chans, opts, extra_opts, fmt_params, codec_opts; if (str_token_sep(&codec, &codec_fmt, '/')) @@ -2270,53 +1807,52 @@ struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct ca if (clockrate && !channels) channels = 1; - struct rtp_payload_type *ret = g_slice_alloc0(sizeof(*ret)); - ret->payload_type = -1; - ret->encoding = codec; - ret->clock_rate = clockrate; - ret->channels = channels; - ret->bitrate = bitrate; - ret->ptime = ptime; - ret->format_parameters = fmt_params; - ret->codec_opts = codec_opts; + struct rtp_payload_type *pt = g_slice_alloc0(sizeof(*pt)); - const codec_def_t *def = codec_find(&ret->encoding, 0); - ret->codec_def = def; + pt->payload_type = -1; + pt->encoding = codec; + pt->clock_rate = clockrate; + pt->channels = channels; + pt->bitrate = bitrate; + pt->ptime = ptime; + pt->format_parameters = fmt_params; + pt->codec_opts = codec_opts; - codec_init_payload_type(ret, media); + codec_init_payload_type(pt, type); - return ret; + return pt; } -void codec_init_payload_type(struct rtp_payload_type *ret, struct call_media *media) { +void codec_init_payload_type(struct rtp_payload_type *pt, enum media_type type) { #ifdef WITH_TRANSCODING - const codec_def_t *def = ret->codec_def; + ensure_codec_def_type(pt, type); + const codec_def_t *def = pt->codec_def; if (def) { - if (!ret->clock_rate) - ret->clock_rate = def->default_clockrate; - if (!ret->channels) - ret->channels = def->default_channels; - if (ret->ptime <= 0) - ret->ptime = def->default_ptime; - if ((!ret->format_parameters.s || !ret->format_parameters.s[0]) && def->default_fmtp) - str_init(&ret->format_parameters, (char *) def->default_fmtp); + if (!pt->clock_rate) + pt->clock_rate = def->default_clockrate; + if (!pt->channels) + pt->channels = def->default_channels; + if (pt->ptime <= 0) + pt->ptime = def->default_ptime; + if (!pt->format_parameters.s && def->default_fmtp) + str_init(&pt->format_parameters, (char *) def->default_fmtp); if (def->init) - def->init(ret); + def->init(pt); - if (def->rfc_payload_type >= 0) { + if (pt->payload_type == -1 && def->rfc_payload_type >= 0) { const struct rtp_payload_type *rfc_pt = rtp_get_rfc_payload_type(def->rfc_payload_type); // only use the RFC payload type if all parameters match if (rfc_pt - && (ret->clock_rate == 0 || ret->clock_rate == rfc_pt->clock_rate) - && (ret->channels == 0 || ret->channels == rfc_pt->channels)) + && (pt->clock_rate == 0 || pt->clock_rate == rfc_pt->clock_rate) + && (pt->channels == 0 || pt->channels == rfc_pt->channels)) { - ret->payload_type = rfc_pt->payload_type; - if (!ret->clock_rate) - ret->clock_rate = rfc_pt->clock_rate; - if (!ret->channels) - ret->channels = rfc_pt->channels; + pt->payload_type = rfc_pt->payload_type; + if (!pt->clock_rate) + pt->clock_rate = rfc_pt->clock_rate; + if (!pt->channels) + pt->channels = rfc_pt->channels; } } } @@ -2327,24 +1863,31 @@ void codec_init_payload_type(struct rtp_payload_type *ret, struct call_media *me char full_full_encoding[64]; char params[32] = ""; - snprintf(full_full_encoding, sizeof(full_full_encoding), STR_FORMAT "/%u/%i", STR_FMT(&ret->encoding), - ret->clock_rate, - ret->channels); + snprintf(full_full_encoding, sizeof(full_full_encoding), STR_FORMAT "/%u/%i", STR_FMT(&pt->encoding), + pt->clock_rate, + pt->channels); - if (ret->channels > 1) { + if (pt->channels > 1) { strcpy(full_encoding, full_full_encoding); - snprintf(params, sizeof(params), "%i", ret->channels); + snprintf(params, sizeof(params), "%i", pt->channels); } else - snprintf(full_encoding, sizeof(full_encoding), STR_FORMAT "/%u", STR_FMT(&ret->encoding), - ret->clock_rate); - - str_init(&ret->encoding_with_params, full_encoding); - str_init(&ret->encoding_with_full_params, full_full_encoding); - str_init(&ret->encoding_parameters, params); - - if (media) - __rtp_payload_type_dup(media->call, ret); + snprintf(full_encoding, sizeof(full_encoding), STR_FORMAT "/%u", STR_FMT(&pt->encoding), + pt->clock_rate); + + // allocate strings + str_init_dup_str(&pt->encoding, &pt->encoding); + str_init_dup(&pt->encoding_with_params, full_encoding); + str_init_dup(&pt->encoding_with_full_params, full_full_encoding); + str_init_dup(&pt->encoding_parameters, params); + str_init_dup_str(&pt->format_parameters, &pt->format_parameters); + str_init_dup_str(&pt->codec_opts, &pt->codec_opts); + + // allocate everything from the rtcp-fb list + for (GList *l = pt->rtcp_fb.head; l; l = l->next) { + str *fb = l->data; + l->data = str_dup(fb); + } } @@ -2363,7 +1906,6 @@ static int handler_func_passthrough_ssrc(struct codec_handler *h, struct media_p // substitute out SSRC etc mp->rtp->ssrc = htonl(mp->ssrc_in->ssrc_map_out); - //mp->rtp->timestamp = htonl(ntohl(mp->rtp->timestamp)); mp->rtp->seq_num = htons(ntohs(mp->rtp->seq_num) + mp->ssrc_out->parent->seq_diff); // keep track of other stats here? @@ -2410,6 +1952,8 @@ uint64_t codec_last_dtmf_event(struct codec_ssrc_handler *ch) { } uint64_t codec_encoder_pts(struct codec_ssrc_handler *ch) { + if (!ch || !ch->encoder) + return 0; return ch->encoder->fifo_pts; } @@ -2448,8 +1992,10 @@ static int codec_decoder_event(enum codec_event event, void *ptr, void *data) { // consumes `packet` if buffered (returns 1) static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *decoder_handler, + struct codec_ssrc_handler *input_handler, struct transcode_packet *packet, struct media_packet *mp, - int (*func)(struct codec_ssrc_handler *ch, struct transcode_packet *packet, + int (*func)(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp)) { if (!dtxb || !mp->sfd || !mp->ssrc_in || !mp->ssrc_out) @@ -2463,6 +2009,8 @@ static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *deco dtxp->func = func; if (decoder_handler) dtxp->decoder_handler = obj_get(&decoder_handler->h); + if (input_handler) + dtxp->input_handler = obj_get(&input_handler->h); media_packet_copy(&dtxp->mp, mp); // add to processing queue @@ -2483,6 +2031,9 @@ static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *deco timerthread_obj_schedule_abs(&dtxb->ct.tt_obj, &dtxb->ct.next); } + // packet now consumed + packet = NULL; + mutex_unlock(&dtxb->lock); return 1; @@ -2494,6 +2045,8 @@ static void dtx_packet_free(struct dtx_packet *dtxp) { media_packet_release(&dtxp->mp); if (dtxp->decoder_handler) obj_put(&dtxp->decoder_handler->h); + if (dtxp->input_handler) + obj_put(&dtxp->input_handler->h); g_slice_free1(sizeof(*dtxp), dtxp); } static void dtx_buffer_stop(struct dtx_buffer **dtxbp) { @@ -2565,6 +2118,7 @@ static void __dtx_send_later(struct codec_timer *ct) { : NULL; if (!ch && dtxb->csh) ch = obj_get(&dtxb->csh->h); + struct codec_ssrc_handler *input_ch = (dtxp && dtxp->input_handler) ? obj_get(&dtxp->input_handler->h) : NULL; struct call *call = dtxb->call ? obj_get(dtxb->call) : NULL; if (!call || !ch || !ps || !ps->ssrc_in @@ -2631,7 +2185,7 @@ static void __dtx_send_later(struct codec_timer *ct) { "%i packets left in queue", ts, p_left); mp_copy.ptime = -1; - ret = dtxp->func(ch, dtxp->packet, &mp_copy); + ret = dtxp->func(ch, input_ch, dtxp->packet, &mp_copy); if (!ret) { if (mp_copy.ptime > 0) ptime = mp_copy.ptime; @@ -2704,6 +2258,8 @@ out: obj_put(call); if (ch) obj_put(&ch->h); + if (input_ch) + obj_put(&input_ch->h); if (dtxp) dtx_packet_free(dtxp); media_packet_release(&mp_copy); @@ -2782,7 +2338,6 @@ static void __silence_detect_ ## type(struct codec_ssrc_handler *ch, AVFrame *fr 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) { \ @@ -2809,7 +2364,6 @@ __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) @@ -2868,19 +2422,12 @@ silence: static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) { struct codec_handler *h = p; - if (h->dtmf_scaler) - ilogs(codec, LOG_DEBUG, "Creating SSRC DTMF transcoder from %s/%u/%i to " - "PT %i", - h->source_pt.codec_def->rtpname, h->source_pt.clock_rate, - h->source_pt.channels, - h->dtmf_payload_type); - else - ilogs(codec, LOG_DEBUG, "Creating SSRC transcoder from %s/%u/%i to " - "%s/%u/%i", - h->source_pt.codec_def->rtpname, h->source_pt.clock_rate, - h->source_pt.channels, - h->dest_pt.codec_def->rtpname, h->dest_pt.clock_rate, - h->dest_pt.channels); + ilogs(codec, LOG_DEBUG, "Creating SSRC transcoder from %s/%u/%i to " + "%s/%u/%i", + h->source_pt.codec_def->rtpname, h->source_pt.clock_rate, + h->source_pt.channels, + h->dest_pt.codec_def->rtpname, h->dest_pt.clock_rate, + h->dest_pt.channels); struct codec_ssrc_handler *ch = obj_alloc0("codec_ssrc_handler", sizeof(*ch), __free_ssrc_handler); ch->handler = h; @@ -2978,7 +2525,6 @@ static void __free_ssrc_handler(void *chp) { static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) { struct codec_ssrc_handler *ch = u1; struct media_packet *mp = u2; - //unsigned int seq_off = (mp->iter_out > mp->iter_in) ? 1 : 0; ilogs(transcoding, LOG_DEBUG, "RTP media successfully encoded: TS %llu, len %i", (unsigned long long) enc->avpkt->pts, enc->avpkt->size); @@ -3044,7 +2590,6 @@ static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) { ch->rtp_mark ? 1 : 0, -1, 0, payload_type, 0); mp->ssrc_out->parent->seq_diff++; - //mp->iter_out++; ch->rtp_mark = 0; } while (repeats--); @@ -3055,7 +2600,6 @@ static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) { // loop around and get more in_pkt = NULL; - //seq_off = 1; // next packet needs last seq + 1 XXX set unkernelize if used } return 0; @@ -3171,7 +2715,8 @@ static int packet_decoded_direct(decoder_t *decoder, AVFrame *frame, void *u1, v return packet_decoded_common(decoder, frame, u1, u2, encoder_input_data); } -static int __rtp_decode(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp) +static int __rtp_decode(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp) { int ret = decoder_input_data_ptime(ch->decoder, packet->payload, packet->ts, &mp->ptime, ch->handler->packet_decoded, @@ -3179,27 +2724,20 @@ static int __rtp_decode(struct codec_ssrc_handler *ch, struct transcode_packet * mp->ssrc_out->parent->seq_diff--; return ret; } -static int packet_decode(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp) +static int packet_decode(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, struct media_packet *mp) { int ret = 0; - struct codec_ssrc_handler *decoder_ch = ch; - - if (ch->handler && ch->handler->source_pt.codec_def && ch->handler->source_pt.codec_def->supplemental) { - struct codec_handler *decoder_handler = __decoder_handler(ch->handler, mp); - decoder_ch = get_ssrc(mp->ssrc_in->parent->h.ssrc, - decoder_handler->ssrc_hash); - } - if (!ch->first_ts) ch->first_ts = packet->ts; ch->last_ts = packet->ts; - if (decoder_ch && decoder_ch->dtmf_start_ts && !rtpe_config.dtmf_no_suppress) { - if ((packet->ts > decoder_ch->dtmf_start_ts && packet->ts - decoder_ch->dtmf_start_ts > 80000) || - (packet->ts < decoder_ch->dtmf_start_ts && decoder_ch->dtmf_start_ts - packet->ts > 80000)) { + if (input_ch->dtmf_start_ts && !rtpe_config.dtmf_no_suppress) { + if ((packet->ts > input_ch->dtmf_start_ts && packet->ts - input_ch->dtmf_start_ts > 80000) || + (packet->ts < input_ch->dtmf_start_ts && input_ch->dtmf_start_ts - packet->ts > 80000)) { ilogs(transcoding, LOG_DEBUG, "Resetting decoder DTMF state due to TS discrepancy"); - decoder_ch->dtmf_start_ts = 0; + input_ch->dtmf_start_ts = 0; } else { ilogs(transcoding, LOG_DEBUG, "Decoder is in DTMF state, discaring codec packet"); @@ -3209,17 +2747,15 @@ static int packet_decode(struct codec_ssrc_handler *ch, struct transcode_packet } } - if (__buffer_dtx(ch->dtx_buffer, ch, packet, mp, __rtp_decode)) + if (__buffer_dtx(input_ch->dtx_buffer, ch, input_ch, packet, mp, __rtp_decode)) ret = 1; // consumed else { ilogs(transcoding, LOG_DEBUG, "Decoding RTP packet now"); - ret = __rtp_decode(ch, packet, mp); + ret = __rtp_decode(ch, input_ch, packet, mp); ret = ret ? -1 : 0; } out: - if (decoder_ch && decoder_ch != ch) - obj_put(&decoder_ch->h); return ret; } @@ -3256,13 +2792,22 @@ static int handler_func_transcode(struct codec_handler *h, struct media_packet * if (mp->call->block_media || mp->media->monologue->block_media) return 0; + // use main codec handler for supp codecs + if (h->source_pt.codec_def->supplemental) { + h->input_handler = __input_handler(h, mp); + h->output_handler = h->input_handler; + } + else + h->input_handler = h; + // create new packet and insert it into sequencer queue ilogs(transcoding, LOG_DEBUG, "Received RTP packet: SSRC %" PRIx32 ", PT %u, seq %u, TS %u, len %zu", ntohl(mp->rtp->ssrc), mp->rtp->m_pt, ntohs(mp->rtp->seq_num), ntohl(mp->rtp->timestamp), mp->payload.len); - codec_calc_jitter(mp->ssrc_in, ntohl(mp->rtp->timestamp), h->source_pt.clock_rate, &mp->tv); + codec_calc_jitter(mp->ssrc_in, ntohl(mp->rtp->timestamp), h->input_handler->source_pt.clock_rate, + &mp->tv); if (h->stats_entry) { unsigned int idx = rtpe_now.tv_sec & 1; @@ -3288,15 +2833,14 @@ static int handler_func_transcode(struct codec_handler *h, struct media_packet * packet->rtp = *mp->rtp; packet->handler = h; - if (h->dtmf_scaler) { + if (h->source_pt.codec_def->dtmf && h->dest_pt.codec_def->dtmf) { + // DTMF scaler packet->func = packet_dtmf; packet->dup_func = packet_dtmf_dup; } int ret = __handler_func_sequencer(mp, packet); - //ilog(LOG_DEBUG, "tc iters: in %u out %u", mp->iter_in, mp->iter_out); - return ret; } @@ -3307,6 +2851,9 @@ static int handler_func_playback(struct codec_handler *h, struct media_packet *m } static int handler_func_inject_dtmf(struct codec_handler *h, struct media_packet *mp) { + h->input_handler = __input_handler(h, mp); + h->output_handler = h->input_handler; + struct codec_ssrc_handler *ch = get_ssrc(mp->ssrc_in->parent->h.ssrc, h->ssrc_hash); decoder_input_data(ch->decoder, &mp->payload, mp->rtp->timestamp, h->packet_decoded, ch, mp); @@ -3321,7 +2868,7 @@ static int handler_func_inject_dtmf(struct codec_handler *h, struct media_packet // special return value `(void *) 0x1` to signal type mismatch static struct rtp_payload_type *codec_make_payload_type_sup(const str *codec_str, struct call_media *media) { - struct rtp_payload_type *ret = codec_make_payload_type(codec_str, media); + struct rtp_payload_type *ret = codec_make_payload_type(codec_str, media->type_id); if (!ret) return NULL; @@ -3348,7 +2895,7 @@ err: static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct call_media *media, - struct call_media *other_media) + struct call_media *other_media, struct codec_store *extra_cs) { struct rtp_payload_type *pt = codec_make_payload_type_sup(codec, media); if (!pt) { @@ -3359,7 +2906,7 @@ static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct if (pt == (void *) 0x1) return NULL; - pt->payload_type = __unused_pt_number(media, other_media, pt); + pt->payload_type = __unused_pt_number(media, other_media, extra_cs, pt); if (pt->payload_type < 0) { ilogs(codec, LOG_WARN, "Ran out of RTP payload type numbers while adding codec '" STR_FORMAT "' for transcoding", @@ -3378,64 +2925,67 @@ static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct -static void __rtp_payload_type_dup(struct call *call, struct rtp_payload_type *pt) { - /* we must duplicate the contents */ - call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params); - // special handling of this one as it's not done by the SDP parser - if (pt->encoding_with_full_params.len) - call_str_cpy(call, &pt->encoding_with_full_params, &pt->encoding_with_full_params); - else { - char buf[64]; - snprintf(buf, sizeof(buf), STR_FORMAT "/%i/%i", STR_FMT(&pt->encoding), - pt->clock_rate, pt->channels); - str s; - str_init(&s, buf); - call_str_cpy(call, &pt->encoding_with_full_params, &s); - } - call_str_cpy(call, &pt->encoding, &pt->encoding); - call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters); - call_str_cpy(call, &pt->format_parameters, &pt->format_parameters); - call_str_cpy(call, &pt->codec_opts, &pt->codec_opts); - for (GList *l = pt->rtcp_fb.head; l; l = l->next) { - str *fb = l->data; - call_str_cpy(call, fb, fb); - } +void payload_type_clear(struct rtp_payload_type *p) { + g_queue_clear_full(&p->rtcp_fb, free); + str_free_dup(&p->encoding); + str_free_dup(&p->encoding_parameters); + str_free_dup(&p->encoding_with_params); + str_free_dup(&p->encoding_with_full_params); + str_free_dup(&p->format_parameters); + str_free_dup(&p->codec_opts); + ZERO(*p); + p->payload_type = -1; +} +void payload_type_free(struct rtp_payload_type *p) { + payload_type_clear(p); + g_slice_free1(sizeof(*p), p); +} + + +// dst must be pre-initialised (zeroed) +static void rtp_payload_type_copy(struct rtp_payload_type *dst, const struct rtp_payload_type *src) { + payload_type_clear(dst); + + *dst = *src; + + // make shallow copy of lists + g_queue_init(&dst->rtcp_fb); + g_queue_append(&dst->rtcp_fb, &src->rtcp_fb); + + // duplicate contents + codec_init_payload_type(dst, MT_UNKNOWN); } -static struct rtp_payload_type *__rtp_payload_type_copy(const struct rtp_payload_type *pt) { - struct rtp_payload_type *pt_copy = g_slice_alloc(sizeof(*pt)); - *pt_copy = *pt; - g_queue_init(&pt_copy->rtcp_fb); - for (GList *l = pt->rtcp_fb.head; l; l = l->next) - g_queue_push_tail(&pt_copy->rtcp_fb, l->data); + +struct rtp_payload_type *rtp_payload_type_dup(const struct rtp_payload_type *pt) { + struct rtp_payload_type *pt_copy = g_slice_alloc0(sizeof(*pt)); + rtp_payload_type_copy(pt_copy, pt); return pt_copy; } -static void __rtp_payload_type_add_name(GHashTable *ht, struct rtp_payload_type *pt) -{ +static void __rtp_payload_type_add_name(GHashTable *ht, struct rtp_payload_type *pt) { GQueue *q = g_hash_table_lookup_queue_new(ht, str_dup(&pt->encoding), free); - g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type)); + g_queue_push_tail(q, GINT_TO_POINTER(pt->payload_type)); q = g_hash_table_lookup_queue_new(ht, str_dup(&pt->encoding_with_params), free); - g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type)); + g_queue_push_tail(q, GINT_TO_POINTER(pt->payload_type)); q = g_hash_table_lookup_queue_new(ht, str_dup(&pt->encoding_with_full_params), free); - g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type)); + g_queue_push_tail(q, GINT_TO_POINTER(pt->payload_type)); } #ifdef WITH_TRANSCODING -static void __insert_codec_tracker(struct call_media *media, GList *link) { +static void __insert_codec_tracker(GHashTable *all_clockrates, GHashTable *all_supp_codecs, + struct codec_tracker *sct, GList *link) +{ struct rtp_payload_type *pt = link->data; - struct codec_tracker *sct = media->codec_tracker; - - ensure_codec_def(pt, media); if (!pt->codec_def || !pt->codec_def->supplemental) - g_hash_table_replace(sct->clockrates, GUINT_TO_POINTER(pt->clock_rate), + g_hash_table_replace(all_clockrates, GUINT_TO_POINTER(pt->clock_rate), GUINT_TO_POINTER(GPOINTER_TO_UINT( - g_hash_table_lookup(sct->clockrates, + g_hash_table_lookup(all_clockrates, GUINT_TO_POINTER(pt->clock_rate))) + 1)); else { - GHashTable *clockrates = g_hash_table_lookup(sct->supp_codecs, &pt->encoding); + GHashTable *clockrates = g_hash_table_lookup(all_supp_codecs, &pt->encoding); if (!clockrates) { clockrates = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_queue_free); - g_hash_table_replace(sct->supp_codecs, str_dup(&pt->encoding), clockrates); + g_hash_table_replace(all_supp_codecs, str_dup(&pt->encoding), clockrates); } GQueue *entries = g_hash_table_lookup_queue_new(clockrates, GUINT_TO_POINTER(pt->clock_rate), NULL); @@ -3443,128 +2993,13 @@ static void __insert_codec_tracker(struct call_media *media, GList *link) { } } #endif -static void __queue_insert_supp(GQueue *q, struct rtp_payload_type *pt, int supp_check) { - // do we care at all? - if (!supp_check) { - g_queue_push_tail(q, pt); - return; - } - - // all new supp codecs go last - if (pt->codec_def && pt->codec_def->supplemental) { - g_queue_push_tail(q, pt); - return; - } - - // find the cut-off point between non-supp and supp codecs - GList *insert_pos = NULL; // last non-supp codec - for (GList *l = q->tail; l; l = l->prev) { - struct rtp_payload_type *ptt = l->data; - if (!ptt->codec_def || !ptt->codec_def->supplemental) { - insert_pos = l; - break; - } - } - // do we have any non-supp codecs? - if (!insert_pos) - g_queue_push_head(q, pt); - else - g_queue_insert_after(q, insert_pos, pt); -} -// consumes 'pt' -void __rtp_payload_type_add_recv(struct call_media *media, struct rtp_payload_type *pt, int supp_check) { - if (!pt) - return; -#ifdef WITH_TRANSCODING - ensure_codec_def(pt, media); -#endif - if (proto_is_not_rtp(media->protocol)) { - payload_type_free(pt); - return; - } - // update ptime in case it was overridden - if (media->ptime > 0) - pt->ptime = media->ptime; - g_hash_table_insert(media->codecs_recv, GINT_TO_POINTER(pt->payload_type), pt); - __rtp_payload_type_add_name(media->codec_names_recv, pt); - __queue_insert_supp(&media->codecs_prefs_recv, pt, supp_check); -} -// consumes 'pt' -void __rtp_payload_type_add_send(struct call_media *other_media, - struct rtp_payload_type *pt) -{ - if (!pt) - return; - if (proto_is_not_rtp(other_media->protocol)) { - payload_type_free(pt); - return; - } - // update ptime in case it was overridden - if (other_media->ptime > 0) - pt->ptime = other_media->ptime; - g_hash_table_insert(other_media->codecs_send, GINT_TO_POINTER(pt->payload_type), pt); - __rtp_payload_type_add_name(other_media->codec_names_send, pt); - g_queue_push_tail(&other_media->codecs_prefs_send, pt); -} -// duplicates 'pt' -void __rtp_payload_type_add_send_dup(struct call_media *other_media, - struct rtp_payload_type *pt) -{ - if (proto_is_not_rtp(other_media->protocol)) - return; - pt = __rtp_payload_type_copy(pt); - __rtp_payload_type_add_send(other_media, pt); -} -// consumes 'pt' -static void __rtp_payload_type_add(struct call_media *media, struct call_media *other_media, - struct rtp_payload_type *pt) -{ - __rtp_payload_type_add_send_dup(other_media, pt); - __rtp_payload_type_add_recv(media, pt, 0); -} - -static void __payload_queue_free(void *qq) { - GQueue *q = qq; - g_queue_free_full(q, (GDestroyNotify) payload_type_free); -} -static int __revert_codec_strip(GHashTable *stripped, GHashTable *masked, const str *codec, - struct call_media *media, struct call_media *other_media) -{ - int ret = 0; - - GQueue *q = g_hash_table_lookup(stripped, codec); - if (q) { - ilogs(codec, LOG_DEBUG, "Restoring codec '" STR_FORMAT "' from stripped codecs (%u payload types)", - STR_FMT(codec), q->length); - while (q->length) { - struct rtp_payload_type *pt = g_queue_pop_head(q); - __rtp_payload_type_add(media, other_media, pt); - } - g_hash_table_remove(stripped, codec); - ret = 1; - } - - q = g_hash_table_lookup(masked, codec); - if (q) { - ilogs(codec, LOG_DEBUG, "Restoring codec '" STR_FORMAT "' from masked codecs (%u payload types)", - STR_FMT(codec), q->length); - while (q->length) { - struct rtp_payload_type *pt = g_queue_pop_head(q); - __rtp_payload_type_add_recv(media, pt, 1); - } - g_hash_table_remove(masked, codec); - ret = 1; - } - - return ret; -} static int __codec_options_set1(struct call *call, struct rtp_payload_type *pt, const str *enc, GHashTable *codec_set) { str *pt_str = g_hash_table_lookup(codec_set, enc); if (!pt_str) return 0; - struct rtp_payload_type *pt_parsed = codec_make_payload_type(pt_str, NULL); + struct rtp_payload_type *pt_parsed = codec_make_payload_type(pt_str, MT_UNKNOWN); if (!pt_parsed) return 0; // match parameters @@ -3575,12 +3010,17 @@ static int __codec_options_set1(struct call *call, struct rtp_payload_type *pt, // match - apply options if (!pt->bitrate) pt->bitrate = pt_parsed->bitrate; - if (!pt->codec_opts.len && pt_parsed->codec_opts.len) - call_str_cpy(call, &pt->codec_opts, &pt_parsed->codec_opts); + if (!pt->codec_opts.len && pt_parsed->codec_opts.len) { + str_free_dup(&pt->codec_opts); + pt->codec_opts = pt_parsed->codec_opts; + pt_parsed->codec_opts = STR_NULL; + } payload_type_free(pt_parsed); return 1; } static void __codec_options_set(struct call *call, struct rtp_payload_type *pt, GHashTable *codec_set) { + if (!call) + return; if (!codec_set) return; if (__codec_options_set1(call, pt, &pt->encoding_with_full_params, codec_set)) @@ -3590,34 +3030,45 @@ static void __codec_options_set(struct call *call, struct rtp_payload_type *pt, if (__codec_options_set1(call, pt, &pt->encoding, codec_set)) return; } -#ifdef WITH_TRANSCODING static void codec_tracker_destroy(struct codec_tracker **sct) { +#ifdef WITH_TRANSCODING if (!*sct) return; - g_hash_table_destroy((*sct)->clockrates); g_hash_table_destroy((*sct)->touched); - g_hash_table_destroy((*sct)->supp_codecs); g_slice_free1(sizeof(**sct), *sct); *sct = NULL; +#endif } -void codec_tracker_init(struct call_media *m) { - codec_tracker_destroy(&m->codec_tracker); - m->codec_tracker = g_slice_alloc0(sizeof(*m->codec_tracker)); - m->codec_tracker->clockrates = g_hash_table_new(g_direct_hash, g_direct_equal); - m->codec_tracker->touched = g_hash_table_new(g_direct_hash, g_direct_equal); - m->codec_tracker->supp_codecs = g_hash_table_new_full(str_case_hash, str_case_equal, free, - (GDestroyNotify) g_hash_table_destroy); +static struct codec_tracker *codec_tracker_init(void) { +#ifdef WITH_TRANSCODING + struct codec_tracker *ret = g_slice_alloc0(sizeof(*ret)); + ret->touched = g_hash_table_new(g_direct_hash, g_direct_equal); + return ret; +#else + return NULL; +#endif } -static void codec_touched(struct rtp_payload_type *pt, struct call_media *media) { - if (!media->codec_tracker) - return; - ensure_codec_def(pt, media); +static void codec_touched(struct codec_store *cs, struct rtp_payload_type *pt) { +#ifdef WITH_TRANSCODING if (pt->codec_def && pt->codec_def->supplemental) { - media->codec_tracker->all_touched = 1; + cs->tracker->all_touched = 1; return; } - g_hash_table_replace(media->codec_tracker->touched, GUINT_TO_POINTER(pt->clock_rate), (void *) 0x1); + g_hash_table_replace(cs->tracker->touched, GUINT_TO_POINTER(pt->clock_rate), (void *) 0x1); +#endif } +static int is_codec_touched(struct codec_store *cs, struct rtp_payload_type *pt) { +#ifdef WITH_TRANSCODING + if (!cs || !cs->tracker || !cs->tracker->touched) + return 0; + if (cs->tracker->all_touched) + return 1; + return g_hash_table_lookup(cs->tracker->touched, GINT_TO_POINTER(pt->clock_rate)) ? 1 : 0; +#else + return 0; +#endif +} +#ifdef WITH_TRANSCODING static int ptr_cmp(const void *a, const void *b) { if (a < b) return -1; @@ -3625,34 +3076,43 @@ static int ptr_cmp(const void *a, const void *b) { return 1; return 0; } -void codec_tracker_finish(struct call_media *media, struct call_media *other_media) { - struct codec_tracker *sct = media->codec_tracker; +void codec_tracker_update(struct codec_store *cs) { + if (!cs) + return; + struct codec_tracker *sct = cs->tracker; if (!sct) return; + ilogs(codec, LOG_DEBUG, "Updating supplemental codecs for " STR_FORMAT " #%u", + STR_FMT(&cs->media->monologue->tag), + cs->media->index); + // build our tables - for (GList *l = media->codecs_prefs_recv.head; l; l = l->next) - __insert_codec_tracker(media, l); + GHashTable *all_clockrates = g_hash_table_new(g_direct_hash, g_direct_equal); + GHashTable *all_supp_codecs = g_hash_table_new_full(str_case_hash, str_case_equal, free, + (GDestroyNotify) g_hash_table_destroy); + for (GList *l = cs->codec_prefs.head; l; l = l->next) + __insert_codec_tracker(all_clockrates, all_supp_codecs, sct, l); // get all supported audio clock rates - GList *clockrates = g_hash_table_get_keys(sct->clockrates); + GList *clockrates = g_hash_table_get_keys(all_clockrates); // and to ensure consistent results clockrates = g_list_sort(clockrates, ptr_cmp); // for each supplemental codec supported ... - GList *supp_codecs = g_hash_table_get_keys(sct->supp_codecs); + GList *supp_codecs = g_hash_table_get_keys(all_supp_codecs); for (GList *l = supp_codecs; l; l = l->next) { // ... compare the list of clock rates against the clock rates supported by the audio codecs str *supp_codec = l->data; - GHashTable *supp_clockrates = g_hash_table_lookup(sct->supp_codecs, supp_codec); + GHashTable *supp_clockrates = g_hash_table_lookup(all_supp_codecs, supp_codec); // iterate audio clock rates and check against supp clockrates for (GList *k = clockrates; k; k = k->next) { unsigned int clockrate = GPOINTER_TO_UINT(k->data); // has it been removed? - if (!g_hash_table_lookup(sct->clockrates, GUINT_TO_POINTER(clockrate))) + if (!g_hash_table_lookup(all_clockrates, GUINT_TO_POINTER(clockrate))) continue; // is this already supported? @@ -3672,28 +3132,12 @@ void codec_tracker_finish(struct call_media *media, struct call_media *other_med str pt_str; str_init(&pt_str, pt_s); - struct rtp_payload_type *pt = codec_add_payload_type(&pt_str, media, NULL); + struct rtp_payload_type *pt = codec_add_payload_type(&pt_str, cs->media, NULL, NULL); if (!pt) continue; pt->for_transcoding = 1; - // there should be an existing entry with a different clock rate - GQueue *existing_pts = g_hash_table_lookup(media->codec_names_recv, &pt->encoding); - struct rtp_payload_type *existing_pt = NULL; - if (existing_pts && existing_pts->length) { - int pt_num = GPOINTER_TO_UINT(existing_pts->head->data); - existing_pt = g_hash_table_lookup(media->codecs_recv, GINT_TO_POINTER(pt_num)); - } - struct codec_handler *existing_handler = existing_pt ? codec_handler_get(media, existing_pt->payload_type) : NULL; - - if (existing_pt && existing_handler && existing_handler->dest_pt.codec_def) { - // since this happens after we ran through the codec matchup, we must create the appropriate handler here - struct codec_handler *handler = __get_pt_handler(media, pt); - // duplicate the codec handler of the existing PT - __make_transcoder(handler, &existing_handler->dest_pt, NULL, existing_handler->dtmf_payload_type, 0); - } - - __rtp_payload_type_add_recv(media, pt, 1); + codec_store_add_raw_order(cs, pt); g_free(pt_s); } @@ -3715,351 +3159,546 @@ void codec_tracker_finish(struct call_media *media, struct call_media *other_med ilogs(codec, LOG_DEBUG, "Eliminating supplemental codec " STR_FORMAT " (%i) with " "stray clock rate %u", STR_FMT(&pt->encoding_with_params), pt->payload_type, clockrate); - - // now we have to check the codec handlers on the opposite side to see - // if any of them were using this as output - struct rtp_payload_type *prim_dtmf = NULL; - struct rtp_payload_type *prim_cn = NULL; - for (GList *o = other_media->codecs_prefs_recv.head; o; o = o->next) { - struct rtp_payload_type *opt = o->data; - struct codec_handler *ch = codec_handler_get(other_media, - opt->payload_type); - if (!ch) - continue; - - // check DTMF - if (!prim_dtmf && ch->dtmf_payload_type != -1) - prim_dtmf = g_hash_table_lookup(other_media->codecs_recv, - GINT_TO_POINTER(ch->dtmf_payload_type)); - if (prim_dtmf) { - if (ch->dest_pt.payload_type == pt->payload_type) { - ilogs(codec, LOG_DEBUG, "Adjusting output DTMF PT for " - "opposite codec handler for " - STR_FORMAT " (%i) to %i", - STR_FMT(&opt->encoding_with_params), - opt->payload_type, - prim_dtmf->payload_type); - __make_transcoder(ch, prim_dtmf, NULL, - prim_dtmf->payload_type, - ch->pcm_dtmf_detect); - } - else if (ch->dtmf_payload_type == pt->payload_type) { - ilogs(codec, LOG_DEBUG, "Adjusting output DTMF PT for " - "opposite codec handler for " - STR_FORMAT " (%i) to %i", - STR_FMT(&opt->encoding_with_params), - opt->payload_type, - prim_dtmf->payload_type); - __make_transcoder(ch, &ch->dest_pt, NULL, - prim_dtmf->payload_type, - ch->pcm_dtmf_detect); - } - } - - // check CN - if (!prim_cn && ch->cn_payload_type != -1) - prim_cn = g_hash_table_lookup(other_media->codecs_recv, - GINT_TO_POINTER(ch->cn_payload_type)); - if (prim_cn) { - if (ch->dest_pt.payload_type == pt->payload_type) { - ilogs(codec, LOG_DEBUG, "Adjusting output CN PT for " - "opposite codec handler for " - STR_FORMAT " (%i) to %i", - STR_FMT(&opt->encoding_with_params), - opt->payload_type, - prim_cn->payload_type); - ch->cn_payload_type = prim_cn->payload_type; - } - else if (ch->cn_payload_type == pt->payload_type) { - ilogs(codec, LOG_DEBUG, "Adjusting output CN PT for " - "opposite codec handler for " - STR_FORMAT " (%i) to %i", - STR_FMT(&opt->encoding_with_params), - opt->payload_type, - prim_cn->payload_type); - ch->cn_payload_type = prim_cn->payload_type; - } - } - } - - __delete_receiver_codec(media, link); + __codec_store_delete_link(link, cs); } } } g_list_free(supp_codecs); g_list_free(clockrates); - codec_tracker_destroy(&media->codec_tracker); + g_hash_table_destroy(all_clockrates); + g_hash_table_destroy(all_supp_codecs); } #endif -int __codec_ht_except(int all_flag, GHashTable *yes_ht, GHashTable *no_ht, struct rtp_payload_type *pt) { - int do_this = 0; - if (all_flag) - do_this = 1; - if (yes_ht) { - if (g_hash_table_lookup(yes_ht, &pt->encoding)) - do_this = 1; - else if (g_hash_table_lookup(yes_ht, &pt->encoding_with_params)) - do_this = 1; - else if (g_hash_table_lookup(yes_ht, &pt->encoding_with_full_params)) - do_this = 1; - } - if (no_ht && all_flag) { - if (g_hash_table_lookup(no_ht, &pt->encoding)) - do_this = 0; - else if (g_hash_table_lookup(no_ht, &pt->encoding_with_params)) - do_this = 0; - else if (g_hash_table_lookup(no_ht, &pt->encoding_with_full_params)) - do_this = 0; - } - return do_this; -} -void __ht_merge(GHashTable **dst, GHashTable *src) { - if (!src) + +void codec_store_cleanup(struct codec_store *cs) { + if (cs->codecs) + g_hash_table_destroy(cs->codecs); + if (cs->codec_names) + g_hash_table_destroy(cs->codec_names); + g_queue_clear_full(&cs->codec_prefs, (GDestroyNotify) payload_type_free); + cs->supp_link = NULL; + codec_tracker_destroy(&cs->tracker); + ZERO(*cs); +} + +void codec_store_init(struct codec_store *cs, struct call_media *media) { + if (!media) + media = cs->media; + + codec_store_cleanup(cs); + + cs->codecs = g_hash_table_new(g_direct_hash, g_direct_equal); + cs->codec_names = g_hash_table_new_full(str_case_hash, str_case_equal, free, + (void (*)(void*)) g_queue_free); + cs->media = media; + cs->tracker = codec_tracker_init(); +} + +static void codec_store_move(struct codec_store *dst, struct codec_store *src) { + *dst = *src; + ZERO(*src); + codec_store_init(src, dst->media); +} + +static void codec_store_add_raw_link(struct codec_store *cs, struct rtp_payload_type *pt, GList *link) { + // cs->media may be NULL + ensure_codec_def(pt, cs->media); + if (cs->media && cs->media->ptime > 0) + pt->ptime = cs->media->ptime; + + ilogs(internals, LOG_DEBUG, "Adding codec '" STR_FORMAT "'/'" STR_FORMAT "'/'" STR_FORMAT "' at pos %p", + STR_FMT(&pt->encoding), + STR_FMT(&pt->encoding_with_params), + STR_FMT(&pt->encoding_with_full_params), link); + g_hash_table_insert(cs->codecs, GINT_TO_POINTER(pt->payload_type), pt); + __rtp_payload_type_add_name(cs->codec_names, pt); + if (!link) + g_queue_push_tail(&cs->codec_prefs, pt); + else + g_queue_insert_before(&cs->codec_prefs, link, pt); + pt->prefs_link = cs->codec_prefs.tail; + if (!cs->supp_link && pt->codec_def && pt->codec_def->supplemental) + cs->supp_link = pt->prefs_link; +} + +// appends to the end, but before supplemental codecs +static void codec_store_add_raw_order(struct codec_store *cs, struct rtp_payload_type *pt) { + codec_store_add_raw_link(cs, pt, cs->supp_link); +} +// appends to the end +void codec_store_add_raw(struct codec_store *cs, struct rtp_payload_type *pt) { + codec_store_add_raw_link(cs, pt, NULL); +} + +static void codec_store_add_link(struct codec_store *cs, struct rtp_payload_type *pt, GList *link) { + if (!cs->media) 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, - GQueue *types, struct sdp_ng_flags *flags) -{ - if (!flags) + + ensure_codec_def(pt, cs->media); + if (proto_is_not_rtp(cs->media->protocol)) return; - // 'media' = receiver of this offer/answer; 'other_media' = sender of this offer/answer - struct call *call = media->call; - struct rtp_payload_type *pt; - static const str str_all = STR_CONST_INIT("all"); - 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 *masked = g_hash_table_new_full(str_case_hash, str_case_equal, free, __payload_queue_free); - int strip_all = 0, mask_all = 0, consume_all = 0, accept_all = 0; + struct rtp_payload_type *copy = rtp_payload_type_dup(pt); + codec_store_add_raw_link(cs, copy, link); +} + +// appends to the end, but before supplemental codecs +static void codec_store_add_order(struct codec_store *cs, struct rtp_payload_type *pt) { + codec_store_add_link(cs, pt, cs->supp_link); +} +// always add to end +static void codec_store_add_end(struct codec_store *cs, struct rtp_payload_type *pt) { + codec_store_add_link(cs, pt, NULL); +} +void codec_store_populate(struct codec_store *dst, struct codec_store *src, GHashTable *codec_set) { // start fresh - if (!proto_is_rtp(other_media->protocol) && proto_is_rtp(media->protocol) && flags->opmode == OP_OFFER) { - // leave them alone if incoming proto is not RTP but outgoing is, - // as this is needed for T.38 decoding during a re-invite. - // this special case is only needed in an offer as in the answer - // we can go by media->codecs_prefs_send. - ; - } - else { - // receiving part for 'media' - g_queue_clear_full(&media->codecs_prefs_recv, (GDestroyNotify) payload_type_free); - g_hash_table_remove_all(media->codecs_recv); - g_hash_table_remove_all(media->codec_names_recv); - } - // and sending part for 'other_media' - g_queue_clear_full(&other_media->codecs_prefs_send, (GDestroyNotify) payload_type_free); - g_hash_table_remove_all(other_media->codecs_send); - g_hash_table_remove_all(other_media->codec_names_send); - - if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_all)) - strip_all = 1; - else if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_full)) - strip_all = 2; - if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_all)) - mask_all = 1; - else if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_full)) - 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 */ - while ((pt = g_queue_pop_head(types))) { - __rtp_payload_type_dup(call, pt); // this takes care of string allocation - - // codec stripping - if (__codec_ht_except(strip_all, flags->codec_strip, flags->codec_except, pt)) { - ilogs(codec, LOG_DEBUG, "Stripping codec '" STR_FORMAT "'", - STR_FMT(&pt->encoding_with_params)); -#ifdef WITH_TRANSCODING - codec_touched(pt, media); -#endif - GQueue *q = g_hash_table_lookup_queue_new(stripped, str_dup(&pt->encoding), free); - g_queue_push_tail(q, __rtp_payload_type_copy(pt)); - q = g_hash_table_lookup_queue_new(stripped, str_dup(&pt->encoding_with_full_params), free); - g_queue_push_tail(q, __rtp_payload_type_copy(pt)); - q = g_hash_table_lookup_queue_new(stripped, str_dup(&pt->encoding_with_params), free); - g_queue_push_tail(q, pt); - continue; + struct codec_store orig_dst; + codec_store_move(&orig_dst, dst); + + struct call_media *media = dst->media; + struct call *call = media ? media->call : NULL; + + for (GList *l = src->codec_prefs.head; l; l = l->next) { + struct rtp_payload_type *pt = l->data; + struct rtp_payload_type *orig_pt = g_hash_table_lookup(orig_dst.codecs, + GINT_TO_POINTER(pt->payload_type)); + ilogs(codec, LOG_DEBUG, "Adding codec " STR_FORMAT " (%i)", + STR_FMT(&pt->encoding_with_params), + pt->payload_type); + if (orig_pt) { + // carry over existing options + pt->ptime = orig_pt->ptime; + pt->for_transcoding = orig_pt->for_transcoding; + pt->accepted = orig_pt->accepted; + pt->bitrate = orig_pt->bitrate; + str_free_dup(&pt->codec_opts); + pt->codec_opts = orig_pt->codec_opts; + orig_pt->codec_opts = STR_NULL; } + __codec_options_set(call, pt, codec_set); + codec_store_add_end(dst, pt); + } - __codec_options_set(call, pt, flags->codec_set); + codec_store_cleanup(&orig_dst); +} - // codec masking - if (__codec_ht_except(mask_all, flags->codec_mask, flags->codec_except, pt)) { - ilogs(codec, LOG_DEBUG, "Masking codec '" STR_FORMAT "'", - STR_FMT(&pt->encoding_with_params)); -#ifdef WITH_TRANSCODING - codec_touched(pt, media); -#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); - g_queue_push_tail(q, __rtp_payload_type_copy(pt)); - q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding_with_full_params), 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(consume_all, flags->codec_consume, flags->codec_except, pt)) { - ilogs(codec, 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_full_params), 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)) { - ilogs(codec, 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); +void codec_store_strip(struct codec_store *cs, GQueue *strip, GHashTable *except) { + for (GList *l = strip->head; l; l = l->next) { + str *codec = l->data; + if (!str_cmp(codec, "all") || !str_cmp(codec, "full")) { + if (!str_cmp(codec, "all")) + cs->strip_all = 1; + else + cs->strip_full = 1; + + // strip all except ... + GList *link = cs->codec_prefs.head; + while (link) { + GList *next = link->next; + struct rtp_payload_type *pt = link->data; + if (except && g_hash_table_lookup(except, &pt->encoding)) + ; + else if (except && g_hash_table_lookup(except, &pt->encoding_with_params)) + ; + else if (except && g_hash_table_lookup(except, &pt->encoding_with_full_params)) + ; + else { + ilogs(codec, LOG_DEBUG, "Stripping codec " STR_FORMAT + " (%i) due to strip=all or strip=full", + STR_FMT(&pt->encoding_with_params), + pt->payload_type); + codec_touched(cs, pt); + next = __codec_store_delete_link(link, cs); + } + link = next; + } + continue; + } + // strip just this one + GQueue *pts = g_hash_table_lookup(cs->codec_names, codec); + if (!pts || !pts->length) { + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT + " not present for stripping", + STR_FMT(codec)); + continue; + } + while (pts->length) { + int pt_num = GPOINTER_TO_INT(pts->head->data); + struct rtp_payload_type *pt = g_hash_table_lookup(cs->codecs, GINT_TO_POINTER(pt_num)); + if (pt) { + ilogs(codec, LOG_DEBUG, "Stripping codec " STR_FORMAT " (%i)", + STR_FMT(&pt->encoding_with_params), pt_num); + codec_touched(cs, pt); + __codec_store_delete_link(pt->prefs_link, cs); + // this removes pts->head + } + else { + ilogs(codec, LOG_DEBUG, "PT %i missing for stripping " STR_FORMAT, pt_num, + STR_FMT(codec)); + break; // should not happen - don't continue + } } - else - __rtp_payload_type_add(media, other_media, pt); } +} - // now restore codecs that have been removed, but should be offered - for (GList *l = flags->codec_offer.head; l; l = l->next) { +void codec_store_offer(struct codec_store *cs, GQueue *offer, struct codec_store *orig) { + // restore stripped codecs in order: codecs must be present in `orig` but not present + // in `cs` + for (GList *l = offer->head; l; l = l->next) { str *codec = l->data; - __revert_codec_strip(stripped, masked, codec, media, other_media); + GQueue *pts = g_hash_table_lookup(cs->codec_names, codec); + if (pts && pts->length) { + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT + " already present (%i)", + STR_FMT(codec), GPOINTER_TO_INT(pts->head->data)); + continue; + } + GQueue *orig_list = g_hash_table_lookup(orig->codec_names, codec); + if (!orig_list || !orig_list->length) { + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT + " not present for offering", + STR_FMT(codec)); + continue; + } + for (GList *l = orig_list->head; l; l = l->next) { + int pt_num = GPOINTER_TO_INT(l->data); + struct rtp_payload_type *orig_pt = g_hash_table_lookup(orig->codecs, + GINT_TO_POINTER(pt_num)); + if (!orig_pt) { + ilogs(codec, LOG_DEBUG, "PT %i missing for offering " STR_FORMAT, pt_num, + STR_FMT(codec)); + continue; + } + if (g_hash_table_lookup(cs->codecs, GINT_TO_POINTER(pt_num))) { + ilogs(codec, LOG_DEBUG, "PT %i (" STR_FORMAT ") already preset", pt_num, + STR_FMT(codec)); + continue; + } + ilogs(codec, LOG_DEBUG, "Re-adding stripped codec " STR_FORMAT " (%i)", + STR_FMT(&orig_pt->encoding_with_params), orig_pt->payload_type); + codec_touched(cs, orig_pt); + codec_store_add_order(cs, orig_pt); + } } +} - if (!flags->asymmetric_codecs) { - // eliminate rejected codecs from the reverse direction. a rejected codec is missing - // from the `send` list. also remove it from the `receive` list. - for (GList *l = other_media->codecs_prefs_recv.head; l;) { - pt = l->data; - if (g_hash_table_lookup(other_media->codec_names_send, &pt->encoding)) { - l = l->next; +void codec_store_accept(struct codec_store *cs, GQueue *accept, struct codec_store *orig) { + // mark codecs as `for transcoding` + for (GList *l = accept->head; l; l = l->next) { + str *codec = l->data; + GQueue *pts; + int pts_is_full_list = 0; // bit of a hack + if (!str_cmp(codec, "all") || !str_cmp(codec, "full")) { + pts = &cs->codec_prefs; + pts_is_full_list = 1; + } + else + pts = g_hash_table_lookup(cs->codec_names, codec); + if (!pts || !pts->length) { + pts_is_full_list = 0; + pts = NULL; + // special case: strip=all, consume=X + if (orig) + pts = g_hash_table_lookup(orig->codec_names, codec); + if (pts && pts->length) { + // re-add from orig, then mark as accepted below + // XXX duplicate code + for (GList *k = pts->head; k; k = k->next) { + int pt_num = GPOINTER_TO_INT(k->data); + struct rtp_payload_type *orig_pt = g_hash_table_lookup(orig->codecs, + GINT_TO_POINTER(pt_num)); + if (!orig_pt) { + ilogs(codec, LOG_DEBUG, "PT %i missing for accepting " STR_FORMAT, + pt_num, + STR_FMT(codec)); + continue; + } + if (g_hash_table_lookup(cs->codecs, GINT_TO_POINTER(pt_num))) { + ilogs(codec, LOG_DEBUG, "PT %i (" STR_FORMAT ") already preset", + pt_num, + STR_FMT(codec)); + continue; + } + ilogs(codec, LOG_DEBUG, "Re-adding stripped codec " STR_FORMAT " (%i)", + STR_FMT(&orig_pt->encoding_with_params), orig_pt->payload_type); + codec_touched(cs, orig_pt); + codec_store_add_order(cs, orig_pt); + } + pts = g_hash_table_lookup(cs->codec_names, codec); + if (!pts) + continue; + // drop down below + } + else { + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT + " not present for accepting", + STR_FMT(codec)); continue; } - ilogs(codec, LOG_DEBUG, "Eliminating asymmetric inbound codec " STR_FORMAT, - STR_FMT(&pt->encoding_with_params)); - l = __delete_receiver_codec(other_media, l); + } + for (GList *k = pts->head; k; k = k->next) { + int pt_num; + if (!pts_is_full_list) + pt_num = GPOINTER_TO_INT(k->data); + else { + struct rtp_payload_type *fpt = k->data; + pt_num = fpt->payload_type; + } + struct rtp_payload_type *pt = g_hash_table_lookup(cs->codecs, + GINT_TO_POINTER(pt_num)); + if (!pt) { + ilogs(codec, LOG_DEBUG, "PT %i missing for accepting " STR_FORMAT, pt_num, + STR_FMT(codec)); + continue; + } + ilogs(codec, LOG_DEBUG, "Accepting codec " STR_FORMAT " (%i)", + STR_FMT(&pt->encoding_with_params), pt->payload_type); + pt->for_transcoding = 1; + pt->accepted = 1; + codec_touched(cs, pt); } } +} +void codec_store_track(struct codec_store *cs, GQueue *q) { #ifdef WITH_TRANSCODING - __single_codec(media, flags); + // just track all codecs from the list as "touched" + for (GList *l = q->head; l; l = l->next) { + str *codec = l->data; + if (!str_cmp(codec, "all") || !str_cmp(codec, "full")) { + cs->tracker->all_touched = 1; + continue; + } + GQueue *pts = g_hash_table_lookup(cs->codec_names, codec); + if (!pts) + continue; + for (GList *k = pts->head; k; k = k->next) { + int pt_num = GPOINTER_TO_INT(k->data); + struct rtp_payload_type *pt = g_hash_table_lookup(cs->codecs, + GINT_TO_POINTER(pt_num)); + codec_touched(cs, pt); + } + } #endif +} +void codec_store_transcode(struct codec_store *cs, GQueue *offer, struct codec_store *orig) { #ifdef WITH_TRANSCODING - // add transcode codecs - for (GList *l = flags->codec_transcode.head; l; l = l->next) { + // special case of codec_store_offer(): synthesise codecs that were not already present + for (GList *l = offer->head; l; l = l->next) { str *codec = l->data; - // if we wish to 'transcode' to a codec that was offered originally - // and removed by a strip=all option, - // simply restore it from the original list and handle it the same way - // as 'offer' - if ((strip_all == 1 || mask_all == 1) - && __revert_codec_strip(stripped, masked, codec, media, other_media)) + GQueue *pts = g_hash_table_lookup(cs->codec_names, codec); + if (pts && pts->length) { + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT + " already present (%i)", + STR_FMT(codec), GPOINTER_TO_INT(pts->head->data)); continue; - // also check if maybe the codec was never stripped - if (g_hash_table_lookup(media->codec_names_recv, codec)) { - ilogs(codec, LOG_DEBUG, "Codec '" STR_FORMAT "' requested for transcoding is already present", + } + GQueue *orig_list = g_hash_table_lookup(orig->codec_names, codec); + if (!orig_list || !orig_list->length || cs->strip_full) { + ilogs(codec, LOG_DEBUG, "Adding codec " STR_FORMAT + " for transcoding", STR_FMT(codec)); + // create new payload type + struct rtp_payload_type *pt = codec_add_payload_type(codec, cs->media, NULL, orig); + if (!pt) + continue; + pt->for_transcoding = 1; + + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT " added for transcoding with payload " + "type %i", + STR_FMT(&pt->encoding_with_params), pt->payload_type); + codec_touched(cs, pt); + codec_store_add_raw_order(cs, pt); continue; } + // XXX duplicate code + for (GList *l = orig_list->head; l; l = l->next) { + int pt_num = GPOINTER_TO_INT(l->data); + struct rtp_payload_type *orig_pt = g_hash_table_lookup(orig->codecs, + GINT_TO_POINTER(pt_num)); + if (!orig_pt) { + ilogs(codec, LOG_DEBUG, "PT %i missing for offering " STR_FORMAT, pt_num, + STR_FMT(codec)); + continue; + } + if (g_hash_table_lookup(cs->codecs, GINT_TO_POINTER(pt_num))) { + ilogs(codec, LOG_DEBUG, "PT %i (" STR_FORMAT ") already preset", pt_num, + STR_FMT(codec)); + continue; + } + ilogs(codec, LOG_DEBUG, "Re-adding stripped codec " STR_FORMAT " (%i)", + STR_FMT(&orig_pt->encoding_with_params), orig_pt->payload_type); + codec_touched(cs, orig_pt); + codec_store_add_order(cs, orig_pt); + } + } +#endif +} - // create new payload type - pt = codec_add_payload_type(codec, media, other_media); - if (!pt) - continue; - pt->for_transcoding = 1; - codec_touched(pt, media); +void codec_store_answer(struct codec_store *dst, struct codec_store *src, struct sdp_ng_flags *flags) { + // retain existing setup for supplemental codecs, but start fresh otherwise + struct codec_store orig_dst; + codec_store_move(&orig_dst, dst); - ilogs(codec, LOG_DEBUG, "Codec '" STR_FORMAT "' added for transcoding with payload type %u", - STR_FMT(&pt->encoding_with_params), pt->payload_type); - __rtp_payload_type_add_recv(media, pt, 1); + struct call_media *src_media = src->media; + struct call_media *dst_media = dst->media; + if (!dst_media || !src_media) + goto out; + +#ifdef WITH_TRANSCODING + // synthetic answer for T.38: + if (dst_media->type_id == MT_AUDIO && src_media->type_id == MT_IMAGE && dst->codec_prefs.length == 0) { + if (dst_media->t38_gateway && dst_media->t38_gateway->pcm_player + && dst_media->t38_gateway->pcm_player->handler) { + codec_store_add_order(dst, &dst_media->t38_gateway->pcm_player->handler->dest_pt); + goto out; + } } +#endif - if (media->type_id == MT_AUDIO && other_media->type_id == MT_IMAGE) { - if (media->codecs_prefs_recv.length == 0) { - // find some codecs to put into our outgoing SDP body + unsigned int num_codecs = 0; + //int codec_order = 0; // to track whether we've added supplemental codecs based on their media codecs + GQueue supp_codecs = G_QUEUE_INIT; // postpone actually adding them until the end - if (media->codecs_prefs_send.length && media->t38_gateway - && flags->opmode == OP_ANSWER) - { - // audio -> T.38 transcoder, answer: - // we answer with the codec that we're sending audio with, taken from - // our PCM player - if (media->t38_gateway && media->t38_gateway->pcm_player - && media->t38_gateway->pcm_player->handler) - __rtp_payload_type_add_recv(media, - __rtp_payload_type_copy(&media->t38_gateway->pcm_player->handler->dest_pt), 1); - } - else if (flags->opmode == OP_OFFER) { - // T.38 -> audio transcoder, initial offer, and no codecs have been given. - // Default to PCMA and PCMU - // XXX can we improve the codec lookup/synthesis? - static const str PCMU_str = STR_CONST_INIT("PCMU"); - static const str PCMA_str = STR_CONST_INIT("PCMA"); - pt = codec_add_payload_type(&PCMU_str, media, NULL); - assert(pt != NULL); - __rtp_payload_type_add_recv(media, pt, 1); - pt = codec_add_payload_type(&PCMA_str, media, NULL); - assert(pt != NULL); - __rtp_payload_type_add_recv(media, pt, 1); - - ilogs(codec, LOG_DEBUG, "Using default codecs PCMU and PCMA for T.38 gateway"); + // populate dst via output PTs from src's codec handlers + for (GList *l = src->codec_prefs.head; l; l = l->next) { + int add_codec = 1; + if (flags && flags->single_codec && num_codecs >= 1) + add_codec = 0; + + struct rtp_payload_type *pt = l->data; + struct codec_handler *h = codec_handler_get(src_media, pt->payload_type); + if (!h || h->dest_pt.payload_type == -1) { + // passthrough or missing + if (pt->for_transcoding) + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT + " (%i) is being transcoded", + STR_FMT(&pt->encoding_with_params), + pt->payload_type); + else { + if (add_codec) { + ilogs(codec, LOG_DEBUG, "Codec " STR_FORMAT + " (%i) is passthrough", + STR_FMT(&pt->encoding_with_params), + pt->payload_type); + codec_store_add_end(dst, pt); + num_codecs++; + } + else + ilogs(codec, LOG_DEBUG, "Skipping passthrough codec " STR_FORMAT + " (%i) due to single-codec flag", + STR_FMT(&pt->encoding_with_params), + pt->payload_type); } + continue; + } + + // supp codecs are handled in-line with their main media codecs + int is_supp = 0; + if (pt->codec_def && pt->codec_def->supplemental) { + is_supp = 1; + if (pt->for_transcoding) + continue; + if (is_codec_touched(dst, pt)) + continue; + if (is_codec_touched(src, pt)) + continue; + if (is_codec_touched(&orig_dst, pt)) + continue; + // except those that were not touched - we pass those through regardless } - else if (flags->opmode == OP_OFFER) { - // re-invite - we remember some codecs from before, or perhaps they - // were added manually through the transcoding options. make sure - // they're all supported by us - for (GList *l = media->codecs_prefs_recv.head; l;) { - pt = l->data; - ensure_codec_def(pt, media); + if (!add_codec && !is_supp) { + ilogs(codec, LOG_DEBUG, "Skipping reverse codec for " STR_FORMAT + " (%i) = " STR_FORMAT " (%i) due to single-codec flag", + STR_FMT(&pt->encoding_with_params), + pt->payload_type, + STR_FMT(&h->dest_pt.encoding_with_params), + h->dest_pt.payload_type); + continue; + } + ilogs(codec, LOG_DEBUG, "Reverse codec for " STR_FORMAT + " (%i) is " STR_FORMAT " (%i)", + STR_FMT(&pt->encoding_with_params), + pt->payload_type, + STR_FMT(&h->dest_pt.encoding_with_params), + h->dest_pt.payload_type); + if (!g_hash_table_lookup(dst->codecs, GINT_TO_POINTER(h->dest_pt.payload_type))) { + if (h->passthrough) + codec_store_add_end(dst, pt); + else + codec_store_add_end(dst, &h->dest_pt); + num_codecs++; + } + + // handle associated supplemental codecs + if (h->cn_payload_type != -1) { + pt = g_hash_table_lookup(orig_dst.codecs, GINT_TO_POINTER(h->cn_payload_type)); + if (!pt) + ilogs(codec, LOG_DEBUG, "CN payload type %i is missing", h->cn_payload_type); + else + g_queue_push_tail(&supp_codecs, rtp_payload_type_dup(pt)); + } + if (h->dtmf_payload_type != -1) { + pt = g_hash_table_lookup(orig_dst.codecs, GINT_TO_POINTER(h->dtmf_payload_type)); + if (!pt) + ilogs(codec, LOG_DEBUG, "DTMF payload type %i is missing", h->dtmf_payload_type); + else + g_queue_push_tail(&supp_codecs, rtp_payload_type_dup(pt)); + } + } + + while (supp_codecs.length) { + struct rtp_payload_type *pt = g_queue_pop_head(&supp_codecs); + if (g_hash_table_lookup(dst->codecs, GINT_TO_POINTER(pt->payload_type))) { + ilogs(codec, LOG_DEBUG, STR_FORMAT " payload type %i already present, skip", + STR_FMT(&pt->encoding_with_full_params), pt->payload_type); + payload_type_free(pt); + continue; + } + ilogs(codec, LOG_DEBUG, "Adding " STR_FORMAT " payload type %i", + STR_FMT(&pt->encoding_with_params), pt->payload_type); + codec_store_add_raw(dst, pt); + } + +out: + codec_store_cleanup(&orig_dst); +} + +// offer codecs for non-RTP transcoding scenarios +void codec_store_synthesise(struct codec_store *dst, struct codec_store *opposite) { + if (!dst->media || !opposite->media) + return; + if (dst->media->type_id == MT_AUDIO && opposite->media->type_id == MT_IMAGE) { + // audio <> T.38 transcoder + if (!dst->codec_prefs.length) { + // no codecs given: add defaults + static const str PCMU_str = STR_CONST_INIT("PCMU"); + static const str PCMA_str = STR_CONST_INIT("PCMA"); + codec_store_add_raw_order(dst, codec_make_payload_type(&PCMU_str, MT_AUDIO)); + codec_store_add_raw_order(dst, codec_make_payload_type(&PCMA_str, MT_AUDIO)); + + ilogs(codec, LOG_DEBUG, "Using default codecs PCMU and PCMA for T.38 gateway"); + } + else { + // we already have a list of codecs - make sure they're all supported by us + for (GList *l = dst->codec_prefs.head; l;) { + struct rtp_payload_type *pt = l->data; if (pt->codec_def) { l = l->next; continue; } - ilogs(codec, LOG_DEBUG, "Eliminating unsupported codec " STR_FORMAT, + ilogs(codec, LOG_DEBUG, "Eliminating unsupported codec " STR_FORMAT + " for T.38 transcoding", STR_FMT(&pt->encoding_with_params)); - codec_touched(pt, media); - l = __delete_receiver_codec(media, l); + codec_touched(dst, pt); + l = __codec_store_delete_link(l, dst); } } } -#endif - - g_hash_table_destroy(stripped); - g_hash_table_destroy(masked); } static void codec_timers_run(void *p) { diff --git a/daemon/dtmf.c b/daemon/dtmf.c index eaf106887..78c1039fb 100644 --- a/daemon/dtmf.c +++ b/daemon/dtmf.c @@ -120,7 +120,7 @@ int dtmf_event(struct media_packet *mp, str *payload, int clockrate) { dtmf = (void *) payload->s; ilog(LOG_DEBUG, "DTMF event: event %u, volume %u, end %u, duration %u", - dtmf->event, dtmf->volume, dtmf->end, dtmf->duration); + dtmf->event, dtmf->volume, dtmf->end, ntohs(dtmf->duration)); int ret = dtmf->end ? 1 : 0; @@ -277,14 +277,14 @@ static const char *dtmf_inject_pcm(struct call_media *media, struct call_monolog uint64_t encoder_pts = codec_encoder_pts(csh); uint64_t skip_pts = codec_decoder_unskip_pts(csh); // reset to zero to take up our new samples - media->dtmf_injector->func(media->dtmf_injector, &packet); + ch->dtmf_injector->func(ch->dtmf_injector, &packet); // insert pause tep.event = 0xff; tep.duration = htons(pause_samples); rtp.seq_num = htons(ssrc_in->parent->sequencer.seq); - media->dtmf_injector->func(media->dtmf_injector, &packet); + ch->dtmf_injector->func(ch->dtmf_injector, &packet); // skip generated samples uint64_t pts_offset = codec_encoder_pts(csh) - encoder_pts; @@ -352,7 +352,7 @@ const char *dtmf_inject(struct call_media *media, int code, int volume, int dura return "No matching codec SSRC handler"; // if we don't have a DTMF payload type, we have to generate PCM - if (media->dtmf_injector->dtmf_payload_type == -1) + if (ch->dtmf_payload_type == -1 && ch->dtmf_injector) return dtmf_inject_pcm(media, monologue, ps, ssrc_in, ch, csh, code, volume, duration, pause); ilog(LOG_DEBUG, "Injecting RFC DTMF event #%i for %i ms (vol %i) from '" STR_FORMAT "' (media #%u) " diff --git a/daemon/media_player.c b/daemon/media_player.c index 053f49c9a..faaa9c8ff 100644 --- a/daemon/media_player.c +++ b/daemon/media_player.c @@ -266,7 +266,7 @@ void send_timer_push(struct send_timer *st, struct codec_packet *cp) { int media_player_setup(struct media_player *mp, const struct rtp_payload_type *src_pt) { // find suitable output payload type struct rtp_payload_type *dst_pt; - for (GList *l = mp->media->codecs_prefs_send.head; l; l = l->next) { + for (GList *l = mp->media->codecs.codec_prefs.head; l; l = l->next) { dst_pt = l->data; ensure_codec_def(dst_pt, mp->media); if (dst_pt->codec_def && !dst_pt->codec_def->supplemental) @@ -326,13 +326,14 @@ static int __ensure_codec_handler(struct media_player *mp, AVStream *avs) { src_pt.encoding = src_pt.codec_def->rtpname_str; src_pt.channels = avs->CODECPAR->channels; src_pt.clock_rate = avs->CODECPAR->sample_rate; - codec_init_payload_type(&src_pt, mp->media); + codec_init_payload_type(&src_pt, MT_AUDIO); if (media_player_setup(mp, &src_pt)) return -1; mp->duration = avs->duration * 1000 * avs->time_base.num / avs->time_base.den; + payload_type_clear(&src_pt); return 0; } diff --git a/daemon/mqtt.c b/daemon/mqtt.c index 75effb9f3..867bd84f3 100644 --- a/daemon/mqtt.c +++ b/daemon/mqtt.c @@ -162,7 +162,7 @@ static void mqtt_ssrc_stats(struct ssrc_ctx *ssrc, JsonBuilder *json, struct cal unsigned int clockrate = 0; //struct codec_handler *h = codec_handler_get(ps->media, prim_pt); - struct rtp_payload_type *pt = g_hash_table_lookup(media->codecs_recv, GUINT_TO_POINTER(prim_pt)); + struct rtp_payload_type *pt = g_hash_table_lookup(media->codecs.codecs, GUINT_TO_POINTER(prim_pt)); if (pt) { json_builder_set_member_name(json, "codec"); json_builder_add_string_value(json, pt->encoding.s); diff --git a/daemon/recording.c b/daemon/recording.c index 4c5da0629..dee608888 100644 --- a/daemon/recording.c +++ b/daemon/recording.c @@ -813,7 +813,7 @@ static void setup_media_proc(struct call_media *media) { append_meta_chunk_null(recording, "MEDIA %u PTIME %i", media->unique_id, media->ptime); - GList *pltypes = g_hash_table_get_values(media->codecs_recv); + GList *pltypes = g_hash_table_get_values(media->codecs.codecs); for (GList *l = pltypes; l; l = l->next) { struct rtp_payload_type *pt = l->data; diff --git a/daemon/redis.c b/daemon/redis.c index c94e41928..3cb540e77 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -1437,7 +1437,7 @@ static struct rtp_payload_type *rbl_cb_plts_g(str *s, GQueue *q, struct redis_li if (str_token(&ptype, s, '/')) return NULL; - struct rtp_payload_type *pt = codec_make_payload_type(s, med); + struct rtp_payload_type *pt = codec_make_payload_type(s, med->type_id); if (!pt) return NULL; @@ -1447,12 +1447,7 @@ static struct rtp_payload_type *rbl_cb_plts_g(str *s, GQueue *q, struct redis_li } static int rbl_cb_plts_r(str *s, GQueue *q, struct redis_list *list, void *ptr) { struct call_media *med = ptr; - __rtp_payload_type_add_recv(med, rbl_cb_plts_g(s, q, list, ptr), 0); - return 0; -} -static int rbl_cb_plts_s(str *s, GQueue *q, struct redis_list *list, void *ptr) { - struct call_media *med = ptr; - __rtp_payload_type_add_send(med, rbl_cb_plts_g(s, q, list, ptr)); + codec_store_add_raw(&med->codecs, rbl_cb_plts_g(s, q, list, ptr)); return 0; } static int json_medias(struct call *c, struct redis_list *medias, JsonReader *root_reader) { @@ -1503,7 +1498,6 @@ static int json_medias(struct call *c, struct redis_list *medias, JsonReader *ro return -1; json_build_list_cb(NULL, c, "payload_types", i, NULL, rbl_cb_plts_r, med, root_reader); - json_build_list_cb(NULL, c, "payload_types_send", i, NULL, rbl_cb_plts_s, med, root_reader); /* XXX dtls */ medias->ptrs[i] = med; @@ -1622,7 +1616,7 @@ static int json_link_streams(struct call *c, struct redis_list *streams, return -1; if (ps->media) - __rtp_stats_update(ps->rtp_stats, ps->media->codecs_recv); + __rtp_stats_update(ps->rtp_stats, &ps->media->codecs); __init_stream(ps); } @@ -2370,19 +2364,7 @@ char* redis_encode_json(struct call *c) { snprintf(tmp, sizeof(tmp), "payload_types-%u", media->unique_id); json_builder_set_member_name(builder, tmp); json_builder_begin_array (builder); - for (m = media->codecs_prefs_recv.head; m; m = m->next) { - pt = m->data; - JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT "/" STR_FORMAT "/%i/%i", - pt->payload_type, STR_FMT(&pt->encoding), - pt->clock_rate, STR_FMT(&pt->encoding_parameters), - STR_FMT(&pt->format_parameters), pt->bitrate, pt->ptime); - } - json_builder_end_array (builder); - - snprintf(tmp, sizeof(tmp), "payload_types_send-%u", media->unique_id); - json_builder_set_member_name(builder, tmp); - json_builder_begin_array (builder); - for (m = media->codecs_prefs_send.head; m; m = m->next) { + for (m = media->codecs.codec_prefs.head; m; m = m->next) { pt = m->data; JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT "/" STR_FORMAT "/%i/%i", pt->payload_type, STR_FMT(&pt->encoding), diff --git a/daemon/rtp.c b/daemon/rtp.c index 36bdd9c6a..d6c1950bf 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -10,6 +10,7 @@ #include "log.h" #include "rtplib.h" #include "ssrc.h" +#include "call.h" @@ -274,13 +275,13 @@ error: return -1; } -const struct rtp_payload_type *rtp_payload_type(unsigned int type, GHashTable *lookup) { +const struct rtp_payload_type *rtp_payload_type(unsigned int type, struct codec_store *cs) { const struct rtp_payload_type *rtp_pt; - if (!lookup) + if (!cs) return rtp_get_rfc_payload_type(type); - rtp_pt = g_hash_table_lookup(lookup, GINT_TO_POINTER(type)); + rtp_pt = g_hash_table_lookup(cs->codecs, GINT_TO_POINTER(type)); if (rtp_pt) return rtp_pt; diff --git a/daemon/sdp.c b/daemon/sdp.c index 6d5638695..ee32d2a53 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -18,6 +18,7 @@ #include "socket.h" #include "call_interfaces.h" #include "rtplib.h" +#include "codec.h" struct network_address { str network_type; @@ -64,7 +65,7 @@ struct sdp_media { struct sdp_session *session; str s; - str media_type; + str media_type_str; str port; str transport; str formats; /* space separated */ @@ -77,6 +78,7 @@ struct sdp_media { int rr, rs; struct sdp_attributes attributes; GQueue format_list; /* list of slice-alloc'd str objects */ + enum media_type media_type_id; }; struct attribute_rtcp { @@ -374,11 +376,12 @@ static int parse_media(str *value_str, struct sdp_media *output) { char *ep; str *sp; - EXTRACT_TOKEN(media_type); + EXTRACT_TOKEN(media_type_str); EXTRACT_TOKEN(port); EXTRACT_TOKEN(transport); output->formats = *value_str; + output->media_type_id = codec_get_type(&output->media_type_str); output->port_num = strtol(output->port.s, &ep, 10); if (ep == output->port.s) return -1; @@ -1365,6 +1368,8 @@ static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media s = g_hash_table_lookup(ht_fmtp, &i); if (s) pt->format_parameters = *s; + else + pt->format_parameters = STR_EMPTY; GQueue *rq = g_hash_table_lookup(ht_rtcp_fb, GINT_TO_POINTER(i)); if (rq) { // steal the list contents and free the list @@ -1379,7 +1384,8 @@ static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media else if (!pt->ptime && ptrfc) pt->ptime = ptrfc->ptime; - g_queue_push_tail(&sp->rtp_payload_types, pt); + codec_init_payload_type(pt, sp->type_id); + codec_store_add_raw(&sp->codecs, pt); } goto out; @@ -1515,6 +1521,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl sp = g_slice_alloc0(sizeof(*sp)); sp->index = ++num; + codec_store_init(&sp->codecs, NULL); errstr = "No address info found for stream"; if (!flags->fragment @@ -1523,7 +1530,8 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl sp->consecutive_ports = media->port_count; sp->protocol = transport_protocol(&media->transport); - sp->type = media->media_type; + sp->type = media->media_type_str; + sp->type_id = media->media_type_id; memcpy(sp->direction, flags->direction, sizeof(sp->direction)); sp->desired_family = flags->address_family; bf_set_clear(&sp->sp_flags, SP_FLAG_ASYMMETRIC, flags->asymmetric); @@ -1655,6 +1663,20 @@ error: return -1; } +static void sp_free(void *p) { + struct stream_params *s = p; + + codec_store_cleanup(&s->codecs); + ice_candidates_free(&s->ice_candidates); + crypto_params_sdes_queue_clear(&s->sdes_params); + g_slice_free1(sizeof(*s), s); +} +void sdp_streams_free(GQueue *q) { + g_queue_clear_full(q, sp_free); +} + + + struct sdp_chopper *sdp_chopper_new(str *input) { struct sdp_chopper *c = g_slice_alloc0(sizeof(*c)); c->input = input; @@ -1783,10 +1805,10 @@ static int replace_codec_list(struct sdp_chopper *chop, if (proto_is_not_rtp(cm->protocol)) return replace_format_str(chop, media, cm); - if (cm->codecs_prefs_recv.length == 0) + if (cm->codecs.codec_prefs.length == 0) return 0; // legacy protocol or usage error - for (GList *l = cm->codecs_prefs_recv.head; l; l = l->next) { + for (GList *l = cm->codecs.codec_prefs.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; chopper_append_printf(chop, " %u", pt->payload_type); } @@ -1796,7 +1818,7 @@ static int replace_codec_list(struct sdp_chopper *chop, } static void insert_codec_parameters(struct sdp_chopper *chop, struct call_media *cm) { - for (GList *l = cm->codecs_prefs_recv.head; l; l = l->next) { + for (GList *l = cm->codecs.codec_prefs.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; if (!pt->encoding_with_params.len) continue; @@ -1826,7 +1848,7 @@ static void insert_sdp_attributes(struct sdp_chopper *chop, struct call_media *c } static int replace_media_type(struct sdp_chopper *chop, struct sdp_media *media, struct call_media *cm) { - str *type = &media->media_type; + str *type = &media->media_type_str; if (!cm->type.s) return 0; @@ -2098,7 +2120,7 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media * case ATTR_RTPMAP: case ATTR_FMTP: - if (media->codecs_prefs_recv.length > 0) + if (media->codecs.codec_prefs.length > 0) goto strip; break; case ATTR_PTIME: @@ -2108,7 +2130,7 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media * case ATTR_RTCP_FB: if (attr->u.rtcp_fb.payload_type == -1) break; // leave this one alone - if (media->codecs_prefs_recv.length > 0) + if (media->codecs.codec_prefs.length > 0) goto strip; break; diff --git a/daemon/ssrc.c b/daemon/ssrc.c index 0535aa343..bc81dab5b 100644 --- a/daemon/ssrc.c +++ b/daemon/ssrc.c @@ -347,7 +347,7 @@ void ssrc_receiver_report(struct call_media *m, const struct ssrc_receiver_repor goto out_nl_put; } - const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->codecs_send); + const struct rtp_payload_type *rpt = rtp_payload_type(pt, &m->codecs); if (!rpt) { ilog(LOG_INFO, "Invalid RTP payload type %i, discarding RTCP RR", pt); goto out_nl_put; diff --git a/daemon/t38.c b/daemon/t38.c index ae7d28c5c..20eb61428 100644 --- a/daemon/t38.c +++ b/daemon/t38.c @@ -375,6 +375,7 @@ int t38_gateway_pair(struct call_media *t38_media, struct call_media *pcm_media, tg->pcm_pt.encoding_with_params = tg->pcm_pt.encoding; tg->pcm_pt.clock_rate = 8000; tg->pcm_pt.channels = 1; + tg->pcm_pt.ptime = 20; err = "Failed to init PCM codec"; ensure_codec_def(&tg->pcm_pt, pcm_media); @@ -467,7 +468,7 @@ void t38_gateway_start(struct t38_gateway *tg) { return; // only start our player only if we can send both ways - if (!tg->pcm_media->codecs_prefs_send.length) + if (!tg->pcm_media->codecs.codec_prefs.length) return; if (!tg->pcm_media->streams.length) return; diff --git a/include/call.h b/include/call.h index 6ee852ce5..75d8e72d0 100644 --- a/include/call.h +++ b/include/call.h @@ -225,9 +225,21 @@ typedef bencode_buffer_t call_buffer_t; +struct codec_store { + GHashTable *codecs; // int payload type -> struct rtp_payload_type + GHashTable *codec_names; // codec name -> GQueue of int payload types; storage container + GQueue codec_prefs; // preference by order in SDP; storage container + GList *supp_link; // tracks location for codec_store_add_end + struct codec_tracker *tracker; + struct call_media *media; + unsigned int strip_all:1, // set by codec_store_strip + strip_full:1; // set by codec_store_strip +}; + struct stream_params { unsigned int index; /* starting with 1 */ str type; + enum media_type type_id; struct endpoint rtp_endpoint; struct endpoint rtcp_endpoint; unsigned int consecutive_ports; @@ -238,7 +250,7 @@ struct stream_params { sockfamily_t *desired_family; struct dtls_fingerprint fingerprint; unsigned int sp_flags; - GQueue rtp_payload_types; /* slice-alloc'd */ + struct codec_store codecs; GQueue ice_candidates; /* slice-alloc'd */ str ice_ufrag; str ice_pwd; @@ -336,27 +348,16 @@ struct call_media { GQueue streams; /* normally RTP + RTCP */ GQueue endpoint_maps; - // what we say we can receive (outgoing SDP): - GHashTable *codecs_recv; // int payload type -> struct rtp_payload_type - GHashTable *codec_names_recv; // codec name -> GQueue of int payload types; storage container - GQueue codecs_prefs_recv; // preference by order in SDP; storage container - - // what we can send, taken from received SDP: - GHashTable *codecs_send; // int payload type -> struct rtp_payload_type - GHashTable *codec_names_send; // codec name -> GQueue of int payload types; storage container - GQueue codecs_prefs_send; // storage container - struct codec_tracker *codec_tracker; - + struct codec_store codecs; GQueue sdp_attributes; // str_sprintf() - GHashTable *codec_handlers; // int payload type -> struct codec_handler - // XXX combine this with 'codecs_recv' hash table? + // XXX combine this with 'codecs' hash table? GQueue codec_handlers_store; // storage for struct codec_handler struct codec_handler *codec_handler_cache; struct rtcp_handler *rtcp_handler; struct rtcp_timer *rtcp_timer; // master lock for scheduling purposes struct mqtt_timer *mqtt_timer; // master lock for scheduling purposes - struct codec_handler *dtmf_injector; + //struct codec_handler *dtmf_injector; struct t38_gateway *t38_gateway; struct codec_handler *t38_handler; #ifdef WITH_TRANSCODING @@ -402,6 +403,7 @@ struct call_monologue { unsigned int block_dtmf:1; unsigned int block_media:1; unsigned int rec_forwarding:1; + unsigned int inject_dtmf:1; }; struct call_iterator_list { @@ -523,6 +525,8 @@ struct call_monologue *call_get_mono_dialogue(struct call *call, const str *from const str *viabranch); struct call *call_get(const str *callid); int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, struct sdp_ng_flags *flags); +void codecs_offer_answer(struct call_media *media, struct call_media *other_media, + struct stream_params *sp, struct sdp_ng_flags *flags); int call_delete_branch(const str *callid, const str *branch, const str *fromtag, const str *totag, bencode_item_t *output, int delete_delay); void call_destroy(struct call *); @@ -538,8 +542,7 @@ int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address void add_total_calls_duration_in_interval(struct timeval *interval_tv); -void payload_type_free(struct rtp_payload_type *p); -void __rtp_stats_update(GHashTable *dst, GHashTable *src); +void __rtp_stats_update(GHashTable *dst, struct codec_store *); int __init_stream(struct packet_stream *ps); void call_stream_crypto_reset(struct packet_stream *ps); diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 7166ba482..6bd80760e 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -38,13 +38,13 @@ struct sdp_ng_flags { str label; str address; sockaddr_t xmlrpc_callback; - GHashTable *codec_strip; + GQueue codec_strip; GHashTable *codec_except; GQueue codec_offer; GQueue codec_transcode; - GHashTable *codec_mask; - GHashTable *codec_accept; - GHashTable *codec_consume; + GQueue codec_mask; + GQueue codec_accept; + GQueue codec_consume; GHashTable *codec_set; int ptime, rev_ptime; @@ -107,9 +107,6 @@ struct sdp_ng_flags { debug:1, loop_protect:1, original_sendrecv:1, - asymmetric_codecs:1, - symmetric_codecs:1, - reorder_codecs:1, single_codec:1, inject_dtmf:1, t38_decode:1, diff --git a/include/codec.h b/include/codec.h index 215b9786e..5adebaf22 100644 --- a/include/codec.h +++ b/include/codec.h @@ -23,6 +23,7 @@ struct supp_codec_tracker; struct rtcp_timer; struct mqtt_timer; struct call; +struct codec_store; typedef int codec_handler_func(struct codec_handler *, struct media_packet *); @@ -34,12 +35,13 @@ struct codec_handler { int dtmf_payload_type; int cn_payload_type; codec_handler_func *func; + unsigned int passthrough:1; unsigned int kernelize:1; unsigned int transcoder:1; - unsigned int dtmf_scaler:1; unsigned int pcm_dtmf_detect:1; struct ssrc_hash *ssrc_hash; + struct codec_handler *input_handler; // == main handler for supp codecs struct codec_handler *output_handler; // == self, or other PT handler struct call_media *media; #ifdef WITH_TRANSCODING @@ -49,6 +51,8 @@ struct codec_handler { // for media playback struct codec_ssrc_handler *ssrc_handler; + // for DTMF injection + struct codec_handler *dtmf_injector; // stats entry char *stats_chain; @@ -78,28 +82,38 @@ struct codec_handler *codec_handler_get(struct call_media *, int payload_type); void codec_handlers_free(struct call_media *); struct codec_handler *codec_handler_make_playback(const struct rtp_payload_type *src_pt, const struct rtp_payload_type *dst_pt, unsigned long ts, struct call_media *); -void ensure_codec_def(struct rtp_payload_type *pt, struct call_media *media); void codec_calc_jitter(struct ssrc_ctx *, unsigned long ts, unsigned int clockrate, const struct timeval *); +void codec_store_cleanup(struct codec_store *cs); +void codec_store_init(struct codec_store *cs, struct call_media *); +void codec_store_populate(struct codec_store *, struct codec_store *, GHashTable *); +void codec_store_add_raw(struct codec_store *cs, struct rtp_payload_type *pt); +void codec_store_strip(struct codec_store *, GQueue *strip, GHashTable *except); +void codec_store_offer(struct codec_store *, GQueue *, struct codec_store *); +void codec_store_accept(struct codec_store *, GQueue *, struct codec_store *); +void codec_store_track(struct codec_store *, GQueue *); +void codec_store_transcode(struct codec_store *, GQueue *, struct codec_store *); +void codec_store_answer(struct codec_store *dst, struct codec_store *src, struct sdp_ng_flags *flags); +void codec_store_synthesise(struct codec_store *dst, struct codec_store *opposite); + void codec_add_raw_packet(struct media_packet *mp, unsigned int clockrate); void codec_packet_free(void *); -void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media, - GQueue *types, struct sdp_ng_flags *flags); +void payload_type_free(struct rtp_payload_type *p); +struct rtp_payload_type *rtp_payload_type_dup(const struct rtp_payload_type *pt); // special return value `(void *) 0x1` to signal type mismatch -struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct call_media *media); -void codec_init_payload_type(struct rtp_payload_type *, struct call_media *); +struct rtp_payload_type *codec_make_payload_type(const str *codec_str, enum media_type); - -// used by redis -void __rtp_payload_type_add_recv(struct call_media *media, struct rtp_payload_type *pt, int supp_check); -void __rtp_payload_type_add_send(struct call_media *other_media, struct rtp_payload_type *pt); +// handle string allocation +void codec_init_payload_type(struct rtp_payload_type *, enum media_type); +void payload_type_clear(struct rtp_payload_type *p); #ifdef WITH_TRANSCODING +void ensure_codec_def(struct rtp_payload_type *pt, struct call_media *media); void codec_handler_free(struct codec_handler **handler); void codec_handlers_update(struct call_media *receiver, struct call_media *sink, const struct sdp_ng_flags *, const struct stream_params *); @@ -108,8 +122,7 @@ uint64_t codec_last_dtmf_event(struct codec_ssrc_handler *ch); uint64_t codec_encoder_pts(struct codec_ssrc_handler *ch); void codec_decoder_skip_pts(struct codec_ssrc_handler *ch, uint64_t); uint64_t codec_decoder_unskip_pts(struct codec_ssrc_handler *ch); -void codec_tracker_init(struct call_media *); -void codec_tracker_finish(struct call_media *, struct call_media *); +void codec_tracker_update(struct codec_store *); void codec_handlers_stop(GQueue *); #else @@ -117,9 +130,9 @@ void codec_handlers_stop(GQueue *); INLINE void codec_handlers_update(struct call_media *receiver, struct call_media *sink, const struct sdp_ng_flags *flags, const struct stream_params *sp) { } INLINE void codec_handler_free(struct codec_handler **handler) { } -INLINE void codec_tracker_init(struct call_media *m) { } -INLINE void codec_tracker_finish(struct call_media *m, struct call_media *mm) { } +INLINE void codec_tracker_update(struct codec_store *cs) { } INLINE void codec_handlers_stop(GQueue *q) { } +INLINE void ensure_codec_def(struct rtp_payload_type *pt, struct call_media *media) { } #endif diff --git a/include/obj.h b/include/obj.h index 57f1d885e..f6c8d9a8c 100644 --- a/include/obj.h +++ b/include/obj.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "compat.h" diff --git a/include/rtp.h b/include/rtp.h index 123a3cb5f..90ce76555 100644 --- a/include/rtp.h +++ b/include/rtp.h @@ -13,12 +13,13 @@ struct rtp_header; struct ssrc_hash; enum ssrc_dir; struct ssrc_ctx; +struct codec_store; -const struct rtp_payload_type *rtp_payload_type(unsigned int, GHashTable *); +const struct rtp_payload_type *rtp_payload_type(unsigned int, struct codec_store *); int rtp_avp2savp(str *, struct crypto_context *, struct ssrc_ctx *); int rtp_savp2avp(str *, struct crypto_context *, struct ssrc_ctx *); diff --git a/include/sdp.h b/include/sdp.h index b68d92b72..ee1f5cd41 100644 --- a/include/sdp.h +++ b/include/sdp.h @@ -20,6 +20,7 @@ void sdp_init(void); int sdp_parse(str *body, GQueue *sessions, const struct sdp_ng_flags *); int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *); +void sdp_streams_free(GQueue *); void sdp_free(GQueue *sessions); int sdp_replace(struct sdp_chopper *, GQueue *, struct call_monologue *, struct sdp_ng_flags *); int sdp_is_duplicate(GQueue *sessions); diff --git a/lib/codeclib.c b/lib/codeclib.c index a6c6f263b..24525d006 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -1303,6 +1303,9 @@ int encoder_config_fmtp(encoder_t *enc, const codec_def_t *def, int bitrate, int encoder_close(enc); + if (ptime <= 0) + ptime = 20; + enc->requested_format = *requested_format; enc->def = def; enc->ptime = ptime / def->clockrate_mult; diff --git a/lib/rtplib.h b/lib/rtplib.h index ad28fbde5..86b000553 100644 --- a/lib/rtplib.h +++ b/lib/rtplib.h @@ -32,8 +32,10 @@ struct rtp_payload_type { int bitrate; const codec_def_t *codec_def; + GList *prefs_link; // link in `codec_prefs` list unsigned int for_transcoding:1; + unsigned int accepted:1; }; diff --git a/lib/str.h b/lib/str.h index b5e7e6aba..fe693d4ed 100644 --- a/lib/str.h +++ b/lib/str.h @@ -238,19 +238,20 @@ INLINE str *str_init_dup(str *out, const char *s) { return out; } INLINE str *str_init_dup_str(str *out, const str *s) { - if (!s || !s->len) { + if (!s) { *out = STR_NULL; return out; } - out->s = malloc(s->len + 1); - memcpy(out->s, s->s, s->len); - out->s[s->len] = '\0'; + char *buf = malloc(s->len + 1); + memcpy(buf, s->s, s->len); + buf[s->len] = '\0'; out->len = s->len; + out->s = buf; return out; } INLINE void str_free_dup(str *out) { if (!out) - return ; + return; if (out->s) free(out->s); diff --git a/t/Makefile b/t/Makefile index c1db24ace..bf392cf12 100644 --- a/t/Makefile +++ b/t/Makefile @@ -85,7 +85,7 @@ COMMONOBJS= str.o auxlib.o rtplib.o loglib.o include ../lib/common.Makefile .PHONY: all-tests unit-tests daemon-tests all-daemon-tests \ - daemon-tests-main daemon-tests-jb daemon-tests-reorder daemon-tests-dtx daemon-tests-dtx-cn + daemon-tests-main daemon-tests-jb daemon-tests-dtx daemon-tests-dtx-cn TESTS= test-bitstr aes-crypt aead-aes-crypt test-const_str_hash.strhash ifeq ($(with_transcoding),yes) @@ -111,7 +111,7 @@ daemon-tests: tests-preload.so $(MAKE) -C ../daemon $(MAKE) all-daemon-tests -all-daemon-tests: daemon-tests-main daemon-tests-jb daemon-tests-reorder daemon-tests-dtx daemon-tests-dtx-cn +all-daemon-tests: daemon-tests-main daemon-tests-jb daemon-tests-dtx daemon-tests-dtx-cn daemon-tests-main: rm -rf fake-$@-sockets @@ -129,14 +129,6 @@ daemon-tests-jb: test "$$(ls fake-$@-sockets)" = "" rmdir fake-$@-sockets -daemon-tests-reorder: - rm -rf fake-$@-sockets - mkdir fake-$@-sockets - LD_PRELOAD=../t/tests-preload.so RTPE_BIN=../daemon/rtpengine TEST_SOCKET_PATH=./fake-$@-sockets \ - perl -I../perl auto-daemon-tests-reorder.pl - test "$$(ls fake-$@-sockets)" = "" - rmdir fake-$@-sockets - daemon-tests-dtx: rm -rf fake-$@-sockets mkdir fake-$@-sockets diff --git a/t/auto-daemon-tests-reorder.pl b/t/auto-daemon-tests-reorder.pl deleted file mode 100755 index bd7b770ca..000000000 --- a/t/auto-daemon-tests-reorder.pl +++ /dev/null @@ -1,8034 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; -use NGCP::Rtpengine::Test; -use NGCP::Rtpclient::SRTP; -use NGCP::Rtpengine::AutoTest; -use Test::More; -use NGCP::Rtpclient::ICE; -use POSIX; - - -autotest_start(qw(--config-file=none -t -1 -i 203.0.113.1 -i 2001:db8:4321::1 - -n 2223 -c 12345 -f -L 7 -E -u 2222 --silence-detect=1 --reorder-codecs)) - or die; - - -my $amr_tests = (POSIX::uname())[1] eq 'moose'; - - -# 100 ms sine wave - -my $wav_file = "\x52\x49\x46\x46\x64\x06\x00\x00\x57\x41\x56\x45\x66\x6d\x74\x20\x10\x00\x00\x00\x01\x00\x01\x00\x40\x1f\x00\x00\x80\x3e\x00\x00\x02\x00\x10\x00\x64\x61\x74\x61\x40\x06\x00\x00\x00\x00\xb0\x22\x45\x41\x25\x58\x95\x64\x24\x65\xbd\x59\xb6\x43\xb4\x25\x35\x03\x5e\xe0\x3b\xc1\x8c\xa9\x0f\x9c\x6a\x9a\xc2\xa4\xe7\xb9\x55\xd7\x92\xf9\x92\x1c\x30\x3c\xb2\x54\x2e\x63\xf3\x65\xa7\x5c\x68\x48\x9b\x2b\xa1\x09\x8a\xe6\x71\xc6\x28\xad\xab\x9d\xcc\x99\x06\xa2\x5c\xb5\x81\xd1\x2d\xf3\x53\x16\xe1\x36\xe8\x50\x64\x61\x59\x66\x36\x5f\xcf\x4c\x56\x31\x04\x10\xd0\xec\xe0\xcb\x19\xb1\xa9\x9f\x98\x99\xa8\x9f\x1a\xb1\xdf\xcb\xd1\xec\x04\x10\x54\x31\xd2\x4c\x33\x5f\x5c\x66\x61\x61\xeb\x50\xde\x36\x56\x16\x2b\xf3\x83\xd1\x59\xb5\x08\xa2\xcb\x99\xac\x9d\x28\xad\x70\xc6\x8a\xe6\xa3\x09\x98\x2b\x6a\x48\xa6\x5c\xf4\x65\x2d\x63\xb3\x54\x2e\x3c\x93\x1c\x93\xf9\x53\xd7\xe9\xb9\xc1\xa4\x69\x9a\x11\x9c\x8b\xa9\x3b\xc1\x5e\xe0\x36\x03\xb2\x25\xba\x43\xb7\x59\x2a\x65\x90\x64\x29\x58\x42\x41\xb2\x22\xff\xff\x50\xdd\xbb\xbe\xdb\xa7\x6b\x9b\xdd\x9a\x42\xa6\x4b\xbc\x4b\xda\xca\xfc\xa5\x1f\xc2\x3e\x77\x56\xed\x63\x9a\x65\x3b\x5b\x1b\x46\xa9\x28\x70\x06\x6c\xe3\xd2\xc3\x4d\xab\xd1\x9c\x10\x9a\x56\xa3\x99\xb7\x67\xd4\x5b\xf6\x79\x19\x8e\x39\xd7\x52\x58\x62\x30\x66\xfd\x5d\xa2\x4a\x81\x2e\xd1\x0c\xae\xe9\x1f\xc9\x17\xaf\x9e\x9e\xa4\x99\xce\xa0\x2c\xb3\xaf\xce\xf8\xef\x33\x13\x1e\x34\xe8\x4e\x57\x60\x68\x66\x57\x60\xe9\x4e\x1c\x34\x35\x13\xf6\xef\xb0\xce\x2d\xb3\xcc\xa0\xa6\x99\x9c\x9e\x17\xaf\x22\xc9\xa9\xe9\xd6\x0c\x7c\x2e\xa7\x4a\xf8\x5d\x36\x66\x52\x62\xdb\x52\x8c\x39\x79\x19\x5c\xf6\x67\xd4\x97\xb7\x59\xa3\x0e\x9a\xd1\x9c\x4e\xab\xd0\xc3\x6e\xe3\x6e\x06\xac\x28\x18\x46\x3d\x5b\x98\x65\xef\x63\x76\x56\xc3\x3e\xa4\x1f\xc9\xfc\x4e\xda\x49\xbc\x43\xa6\xdd\x9a\x69\x9b\xdd\xa7\xbb\xbe\x4f\xdd\x01\x00\xaf\x22\x47\x41\x23\x58\x96\x64\x24\x65\xbb\x59\xba\x43\xb0\x25\x39\x03\x59\xe0\x40\xc1\x87\xa9\x15\x9c\x65\x9a\xc4\xa4\xe7\xb9\x56\xd7\x90\xf9\x94\x1c\x2e\x3c\xb3\x54\x2f\x63\xf1\x65\xa8\x5c\x68\x48\x9a\x2b\xa2\x09\x8a\xe6\x71\xc6\x27\xad\xac\x9d\xcb\x99\x08\xa2\x59\xb5\x84\xd1\x2a\xf3\x56\x16\xe0\x36\xe7\x50\x65\x61\x59\x66\x35\x5f\xd1\x4c\x54\x31\x04\x10\xd2\xec\xdd\xcb\x1c\xb1\xa5\x9f\x9b\x99\xa8\x9f\x18\xb1\xe2\xcb\xcd\xec\x07\x10\x54\x31\xd1\x4c\x33\x5f\x5d\x66\x60\x61\xec\x50\xdd\x36\x57\x16\x29\xf3\x86\xd1\x57\xb5\x09\xa2\xcb\x99\xab\x9d\x29\xad\x70\xc6\x8a\xe6\xa2\x09\x9a\x2b\x69\x48\xa7\x5c\xf2\x65\x2e\x63\xb2\x54\x31\x3c\x91\x1c\x93\xf9\x53\xd7\xe9\xb9\xc1\xa4\x6a\x9a\x10\x9c\x8a\xa9\x3f\xc1\x59\xe0\x3a\x03\xb0\x25\xb8\x43\xbd\x59\x24\x65\x95\x64\x24\x58\x46\x41\xaf\x22\x02\x00\x4e\xdd\xbb\xbe\xdd\xa7\x68\x9b\xdf\x9a\x42\xa6\x48\xbc\x50\xda\xc6\xfc\xa7\x1f\xc2\x3e\x75\x56\xef\x63\x99\x65\x3c\x5b\x1a\x46\xaa\x28\x6e\x06\x6e\xe3\xd1\xc3\x4e\xab\xd1\x9c\x0e\x9a\x57\xa3\x9a\xb7\x64\xd4\x60\xf6\x75\x19\x90\x39\xd7\x52\x55\x62\x34\x66\xf9\x5d\xa8\x4a\x7a\x2e\xd8\x0c\xa7\xe9\x23\xc9\x16\xaf\x9d\x9e\xa6\x99\xcb\xa0\x2f\xb3\xad\xce\xfa\xef\x30\x13\x21\x34\xe6\x4e\x59\x60\x66\x66\x5a\x60\xe4\x4e\x23\x34\x2e\x13\xfc\xef\xab\xce\x30\xb3\xcb\xa0\xa5\x99\x9f\x9e\x14\xaf\x24\xc9\xa7\xe9\xd8\x0c\x7b\x2e\xa8\x4a\xf7\x5d\x36\x66\x53\x62\xda\x52\x8d\x39\x78\x19\x5d\xf6\x67\xd4\x97\xb7\x59\xa3\x0d\x9a\xd2\x9c\x4e\xab\xd1\xc3\x6d\xe3\x6f\x06\xaa\x28\x19\x46\x3f\x5b\x95\x65\xf2\x63\x74\x56\xc2\x3e\xa8\x1f\xc4\xfc\x52\xda\x45\xbc\x46\xa6\xdc\x9a\x6a\x9b\xdc\xa7\xba\xbe\x51\xdd\xff\xff\xb1\x22\x45\x41\x24\x58\x97\x64\x22\x65\xbd\x59\xb7\x43\xb3\x25\x37\x03\x5b\xe0\x3e\xc1\x89\xa9\x11\x9c\x6a\x9a\xc0\xa4\xeb\xb9\x51\xd7\x94\xf9\x91\x1c\x31\x3c\xb1\x54\x2f\x63\xf3\x65\xa5\x5c\x6c\x48\x95\x2b\xa7\x09\x86\xe6\x73\xc6\x28\xad\xa9\x9d\xcf\x99\x04\xa2\x5b\xb5\x84\xd1\x29\xf3\x57\x16\xde\x36\xe9\x50\x65\x61\x57\x66\x38\x5f\xcd\x4c\x57\x31\x04\x10\xd0\xec\xe1\xcb\x17\xb1\xaa\x9f\x97\x99\xaa\x9f\x18\xb1\xe1\xcb\xce\xec\x07\x10\x53\x31\xd0\x4c\x38\x5f\x55\x66\x68\x61\xe6\x50\xe0\x36\x56\x16\x2b\xf3\x81\xd1\x5d\xb5\x04\xa2\xce\x99\xaa\x9d\x29\xad\x70\xc6\x8a\xe6\xa2\x09\x9b\x2b\x67\x48\xa9\x5c\xf1\x65\x2e\x63\xb4\x54\x2e\x3c\x93\x1c\x92\xf9\x54\xd7\xe8\xb9\xc2\xa4\x69\x9a\x10\x9c\x8c\xa9\x3c\xc1\x5c\xe0\x37\x03\xb2\x25\xb8\x43\xbc\x59\x24\x65\x95\x64\x26\x58\x43\x41\xb2\x22\xff\xff\x50\xdd\xba\xbe\xde\xa7\x68\x9b\xdd\x9a\x45\xa6\x45\xbc\x52\xda\xc5\xfc\xa8\x1f\xbf\x3e\x79\x56\xec\x63\x9b\x65\x3b\x5b\x1a\x46\xaa\x28\x6f\x06\x6e\xe3\xd0\xc3\x4f\xab\xd0\x9c\x0f\x9a\x58\xa3\x97\xb7\x68\xd4\x5c\xf6\x78\x19\x8f\x39\xd6\x52\x57\x62\x32\x66\xfb\x5d\xa6\x4a\x7b\x2e\xd8\x0c\xa6\xe9\x25\xc9\x15\xaf\x9c\x9e\xa9\x99\xc7\xa0\x33\xb3\xa9\xce\xfd\xef\x2f\x13\x21\x34\xe6\x4e\x58\x60\x67\x66\x59\x60\xe5\x4e\x23\x34\x2c\x13\x00\xf0\xa6\xce\x35\xb3\xc7\xa0\xa8\x99\x9d\x9e\x15\xaf\x24\xc9\xa8\xe9\xd5\x0c\x7e\x2e\xa5\x4a\xfa\x5d\x35\x66\x52\x62\xdb\x52\x8d\x39\x77\x19\x5e\xf6\x66\xd4\x98\xb7\x59\xa3\x0c\x9a\xd3\x9c\x4d\xab\xd1\xc3\x6e\xe3\x6e\x06\xaa\x28\x1b\x46\x3b\x5b\x9a\x65\xed\x63\x76\x56\xc4\x3e\xa3\x1f\xcb\xfc\x4b\xda\x4a\xbc\x43\xa6\xdd\x9a\x6a\x9b\xdc\xa7\xba\xbe\x51\xdd\xff\xff\xb1\x22\x44\x41\x25\x58\x96\x64\x23\x65\xbd\x59\xb6\x43\xb4\x25\x36\x03\x5c\xe0\x3d\xc1\x8a\xa9\x12\x9c\x67\x9a\xc4\xa4\xe6\xb9\x55\xd7\x93\xf9\x91\x1c\x31\x3c\xb0\x54\x31\x63\xef\x65\xab\x5c\x66\x48\x9a\x2b\xa4\x09\x87\xe6\x73\xc6\x26\xad\xad\x9d\xcb\x99\x07\xa2\x5b\xb5\x81\xd1\x2c\xf3\x56\x16\xde\x36\xeb\x50\x62\x61\x59\x66\x38\x5f\xcc\x4c\x59\x31\x01\x10\xd3\xec\xdd\xcb\x1b\xb1\xa8\x9f\x98\x99\xa9\x9f\x18\xb1\xe0\xcb\xd1\xec\x03\x10\x57\x31\xce\x4c\x37\x5f\x58\x66\x63\x61\xec\x50\xdb\x36\x5a\x16\x27\xf3\x85\xd1\x5a\xb5\x05\xa2\xce\x99\xaa\x9d\x29\xad\x70\xc6\x8a\xe6\xa2\x09\x9a\x2b\x69\x48\xa6\x5c\xf4\x65\x2e\x63\xb1\x54\x32\x3c\x8e\x1c\x96\xf9\x52\xd7\xea\xb9\xc1\xa4\x67\x9a\x13\x9c\x8a\xa9\x3c\xc1\x5e\xe0\x33\x03\xb7\x25\xb4\x43\xbf\x59\x21\x65\x99\x64\x21\x58\x48\x41\xad\x22\x03\x00\x4f\xdd\xbb\xbe\xdb\xa7\x6a\x9b\xdd\x9a\x43\xa6\x4b\xbc\x4a\xda\xcb\xfc\xa4\x1f\xc3\x3e\x76\x56\xef\x63\x96\x65\x40\x5b\x17\x46\xac\x28\x6e\x06\x6d\xe3\xd2\xc3\x4d\xab\xd2\x9c\x0d\x9a\x59\xa3\x97\xb7\x68\xd4\x5c\xf6\x77\x19\x8f\x39\xd8\x52\x55\x62\x33\x66\xfb\x5d\xa4\x4a\x7f\x2e\xd4\x0c\xab\xe9\x20\xc9\x17\xaf\x9d\x9e\xa7\x99\xc9\xa0\x32\xb3\xa9\xce\xfd\xef\x2f\x13\x20\x34\xe8\x4e\x56\x60\x6a\x66\x55\x60\xe9\x4e\x1f\x34\x31\x13\xfa\xef\xad\xce\x2e\xb3\xcc\xa0\xa7\x99\x9b\x9e\x18\xaf\x20\xc9\xac\xe9\xd2\x0c\x81\x2e\xa1\x4a\xff\x5d\x30\x66\x56\x62\xd7\x52\x90\x39\x77\x19\x5d\xf6\x67\xd4\x96\xb7\x5a\xa3\x0e\x9a\xd0\x9c\x50\xab\xcf\xc3\x6e\xe3\x6f\x06\xaa\x28\x1a\x46\x3d\x5b\x98\x65\xee\x63\x77\x56\xc1\x3e\xa7\x1f\xc8\xfc\x4c\xda\x4b\xbc\x41\xa6\xdf\x9a\x68\x9b\xdd\xa7\xba\xbe\x51\xdd"; -is length($wav_file), 1644, 'embedded binary wav file'; - -my $pcma_1 = "\xd5\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\x55\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c"; -my $pcma_2 = "\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34\xd5\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\xd5\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09"; -my $pcma_3 = "\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34\x55\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0"; -my $pcma_4 = "\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\x55\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34\x55\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1"; -my $pcma_5 = "\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\xd5\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34"; - - - -my ($sock_a, $sock_b, $sock_c, $sock_d, $port_a, $port_b, $ssrc, $ssrc_b, $resp, - $sock_ax, $sock_bx, $port_ax, $port_bx, - $srtp_ctx_a, $srtp_ctx_b, $srtp_ctx_a_rev, $srtp_ctx_b_rev, - @ret1, @ret2, @ret3, @ret4, $srtp_key_a, $srtp_key_b, $ts, $seq); - - - - - - -new_call; - -offer('gh#1136', - { }, < { accept => ['PCMU'] } }, < { consume => ['PCMU'] } }, < { mask => ['PCMA'] } }, < { mask => ['PCMU'] } }, < { mask => ['PCMA'] } }, < { mask => ['PCMU'] } }, < { mask => ['PCMA'] } }, < { mask => ['PCMU'] } }, < { mask => ['telephone-event'] } }, < { mask => ['telephone-event'] } }, < { - strip => ['all'], - consume => ['CN'], - offer => ['PCMA', 'PCMU', 'telephone-event'], - } }, < { - strip => ['all'], - consume => ['CN'], - offer => ['PCMA', 'PCMU', 'telephone-event'], - }, - flags => ['symmetric codecs'], - }, < ['symmetric codecs'], - }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], flags => ['always transcode'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], - codec => { - strip => ['all'], - consume => ['CN'], - offer => ['PCMA','PCMU','telephone-event'], - } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['G722'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['G722'] } }, < 'remove', replace => ['origin'], flags => ['symmetric-codecs'] }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMU', 'CN'] } }, < 'remove', replace => ['origin'] }, < { - mask => ['all'], - transcode => ['G722', 'opus/48000/1//test=1', 'speex', 'PCMA', 'telephone-event'], - } -}, < 'remove', replace => ['origin'], - codec => { - mask => ['all'], - transcode => [ - 'PCMA', - 'AMR', - 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', - 'telephone-event', - ], - } }, < 'remove', replace => ['origin'], flags => ['reorder-codecs'] }, < 'remove', replace => ['origin'], - codec => { - mask => ['all'], - transcode => [ - 'PCMA', - 'AMR', - 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', - 'telephone-event', - ], - } }, < 'remove', replace => ['origin'], flags => ['reorder-codecs','single-codec'] }, < { - mask => ['all'], - transcode => ['G722', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'AMR', 'PCMA', 'telephone-event'], - } -}, < ['single-codec'], -}, < { - mask => ['all'], - transcode => ['G722', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'AMR', 'PCMA', 'telephone-event'], - } -}, < { - strip => ['all'], - offer => ['PCMA'], - } -}, < { - mask => ['all'], - transcode => ['G722', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'AMR', 'PCMA', 'telephone-event'], - } -}, < { - strip => ['all'], - offer => ['PCMA', 'telephone-event'], - } -}, < 'remove', replace => ['origin'], flags => [qw(inject-DTMF)] }, < 'remove', replace => ['origin'], flags => [qw(inject-DTMF)] }, < 'remove', - flags => ['inject-DTMF'], - codec => {transcode => ['G722']}, - }, < 'remove', - flags => ['inject-DTMF'], - }, < 'remove', - flags => [], - codec => {transcode => ['G722']}, - }, < 'remove', - flags => [], - }, < { - mask => ['all'], - transcode => ['G722', 'opus/48000/1', 'PCMA', 'telephone-event'] - }, - }, < 'remove', - flags => [], - codec => {transcode => ['G722']}, - }, < 'remove', - flags => [], - }, < 'remove', - flags => [], - codec => {transcode => ['G722']}, - }, < 'remove', - flags => [], - }, < 'remove', - flags => [], - codec => {transcode => ['G722']}, - }, < 'remove', - flags => ['single codec'], - }, < 'remove', - flags => [], - }, < 'remove', - flags => [], - }, < 'remove', - flags => [], - }, < 'remove', - flags => ['single codec'], - }, < 'remove', - flags => [], - codec => {transcode => ['opus']}, - }, < 'remove', - flags => ['single codec'], - }, < 'remove', - flags => [], - codec => {mask => ['all'], transcode => ['opus/48000/1', 'PCMA', 'telephone-event']}, - }, < 'remove', - flags => ['single codec'], - }, < 'remove', - flags => [], - codec => {transcode => ['PCMA']}, - }, < 'remove', - flags => ["codec-mask-all", "codec-strip-telephone-event", "codec-transcode-PCMU", "codec-transcode-G722", "codec-transcode-t38", "codec-offer-telephone-event", "port-latching"], - 'to-tag' => tt(), - }, < 'remove', - flags => ["trust-address", "symmetric-codecs"], - }, < 'remove', - flags => ["codec-mask-all", "codec-strip-telephone-event", "codec-transcode-PCMU", "codec-transcode-G722", "codec-transcode-t38", "codec-offer-telephone-event", "port-latching"], - 'to-tag' => tt(), - }, < 'remove', 'transport-protocol' => 'RTP/AVP', - }, < 'remove', - }, < 'remove', 'transport-protocol' => 'RTP/AVP', - 'DTLS-reverse' => 'passive', - }, < 'remove', - }, < 'remove', 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux'], - }, < 'remove', }, < 'remove', }, < ft(), blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -($seq, $ts, $ssrc) = rcv($sock_a, $port_b, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -# SR LEN SSRC NTP1 NTP2 RTP PACKETS OCTETS SSRC LOST SEQ JITTER LAST SR DLSR CNAME -@ret1 = rcv($sock_ax, $port_bx, qr/^\x81\xc8\x00\x0c(.{4})(.{4})(.{4})(.{4})\x00\x00\x00\x01\x00\x00\x00\xac\x00\x00\x12\x34\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\xca\x00\x05(.{4})\x01\x0c([0-9a-f]{12})\x00\x00$/s); -is $ret1[0], $ssrc, 'SSRC matches'; -is $ret1[3], $ts, 'TS matches'; -is $ret1[4], $ssrc, 'SSRC matches'; - -rtpe_req('delete', "delete", { 'from-tag' => ft() }); - - - - - -($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call( - [qw(198.51.100.1 7400)], - [qw(198.51.100.1 7401)], - [qw(198.51.100.3 7402)], - [qw(198.51.100.3 7403)], -); - -($port_a, $port_ax) = offer('RTCP player w/ previous SR', { ICE => 'remove', }, < 'remove', }, < ft(), blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -($seq, $ts, $ssrc) = rcv($sock_a, $port_b, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -# SR LEN SSRC NTP1 NTP2 RTP PACKETS OCTETS SSRC LOST SEQ JITTER LAST SR DLSR CNAME -@ret1 = rcv($sock_ax, $port_bx, qr/^\x81\xc8\x00\x0c(.{4})(.{4})(.{4})(.{4})\x00\x00\x00\x01\x00\x00\x00\xac\x00\x00\x12\x34\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x00\x56\x78\x9a\xbc(.{4})\x81\xca\x00\x05(.{4})\x01\x0c([0-9a-f]{12})\x00\x00$/s); -is $ret1[0], $ssrc, 'SSRC matches'; -is $ret1[3], $ts, 'TS matches'; -cmp_ok $ret1[4], '<', 1000, 'DSLR ok'; -is $ret1[5], $ssrc, 'SSRC matches'; - -rtpe_req('delete', "delete", { 'from-tag' => ft() }); - - - - -# MSRP (GH 959) - -new_call(); - -offer('gh 959 media c=', { ICE => 'remove', }, < 'remove', }, < 'remove', codec => { mask => ['full'], transcode => ['PCMA','telephone-event'] } }, < 'remove', }, < 'remove', codec => { mask => ['all'], transcode => ['PCMA','telephone-event'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', flags => ['symmetric codecs'] }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', flags => ['symmetric codecs'] }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', flags => ['symmetric codecs'] }, < [ 'decode' ], ICE => 'remove', - }, < ft() }); - - - - - - -new_call(); - -offer('T.38 forward re-invite', { ICE => 'remove', - }, < 'remove', - }, < 'remove', 'T.38' => [ 'force' ], - }, < 'remove', - }, < ft() }); - - - - -new_call(); - -offer('T.38 reverse re-invite', { ICE => 'remove', - }, < 'remove', - }, < 'remove', 'T.38' => [ 'decode' ], - }, < 'remove' }, < ft() }); - - - - - - -new_call(); - -offer('T.38 forward re-invite w/ unsupported codec', { ICE => 'remove', - }, < 'remove', - }, < 'remove', 'T.38' => [ 'force' ], - }, < 'remove', - }, < ft() }); - - - - -new_call(); - -offer('T.38 reverse re-invite w/ unsupported codec', { ICE => 'remove', - }, < 'remove', - }, < 'remove', 'T.38' => [ 'decode' ], - }, < 'remove' }, < ft() }); - - - -new_call; - -offer('T.38 FEC invite', { ICE => 'remove', 'T.38' => [ 'force', 'FEC' ], - }, < ft() }); - - - - - -# github issue 850 - -new_call; - -@ret1 = offer('gh 850', - { - ICE => 'force-relay', flags => [qw(SDES-off)], 'transport-protocol' => 'UDP/TLS/RTP/SAVPF', - 'rtcp-mux' => [qw(accept offer)], 'via-branch' => 'z9hG4bK9463.af303705.113', - }, < 'force-relay', flags => [qw(SDES-off)], 'transport-protocol' => 'UDP/TLS/RTP/SAVPF', - 'rtcp-mux' => [qw(accept offer)], 'via-branch' => 'z9hG4bK9463.af303705.113', - }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => '0', volume => 10, duration => 100 }); - -snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96 | 0x80, 1002, 3320, $ssrc, "\x00\x0a\x00\xa0")); -snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1003, 3320, $ssrc, "\x00\x0a\x01\x40")); -snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1004, 3320, $ssrc, "\x00\x0a\x01\xe0")); -snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1005, 3320, $ssrc, "\x00\x0a\x02\x80")); -snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1006, 3320, $ssrc, "\x00\x0a\x03\x20")); -snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1007, 3320, $ssrc, "\x00\x8a\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1008, 3320, $ssrc, "\x00\x8a\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1009, 3320, $ssrc, "\x00\x8a\x03\xc0")); -snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1010, 4280, $ssrc, "\x00" x 160)); - - - -snd($sock_b, $port_a, rtp(0, 4000, 8000, 0x6543, "\x00" x 160)); -($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); -snd($sock_b, $port_a, rtp(0, 4001, 8160, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); - -$resp = rtpe_req('play DTMF', 'inject DTMF towards A', - { 'from-tag' => tt(), code => '*', volume => 10, duration => 100 }); - -snd($sock_b, $port_a, rtp(0, 4002, 8320, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(96 | 0x80, 4002, 8320, $ssrc, "\x0a\x0a\x00\xa0")); -snd($sock_b, $port_a, rtp(0, 4003, 8480, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4003, 8320, $ssrc, "\x0a\x0a\x01\x40")); -snd($sock_b, $port_a, rtp(0, 4004, 8640, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4004, 8320, $ssrc, "\x0a\x0a\x01\xe0")); -snd($sock_b, $port_a, rtp(0, 4005, 8800, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4005, 8320, $ssrc, "\x0a\x0a\x02\x80")); -snd($sock_b, $port_a, rtp(0, 4006, 8960, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4006, 8320, $ssrc, "\x0a\x0a\x03\x20")); -snd($sock_b, $port_a, rtp(0, 4007, 9120, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4007, 8320, $ssrc, "\x0a\x8a\x03\xc0")); -rcv($sock_a, $port_b, rtpm(96, 4008, 8320, $ssrc, "\x0a\x8a\x03\xc0")); -rcv($sock_a, $port_b, rtpm(96, 4009, 8320, $ssrc, "\x0a\x8a\x03\xc0")); -snd($sock_b, $port_a, rtp(0, 4008, 9280, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4010, 9280, $ssrc, "\x00" x 160)); - - - - - -# transcoding, RFC payload type present on both sides - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 6110)], [qw(198.51.100.3 6112)]); - -($port_a) = offer('transcoding, RFC payload type present on both sides', - { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'], - codec => { transcode => ['PCMA'] }}, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => '0', volume => 10, duration => 100 }); - -snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96 | 0x80, 1002, 3320, $ssrc, "\x00\x0a\x00\xa0")); -snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1003, 3320, $ssrc, "\x00\x0a\x01\x40")); -snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1004, 3320, $ssrc, "\x00\x0a\x01\xe0")); -snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1005, 3320, $ssrc, "\x00\x0a\x02\x80")); -snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1006, 3320, $ssrc, "\x00\x0a\x03\x20")); -snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1007, 3320, $ssrc, "\x00\x8a\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1008, 3320, $ssrc, "\x00\x8a\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1009, 3320, $ssrc, "\x00\x8a\x03\xc0")); -snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1010, 4280, $ssrc, "\x2a" x 160)); - - - -snd($sock_b, $port_a, rtp(8, 4000, 8000, 0x6543, "\x2a" x 160)); -($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); -snd($sock_b, $port_a, rtp(8, 4001, 8160, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); - -$resp = rtpe_req('play DTMF', 'inject DTMF towards A', - { 'from-tag' => tt(), code => '#', volume => -10, duration => 100 }); - -snd($sock_b, $port_a, rtp(8, 4002, 8320, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(96 | 0x80, 4002, 8320, $ssrc, "\x0b\x0a\x00\xa0")); -snd($sock_b, $port_a, rtp(8, 4003, 8480, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4003, 8320, $ssrc, "\x0b\x0a\x01\x40")); -snd($sock_b, $port_a, rtp(8, 4004, 8640, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4004, 8320, $ssrc, "\x0b\x0a\x01\xe0")); -snd($sock_b, $port_a, rtp(8, 4005, 8800, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4005, 8320, $ssrc, "\x0b\x0a\x02\x80")); -snd($sock_b, $port_a, rtp(8, 4006, 8960, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4006, 8320, $ssrc, "\x0b\x0a\x03\x20")); -snd($sock_b, $port_a, rtp(8, 4007, 9120, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(96, 4007, 8320, $ssrc, "\x0b\x8a\x03\xc0")); -rcv($sock_a, $port_b, rtpm(96, 4008, 8320, $ssrc, "\x0b\x8a\x03\xc0")); -rcv($sock_a, $port_b, rtpm(96, 4009, 8320, $ssrc, "\x0b\x8a\x03\xc0")); -snd($sock_b, $port_a, rtp(8, 4008, 9280, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4010, 9280, $ssrc, "\x00" x 160)); - - - -# no transcoding, no RFC payload type present - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 6014)], [qw(198.51.100.3 6016)]); - -($port_a) = offer('no transcoding, no RFC payload type present', - { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => 'C', volume => 5, duration => 120, pause => 110 }); - -snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1002, 3320, $ssrc, "\xff\x93\x94\xbc\x2e\x56\xbf\x2b\x13\x1b\xa7\x8e\x98\x47\x25\x41\xe2\x24\x16\x2b\x99\x8e\x9f\x28\x1e\x3d\x5b\x23\x1c\xdf\x92\x8f\xb6\x1c\x1c\x40\x5d\x26\x25\xaa\x8f\x95\x3b\x15\x1d\x5e\xde\x2c\x38\x9d\x8f\x9e\x1f\x11\x20\xc0\xc1\x37\xdd\x99\x92\xb7\x15\x10\x2c\xac\xb5\x49\xb8\x97\x99\x37\x0f\x13\x58\xa0\xae\x67\xae\x99\xa4\x1f\x0d\x1a\xae\x9b\xad\x7b\xad\x9d\xbf\x16\x0e\x27\x9d\x98\xb0\x55\xb1\xa6\x3a\x11\x11\x63\x95\x98\xbf\x3e\xbb\xb4\x26\x10\x1a\xa9\x90\x9a\x4e\x30\xce\xd4\x1e\x12\x29\x99\x8e\xa1\x2d\x29\x6d\x4b\x1c\x18\xef\x91\x8f\xb6\x1f\x24\x57\x3e\x1d\x20\xa9\x8e\x95\x3e\x19\x23\x67\x3e\x21\x31\x9c\x8e\x9e\x22\x14\x26\xcd\x4a")); -snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1003, 3480, $ssrc, "\x2a\xdf\x96\x90\xb5\x17\x13\x2f\xb6\xf5\x36\xb1\x93\x96\x39\x10\x15\x55\xaa\xc8\x4c\xa7\x95\xa0\x1f\x0e\x1b\xb4\xa1\xbd\xed\xa4\x99\xbb\x15\x0e\x27\xa0\x9d\xbd\xda\xa4\x9f\x39\x10\x11\x58\x98\x9c\xc8\xf9\xa9\xac\x23\x0e\x19\xab\x92\x9e\x59\x4c\xb0\xca\x1b\x10\x27\x9a\x90\xa5\x35\x3a\xbe\x43\x18\x15\x6c\x92\x91\xb7\x26\x30\xd6\x32\x18\x1d\xa9\x8e\x96\x44\x1d\x2d\xfc\x2e\x1b\x2d\x9a\x8d\x9e\x25\x19\x2d\xe7\x2f\x20\xea\x94\x8f\xb3\x19\x17\x36\xc8\x36\x2c\xae\x90\x95\x3b\x12\x18\x55\xb7\x43\x3e\xa1\x91\x9e\x1f\x0f\x1d\xba\xac\x64\xe8\x9d\x95\xb7\x15\x0e\x29\xa6\xa6\xda\xc3\x9d\x9b\x39\x0f\x11\x51\x9c\xa2\xd8\xbe\x9f\xa7\x21\x0e\x18\xad")); -snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1004, 3640, $ssrc, "\x96\xa3\x68\xc4\xa5\xc2\x19\x0e\x26\x9c\x93\xa9\x3f\xdb\xae\x3e\x14\x12\x5b\x93\x93\xb9\x2e\x51\xbe\x2c\x14\x1b\xa9\x8f\x97\x4c\x25\x3f\xde\x25\x16\x2a\x9a\x8e\x9e\x29\x1e\x3b\x5e\x24\x1b\x7b\x92\x8f\xb2\x1c\x1c\x3e\x61\x27\x25\xac\x8f\x94\x3e\x15\x1c\x59\xdb\x2d\x37\x9e\x8f\x9d\x20\x11\x1f\xc2\xbf\x38\xea\x99\x92\xb4\x16\x10\x2b\xad\xb4\x49\xba\x98\x98\x3a\x0f\x12\x4e\xa1\xad\x68\xaf\x99\xa3\x20\x0d\x19\xb0\x9b\xac\x7b\xae\x9d\xbc\x17\x0e\x25\x9e\x98\xaf\x55\xb2\xa6\x3d\x12\x11\x52\x96\x97\xbd\x3e\xbc\xb3\x28\x10\x19\xab\x90\x9a\x54\x2f\xd0\xcf\x1f\x12\x27\x9a\x8e\xa0\x2e\x28\x66\x4e\x1d\x18\x62\x92\x8f\xb2\x20\x23\x53\x3f\x1d\x1f")); -snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1005, 3800, $ssrc, "\xab\x8e\x94\x44\x19\x22\x61\x40\x21\x2f\x9c\x8e\x9d\x23\x14\x25\xce\x4d\x2a\xf7\x96\x8f\xb1\x18\x13\x2e\xb7\xe8\x36\xb3\x94\x96\x3c\x10\x15\x4d\xaa\xc5\x4b\xa8\x95\x9f\x20\x0e\x1a\xb6\xa0\xbc\xf5\xa4\x99\xb8\x16\x0e\x26\xa1\x9d\xbb\xdd\xa5\x9f\x3c\x10\x10\x4c\x99\x9b\xc5\x78\xaa\xac\x24\x0f\x18\xac\x93\x9d\x5f\x4a\xb1\xc7\x1c\x0f\x25\x9b\x90\xa3\x36\x39\xbf\x47\x18\x14\x56\x92\x90\xb4\x27\x2f\xd7\x34\x18\x1c\xab\x8e\x95\x4b\x1d\x2c\xfe\x2f\x1b\x2c\x9b\x8d\x9d\x27\x19\x2c\xe7\x30\x20\x6d\x94\x8f\xaf\x1a\x17\x34\xc8\x37\x2b\xaf\x91\x94\x3f\x12\x18\x4e\xb6\x45\x3d\xa3\x91\x9e\x20\x0f\x1c\xbc\xab\x6c\xf5\x9e\x95\xb3\x16\x0e\x27\xa7\xa5")); -snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1006, 3960, $ssrc, "\xd6\xc6\x9d\x9b\x3d\x0f\x11\x49\x9c\xa1\xd4\xbf\x9f\xa6\x22\x0e\x18\xaf\x96\xa2\x6e\xc6\xa5\xbe\x19\x0e\x24\x9d\x93\xa8\x40\xe1\xae\x42\x15\x12\x4e\x94\x93\xb7\x2e\x4e\xbe\x2d\x14\x1a\xab\x8f\x97\x52\x25\x3e\xdc\x26\x16\x28\x9b\x8e\x9e\x2b\x1e\x3a\x61\x25\x1b\x5d\x93\x8f\xaf\x1d\x1c\x3d\x67\x27\x24\xad\x8f\x93\x45\x15\x1c\x53\xd7\x2d\x35\x9f\x8f\x9c\x22\x11\x1f\xc5\xbe\x38\x7a\x9a\x91\xb0\x17\x10\x29\xad\xb3\x4a\xbc\x98\x98\x3e\x10\x12\x48\xa1\xad\x6a\xb1\x9a\xa1\x21\x0e\x18\xb3\x9b\xab\x7d\xaf\x9d\xb9\x18\x0e\x23\x9f\x97\xae\x55\xb4\xa5\x40\x12\x10\x49\x96\x97\xbb\x3d\xbd\xb2\x29\x10\x18\xac\x90\x99\x5d\x2f\xd4\xcd\x1f\x12\x25\x9b")); -snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1007, 4120, $ssrc, "\x8e\x9f\x2f\x28\x5f\x51\x1d\x17\x52\x92\x8f\xaf\x20\x22\x50\x42\x1e\x1f\xad\x8e\x93\x4b\x19\x21\x5d\x42\x22\x2e\x9d\x8e\x9c\x25\x14\x24\xd0\x4f\x2a\x68\x97\x8f\xae\x18\x12\x2c\xb7\xdf\x36\xb6\x94\x95\x41\x11\x14\x48\xaa\xc3\x4a\xaa\x95\x9e\x21\x0e\x19\xb8\xa0\xba\xfe\xa5\x99\xb4\x17\x0e\x24\xa2\x9c\xba\xe0\xa6\x9e\x40\x10\x10\x45\x99\x9b\xc2\x6d\xaa\xab\x26\x0f\x17\xae\x93\x9c\x6a\x48\xb2\xc3\x1c\x0f\x23\x9c\x90\xa2\x37\x38\xbf\x4b\x19\x14\x4b\x93\x90\xb1\x27\x2e\xd8\x36\x19\x1c\xad\x8e\x94\x52\x1d\x2b\x7d\x30\x1b\x2a\x9c\x8d\x9c\x28\x19\x2b\xe7\x31\x20\x5a\x95\x8f\xad\x1a\x16\x32\xc8\x39\x2b\xb2\x91\x94\x46\x13\x17\x4a\xb6\x48\x3c")); -# pause -snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1008, 4280, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1009, 4440, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1010, 4600, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1011, 4760, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1012, 4920, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1013, 5080, $ssrc, "\xff" x 80 . "\x00" x 80)); - - - -snd($sock_b, $port_a, rtp(0, 4000, 8000, 0x6543, "\x00" x 160)); -($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); -snd($sock_b, $port_a, rtp(0, 4001, 8160, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); - -$resp = rtpe_req('play DTMF', 'inject DTMF towards A', - { 'from-tag' => tt(), code => '4', volume => 3, duration => 150, pause => 100 }); - -snd($sock_b, $port_a, rtp(0, 4002, 8320, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4002, 8320, $ssrc, "\xff\x90\x8a\x93\xd9\x1b\x18\x27\x65\xe5\x33\x29\x4c\x9e\x8f\x91\xb8\x15\x09\x0d\x32\x98\x8e\x96\xbb\x2c\x2b\x4c\xd8\x34\x1c\x18\x2e\x9d\x8c\x8c\xa5\x1a\x0b\x0d\x27\xa3\x97\x9e\xbd\x4f\xc4\xaa\xb2\x2c\x12\x0e\x1e\xa1\x8b\x8a\x9c\x25\x0e\x10\x25\xb7\xa7\xb7\x5e\xcb\xa2\x98\x9f\x30\x0f\x0a\x16\xae\x8d\x8a\x98\x3a\x18\x19\x2c\xdd\xfd\x30\x2b\xce\x99\x8e\x95\x4c\x0f\x09\x10\xdf\x93\x8e\x9a\xec\x28\x2c\x56\xee\x2d\x1a\x1a\x48\x97\x8b\x8e\xba\x14\x0a\x0f\x39\x9d\x96\xa1\xcd\x4e\xbe\xab\xbe\x23\x10\x10\x2b\x99\x8a\x8c\xa7\x1b\x0d\x12\x2f\xad\xa7\xbc\x5e\xbd\x9f\x99\xa8\x23\x0d\x0b\x1d\x9f\x8b\x8c\x9f\x29\x16\x1b\x34\xcd\x60\x2f\x2f\xb6\x96")); -snd($sock_b, $port_a, rtp(0, 4003, 8480, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4003, 8480, $ssrc, "\x8e\x9b\x2b\x0c\x09\x17\xae\x8f\x8e\x9e\x3f\x25\x2e\x65\x5c\x28\x1a\x1e\xc2\x92\x8a\x92\x44\x0f\x0a\x14\xd6\x99\x97\xa6\x7c\x4e\xba\xad\xe5\x1d\x0f\x13\x49\x92\x89\x8e\xbe\x15\x0d\x16\x43\xa8\xa7\xc1\x66\xb5\x9d\x9a\xb6\x1b\x0c\x0d\x2b\x98\x8a\x8d\xab\x1f\x15\x1d\x3f\xc7\x52\x2e\x39\xaa\x93\x8f\xa3\x1e\x0b\x0b\x1e\x9f\x8d\x8f\xa7\x30\x23\x31\x7c\x4a\x24\x1a\x24\xac\x8e\x8b\x99\x28\x0c\x0a\x1a\xb0\x96\x98\xac\x4f\x53\xb7\xaf\x44\x19\x0f\x18\xba\x8e\x89\x93\x3f\x10\x0d\x1a\xd5\xa3\xa8\xca\xf9\xae\x9c\x9d\xec\x16\x0b\x10\x4e\x91\x89\x90\xc6\x1a\x14\x20\x55\xc3\x4a\x2f\x49\xa2\x91\x92\xb2\x17\x09\x0c\x2d\x99\x8d\x92\xb3\x29\x23\x36\xf2")); -snd($sock_b, $port_a, rtp(0, 4004, 8640, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4004, 8640, $ssrc, "\x3e\x20\x1b\x2d\xa0\x8d\x8c\xa1\x1c\x0a\x0c\x22\xa3\x94\x9a\xb5\x44\x5c\xb5\xb6\x32\x16\x0f\x1e\xa6\x8c\x8a\x99\x28\x0e\x0e\x20\xb7\xa1\xab\xd4\xdb\xaa\x9c\xa1\x38\x11\x0b\x15\xb5\x8d\x8a\x96\x3f\x16\x15\x26\xdd\xc2\x43\x31\xdf\x9d\x90\x96\x6d\x11\x09\x0f\x5a\x93\x8c\x97\xd2\x23\x23\x3b\xf6\x37\x1f\x1d\x40\x9a\x8c\x8e\xb2\x15\x09\x0e\x31\x9c\x93\x9c\xc2\x3e\x74\xb4\xbf\x29\x14\x11\x29\x9b\x8a\x8b\xa3\x1c\x0d\x0f\x2a\xab\x9f\xad\xe0\xcc\xa6\x9c\xa9\x28\x0e\x0c\x1c\xa2\x8b\x8b\x9c\x2a\x14\x17\x2c\xc6\xc4\x3e\x36\xbd\x99\x90\x9b\x30\x0d\x09\x15\xb3\x8f\x8d\x9b\x42\x1f\x25\x42\x70\x30\x1e\x1f\xcf\x95\x8b\x92\x58\x0f\x09\x12\x6f\x98\x93")); -snd($sock_b, $port_a, rtp(0, 4005, 8800, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4005, 8800, $ssrc, "\x9f\xe5\x3b\xe2\xb5\xd9\x21\x12\x14\x3e\x95\x89\x8d\xb6\x16\x0c\x13\x3a\xa4\x9f\xb1\xf1\xc0\xa3\x9d\xb4\x1e\x0d\x0d\x27\x99\x8a\x8c\xa7\x1f\x12\x19\x37\xbc\xc8\x3c\x3c\xaf\x97\x91\xa2\x21\x0b\x0a\x1c\xa2\x8d\x8e\xa2\x2f\x1e\x28\x4c\x5d\x2c\x1e\x25\xb0\x90\x8c\x98\x2c\x0c\x0a\x18\xb4\x94\x94\xa6\x4d\x3a\xd4\xb8\x4f\x1d\x11\x18\xc5\x8f\x89\x91\x4d\x10\x0c\x17\xec\x9f\xa0\xb8\xff\xba\xa1\x9f\xd3\x19\x0c\x0f\x3f\x92\x89\x8f\xbb\x19\x11\x1c\x48\xb8\xce\x3b\x4a\xa8\x95\x93\xaf\x19\x0a\x0c\x29\x99\x8c\x8f\xad\x27\x1d\x2b\x59\x4f\x29\x1e\x2d\xa5\x8e\x8d\x9f\x1e\x0b\x0b\x1e\xa4\x91\x96\xad\x3e\x3b\xcc\xbc\x3a\x1a\x12\x1e\xaa\x8d\x8a\x98\x2b")); -snd($sock_b, $port_a, rtp(0, 4006, 8960, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4006, 8960, $ssrc, "\x0e\x0c\x1d\xb8\x9d\xa2\xbe\xf9\xb4\xa0\xa3\x3f\x14\x0c\x14\xbd\x8e\x89\x93\x49\x15\x12\x1f\xe7\xb5\xd9\x3c\x7c\xa1\x93\x97\xd5\x13\x09\x0e\x45\x93\x8b\x93\xc4\x20\x1d\x2e\x6b\x46\x26\x1f\x3d\x9d\x8d\x8e\xae\x17\x09\x0d\x2c\x9c\x90\x98\xba\x36\x3d\xc7\xc4\x2e\x17\x13\x27\x9e\x8b\x8b\x9f\x1e\x0c\x0e\x25\xaa\x9c\xa5\xc8\xe8\xae\xa0\xaa\x2d\x10\x0c\x1b\xa6\x8c\x8a\x9a\x2c\x12\x13\x27\xc3\xb3\xed\x3e\xc8\x9d\x93\x9b\x38\x0f\x09\x13\xba\x8f\x8b\x98\x4a\x1d\x1e\x34\xf9\x3e\x24\x23\xea\x98\x8c\x92\xdf\x10\x09\x0f\x4d\x97\x90\x9c\xd2\x31\x3f\xc5\xd6\x28\x16\x16\x39\x97\x8a\x8d\xaf\x17\x0b\x10\x32\xa2\x9b\xa8\xd6\xd9\xac\xa1\xb3\x22\x0e\x0e")); -snd($sock_b, $port_a, rtp(0, 4007, 9120, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4007, 9120, $ssrc, "\x24\x9b\x8a\x8b\xa2\x1f\x10\x15\x2f\xb8\xb4\x68\x43\xb8\x9a\x94\xa1\x25\x0c\x0a\x1a\xa5\x8d\x8c\x9e\x30\x1b\x1f\x3c\xee\x38\x23\x28\xb8\x93\x8d\x97\x31\x0d\x09\x15\xb9\x93\x90\xa0\x4f\x2f\x46\xc4\x5e\x21\x15\x19\xd7\x91\x89\x90\x7b\x10\x0b\x14\x5b\x9d\x9c\xad\xed\xcd\xa9\xa3\xca\x1c\x0d\x10\x38\x94\x89\x8e\xb3\x19\x0f\x18\x3e\xb0\xb5\x59\x4d\xae\x98\x95\xad\x1c\x0b\x0c\x25\x9b\x8b\x8e\xa9\x26\x1a\x22\x46\xf5\x33\x23\x2e\xaa\x90\x8d\x9e\x21\x0b\x0a\x1c\xa6\x90\x92\xa8\x3b\x2e\x4d\xc7\x43\x1e\x15\x1e\xaf\x8e\x8a\x96\x2e\x0e\x0b\x1a\xbb\x9b\x9d\xb2\x68\xc5\xa8\xa7\x4c\x17\x0d\x14\xcb\x8f\x89\x91\x5e\x14\x0f\x1c\x6e\xad\xb8\x52\x68\xa8")); -snd($sock_b, $port_a, rtp(0, 4008, 9280, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4008, 9280, $ssrc, "\x97\x98\xc7\x16\x0a\x0e\x3a\x94\x8a\x90\xbb\x1e\x1a\x27\x56\x6f\x2f\x25\x3b\xa0\x8e\x8f\xaa\x19\x09\x0c\x28\x9c\x8f\x95\xb2\x31\x2e\x59\xcc\x37\x1b\x16\x26\xa1\x8c\x8b\x9d\x1f\x0c\x0c\x20\xab\x99\x9e\xbb\x5d\xbe\xa7\xac\x32\x13\x0d\x1a\xab\x8c\x89\x97\x2e\x10\x10\x21\xc3\xab\xbc\x4f\xd4\xa2\x96\x9c\x3f\x10\x0a\x12\xc4\x8f\x8a\x95\x57\x1b\x1a\x2b\xfd\x5d\x2d\x27\x62\x9b\x8e\x92\xc9\x12\x09\x0e\x3f\x97\x8e\x98\xc6\x2c\x2f\x6b\xd9\x2e\x1a\x18\x34\x9a\x8b\x8d\xab\x18\x0a\x0e\x2d\xa1\x98\xa1\xc7\x5b\xb9\xa7\xb4\x27\x10\x0e\x22\x9d\x8a\x8b\x9f\x20\x0e\x12\x2a\xb4\xaa\xc0\x50\xc0\x9e\x97\xa1\x2a\x0e\x0a\x19\xa8\x8c\x8b\x9b\x31\x18\x1b\x31")); -snd($sock_b, $port_a, rtp(0, 4009, 9440, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4009, 9440, $ssrc, "\xda\x50\x2c\x2b\xc0\x97\x8e\x97\x39\x0e\x09\x13\xbf\x92\x8e\x9c\x57\x29\x31\xef\x72\x28\x19\x1b\x6d\x94\x8a\x8f\xce\x11\x0a\x11\x48\x9c\x98\xa5\xdc\x5e\xb5\xa9\xc6\x1f\x0f\x10\x31\x96\x89\x8d\xad\x19\x0e\x15\x37\xac\xaa\xc8\x57\xb7\x9c\x98\xac\x1e\x0c\x0c\x21\x9c\x8b\x8d\xa4\x25\x17\x1d\x3b\xcf\x48\x2b\x30\xae\x93\x8e" . "\xff" x 80)); -# pause -snd($sock_b, $port_a, rtp(0, 4010, 9600, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4010, 9600, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4011, 9760, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4011, 9760, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4012, 9920, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4012, 9920, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4013, 10080, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4013, 10080, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4014, 10240, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4014, 10240, $ssrc, "\xff" x 80 . "\x00" x 80)); - - - - -# transcoding, no RFC payload type present - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 6018)], [qw(198.51.100.3 6020)]); - -($port_a) = offer('transcoding, no RFC payload type present', - { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'], - codec => { transcode => ['PCMA'] } }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => 'C', volume => 5, duration => 120 }); - -snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1002, 3320, $ssrc, "\xd5\xb9\xbe\x97\x05\x70\xea\x01\x3e\x31\x82\xa5\xb2\x63\x0f\x69\xc1\x0f\x3d\x06\xb3\xa4\x8a\x03\x35\x14\x75\x0e\x36\xcc\xb8\xa5\x9d\x36\x36\x68\x49\x0d\x0c\x81\xa5\xbf\x16\x3f\x37\x4f\xcf\x07\x13\xb4\xa5\xb4\x0a\x3b\x0b\xeb\xe9\x12\xc9\xb3\xb8\x92\x3c\x3a\x07\x87\x9c\x61\x93\xb2\xb3\x12\x25\x39\x76\x8b\x85\x5a\x85\xb3\x8e\x35\x24\x30\x85\xb1\x87\x57\x84\xb7\xeb\x3c\x24\x0d\xb4\xb2\x9b\x70\x98\x8c\x11\x3b\x38\x41\xbf\xb2\xeb\x15\x96\x9f\x0d\x3a\x30\x83\xba\xb1\x7b\x1b\xfa\xf2\x34\x39\x03\xb0\xa5\x88\x04\x03\x5f\x67\x37\x32\xdd\xb8\xba\x9d\x35\x0e\x71\x15\x37\x0a\x80\xa4\xbf\x15\x33\x09\x45\x15\x0b\x18\xb6\xa4\xb4\x08\x3f\x0d\xe5\x66")); -snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1003, 3480, $ssrc, "\x00\xcd\xbc\xba\x9c\x3d\x39\x1a\x9d\xd1\x1d\x98\xbe\xbd\x10\x3a\x3f\x73\x80\xe0\x64\x82\xbf\x8b\x35\x24\x31\x9f\x8b\x94\xdf\x8e\xb3\x96\x3c\x24\x02\x8b\xb7\x94\xf4\x8f\xb5\x10\x3a\x3b\x76\xb2\xb6\xe0\xd6\x80\x87\x09\x25\x33\x81\xb9\xb4\x74\x64\x9b\xe6\x31\x3a\x0d\xb1\xba\x8f\x1c\x11\x95\x6f\x32\x3f\x5e\xb8\xbb\x92\x0d\x1a\xf0\x19\x32\x37\x83\xa4\xbc\x6d\x37\x07\xd4\x04\x31\x07\xb1\xa4\xb4\x0c\x33\x04\xc5\x05\x0b\xd8\xbe\xa5\x9e\x30\x3d\x1d\xe0\x1d\x06\x84\xbb\xbf\x16\x38\x33\x73\x92\x6f\x15\x88\xbb\xb5\x35\x25\x37\x91\x86\x46\xda\xb7\xbf\x92\x3c\x25\x03\x8d\x8c\xf4\xef\xb7\xb6\x10\x25\x3b\x7f\xb6\x89\xf6\x95\xb5\x82\x0b\x24\x33\x84")); -snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1004, 3640, $ssrc, "\xbd\x8e\x5a\xec\x8c\xee\x33\x24\x0c\xb6\xbe\x80\x6b\xf5\x85\x6a\x3f\x39\x4a\xbe\xbe\x90\x05\x7f\x95\x06\x3e\x31\x80\xa5\xbd\x64\x0f\x6b\xcc\x0c\x3d\x00\xb0\xa4\xb5\x00\x34\x16\x4e\x0e\x36\x57\xb9\xa5\x99\x36\x36\x6a\x43\x0d\x0f\x86\xa5\xbe\x15\x3f\x36\x77\xf5\x07\x12\xb4\xa5\xb4\x0b\x3b\x0a\xee\xeb\x13\xd8\xb0\xb8\x9f\x3c\x3a\x01\x87\x9f\x66\x91\xb2\xb3\x11\x25\x39\x7a\x8b\x84\x5b\x9a\xb0\x89\x0a\x24\x33\x9b\xb1\x87\x54\x85\xb7\x97\x3d\x24\x0c\xb4\xb2\x9a\x73\x99\x8c\x14\x38\x3b\x7c\xbc\xbd\x94\x15\x97\x9e\x02\x3a\x33\x81\xba\xb0\x73\x1a\xfe\xf9\x35\x39\x02\xb1\xa4\x8a\x05\x03\x44\x7a\x37\x32\x40\xb8\xa5\x99\x0a\x0e\x72\x6b\x34\x35")); -snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1005, 3800, $ssrc, "\x81\xa4\xbe\x6c\x33\x08\x43\x68\x08\x1a\xb7\xa4\xb7\x0e\x3f\x0c\xfb\x65\x00\xd1\xbd\xba\x98\x32\x39\x04\x92\xdb\x1d\x9e\xbe\xbc\x17\x3a\x3f\x65\x80\xed\x67\x83\xbf\xb5\x0a\x24\x30\x9d\x8b\x97\xd0\x8f\xb3\x93\x3c\x24\x0c\x88\xb7\x96\xc9\x8c\xb5\x17\x3a\x3a\x64\xb3\xb6\xed\x56\x80\x86\x0f\x25\x32\x87\xb9\xb7\x4d\x66\x98\xe3\x36\x3a\x0c\xb1\xba\x8e\x1d\x10\xea\x63\x33\x3f\x70\xb9\xbb\x9f\x0d\x05\xf1\x1f\x33\x36\x81\xa4\xbf\x67\x34\x06\xd5\x05\x31\x06\xb6\xa4\xb7\x0d\x33\x07\xc5\x1a\x0a\x5f\xbe\xa5\x9a\x30\x3d\x1f\xe0\x12\x06\x9a\xbb\xbf\x6b\x39\x32\x7b\x9d\x62\x14\x89\xbb\xb4\x0b\x25\x36\x97\x86\x5e\xd1\xb4\xbf\x9e\x3c\x24\x0d\x82\x8c")); -snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1006, 3960, $ssrc, "\xf0\xe2\xb7\xb1\x14\x3a\x3b\x61\xb6\x88\xf3\xeb\xb5\x8d\x09\x24\x32\x85\xbd\x89\x5c\xe2\x8c\x95\x30\x24\x0e\xb7\xb9\x83\x68\xc3\x85\x6e\x3f\x38\x7a\xbe\xb9\x92\x05\x7a\x95\x07\x3e\x30\x86\xa5\xbd\x7c\x0f\x15\xcb\x0d\x3d\x03\xb1\xa4\xb4\x01\x34\x11\x40\x0f\x36\x48\xb9\xa5\x85\x37\x36\x14\x45\x02\x0f\x84\xa5\xbe\x6d\x3c\x36\x7d\xf1\x04\x1c\xb5\xa5\xb7\x09\x3b\x35\xed\xea\x13\x57\xb0\xb8\x9b\x3d\x3a\x00\x84\x9e\x66\x97\xb2\xb2\x15\x3a\x38\x60\x8b\x87\x58\x98\xb0\x88\x08\x24\x32\x9e\xb1\x86\x54\x9a\xb7\x90\x32\x24\x0e\xb5\xb2\x84\x73\x9f\x8c\x68\x38\x3b\x61\xbc\xbd\x96\x14\x94\x99\x03\x3b\x32\x87\xba\xb3\x48\x1a\xf2\xe5\x0a\x39\x0c\xb1")); -snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1007, 4120, $ssrc, "\xa4\xb5\x1a\x02\x4c\x7f\x37\x32\x7c\xb9\xa5\x9a\x0a\x09\x7e\x6e\x34\x35\x87\xa5\xbe\x67\x33\x0b\x48\x6e\x08\x05\xb7\xa4\xb6\x0f\x3f\x0e\xfe\x79\x00\x5a\xbd\xa5\x85\x32\x39\x07\x92\xcd\x1d\x9d\xbe\xbc\x69\x3b\x3e\x60\x80\xef\x66\x80\xbf\xb5\x08\x24\x30\x90\x8b\x91\xd5\x8c\xb3\x9f\x3d\x24\x0e\x89\xb7\x91\xc2\x8c\xb5\x68\x3b\x3a\x6d\xb3\xb1\xee\x5c\x81\x81\x0c\x25\x3d\x85\xb9\xb7\x58\x60\x99\xef\x37\x3a\x0e\xb6\xba\x89\x12\x13\xeb\x67\x33\x3e\x67\xb9\xba\x98\x02\x05\xf7\x1d\x33\x36\x87\xa4\xbe\x7c\x34\x01\x54\x1a\x31\x01\xb6\xa4\xb6\x03\x33\x06\xda\x18\x0a\x75\xbf\xa5\x84\x31\x3d\x19\xe0\x10\x01\x99\xbb\xbe\x62\x39\x3d\x66\x9d\x60\x17")); -# pause -snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1008, 4280, $ssrc, "\xd5" x 160)); -snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1009, 4440, $ssrc, "\xd5" x 160)); -snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1010, 4600, $ssrc, "\xd5" x 160)); -snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1011, 4760, $ssrc, "\xd5" x 160)); -snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1012, 4920, $ssrc, "\xd5" x 160)); -snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(8, 1013, 5080, $ssrc, "\x2a" x 160)); - - - - -snd($sock_b, $port_a, rtp(8, 4000, 8000, 0x6543, "\x2a" x 160)); -($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); -snd($sock_b, $port_a, rtp(8, 4001, 8160, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); - -$resp = rtpe_req('play DTMF', 'inject DTMF towards A', - { 'from-tag' => tt(), code => '4', volume => 3, duration => 150 }); - -snd($sock_b, $port_a, rtp(8, 4002, 8320, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4002, 8320, $ssrc, "\xff\x90\x8a\x93\xd9\x1b\x18\x27\x65\xe5\x33\x29\x4c\x9e\x8f\x91\xb8\x15\x09\x0d\x32\x98\x8e\x96\xbb\x2c\x2b\x4c\xd8\x34\x1c\x18\x2e\x9d\x8c\x8c\xa5\x1a\x0b\x0d\x27\xa3\x97\x9e\xbd\x4f\xc4\xaa\xb2\x2c\x12\x0e\x1e\xa1\x8b\x8a\x9c\x25\x0e\x10\x25\xb7\xa7\xb7\x5e\xcb\xa2\x98\x9f\x30\x0f\x0a\x16\xae\x8d\x8a\x98\x3a\x18\x19\x2c\xdd\xfd\x30\x2b\xce\x99\x8e\x95\x4c\x0f\x09\x10\xdf\x93\x8e\x9a\xec\x28\x2c\x56\xee\x2d\x1a\x1a\x48\x97\x8b\x8e\xba\x14\x0a\x0f\x39\x9d\x96\xa1\xcd\x4e\xbe\xab\xbe\x23\x10\x10\x2b\x99\x8a\x8c\xa7\x1b\x0d\x12\x2f\xad\xa7\xbc\x5e\xbd\x9f\x99\xa8\x23\x0d\x0b\x1d\x9f\x8b\x8c\x9f\x29\x16\x1b\x34\xcd\x60\x2f\x2f\xb6\x96")); -snd($sock_b, $port_a, rtp(8, 4003, 8480, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4003, 8480, $ssrc, "\x8e\x9b\x2b\x0c\x09\x17\xae\x8f\x8e\x9e\x3f\x25\x2e\x65\x5c\x28\x1a\x1e\xc2\x92\x8a\x92\x44\x0f\x0a\x14\xd6\x99\x97\xa6\x7c\x4e\xba\xad\xe5\x1d\x0f\x13\x49\x92\x89\x8e\xbe\x15\x0d\x16\x43\xa8\xa7\xc1\x66\xb5\x9d\x9a\xb6\x1b\x0c\x0d\x2b\x98\x8a\x8d\xab\x1f\x15\x1d\x3f\xc7\x52\x2e\x39\xaa\x93\x8f\xa3\x1e\x0b\x0b\x1e\x9f\x8d\x8f\xa7\x30\x23\x31\x7c\x4a\x24\x1a\x24\xac\x8e\x8b\x99\x28\x0c\x0a\x1a\xb0\x96\x98\xac\x4f\x53\xb7\xaf\x44\x19\x0f\x18\xba\x8e\x89\x93\x3f\x10\x0d\x1a\xd5\xa3\xa8\xca\xf9\xae\x9c\x9d\xec\x16\x0b\x10\x4e\x91\x89\x90\xc6\x1a\x14\x20\x55\xc3\x4a\x2f\x49\xa2\x91\x92\xb2\x17\x09\x0c\x2d\x99\x8d\x92\xb3\x29\x23\x36\xf2")); -snd($sock_b, $port_a, rtp(8, 4004, 8640, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4004, 8640, $ssrc, "\x3e\x20\x1b\x2d\xa0\x8d\x8c\xa1\x1c\x0a\x0c\x22\xa3\x94\x9a\xb5\x44\x5c\xb5\xb6\x32\x16\x0f\x1e\xa6\x8c\x8a\x99\x28\x0e\x0e\x20\xb7\xa1\xab\xd4\xdb\xaa\x9c\xa1\x38\x11\x0b\x15\xb5\x8d\x8a\x96\x3f\x16\x15\x26\xdd\xc2\x43\x31\xdf\x9d\x90\x96\x6d\x11\x09\x0f\x5a\x93\x8c\x97\xd2\x23\x23\x3b\xf6\x37\x1f\x1d\x40\x9a\x8c\x8e\xb2\x15\x09\x0e\x31\x9c\x93\x9c\xc2\x3e\x74\xb4\xbf\x29\x14\x11\x29\x9b\x8a\x8b\xa3\x1c\x0d\x0f\x2a\xab\x9f\xad\xe0\xcc\xa6\x9c\xa9\x28\x0e\x0c\x1c\xa2\x8b\x8b\x9c\x2a\x14\x17\x2c\xc6\xc4\x3e\x36\xbd\x99\x90\x9b\x30\x0d\x09\x15\xb3\x8f\x8d\x9b\x42\x1f\x25\x42\x70\x30\x1e\x1f\xcf\x95\x8b\x92\x58\x0f\x09\x12\x6f\x98\x93")); -snd($sock_b, $port_a, rtp(8, 4005, 8800, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4005, 8800, $ssrc, "\x9f\xe5\x3b\xe2\xb5\xd9\x21\x12\x14\x3e\x95\x89\x8d\xb6\x16\x0c\x13\x3a\xa4\x9f\xb1\xf1\xc0\xa3\x9d\xb4\x1e\x0d\x0d\x27\x99\x8a\x8c\xa7\x1f\x12\x19\x37\xbc\xc8\x3c\x3c\xaf\x97\x91\xa2\x21\x0b\x0a\x1c\xa2\x8d\x8e\xa2\x2f\x1e\x28\x4c\x5d\x2c\x1e\x25\xb0\x90\x8c\x98\x2c\x0c\x0a\x18\xb4\x94\x94\xa6\x4d\x3a\xd4\xb8\x4f\x1d\x11\x18\xc5\x8f\x89\x91\x4d\x10\x0c\x17\xec\x9f\xa0\xb8\xff\xba\xa1\x9f\xd3\x19\x0c\x0f\x3f\x92\x89\x8f\xbb\x19\x11\x1c\x48\xb8\xce\x3b\x4a\xa8\x95\x93\xaf\x19\x0a\x0c\x29\x99\x8c\x8f\xad\x27\x1d\x2b\x59\x4f\x29\x1e\x2d\xa5\x8e\x8d\x9f\x1e\x0b\x0b\x1e\xa4\x91\x96\xad\x3e\x3b\xcc\xbc\x3a\x1a\x12\x1e\xaa\x8d\x8a\x98\x2b")); -snd($sock_b, $port_a, rtp(8, 4006, 8960, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4006, 8960, $ssrc, "\x0e\x0c\x1d\xb8\x9d\xa2\xbe\xf9\xb4\xa0\xa3\x3f\x14\x0c\x14\xbd\x8e\x89\x93\x49\x15\x12\x1f\xe7\xb5\xd9\x3c\x7c\xa1\x93\x97\xd5\x13\x09\x0e\x45\x93\x8b\x93\xc4\x20\x1d\x2e\x6b\x46\x26\x1f\x3d\x9d\x8d\x8e\xae\x17\x09\x0d\x2c\x9c\x90\x98\xba\x36\x3d\xc7\xc4\x2e\x17\x13\x27\x9e\x8b\x8b\x9f\x1e\x0c\x0e\x25\xaa\x9c\xa5\xc8\xe8\xae\xa0\xaa\x2d\x10\x0c\x1b\xa6\x8c\x8a\x9a\x2c\x12\x13\x27\xc3\xb3\xed\x3e\xc8\x9d\x93\x9b\x38\x0f\x09\x13\xba\x8f\x8b\x98\x4a\x1d\x1e\x34\xf9\x3e\x24\x23\xea\x98\x8c\x92\xdf\x10\x09\x0f\x4d\x97\x90\x9c\xd2\x31\x3f\xc5\xd6\x28\x16\x16\x39\x97\x8a\x8d\xaf\x17\x0b\x10\x32\xa2\x9b\xa8\xd6\xd9\xac\xa1\xb3\x22\x0e\x0e")); -snd($sock_b, $port_a, rtp(8, 4007, 9120, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4007, 9120, $ssrc, "\x24\x9b\x8a\x8b\xa2\x1f\x10\x15\x2f\xb8\xb4\x68\x43\xb8\x9a\x94\xa1\x25\x0c\x0a\x1a\xa5\x8d\x8c\x9e\x30\x1b\x1f\x3c\xee\x38\x23\x28\xb8\x93\x8d\x97\x31\x0d\x09\x15\xb9\x93\x90\xa0\x4f\x2f\x46\xc4\x5e\x21\x15\x19\xd7\x91\x89\x90\x7b\x10\x0b\x14\x5b\x9d\x9c\xad\xed\xcd\xa9\xa3\xca\x1c\x0d\x10\x38\x94\x89\x8e\xb3\x19\x0f\x18\x3e\xb0\xb5\x59\x4d\xae\x98\x95\xad\x1c\x0b\x0c\x25\x9b\x8b\x8e\xa9\x26\x1a\x22\x46\xf5\x33\x23\x2e\xaa\x90\x8d\x9e\x21\x0b\x0a\x1c\xa6\x90\x92\xa8\x3b\x2e\x4d\xc7\x43\x1e\x15\x1e\xaf\x8e\x8a\x96\x2e\x0e\x0b\x1a\xbb\x9b\x9d\xb2\x68\xc5\xa8\xa7\x4c\x17\x0d\x14\xcb\x8f\x89\x91\x5e\x14\x0f\x1c\x6e\xad\xb8\x52\x68\xa8")); -snd($sock_b, $port_a, rtp(8, 4008, 9280, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4008, 9280, $ssrc, "\x97\x98\xc7\x16\x0a\x0e\x3a\x94\x8a\x90\xbb\x1e\x1a\x27\x56\x6f\x2f\x25\x3b\xa0\x8e\x8f\xaa\x19\x09\x0c\x28\x9c\x8f\x95\xb2\x31\x2e\x59\xcc\x37\x1b\x16\x26\xa1\x8c\x8b\x9d\x1f\x0c\x0c\x20\xab\x99\x9e\xbb\x5d\xbe\xa7\xac\x32\x13\x0d\x1a\xab\x8c\x89\x97\x2e\x10\x10\x21\xc3\xab\xbc\x4f\xd4\xa2\x96\x9c\x3f\x10\x0a\x12\xc4\x8f\x8a\x95\x57\x1b\x1a\x2b\xfd\x5d\x2d\x27\x62\x9b\x8e\x92\xc9\x12\x09\x0e\x3f\x97\x8e\x98\xc6\x2c\x2f\x6b\xd9\x2e\x1a\x18\x34\x9a\x8b\x8d\xab\x18\x0a\x0e\x2d\xa1\x98\xa1\xc7\x5b\xb9\xa7\xb4\x27\x10\x0e\x22\x9d\x8a\x8b\x9f\x20\x0e\x12\x2a\xb4\xaa\xc0\x50\xc0\x9e\x97\xa1\x2a\x0e\x0a\x19\xa8\x8c\x8b\x9b\x31\x18\x1b\x31")); -snd($sock_b, $port_a, rtp(8, 4009, 9440, 0x6543, "\x2a" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4009, 9440, $ssrc, "\xda\x50\x2c\x2b\xc0\x97\x8e\x97\x39\x0e\x09\x13\xbf\x92\x8e\x9c\x57\x29\x31\xef\x72\x28\x19\x1b\x6d\x94\x8a\x8f\xce\x11\x0a\x11\x48\x9c\x98\xa5\xdc\x5e\xb5\xa9\xc6\x1f\x0f\x10\x31\x96\x89\x8d\xad\x19\x0e\x15\x37\xac\xaa\xc8\x57\xb7\x9c\x98\xac\x1e\x0c\x0c\x21\x9c\x8b\x8d\xa4\x25\x17\x1d\x3b\xcf\x48\x2b\x30\xae\x93\x8e" . "\xff" x 80)); -# pause -snd($sock_b, $port_a, rtp(0, 4010, 9600, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4010, 9600, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4011, 9760, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4011, 9760, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4012, 9920, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4012, 9920, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4013, 10080, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4013, 10080, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4014, 10240, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4014, 10240, $ssrc, "\xff" x 80 . "\x00" x 80)); - - - - -# multiple consecutive DTMF events - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 6024)], [qw(198.51.100.3 6026)]); - -($port_a) = offer('multiple consecutive DTMF events', - { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => 'C', volume => 5, duration => 100 }); -$resp = rtpe_req('play DTMF', 'inject DTMF towards B', - { 'from-tag' => ft(), code => '4', volume => 5, duration => 100 }); - -snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1002, 3320, $ssrc, "\xff\x93\x94\xbc\x2e\x56\xbf\x2b\x13\x1b\xa7\x8e\x98\x47\x25\x41\xe2\x24\x16\x2b\x99\x8e\x9f\x28\x1e\x3d\x5b\x23\x1c\xdf\x92\x8f\xb6\x1c\x1c\x40\x5d\x26\x25\xaa\x8f\x95\x3b\x15\x1d\x5e\xde\x2c\x38\x9d\x8f\x9e\x1f\x11\x20\xc0\xc1\x37\xdd\x99\x92\xb7\x15\x10\x2c\xac\xb5\x49\xb8\x97\x99\x37\x0f\x13\x58\xa0\xae\x67\xae\x99\xa4\x1f\x0d\x1a\xae\x9b\xad\x7b\xad\x9d\xbf\x16\x0e\x27\x9d\x98\xb0\x55\xb1\xa6\x3a\x11\x11\x63\x95\x98\xbf\x3e\xbb\xb4\x26\x10\x1a\xa9\x90\x9a\x4e\x30\xce\xd4\x1e\x12\x29\x99\x8e\xa1\x2d\x29\x6d\x4b\x1c\x18\xef\x91\x8f\xb6\x1f\x24\x57\x3e\x1d\x20\xa9\x8e\x95\x3e\x19\x23\x67\x3e\x21\x31\x9c\x8e\x9e\x22\x14\x26\xcd\x4a")); -snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1003, 3480, $ssrc, "\x2a\xdf\x96\x90\xb5\x17\x13\x2f\xb6\xf5\x36\xb1\x93\x96\x39\x10\x15\x55\xaa\xc8\x4c\xa7\x95\xa0\x1f\x0e\x1b\xb4\xa1\xbd\xed\xa4\x99\xbb\x15\x0e\x27\xa0\x9d\xbd\xda\xa4\x9f\x39\x10\x11\x58\x98\x9c\xc8\xf9\xa9\xac\x23\x0e\x19\xab\x92\x9e\x59\x4c\xb0\xca\x1b\x10\x27\x9a\x90\xa5\x35\x3a\xbe\x43\x18\x15\x6c\x92\x91\xb7\x26\x30\xd6\x32\x18\x1d\xa9\x8e\x96\x44\x1d\x2d\xfc\x2e\x1b\x2d\x9a\x8d\x9e\x25\x19\x2d\xe7\x2f\x20\xea\x94\x8f\xb3\x19\x17\x36\xc8\x36\x2c\xae\x90\x95\x3b\x12\x18\x55\xb7\x43\x3e\xa1\x91\x9e\x1f\x0f\x1d\xba\xac\x64\xe8\x9d\x95\xb7\x15\x0e\x29\xa6\xa6\xda\xc3\x9d\x9b\x39\x0f\x11\x51\x9c\xa2\xd8\xbe\x9f\xa7\x21\x0e\x18\xad")); -snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1004, 3640, $ssrc, "\x96\xa3\x68\xc4\xa5\xc2\x19\x0e\x26\x9c\x93\xa9\x3f\xdb\xae\x3e\x14\x12\x5b\x93\x93\xb9\x2e\x51\xbe\x2c\x14\x1b\xa9\x8f\x97\x4c\x25\x3f\xde\x25\x16\x2a\x9a\x8e\x9e\x29\x1e\x3b\x5e\x24\x1b\x7b\x92\x8f\xb2\x1c\x1c\x3e\x61\x27\x25\xac\x8f\x94\x3e\x15\x1c\x59\xdb\x2d\x37\x9e\x8f\x9d\x20\x11\x1f\xc2\xbf\x38\xea\x99\x92\xb4\x16\x10\x2b\xad\xb4\x49\xba\x98\x98\x3a\x0f\x12\x4e\xa1\xad\x68\xaf\x99\xa3\x20\x0d\x19\xb0\x9b\xac\x7b\xae\x9d\xbc\x17\x0e\x25\x9e\x98\xaf\x55\xb2\xa6\x3d\x12\x11\x52\x96\x97\xbd\x3e\xbc\xb3\x28\x10\x19\xab\x90\x9a\x54\x2f\xd0\xcf\x1f\x12\x27\x9a\x8e\xa0\x2e\x28\x66\x4e\x1d\x18\x62\x92\x8f\xb2\x20\x23\x53\x3f\x1d\x1f")); -snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1005, 3800, $ssrc, "\xab\x8e\x94\x44\x19\x22\x61\x40\x21\x2f\x9c\x8e\x9d\x23\x14\x25\xce\x4d\x2a\xf7\x96\x8f\xb1\x18\x13\x2e\xb7\xe8\x36\xb3\x94\x96\x3c\x10\x15\x4d\xaa\xc5\x4b\xa8\x95\x9f\x20\x0e\x1a\xb6\xa0\xbc\xf5\xa4\x99\xb8\x16\x0e\x26\xa1\x9d\xbb\xdd\xa5\x9f\x3c\x10\x10\x4c\x99\x9b\xc5\x78\xaa\xac\x24\x0f\x18\xac\x93\x9d\x5f\x4a\xb1\xc7\x1c\x0f\x25\x9b\x90\xa3\x36\x39\xbf\x47\x18\x14\x56\x92\x90\xb4\x27\x2f\xd7\x34\x18\x1c\xab\x8e\x95\x4b\x1d\x2c\xfe\x2f\x1b\x2c\x9b\x8d\x9d\x27\x19\x2c\xe7\x30\x20\x6d\x94\x8f\xaf\x1a\x17\x34\xc8\x37\x2b\xaf\x91\x94\x3f\x12\x18\x4e\xb6\x45\x3d\xa3\x91\x9e\x20\x0f\x1c\xbc\xab\x6c\xf5\x9e\x95\xb3\x16\x0e\x27\xa7\xa5")); -snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1006, 3960, $ssrc, "\xd6\xc6\x9d\x9b\x3d\x0f\x11\x49\x9c\xa1\xd4\xbf\x9f\xa6\x22\x0e\x18\xaf\x96\xa2\x6e\xc6\xa5\xbe\x19\x0e\x24\x9d\x93\xa8\x40\xe1\xae\x42\x15\x12\x4e\x94\x93\xb7\x2e\x4e\xbe\x2d\x14\x1a\xab\x8f\x97\x52\x25\x3e\xdc\x26\x16\x28\x9b\x8e\x9e\x2b\x1e\x3a\x61\x25\x1b\x5d\x93\x8f\xaf\x1d\x1c\x3d\x67\x27\x24\xad\x8f\x93\x45\x15\x1c\x53\xd7\x2d\x35\x9f\x8f\x9c\x22\x11\x1f\xc5\xbe\x38\x7a\x9a\x91\xb0\x17\x10\x29\xad\xb3\x4a\xbc\x98\x98\x3e\x10\x12\x48\xa1\xad\x6a\xb1\x9a\xa1\x21\x0e\x18\xb3\x9b\xab\x7d\xaf\x9d\xb9\x18\x0e\x23\x9f\x97\xae\x55\xb4\xa5\x40\x12\x10\x49\x96\x97\xbb\x3d\xbd\xb2\x29\x10\x18\xac\x90\x99\x5d\x2f\xd4\xcd\x1f\x12\x25\x9b")); -# pause -snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1007, 4120, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1008, 4280, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1009, 4440, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1010, 4600, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1011, 4760, $ssrc, "\xff" x 160)); -# next event -snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1012, 4920, $ssrc, "\xff\x96\x8e\x99\xdd\x1f\x1d\x2c\x69\xe9\x39\x2d\x50\xa3\x95\x97\xbd\x1a\x0e\x12\x38\x9d\x93\x9b\xbf\x30\x2f\x4f\xdc\x39\x20\x1d\x33\xa2\x90\x91\xaa\x1f\x0f\x12\x2c\xa9\x9c\xa3\xc2\x55\xc9\xaf\xb8\x30\x18\x14\x24\xa7\x8f\x8e\xa0\x2a\x14\x16\x2a\xbc\xac\xbc\x61\xcf\xa8\x9d\xa6\x36\x15\x0f\x1b\xb4\x92\x8f\x9d\x3e\x1d\x1e\x31\xe0\xfe\x36\x30\xd3\x9e\x94\x9b\x50\x15\x0d\x17\xe3\x99\x93\x9e\xee\x2c\x30\x5b\xf0\x32\x1f\x1f\x4c\x9c\x8f\x94\xbe\x19\x0e\x15\x3d\xa2\x9b\xa7\xd2\x52\xc3\xaf\xc4\x29\x16\x16\x2f\x9e\x8e\x90\xac\x20\x13\x18\x34\xb2\xac\xc0\x61\xc2\xa5\x9d\xad\x29\x12\x10\x23\xa5\x8f\x90\xa5\x2d\x1b\x1f\x39\xd1\x65\x34\x36\xbb\x9b")); -snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1013, 5080, $ssrc, "\x94\x9f\x2f\x11\x0e\x1c\xb3\x95\x94\xa4\x45\x2a\x33\x69\x60\x2d\x1e\x23\xc7\x98\x8f\x98\x49\x15\x0e\x1a\xda\x9d\x9c\xab\x7d\x53\xbe\xb1\xe8\x22\x15\x19\x4d\x98\x8d\x94\xc3\x1b\x12\x1b\x48\xac\xac\xc7\x69\xba\xa2\x9f\xbb\x1f\x10\x12\x2f\x9c\x8e\x93\xb0\x25\x1a\x22\x44\xcb\x57\x34\x3d\xae\x99\x96\xa9\x23\x0f\x0f\x24\xa6\x93\x96\xac\x36\x29\x37\x7c\x4e\x29\x1e\x29\xb0\x94\x8f\x9e\x2d\x11\x0f\x1f\xb6\x9b\x9d\xb0\x55\x58\xbc\xb5\x49\x1e\x15\x1d\xbe\x94\x8e\x99\x45\x17\x12\x1f\xd9\xa9\xad\xce\xfa\xb3\xa0\xa2\xef\x1b\x0f\x16\x52\x97\x8e\x96\xcb\x1e\x1a\x26\x59\xc8\x4e\x35\x4d\xa8\x97\x98\xb8\x1c\x0e\x11\x31\x9d\x91\x98\xb9\x2d\x29\x3b\xf5")); -snd($sock_a, $port_b, rtp(0, 1014, 5240, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1014, 5240, $ssrc, "\x43\x27\x1f\x32\xa6\x92\x91\xa7\x21\x0f\x10\x28\xa9\x99\x9e\xba\x49\x60\xba\xbb\x38\x1b\x16\x23\xab\x90\x8e\x9e\x2d\x14\x13\x26\xbc\xa7\xaf\xd8\xde\xae\xa0\xa7\x3d\x17\x0f\x1a\xba\x93\x8e\x9b\x44\x1b\x1b\x2b\xe0\xc8\x48\x37\xe4\xa2\x96\x9b\x6f\x17\x0e\x15\x5d\x99\x91\x9c\xd7\x29\x29\x3f\xf8\x3c\x24\x21\x46\x9e\x90\x94\xb8\x1a\x0e\x14\x37\xa1\x99\xa1\xc8\x43\x76\xba\xc5\x2d\x19\x17\x2d\xa0\x8f\x8f\xa8\x21\x11\x16\x2e\xaf\xa6\xb2\xe5\xcf\xab\xa0\xad\x2d\x14\x10\x20\xa8\x90\x8f\xa1\x2e\x19\x1c\x31\xcb\xc9\x44\x3b\xc2\x9e\x96\x9f\x36\x13\x0e\x1a\xb8\x95\x92\xa0\x48\x26\x2a\x48\x73\x36\x23\x25\xd4\x9a\x90\x98\x5c\x15\x0e\x18\x72\x9c\x99")); -snd($sock_a, $port_b, rtp(0, 1015, 5400, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1015, 5400, $ssrc, "\xa6\xe8\x3f\xe7\xba\xdd\x27\x18\x1a\x43\x9a\x8e\x93\xbb\x1b\x10\x19\x3e\xaa\xa5\xb7\xf4\xc6\xa9\xa2\xba\x23\x12\x12\x2c\x9e\x8e\x91\xac\x25\x18\x1e\x3c\xc1\xcd\x41\x40\xb5\x9c\x97\xa8\x27\x10\x0f\x21\xa8\x92\x93\xa8\x35\x24\x2c\x50\x61\x30\x23\x2b\xb7\x97\x90\x9d\x31\x11\x0e\x1c\xb9\x9a\x9a\xab\x52\x3f\xd9\xbc\x54\x22\x18\x1d\xca\x96\x8e\x97\x52\x17\x10\x1c\xef\xa5\xa6\xbc\xff\xbe\xa7\xa5\xd8\x1d\x10\x16\x45\x98\x8e\x95\xbf\x1e\x17\x20\x4d\xbc\xd2\x3f\x4e\xad\x9a\x99\xb4\x1e\x0e\x10\x2d\x9e\x90\x96\xb2\x2c\x22\x2f\x5c\x54\x2d\x24\x32\xaa\x94\x91\xa5\x24\x0f\x0f\x24\xaa\x98\x9b\xb2\x43\x3f\xcf\xc0\x3e\x1e\x18\x23\xaf\x92\x8e\x9c\x2f")); -snd($sock_a, $port_b, rtp(0, 1016, 5560, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1016, 5560, $ssrc, "\x13\x11\x21\xbd\xa2\xa8\xc3\xfa\xb9\xa6\xa9\x45\x19\x10\x1a\xc2\x94\x8e\x99\x4e\x1a\x18\x26\xeb\xba\xdd\x40\x7d\xa7\x99\x9c\xda\x19\x0e\x14\x4a\x99\x90\x99\xc9\x26\x23\x34\x6d\x4b\x2b\x25\x41\xa1\x92\x94\xb3\x1c\x0e\x12\x30\xa0\x96\x9d\xbe\x3b\x41\xcc\xc9\x34\x1c\x19\x2c\xa3\x8f\x8f\xa5\x23\x10\x13\x2a\xaf\xa0\xaa\xcd\xeb\xb4\xa6\xae\x31\x16\x11\x1f\xab\x90\x8e\x9e\x30\x18\x19\x2c\xc8\xb9\xf0\x43\xcc\xa2\x99\x9f\x3c\x14\x0e\x19\xbe\x95\x90\x9d\x4e\x22\x24\x3a\xfa\x43\x2a\x28\xec\x9d\x91\x98\xe4\x16\x0d\x16\x51\x9c\x96\xa0\xd7\x37\x45\xca\xda\x2c\x1b\x1b\x3d\x9c\x8e\x92\xb4\x1c\x0f\x16\x38\xa8\xa0\xad\xda\xdd\xb0\xa7\xb9\x28\x14\x13")); -# pause -snd($sock_a, $port_b, rtp(0, 1017, 5720, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1017, 5720, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1018, 5880, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1018, 5880, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1019, 6040, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1019, 6040, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1020, 6200, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1020, 6200, $ssrc, "\xff" x 160)); -snd($sock_a, $port_b, rtp(0, 1021, 6360, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1021, 6360, $ssrc, "\xff" x 160)); -# resume -snd($sock_a, $port_b, rtp(0, 1022, 6520, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1022, 6520, $ssrc, "\x00" x 160)); - - - - -# RFC payload type present - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 6010)], [qw(198.51.100.3 6012)]); - -($port_a) = offer('multi- no transcoding, RFC payload type present', - { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => '0', volume => 10, duration => 100 }); -$resp = rtpe_req('play DTMF', 'inject DTMF towards B', - { 'from-tag' => ft(), code => '1', volume => 6, duration => 100 }); - -snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96 | 0x80, 1002, 3320, $ssrc, "\x00\x0a\x00\xa0")); -snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1003, 3320, $ssrc, "\x00\x0a\x01\x40")); -snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1004, 3320, $ssrc, "\x00\x0a\x01\xe0")); -snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1005, 3320, $ssrc, "\x00\x0a\x02\x80")); -snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1006, 3320, $ssrc, "\x00\x0a\x03\x20")); -snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1007, 3320, $ssrc, "\x00\x8a\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1008, 3320, $ssrc, "\x00\x8a\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1009, 3320, $ssrc, "\x00\x8a\x03\xc0")); -snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1010, 4280, $ssrc, "\x00" x 160)); -snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1011, 4440, $ssrc, "\x00" x 160)); -snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1012, 4600, $ssrc, "\x00" x 160)); -snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1013, 4760, $ssrc, "\x00" x 160)); -snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96 | 0x80, 1014, 4920, $ssrc, "\x01\x06\x00\xa0")); -snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1015, 4920, $ssrc, "\x01\x06\x01\x40")); -snd($sock_a, $port_b, rtp(0, 1014, 5240, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1016, 4920, $ssrc, "\x01\x06\x01\xe0")); -snd($sock_a, $port_b, rtp(0, 1015, 5400, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1017, 4920, $ssrc, "\x01\x06\x02\x80")); -snd($sock_a, $port_b, rtp(0, 1016, 5560, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1018, 4920, $ssrc, "\x01\x06\x03\x20")); -snd($sock_a, $port_b, rtp(0, 1017, 5720, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(96, 1019, 4920, $ssrc, "\x01\x86\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1020, 4920, $ssrc, "\x01\x86\x03\xc0")); -rcv($sock_b, $port_a, rtpm(96, 1021, 4920, $ssrc, "\x01\x86\x03\xc0")); -snd($sock_a, $port_b, rtp(0, 1018, 5880, 0x1234, "\x00" x 160)); -rcv($sock_b, $port_a, rtpm(0, 1022, 5880, $ssrc, "\x00" x 160)); - - - - - -# SDP in/out tests, various ICE options - -new_call; - -offer('plain SDP, no ICE', { ICE => 'remove' }, < 'remove' }, < 'default' }, < 'default' }, < 'optional' }, < 'remove' }, < 'remove' }, < 'optional' }, < '3.4.5.6', - flags => ['full-rtcp-attribute'], ICE => 'optional', }, < 'optional' }, < 'optional' }, < 'force' }, < 'optional' }, < 'force' }, < 'optional' }, < 'force' }, < 'force' }, < 'default' }, < 'default' }, < 'default' }, < 'force' }, < 'force' }, < 'remove' }, < 'remove', replace => [qw(origin session-connection)], - flags => [qw(codec-mask-opus codec-mask-G722 codec-strip-G7221)] }, < 'remove', replace => [qw(origin session-connection)] }, < 'remove', replace => [qw(origin session-connection)], - flags => [qw(codec-mask-opus codec-mask-G722 codec-strip-G7221 always-transcode)] }, < 'remove', replace => [qw(origin session-connection)] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], - codec => { transcode => ['PCMA'] }}, < ['origin'] }, < 'remove', replace => ['origin'], - codec => { transcode => ['PCMA'] }}, < ['origin'] }, < 'remove', replace => ['origin'], - codec => { transcode => ['pcma'] }}, < ['origin'] }, < 'remove', replace => ['origin'] }, < ft(), blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -(undef, $seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); -rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); -rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); -rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); - - - - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 2020)], [qw(198.51.100.3 2022)]); - -offer('media playback, side A', { ICE => 'remove', replace => ['origin'] }, < ['origin'] }, < ft(), blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -(undef, $seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); -rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); -rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); -rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); - - - - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 2030)], [qw(198.51.100.3 2032)]); - -offer('media playback, side B', { ICE => 'remove', replace => ['origin'] }, < ['origin'] }, < tt(), blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -(undef, $seq, $ts, $ssrc) = rcv($sock_b, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -rcv($sock_b, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); -rcv($sock_b, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); -rcv($sock_b, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); -rcv($sock_b, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); - -$resp = rtpe_req('play media', 'restart media playback', { 'from-tag' => tt(), blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -$ts += 160 * 5; -my $old_ts = $ts; -(undef, $ts) = rcv($sock_b, -1, rtpm(8 | 0x80, $seq + 5, -1, $ssrc, $pcma_1)); -print("ts $ts old $old_ts\n"); -SKIP: { - skip 'random timestamp too close to margin', 2 if $old_ts < 500 or $old_ts > 4294966795; - cmp_ok($ts, '<', $old_ts + 500, 'ts within < range'); - cmp_ok($ts, '>', $old_ts - 500, 'ts within > range'); -} -rcv($sock_b, -1, rtpm(8, $seq + 6, $ts + 160 * 1, $ssrc, $pcma_2)); -rcv($sock_b, -1, rtpm(8, $seq + 7, $ts + 160 * 2, $ssrc, $pcma_3)); -rcv($sock_b, -1, rtpm(8, $seq + 8, $ts + 160 * 3, $ssrc, $pcma_4)); -rcv($sock_b, -1, rtpm(8, $seq + 9, $ts + 160 * 4, $ssrc, $pcma_5)); - - - - -($sock_a, $sock_b) = new_call([qw(198.51.100.9 2020)], [qw(198.51.100.9 2022)]); - -offer('media playback, side A, select by label', { ICE => 'remove', replace => ['origin'], - label => 'foobar' }, < ['origin'], label => 'blah' }, < 'foobar', - blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -(undef, $seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); -rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); -rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); -rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); - - - - -($sock_a, $sock_b) = new_call([qw(198.51.100.9 2030)], [qw(198.51.100.9 2032)]); - -offer('media playback, side B, select by label', { ICE => 'remove', replace => ['origin'], - label => 'quux' }, < ['origin'], label => 'meh' }, < 'meh', blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -(undef, $seq, $ts, $ssrc) = rcv($sock_b, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -rcv($sock_b, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); -rcv($sock_b, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); -rcv($sock_b, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); -rcv($sock_b, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); - - - - - -# gh #730 - -($sock_a, $sock_b) = new_call([qw(198.51.100.1 7300)], [qw(198.51.100.3 7302)]); - -($port_a) = offer('gh 730', { - ICE => 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin', 'session-connection'], - flags => [ "loop-protect", "asymmetric" ] }, < 'remove', replace => ['origin', 'session-connection'], - flags => [ "loop-protect", "asymmetric" ] }, < tt(), - ICE => 'remove', replace => ['origin', 'session-connection'], - flags => [ "loop-protect", "asymmetric" ] }, < 'remove', replace => ['origin', 'session-connection'], - flags => [ "loop-protect", "asymmetric" ] }, < 'remove', replace => ['origin'], - codec => { transcode => ['telephone-event'] }}, < ['origin'] }, < 'remove', replace => ['origin'], - codec => { transcode => ['PCMA', 'telephone-event'] }}, < ['origin'] }, < 'remove', replace => ['origin'], - codec => { transcode => ['telephone-event'] }}, < ['origin'] }, < 'remove', replace => ['origin'], - codec => { transcode => ['telephone-event'] } }, < 'remove', replace => ['origin'], - codec => { transcode => ['opus'] } }, < 'remove', replace => ['origin'], - codec => { strip => ['PCMA'] } }, < 'remove', replace => ['origin'], - codec => { transcode => ['opus'], mask => ['PCMA'] } }, < 'remove', replace => ['origin'], - flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin'], - flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin'], - codec => { transcode => ['PCMA', 'telephone-event/8000'] }, - flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin', 'session-connection'], - flags => ['strict-source'], - ptime => 20, 'ptime-reverse' => 60, 'rtcp-mux' => ['demux'], - }, < 'remove', 'via-branch' => 'x123', 'rtcp-mux' => ['demux'] }, < 'remove', 'via-branch' => 'x456', 'rtcp-mux' => ['offer'] }, < 'remove', 'via-branch' => 'x123' }, < 'remove', 'via-branch' => 'x123', 'rtcp-mux' => ['demux'] }, < 'remove', 'via-branch' => 'x456', 'rtcp-mux' => ['offer'] }, < 'remove', 'via-branch' => 'x456' }, < 'remove', replace => ['origin'], - 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx', flags => ['strict-source', 'record-call'], - 'transport-protocol' => 'RTP/AVP' }, < ['origin'], 'transport-protocol' => 'RTP/AVP', - 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx' }, < ft() }); - -# new to-tag -new_tt(); - -offer('media playback after delete', { ICE => 'remove', replace => ['origin'], - 'transport-protocol' => 'transparent', flags => ['strict-source', 'record-call'], - 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx' }, < ['origin'], 'transport-protocol' => 'RTP/AVP', - 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx' }, < tt(), 'to-tag' => tt(), - blob => $wav_file }); -is $resp->{duration}, 100, 'media duration'; - -(undef, $seq, $ts, $ssrc) = rcv($sock_b, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); -rcv($sock_b, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); -rcv($sock_b, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); -rcv($sock_b, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); -rcv($sock_b, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); - - - -# GH 1042 - -$resp = rtpe_req('statistics', 'statistics'); - - -done_testing(); diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index b02bf3635..2de39a290 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -165,7 +165,7 @@ t=0 0 m=audio PORT RTP/AVP 0 102 8 c=IN IP4 203.0.113.1 a=rtpmap:0 PCMU/8000 -a=rtpmap:102 opus/48000/1 +a=rtpmap:102 opus/48000 a=rtpmap:8 PCMA/8000 a=sendrecv a=rtcp:PORT @@ -1708,10 +1708,9 @@ v=0 o=- 1545997027 1 IN IP4 198.51.101.40 s=tester t=0 0 -m=audio PORT RTP/AVP 0 8 +m=audio PORT RTP/AVP 0 c=IN IP4 203.0.113.1 a=rtpmap:0 PCMU/8000 -a=rtpmap:8 PCMA/8000 a=sendrecv a=rtcp:PORT SDP @@ -1751,10 +1750,9 @@ v=0 o=- 1545997027 1 IN IP4 198.51.101.40 s=tester t=0 0 -m=audio PORT RTP/AVP 0 8 +m=audio PORT RTP/AVP 0 c=IN IP4 203.0.113.1 a=rtpmap:0 PCMU/8000 -a=rtpmap:8 PCMA/8000 a=sendrecv a=rtcp:PORT SDP @@ -2415,102 +2413,13 @@ o=testlab 3815920663 3815920664 IN IP4 89.250.11.190 s=pjmedia c=IN IP4 89.250.11.190 t=0 0 -m=audio PORT RTP/AVP 8 0 101 13 +m=audio PORT RTP/AVP 8 13 101 c=IN IP4 203.0.113.1 a=mid:1 a=rtpmap:8 PCMA/8000 -a=rtpmap:0 PCMU/8000 -a=rtpmap:101 telephone-event/8000 -a=fmtp:101 0-16 -a=rtpmap:13 CN/8000 -a=sendrecv -a=rtcp:PORT -a=ptime:20 -SDP - -offer('strip-all w consume and offer and s/c', - { codec => { - strip => ['all'], - consume => ['CN'], - offer => ['PCMA', 'PCMU', 'telephone-event'], - }, - flags => ['symmetric codecs'], - }, < ['symmetric codecs'], - }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'], codec => { transcode => ['G722'] } }, < 'remove', replace => ['origin'], flags => ['symmetric-codecs'] }, < AMR-WB answer mode-set', @@ -4877,14 +4726,14 @@ o=- 3815883745 3815883745 IN IP4 ims.imscore.net s=- c=IN IP4 203.0.113.1 t=0 0 -m=audio PORT RTP/AVP 9 111 8 101 96 +m=audio PORT RTP/AVP 9 111 8 96 101 a=rtpmap:9 G722/8000 a=rtpmap:111 opus/48000 a=rtpmap:8 PCMA/8000 -a=rtpmap:101 telephone-event/48000 -a=fmtp:101 0-15 a=rtpmap:96 telephone-event/8000 a=fmtp:96 0-15 +a=rtpmap:101 telephone-event/48000 +a=fmtp:101 0-15 a=sendrecv a=rtcp:PORT a=ptime:20 @@ -4913,7 +4762,7 @@ m=audio PORT RTP/AVP 8 96 a=silenceSupp:off - - - - a=rtpmap:8 PCMA/8000 a=rtpmap:96 telephone-event/8000 -a=fmtp:96 0-16 +a=fmtp:96 0-15 a=sendrecv a=rtcp:PORT a=ptime:20 @@ -5017,9 +4866,8 @@ o=Z 58440449 0 IN IP4 89.225.243.254 s=Z c=IN IP4 203.0.113.1 t=0 0 -m=audio PORT RTP/AVP 0 8 +m=audio PORT RTP/AVP 0 a=rtpmap:0 PCMU/8000 -a=rtpmap:8 PCMA/8000 a=sendrecv a=rtcp:PORT SDP @@ -5203,13 +5051,13 @@ o=Z 58440449 0 IN IP4 89.225.243.254 s=Z c=IN IP4 203.0.113.1 t=0 0 -m=audio PORT RTP/AVP 0 8 96 101 97 +m=audio PORT RTP/AVP 0 8 96 97 101 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:96 opus/48000/2 -a=rtpmap:101 telephone-event/8000 a=rtpmap:97 telephone-event/48000 a=fmtp:97 0-15 +a=rtpmap:101 telephone-event/8000 a=sendrecv a=rtcp:PORT SDP @@ -5223,10 +5071,10 @@ o=Z 58440449 0 IN IP4 89.225.243.254 s=Z c=IN IP4 89.225.243.254 t=0 0 -m=audio 8000 RTP/AVP 0 8 96 101 97 +m=audio 8000 RTP/AVP 0 8 96 101 98 a=rtpmap:96 opus/48000/2 a=rtpmap:101 telephone-event/8000 -a=rtpmap:97 telephone-event/48000 +a=rtpmap:98 telephone-event/48000 a=sendrecv -------------------------------------- v=0 @@ -5266,11 +5114,11 @@ o=Z 58440449 0 IN IP4 89.225.243.254 s=Z c=IN IP4 203.0.113.1 t=0 0 -m=audio PORT RTP/AVP 97 8 102 101 +m=audio PORT RTP/AVP 97 8 101 102 a=rtpmap:97 opus/48000 a=rtpmap:8 PCMA/8000 -a=rtpmap:102 telephone-event/48000 a=rtpmap:101 telephone-event/8000 +a=rtpmap:102 telephone-event/48000 a=sendrecv a=rtcp:PORT SDP @@ -5946,11 +5794,9 @@ o=dev 5418 9648 IN IP4 8.8.8.60 s=SIP Call c=IN IP4 8.8.8.60 t=0 0 -m=audio PORT RTP/AVP 0 8 3 101 +m=audio PORT RTP/AVP 0 101 c=IN IP4 203.0.113.1 a=rtpmap:0 PCMU/8000 -a=rtpmap:8 PCMA/8000 -a=rtpmap:3 GSM/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-15 a=sendrecv @@ -7874,11 +7720,10 @@ s=pjmedia b=AS:117 t=0 0 a=X-nat:0 -m=audio PORT RTP/AVP 8 107 101 +m=audio PORT RTP/AVP 107 101 c=IN IP4 203.0.113.1 b=TIAS:96000 a=ssrc:243811319 cname:04389d431bdd5c52 -a=rtpmap:8 PCMA/8000 a=rtpmap:107 opus/48000/2 a=fmtp:107 useinbandfec=1 a=rtpmap:101 telephone-event/8000 @@ -8023,15 +7868,15 @@ s=pjmedia b=AS:117 t=0 0 a=X-nat:0 -m=audio PORT RTP/AVP 8 107 101 +m=audio PORT RTP/AVP 107 101 8 c=IN IP4 203.0.113.1 b=TIAS:96000 a=ssrc:243811319 cname:04389d431bdd5c52 -a=rtpmap:8 PCMA/8000 a=rtpmap:107 opus/48000/2 a=fmtp:107 useinbandfec=1 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-16 +a=rtpmap:8 PCMA/8000 a=sendrecv a=rtcp:PORT a=ptime:20 @@ -8098,15 +7943,15 @@ s=pjmedia b=AS:117 t=0 0 a=X-nat:0 -m=audio PORT RTP/AVP 107 8 101 +m=audio PORT RTP/AVP 107 101 8 c=IN IP4 203.0.113.1 b=TIAS:96000 a=ssrc:243811319 cname:04389d431bdd5c52 a=rtpmap:107 opus/48000/2 a=fmtp:107 useinbandfec=1 -a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-16 +a=rtpmap:8 PCMA/8000 a=sendrecv a=rtcp:PORT a=ptime:20 @@ -8170,16 +8015,11 @@ s=pjmedia b=AS:117 t=0 0 a=X-nat:0 -m=audio PORT RTP/AVP 8 107 101 96 +m=audio PORT RTP/AVP 8 c=IN IP4 203.0.113.1 b=TIAS:96000 a=ssrc:243811319 cname:04389d431bdd5c52 a=rtpmap:8 PCMA/8000 -a=rtpmap:107 opus/48000/2 -a=rtpmap:101 telephone-event/8000 -a=fmtp:101 0-16 -a=rtpmap:96 telephone-event/48000 -a=fmtp:96 0-15 a=sendrecv a=rtcp:PORT a=ptime:20 @@ -8242,16 +8082,11 @@ s=pjmedia b=AS:117 t=0 0 a=X-nat:0 -m=audio PORT RTP/AVP 8 107 101 96 +m=audio PORT RTP/AVP 8 c=IN IP4 203.0.113.1 b=TIAS:96000 a=ssrc:243811319 cname:04389d431bdd5c52 a=rtpmap:8 PCMA/8000 -a=rtpmap:107 opus/48000/2 -a=rtpmap:101 telephone-event/8000 -a=fmtp:101 0-16 -a=rtpmap:96 telephone-event/48000 -a=fmtp:96 0-15 a=sendrecv a=rtcp:PORT a=ptime:20 @@ -8298,7 +8133,6 @@ rtpe_req('delete', "delete", { 'from-tag' => ft() }); - new_call(); offer('T.38 forward re-invite', { ICE => 'remove', @@ -8984,7 +8818,6 @@ is $ret1[5], $ret1[6], 'rtp rport 2'; - if (0) { # github issue 854 @@ -9384,7 +9217,6 @@ rcv($sock_a, $port_b, rtpm(0, 4010, 9280, $ssrc, "\x00" x 160)); - # transcoding, RFC payload type present on both sides ($sock_a, $sock_b) = new_call([qw(198.51.100.1 6110)], [qw(198.51.100.3 6112)]); @@ -9724,16 +9556,16 @@ rcv($sock_a, $port_b, rtpm(0, 4008, 9280, $ssrc, "\x97\x98\xc7\x16\x0a\x0e\x3a\x snd($sock_b, $port_a, rtp(8, 4009, 9440, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4009, 9440, $ssrc, "\xda\x50\x2c\x2b\xc0\x97\x8e\x97\x39\x0e\x09\x13\xbf\x92\x8e\x9c\x57\x29\x31\xef\x72\x28\x19\x1b\x6d\x94\x8a\x8f\xce\x11\x0a\x11\x48\x9c\x98\xa5\xdc\x5e\xb5\xa9\xc6\x1f\x0f\x10\x31\x96\x89\x8d\xad\x19\x0e\x15\x37\xac\xaa\xc8\x57\xb7\x9c\x98\xac\x1e\x0c\x0c\x21\x9c\x8b\x8d\xa4\x25\x17\x1d\x3b\xcf\x48\x2b\x30\xae\x93\x8e" . "\xff" x 80)); # pause -snd($sock_b, $port_a, rtp(0, 4010, 9600, 0x6543, "\x00" x 160)); +snd($sock_b, $port_a, rtp(8, 4010, 9600, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4010, 9600, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4011, 9760, 0x6543, "\x00" x 160)); +snd($sock_b, $port_a, rtp(8, 4011, 9760, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4011, 9760, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4012, 9920, 0x6543, "\x00" x 160)); +snd($sock_b, $port_a, rtp(8, 4012, 9920, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4012, 9920, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4013, 10080, 0x6543, "\x00" x 160)); +snd($sock_b, $port_a, rtp(8, 4013, 10080, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4013, 10080, $ssrc, "\xff" x 160)); -snd($sock_b, $port_a, rtp(0, 4014, 10240, 0x6543, "\x00" x 160)); -rcv($sock_a, $port_b, rtpm(0, 4014, 10240, $ssrc, "\xff" x 80 . "\x00" x 80)); +snd($sock_b, $port_a, rtp(8, 4014, 10240, 0x6543, "\x00" x 160)); +rcv($sock_a, $port_b, rtpm(0, 4014, 10240, $ssrc, "\xff" x 80 . "\x29" x 80)); @@ -11382,15 +11214,9 @@ o=- 1545997027 1 IN IP4 203.0.113.1 s=tester c=IN IP4 203.0.113.1 t=0 0 -m=audio PORT RTP/AVP 120 8 0 101 96 +m=audio PORT RTP/AVP 120 a=rtpmap:120 opus/48000/2 a=fmtp:120 useinbandfec=1; usedtx=1; maxaveragebitrate=64000 -a=rtpmap:8 PCMA/8000 -a=rtpmap:0 PCMU/8000 -a=rtpmap:101 telephone-event/8000 -a=fmtp:101 0-15 -a=rtpmap:96 telephone-event/48000 -a=fmtp:96 0-15 a=sendrecv a=rtcp:PORT SDP @@ -13247,98 +13073,6 @@ rcv($sock_a, $port_b, rtpm(0, $seq+4, 4000+640, $ssrc, "\xbd\xd3\x77\xd9\xc5\xd0 -($sock_a, $sock_b) = new_call([qw(198.51.100.1 7030)], [qw(198.51.100.3 7032)]); - -($port_a) = offer('PCM to RFC DTMF transcoding w/ forced PCM transcoding', { ICE => 'remove', replace => ['origin'], - codec => { transcode => ['telephone-event'] }}, < ['origin'] }, <codecs_prefs_ ## dir, codecs) +#define expect(side, exp_str) \ + __expect(__FILE__, __LINE__, &media_ ## side->codecs.codec_prefs, exp_str) static void __expect(const char *file, int line, GQueue *dumper, const char *codecs) { printf("running test %s:%i\n", file, line); @@ -377,6 +367,8 @@ static void dtmf(const char *s) { #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 int main(void) { + rtpe_common_config_ptr = &rtpe_config.common; + unsigned long random_seed = 0; codeclib_init(0); @@ -388,16 +380,12 @@ int main(void) { start(); sdp_pt(0, PCMU, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000"); - expect(B, send, ""); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); sdp_pt(0, PCMU, 8000); answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000"); - expect(B, send, "0/PCMU/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); packet(A, 0, PCMU_payload, 0, PCMU_payload); packet(B, 0, PCMU_payload, 0, PCMU_payload); end(); @@ -407,17 +395,13 @@ int main(void) { sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000 8/PCMA/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); + expect(A, "0/PCMU/8000 8/PCMA/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "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, send, "0/PCMU/8000 8/PCMA/8000"); + expect(A, "0/PCMU/8000 8/PCMA/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload); @@ -429,85 +413,50 @@ int main(void) { sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000 8/PCMA/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); + expect(A, "0/PCMU/8000 8/PCMA/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "8/PCMA/8000"); - expect(A, send, "8/PCMA/8000"); - expect(B, recv, "8/PCMA/8000"); - expect(B, send, "8/PCMA/8000"); + expect(A, "8/PCMA/8000"); + expect(B, "8/PCMA/8000"); packet(A, 8, PCMA_payload, 8, PCMA_payload); packet(B, 8, PCMA_payload, 8, PCMA_payload); end(); - // plain with two offered and one answered + asymmetric codecs - start(); - sdp_pt(0, PCMU, 8000); - sdp_pt(8, PCMA, 8000); - offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000 8/PCMA/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); - sdp_pt(8, PCMA, 8000); - flags.asymmetric_codecs = 1; - answer(); - expect(A, recv, "8/PCMA/8000"); - expect(A, send, "0/PCMU/8000 8/PCMA/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, "8/PCMA/8000"); - packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); - packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload); - packet(B, 8, PCMA_payload, 8, PCMA_payload); - end(); - // plain with two offered and two answered + always-transcode one way start(); - ht_set(codec_accept, all); + c_accept(all); sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000 8/PCMA/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); + expect(A, "0/PCMU/8000 8/PCMA/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "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, send, "0/PCMU/8000 8/PCMA/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); - packet_seq(A, 8, PCMA_payload, 160, 1, 8, PCMA_payload); packet_seq(B, 8, PCMA_payload, 160, 1, 0, PCMU_payload); end(); // plain with two offered and two answered + always-transcode both ways start(); - ht_set(codec_accept, all); + c_accept(all); sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000 8/PCMA/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); - ht_set(codec_accept, all); + expect(A, "0/PCMU/8000 8/PCMA/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); + c_accept(all); sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "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, send, "0/PCMU/8000 8/PCMA/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); packet_seq(A, 0, PCMU_payload, 0, 0, 0, PCMU_payload); packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); - packet_seq(A, 8, PCMA_payload, 160, 1, 0, PCMU_payload); packet_seq(B, 8, PCMA_payload, 160, 1, 0, PCMU_payload); end(); @@ -516,17 +465,13 @@ int main(void) { sdp_pt(0, PCMU, 8000); transcode(PCMA); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); sdp_pt(0, PCMU, 8000); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, "0/PCMU/8000 8/PCMA/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); packet(A, 0, PCMU_payload, 0, PCMU_payload); packet_seq(B, 0, PCMU_payload, 0, 0, 0, PCMU_payload); packet_seq(B, 8, PCMA_payload, 160, 1, 0, PCMU_payload); @@ -537,41 +482,16 @@ int main(void) { sdp_pt(0, PCMU, 8000); transcode(PCMA); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 8/PCMA/8000"); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "8/PCMA/8000"); - expect(B, send, "8/PCMA/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "8/PCMA/8000"); packet(A, 0, PCMU_payload, 8, PCMA_payload); packet(B, 8, PCMA_payload, 0, PCMU_payload); end(); - // same as above, but allow asymmetric codecs - start(); - sdp_pt(0, PCMU, 8000); - transcode(PCMA); - offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, ""); - sdp_pt(8, PCMA, 8000); - flags.asymmetric_codecs = 1; - answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 8/PCMA/8000"); - expect(B, send, "8/PCMA/8000"); - packet(A, 0, PCMU_payload, 8, PCMA_payload); - packet_seq(B, 8, PCMA_payload, 0, 0, 0, PCMU_payload); - packet_seq(B, 0, PCMU_payload, 160, 1, 0, PCMU_payload); - end(); - #ifdef WITH_AMR_TESTS { str codec_name = STR_CONST_INIT("AMR-WB"); @@ -583,16 +503,16 @@ int main(void) { sdp_pt(0, PCMU, 8000); transcode(AMR-WB); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 96/AMR-WB/16000/octet-align=1"); - expect(B, send, ""); + expect(A, ""); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 96/AMR-WB/16000/octet-align=1"); + expect(B, ""); sdp_pt_fmt(96, AMR-WB, 16000, "octet-align=1"); answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "96/AMR-WB/16000/octet-align=1"); - expect(B, send, "96/AMR-WB/16000/octet-align=1"); + expect(A, "0/PCMU/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "96/AMR-WB/16000/octet-align=1"); + expect(B, "96/AMR-WB/16000/octet-align=1"); packet_seq(A, 0, PCMU_payload, 0, 0, -1, ""); // nothing due to resampling buffer packet_seq_nf(A, 0, PCMU_payload, 160, 1, 96, AMR_WB_payload); packet_seq(B, 96, AMR_WB_payload, 0, 0, -1, ""); // nothing due to resampling/decoding buffer @@ -604,16 +524,16 @@ int main(void) { sdp_pt_fmt(96, AMR-WB, 16000, "octet-align=1"); transcode(PCMU); offer(); - expect(A, recv, ""); - expect(A, send, "96/AMR-WB/16000/octet-align=1"); - expect(B, recv, "96/AMR-WB/16000/octet-align=1 0/PCMU/8000"); - expect(B, send, ""); + expect(A, ""); + expect(A, "96/AMR-WB/16000/octet-align=1"); + expect(B, "96/AMR-WB/16000/octet-align=1 0/PCMU/8000"); + expect(B, ""); sdp_pt(0, PCMU, 8000); answer(); - expect(A, recv, "96/AMR-WB/16000/octet-align=1"); - expect(A, send, "96/AMR-WB/16000/octet-align=1"); - expect(B, recv, "0/PCMU/8000"); - expect(B, send, "0/PCMU/8000"); + expect(A, "96/AMR-WB/16000/octet-align=1"); + expect(A, "96/AMR-WB/16000/octet-align=1"); + expect(B, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); packet_seq(B, 0, PCMU_payload, 0, 0, -1, ""); // nothing due to resampling buffer packet_seq_nf(B, 0, PCMU_payload, 160, 1, 96, AMR_WB_payload); packet_seq(A, 96, AMR_WB_payload, 0, 0, -1, ""); // nothing due to resampling/decoding buffer @@ -625,16 +545,16 @@ int main(void) { sdp_pt(96, AMR-WB, 16000); transcode(PCMU); offer(); - expect(A, recv, ""); - expect(A, send, "96/AMR-WB/16000"); - expect(B, recv, "96/AMR-WB/16000 0/PCMU/8000"); - expect(B, send, ""); + expect(A, ""); + expect(A, "96/AMR-WB/16000"); + expect(B, "96/AMR-WB/16000 0/PCMU/8000"); + expect(B, ""); sdp_pt(0, PCMU, 8000); answer(); - expect(A, recv, "96/AMR-WB/16000"); - expect(A, send, "96/AMR-WB/16000"); - expect(B, recv, "0/PCMU/8000"); - expect(B, send, "0/PCMU/8000"); + expect(A, "96/AMR-WB/16000"); + expect(A, "96/AMR-WB/16000"); + expect(B, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); packet_seq(B, 0, PCMU_payload, 0, 0, -1, ""); // nothing due to resampling buffer packet_seq_nf(B, 0, PCMU_payload, 160, 1, 96, AMR_WB_payload_noe); packet_seq(A, 96, AMR_WB_payload_noe, 0, 0, -1, ""); // nothing due to resampling/decoding buffer @@ -653,16 +573,16 @@ int main(void) { sdp_pt(0, PCMU, 8000); transcode(AMR); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 96/AMR/8000/octet-align=1"); - expect(B, send, ""); + expect(A, ""); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 96/AMR/8000/octet-align=1"); + expect(B, ""); sdp_pt_fmt(96, AMR, 8000, "octet-align=1"); answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "96/AMR/8000/octet-align=1"); - expect(B, send, "96/AMR/8000/octet-align=1"); + expect(A, "0/PCMU/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "96/AMR/8000/octet-align=1"); + expect(B, "96/AMR/8000/octet-align=1"); check_encoder(A, 0, 96, 0); // uses codec default check_encoder(B, 96, 0, 0); end(); @@ -672,16 +592,16 @@ int main(void) { sdp_pt(96, AMR, 8000); transcode(PCMU); offer(); - expect(A, recv, ""); - expect(A, send, "96/AMR/8000"); - expect(B, recv, "96/AMR/8000 0/PCMU/8000"); - expect(B, send, ""); + expect(A, ""); + expect(A, "96/AMR/8000"); + expect(B, "96/AMR/8000 0/PCMU/8000"); + expect(B, ""); sdp_pt(0, PCMU, 8000); answer(); - expect(A, recv, "96/AMR/8000"); - expect(A, send, "96/AMR/8000"); - expect(B, recv, "0/PCMU/8000"); - expect(B, send, "0/PCMU/8000"); + expect(A, "96/AMR/8000"); + expect(A, "96/AMR/8000"); + expect(B, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); check_encoder(A, 96, 0, 0); check_encoder(B, 0, 96, 0); // uses codec default end(); @@ -691,16 +611,16 @@ int main(void) { sdp_pt(0, PCMU, 8000); transcode(AMR/8000/1/6700); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 96/AMR/8000/octet-align=1"); - expect(B, send, ""); + expect(A, ""); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 96/AMR/8000/octet-align=1"); + expect(B, ""); sdp_pt_fmt(96, AMR, 8000, "octet-align=1"); answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "96/AMR/8000/octet-align=1"); - expect(B, send, "96/AMR/8000/octet-align=1"); + expect(A, "0/PCMU/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "96/AMR/8000/octet-align=1"); + expect(B, "96/AMR/8000/octet-align=1"); check_encoder(A, 0, 96, 6700); check_encoder(B, 96, 0, 0); end(); @@ -710,16 +630,16 @@ int main(void) { sdp_pt(0, PCMU, 8000); transcode(AMR/8000/1/7400); offer(); - expect(A, recv, ""); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "0/PCMU/8000 96/AMR/8000/octet-align=1"); - expect(B, send, ""); + expect(A, ""); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 96/AMR/8000/octet-align=1"); + expect(B, ""); sdp_pt_fmt(96, AMR, 8000, "octet-align=1"); answer(); - expect(A, recv, "0/PCMU/8000"); - expect(A, send, "0/PCMU/8000"); - expect(B, recv, "96/AMR/8000/octet-align=1"); - expect(B, send, "96/AMR/8000/octet-align=1"); + expect(A, "0/PCMU/8000"); + expect(A, "0/PCMU/8000"); + expect(B, "96/AMR/8000/octet-align=1"); + expect(B, "96/AMR/8000/octet-align=1"); check_encoder(A, 0, 96, 7400); check_encoder(B, 96, 0, 0); end(); @@ -730,16 +650,16 @@ int main(void) { transcode(PCMU); codec_set("AMR/8000/1/6700"); offer(); - expect(A, recv, ""); - expect(A, send, "96/AMR/8000"); - expect(B, recv, "96/AMR/8000 0/PCMU/8000"); - expect(B, send, ""); + expect(A, ""); + expect(A, "96/AMR/8000"); + expect(B, "96/AMR/8000 0/PCMU/8000"); + expect(B, ""); sdp_pt(0, PCMU, 8000); answer(); - expect(A, recv, "96/AMR/8000"); - expect(A, send, "96/AMR/8000"); - expect(B, recv, "0/PCMU/8000"); - expect(B, send, "0/PCMU/8000"); + expect(A, "96/AMR/8000"); + expect(A, "96/AMR/8000"); + expect(B, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); check_encoder(A, 96, 0, 0); check_encoder(B, 0, 96, 6700); end(); @@ -750,16 +670,16 @@ int main(void) { transcode(PCMU); codec_set("AMR/8000/1/7400"); offer(); - expect(A, recv, ""); - expect(A, send, "96/AMR/8000"); - expect(B, recv, "96/AMR/8000 0/PCMU/8000"); - expect(B, send, ""); + expect(A, ""); + expect(A, "96/AMR/8000"); + expect(B, "96/AMR/8000 0/PCMU/8000"); + expect(B, ""); sdp_pt(0, PCMU, 8000); answer(); - expect(A, recv, "96/AMR/8000"); - expect(A, send, "96/AMR/8000"); - expect(B, recv, "0/PCMU/8000"); - expect(B, send, "0/PCMU/8000"); + expect(A, "96/AMR/8000"); + expect(A, "96/AMR/8000"); + expect(B, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); check_encoder(A, 96, 0, 0); check_encoder(B, 0, 96, 7400); end(); @@ -772,16 +692,12 @@ int main(void) { sdp_pt(8, PCMA, 8000); transcode(G722); offer(); - expect(A, recv, ""); - expect(A, send, "8/PCMA/8000"); - expect(B, recv, "8/PCMA/8000 9/G722/8000"); - expect(B, send, ""); + expect(A, "8/PCMA/8000"); + expect(B, "8/PCMA/8000 9/G722/8000"); sdp_pt(9, G722, 8000); answer(); - expect(A, recv, "8/PCMA/8000"); - expect(A, send, "8/PCMA/8000"); - expect(B, recv, "9/G722/8000"); - expect(B, send, "9/G722/8000"); + expect(A, "8/PCMA/8000"); + expect(B, "9/G722/8000"); packet_seq(A, 8, PCMA_payload, 0, 0, -1, ""); // nothing due to resampling packet_seq_nf(A, 8, PCMA_payload, 160, 1, 9, G722_payload); packet_seq_ts(A, 8, PCMA_payload, 320, 2, 9, G722_payload, 160, 0); @@ -796,37 +712,15 @@ int main(void) { sdp_pt(9, G722, 8000); sdp_pt(8, PCMA, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, send, ""); + expect(A, "97/opus/48000 9/G722/8000 8/PCMA/8000"); + expect(B, "97/opus/48000 9/G722/8000 8/PCMA/8000"); sdp_pt(9, G722, 8000); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "9/G722/8000 8/PCMA/8000"); - expect(A, send, "9/G722/8000 8/PCMA/8000"); - expect(B, recv, "9/G722/8000 8/PCMA/8000"); - expect(B, send, "9/G722/8000 8/PCMA/8000"); - end(); - - // A includes unsupported codec by B - no transcoding (GH#562 control case) + asymmetric codecs - start(); - sdp_pt(97, opus, 48000); - sdp_pt(9, G722, 8000); - sdp_pt(8, PCMA, 8000); - offer(); - expect(A, recv, ""); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, send, ""); - sdp_pt(9, G722, 8000); - sdp_pt(8, PCMA, 8000); - flags.asymmetric_codecs = 1; - answer(); - expect(A, recv, "9/G722/8000 8/PCMA/8000"); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, send, "9/G722/8000 8/PCMA/8000"); + expect(A, "9/G722/8000 8/PCMA/8000"); + expect(A, "9/G722/8000 8/PCMA/8000"); + expect(B, "9/G722/8000 8/PCMA/8000"); + expect(B, "9/G722/8000 8/PCMA/8000"); end(); // A includes unsupported codec by B - transcoded codec accepted (GH#562 control case) @@ -836,18 +730,16 @@ int main(void) { sdp_pt(8, PCMA, 8000); transcode(PCMU); // standin for G729 offer(); - expect(A, recv, ""); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000 0/PCMU/8000"); - expect(B, send, ""); + expect(A, "97/opus/48000 9/G722/8000 8/PCMA/8000"); + expect(B, "97/opus/48000 9/G722/8000 8/PCMA/8000 0/PCMU/8000"); sdp_pt(9, G722, 8000); sdp_pt(8, PCMA, 8000); sdp_pt(0, PCMU, 8000); answer(); - expect(A, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "9/G722/8000 8/PCMA/8000 0/PCMU/8000"); - expect(B, send, "9/G722/8000 8/PCMA/8000 0/PCMU/8000"); + expect(A, "9/G722/8000 8/PCMA/8000 97/opus/48000"); + expect(A, "9/G722/8000 8/PCMA/8000 97/opus/48000"); + expect(B, "9/G722/8000 8/PCMA/8000 0/PCMU/8000"); + expect(B, "9/G722/8000 8/PCMA/8000 0/PCMU/8000"); end(); // A includes unsupported codec by B - transcoded codec rejected (GH#562) @@ -857,38 +749,15 @@ int main(void) { sdp_pt(8, PCMA, 8000); transcode(PCMU); // standin for G729 offer(); - expect(A, recv, ""); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000 0/PCMU/8000"); - expect(B, send, ""); + expect(A, "97/opus/48000 9/G722/8000 8/PCMA/8000"); + expect(B, "97/opus/48000 9/G722/8000 8/PCMA/8000 0/PCMU/8000"); sdp_pt(9, G722, 8000); sdp_pt(8, PCMA, 8000); answer(); - expect(A, recv, "9/G722/8000 8/PCMA/8000"); - expect(A, send, "9/G722/8000 8/PCMA/8000"); - expect(B, recv, "9/G722/8000 8/PCMA/8000"); - expect(B, send, "9/G722/8000 8/PCMA/8000"); - end(); - - // A includes unsupported codec by B - transcoded codec rejected (GH#562) + asymmetric codecs - start(); - sdp_pt(97, opus, 48000); - sdp_pt(9, G722, 8000); - sdp_pt(8, PCMA, 8000); - transcode(PCMU); // standin for G729 - offer(); - expect(A, recv, ""); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000 0/PCMU/8000"); - expect(B, send, ""); - sdp_pt(9, G722, 8000); - sdp_pt(8, PCMA, 8000); - flags.asymmetric_codecs = 1; - answer(); - expect(A, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(A, send, "97/opus/48000 9/G722/8000 8/PCMA/8000"); - expect(B, recv, "97/opus/48000 9/G722/8000 8/PCMA/8000 0/PCMU/8000"); - expect(B, send, "9/G722/8000 8/PCMA/8000"); + expect(A, "9/G722/8000 8/PCMA/8000"); + expect(A, "9/G722/8000 8/PCMA/8000"); + expect(B, "9/G722/8000 8/PCMA/8000"); + expect(B, "9/G722/8000 8/PCMA/8000"); end(); _log_facility_dtmf = 1; // dummy enabler @@ -898,17 +767,15 @@ int main(void) { sdp_pt(8, PCMA, 8000); sdp_pt(101, telephone-event, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, recv, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, send, ""); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); 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"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); packet_seq(A, 8, PCMA_payload, 1000000, 200, 8, PCMA_payload); // start with marker packet_seq(A, 101 | 0x80, "\x08\x0a\x00\xa0", 1000160, 201, 101 | 0x80, "\x08\x0a\x00\xa0"); @@ -951,17 +818,15 @@ int main(void) { sdp_pt(101, telephone-event, 8000); transcode(PCMU); offer(); - expect(A, recv, ""); - expect(A, send, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, recv, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000"); - expect(B, send, ""); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000"); sdp_pt(0, PCMU, 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, "0/PCMU/8000 101/telephone-event/8000"); - expect(B, send, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "0/PCMU/8000 101/telephone-event/8000"); packet_seq(A, 8, PCMA_payload, 1000000, 200, 0, PCMU_payload); // start with marker packet_seq(A, 101 | 0x80, "\x08\x0a\x00\xa0", 1000160, 201, 101 | 0x80, "\x08\x0a\x00\xa0"); @@ -1003,17 +868,15 @@ int main(void) { sdp_pt(8, PCMA, 8000); sdp_pt(101, telephone-event, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, recv, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, send, ""); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); 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"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); packet_seq(A, 0, PCMU_payload, 1000000, 200, 0, PCMU_payload); // start with marker packet_seq(A, 101 | 0x80, "\x08\x0a\x00\xa0", 1000160, 201, 101 | 0x80, "\x08\x0a\x00\xa0"); @@ -1055,17 +918,15 @@ int main(void) { sdp_pt(8, PCMA, 8000); sdp_pt(101, telephone-event, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, recv, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, send, ""); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); 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"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); packet_seq(A, 8, PCMA_payload, 1000000, 200, 8, PCMA_payload); // start with marker packet_seq(A, 101 | 0x80, "\x08\x0a\x00\xa0", 1000160, 201, 101 | 0x80, "\x08\x0a\x00\xa0"); @@ -1124,17 +985,15 @@ int main(void) { sdp_pt(101, telephone-event, 8000); transcode(PCMU); offer(); - expect(A, recv, ""); - expect(A, send, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, recv, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000"); - expect(B, send, ""); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000"); sdp_pt(0, PCMU, 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, "0/PCMU/8000 101/telephone-event/8000"); - expect(B, send, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "0/PCMU/8000 101/telephone-event/8000"); packet_seq(A, 8, PCMA_payload, 1000000, 200, 0, PCMU_payload); // start with marker packet_seq(A, 101 | 0x80, "\x08\x0a\x00\xa0", 1000160, 201, 101 | 0x80, "\x08\x0a\x00\xa0"); @@ -1192,17 +1051,15 @@ int main(void) { sdp_pt(8, PCMA, 8000); sdp_pt(101, telephone-event, 8000); offer(); - expect(A, recv, ""); - expect(A, send, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, recv, "8/PCMA/8000 101/telephone-event/8000"); - expect(B, send, ""); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); 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"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); packet_seq(A, 0, PCMU_payload, 1000000, 200, 0, PCMU_payload); // start with marker packet_seq(A, 101 | 0x80, "\x08\x0a\x00\xa0", 1000160, 201, 101 | 0x80, "\x08\x0a\x00\xa0"); @@ -1266,17 +1123,15 @@ int main(void) { 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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); 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"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // codec-mask only start(); @@ -1287,19 +1142,17 @@ int main(void) { sdp_pt(101, telephone-event, 8000); sdp_pt(13, CN, 8000); sdp_pt(118, CN, 16000); - ht_set(codec_mask, PCMU); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "104/SILK/16000 9/G722/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); 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"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // codec-mask + transcode + reject transcoded codec start(); @@ -1310,20 +1163,18 @@ int main(void) { sdp_pt(101, telephone-event, 8000); sdp_pt(13, CN, 8000); sdp_pt(118, CN, 16000); - ht_set(codec_mask, PCMU); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // codec-mask + transcode + accept transcoded codec start(); @@ -1334,21 +1185,19 @@ int main(void) { sdp_pt(101, telephone-event, 8000); sdp_pt(13, CN, 8000); sdp_pt(118, CN, 16000); - ht_set(codec_mask, PCMU); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "8/PCMA/8000 9/G722/8000 101/telephone-event/8000"); + expect(A, "8/PCMA/8000 9/G722/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000"); + expect(B, "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); @@ -1356,32 +1205,6 @@ int main(void) { // 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); @@ -1391,43 +1214,17 @@ int main(void) { 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); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // codec-consume + transcode + reject transcoded codec start(); @@ -1438,20 +1235,18 @@ int main(void) { sdp_pt(101, telephone-event, 8000); sdp_pt(13, CN, 8000); sdp_pt(118, CN, 16000); - ht_set(codec_consume, PCMU); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // codec-consume + transcode + accept transcoded codec start(); @@ -1462,47 +1257,19 @@ int main(void) { 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); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000"); end(); // codec-accept only start(); @@ -1513,43 +1280,17 @@ int main(void) { sdp_pt(101, telephone-event, 8000); sdp_pt(13, CN, 8000); sdp_pt(118, CN, 16000); - ht_set(codec_accept, PCMU); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // codec-accept + transcode + reject transcoded codec start(); @@ -1560,20 +1301,18 @@ int main(void) { sdp_pt(101, telephone-event, 8000); sdp_pt(13, CN, 8000); sdp_pt(118, CN, 16000); - ht_set(codec_accept, PCMU); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // codec-accept + transcode + accept transcoded codec start(); @@ -1584,47 +1323,19 @@ int main(void) { 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); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 3/GSM/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(A, "0/PCMU/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 3/GSM/8000 101/telephone-event/8000"); end(); // codec-accept first codec start(); @@ -1635,19 +1346,17 @@ int main(void) { sdp_pt(101, telephone-event, 8000); sdp_pt(13, CN, 8000); sdp_pt(118, CN, 16000); - ht_set(codec_accept, G722); + c_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, ""); + expect(A, "104/SILK/16000 9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000 118/CN/16000"); + expect(B, "9/G722/8000 0/PCMU/8000 8/PCMA/8000 101/telephone-event/8000 13/CN/8000"); 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"); + expect(A, "9/G722/8000 101/telephone-event/8000"); + expect(A, "9/G722/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); + expect(B, "8/PCMA/8000 101/telephone-event/8000"); end(); // gh 664 codec masking a/t start(); @@ -1655,16 +1364,16 @@ int main(void) { 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); + c_mask(opus); + c_mask(G722); + c_mask(G7221); + c_accept(all); offer(); - expect(B, recv, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000"); + expect(B, "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"); + expect(A, "120/opus/48000"); end(); // gh 664 codec masking accept=all start(); @@ -1672,16 +1381,16 @@ int main(void) { 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); + c_mask(opus); + c_mask(G722); + c_mask(G7221); + c_accept(all); offer(); - expect(B, recv, "8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000"); + expect(B, "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"); + expect(A, "120/opus/48000"); end(); // CN transcoding @@ -1693,12 +1402,12 @@ int main(void) { sdp_pt(0, PCMU, 8000); transcode(CN); offer(); - expect(B, recv, "8/PCMA/8000 0/PCMU/8000 13/CN/8000"); + expect(B, "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"); + expect(A, "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"); @@ -1709,13 +1418,13 @@ int main(void) { sdp_pt(8, PCMA, 8000); sdp_pt(0, PCMU, 8000); sdp_pt(13, CN, 8000); - ht_set(codec_consume, CN); + c_consume(CN); offer(); - expect(B, recv, "8/PCMA/8000 0/PCMU/8000"); + expect(B, "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"); + expect(A, "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"); @@ -1726,13 +1435,13 @@ int main(void) { sdp_pt(8, PCMA, 8000); sdp_pt(0, PCMU, 8000); sdp_pt(13, CN, 8000); - ht_set(codec_accept, CN); + c_accept(CN); offer(); - expect(B, recv, "8/PCMA/8000 0/PCMU/8000 13/CN/8000"); + expect(B, "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"); + expect(A, "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"); @@ -1743,17 +1452,17 @@ int main(void) { sdp_pt(9, G722, 8000); sdp_pt(8, PCMA, 8000); sdp_pt(101, telephone-event, 8000); - ht_set(codec_mask, all); + c_mask(all); transcode(opus/48000/1); transcode(PCMA); transcode(telephone-event); offer(); - expect(B, recv, "96/opus/48000 8/PCMA/8000 101/telephone-event/8000 97/telephone-event/48000/0-15"); + expect(B, "96/opus/48000 8/PCMA/8000 97/telephone-event/48000/0-15 101/telephone-event/8000"); sdp_pt(96, opus, 48000); sdp_pt(97, telephone-event, 48000); flags.single_codec = 1; answer(); - expect(A, recv, "9/G722/8000 101/telephone-event/8000"); + expect(A, "9/G722/8000 101/telephone-event/8000"); packet_seq(A, 101, "\x05\x07\x01\x40", 4000, 10, 97, "\x05\x07\x07\x80"); packet_seq(B, 97, "\x05\x07\x07\x80", 4000, 10, 101, "\x05\x07\x01\x40"); end(); @@ -1763,18 +1472,18 @@ int main(void) { sdp_pt(8, PCMA, 8000); sdp_pt(102, telephone-event, 48000); sdp_pt(101, telephone-event, 8000); - ht_set(codec_mask, all); + c_mask(all); transcode(opus); transcode(PCMA); transcode(PCMU); transcode(telephone-event); offer(); - expect(B, recv, "96/opus/48000 8/PCMA/8000 0/PCMU/8000 102/telephone-event/48000 101/telephone-event/8000"); + expect(B, "96/opus/48000 8/PCMA/8000 0/PCMU/8000 101/telephone-event/8000 102/telephone-event/48000"); sdp_pt(0, PCMU, 8000); sdp_pt(101, telephone-event, 8000); flags.single_codec = 1; answer(); - expect(A, recv, "96/opus/48000 102/telephone-event/48000"); + expect(A, "96/opus/48000 102/telephone-event/48000"); packet_seq(A, 102, "\x05\x07\x01\x40", 4000, 10, 101, "\x05\x07\x00\x35"); packet_seq(B, 101, "\x05\x07\x07\x80", 4000, 10, 102, "\x05\x07\x2d\x00"); @@ -1782,5 +1491,5 @@ int main(void) { } int get_local_log_level(unsigned int u) { - return -1; + return 7; } diff --git a/utils/rtpengine-ng-client b/utils/rtpengine-ng-client index 1a1ccce7c..8d66fb0e7 100755 --- a/utils/rtpengine-ng-client +++ b/utils/rtpengine-ng-client @@ -59,6 +59,7 @@ GetOptions( 'codec-consume=s@' => \$options{'codec-consume'}, 'codec-accept=s@' => \$options{'codec-accept'}, 'codec-set=s@' => \$options{'codec-set'}, + 'codec-except=s@' => \$options{'codec-except'}, 'ptime=i' => \$options{'ptime'}, 'flags=s@' => \$options{'flags'}, 'codec-options-flat' => \$options{'codec options flag'}, @@ -117,7 +118,7 @@ if (defined($options{direction})) { $options{direction} =~ /(.*),(.*)/ or die; $packet{direction} = [$1,$2]; } -for my $x (qw(strip offer transcode mask set consume accept)) { +for my $x (qw(strip offer transcode mask set consume accept except)) { if ($options{'codec-'.$x} && @{$options{'codec-'.$x}}) { if (!$options{'codec options flag'}) { $packet{codec}{$x} = $options{'codec-'.$x};