|
|
@ -24,6 +24,7 @@ |
|
|
#include "fix_frame_channel_layout.h" |
|
|
#include "fix_frame_channel_layout.h" |
|
|
#endif |
|
|
#endif |
|
|
#include "bufferpool.h" |
|
|
#include "bufferpool.h" |
|
|
|
|
|
#include "ng_client.h" |
|
|
|
|
|
|
|
|
struct codec_timer { |
|
|
struct codec_timer { |
|
|
struct timerthread_obj tt_obj; |
|
|
struct timerthread_obj tt_obj; |
|
|
@ -261,6 +262,19 @@ struct rtcp_timer { |
|
|
struct call_media *media; |
|
|
struct call_media *media; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
struct transform_handler { |
|
|
|
|
|
struct obj obj; |
|
|
|
|
|
call_t *call; |
|
|
|
|
|
struct call_media *source_media; |
|
|
|
|
|
struct call_monologue *transform_ml; |
|
|
|
|
|
struct call_media *transform_media; // points to the remote rtpengine |
|
|
|
|
|
const struct transcode_config *tcc; |
|
|
|
|
|
struct codec_handler *handler; |
|
|
|
|
|
endpoint_t remote; |
|
|
|
|
|
str call_id; |
|
|
|
|
|
str tag; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static mutex_t transcode_lock = MUTEX_STATIC_INIT; |
|
|
static mutex_t transcode_lock = MUTEX_STATIC_INIT; |
|
|
@ -304,6 +318,8 @@ static struct ssrc_entry *__ssrc_handler_decode_new(void *p); |
|
|
static struct ssrc_entry *__ssrc_handler_new(void *p); |
|
|
static struct ssrc_entry *__ssrc_handler_new(void *p); |
|
|
static void __ssrc_handler_stop(void *p, void *dummy); |
|
|
static void __ssrc_handler_stop(void *p, void *dummy); |
|
|
static void __free_ssrc_handler(struct codec_ssrc_handler *); |
|
|
static void __free_ssrc_handler(struct codec_ssrc_handler *); |
|
|
|
|
|
static struct codec_handler *__get_pt_handler(struct call_media *receiver, rtp_payload_type *pt, |
|
|
|
|
|
struct call_media *sink); |
|
|
|
|
|
|
|
|
static void __transcode_packet_free(struct transcode_packet *); |
|
|
static void __transcode_packet_free(struct transcode_packet *); |
|
|
|
|
|
|
|
|
@ -359,7 +375,45 @@ static struct codec_handler codec_handler_stub_blackhole = { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void __transform_handler_shutdown(struct transform_handler *tfh) { |
|
|
|
|
|
if (!tfh) |
|
|
|
|
|
return; |
|
|
|
|
|
if (!tfh->call) |
|
|
|
|
|
return; |
|
|
|
|
|
obj_release(tfh->call); |
|
|
|
|
|
__unsubscribe_media(tfh->transform_media, tfh->source_media); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define BENC_GS_APPEND(s) g_string_append_printf(req, "%zu:" STR_FORMAT, (s)->len, STR_FMT(s)) |
|
|
|
|
|
|
|
|
|
|
|
static void __transform_handler_destroy(struct transform_handler *tfh) { |
|
|
|
|
|
if (!tfh) |
|
|
|
|
|
return; |
|
|
|
|
|
__transform_handler_shutdown(tfh); |
|
|
|
|
|
|
|
|
|
|
|
__auto_type tcc = tfh->tcc; |
|
|
|
|
|
if (tcc && tfh->call_id.len) { |
|
|
|
|
|
// manually construct the request |
|
|
|
|
|
g_autoptr(GString) req = g_string_new("d7:command6:delete7:call-id"); |
|
|
|
|
|
BENC_GS_APPEND(&tfh->call_id); |
|
|
|
|
|
g_string_append(req, "8:from-tag"); |
|
|
|
|
|
BENC_GS_APPEND(&tfh->tag); |
|
|
|
|
|
g_string_append(req, "12:delete delayi0ee"); |
|
|
|
|
|
|
|
|
|
|
|
str result; |
|
|
|
|
|
__auto_type ret = ng_client_request(&tcc->transform, &STR_LEN(req->str, req->len), memory_arena); |
|
|
|
|
|
if (!ret || !bencode_dictionary_get_str(ret, "result", &result) || str_cmp(&result, "ok")) |
|
|
|
|
|
ilog(LOG_WARN, "Failed to delete remote 'transform' session"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static void __handler_shutdown(struct codec_handler *handler) { |
|
|
static void __handler_shutdown(struct codec_handler *handler) { |
|
|
|
|
|
ilogs(codec, LOG_DEBUG, "Shutting down codec handler for " STR_FORMAT, |
|
|
|
|
|
STR_FMT(&handler->source_pt.encoding_with_full_params)); |
|
|
|
|
|
__transform_handler_shutdown(handler->transform); |
|
|
|
|
|
handler->tcc = NULL; |
|
|
|
|
|
obj_release(handler->transform); |
|
|
ssrc_hash_foreach(handler->ssrc_hash, __ssrc_handler_stop, NULL); |
|
|
ssrc_hash_foreach(handler->ssrc_hash, __ssrc_handler_stop, NULL); |
|
|
free_ssrc_hash(&handler->ssrc_hash); |
|
|
free_ssrc_hash(&handler->ssrc_hash); |
|
|
if (handler->delay_buffer) { |
|
|
if (handler->delay_buffer) { |
|
|
@ -499,6 +553,187 @@ static int handler_func_blackhole(struct codec_handler *h, struct media_packet * |
|
|
return 0; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void __transform_subscriptions(struct codec_handler *handler) { |
|
|
|
|
|
__auto_type tfh = handler->transform; |
|
|
|
|
|
|
|
|
|
|
|
// subscribe source to destination: send media to the remote |
|
|
|
|
|
__add_media_subscription(tfh->transform_media, tfh->source_media, NULL); |
|
|
|
|
|
|
|
|
|
|
|
// subscribe destination to output: forward received media to output |
|
|
|
|
|
__add_media_subscription(handler->i.sink, tfh->transform_media, NULL); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void append_pt(GString *req, const rtp_payload_type *pt) { |
|
|
|
|
|
BENC_GS_APPEND(&pt->encoding); |
|
|
|
|
|
g_string_append(req, "12:payload type"); |
|
|
|
|
|
g_string_append_printf(req, "i%ie", pt->payload_type); |
|
|
|
|
|
g_string_append(req, "10:clock rate"); |
|
|
|
|
|
g_string_append_printf(req, "i%ue", pt->clock_rate); |
|
|
|
|
|
g_string_append(req, "8:channels"); |
|
|
|
|
|
g_string_append_printf(req, "i%ue", pt->channels); |
|
|
|
|
|
g_string_append(req, "6:format"); |
|
|
|
|
|
BENC_GS_APPEND(&pt->format_parameters); |
|
|
|
|
|
g_string_append(req, "7:options"); |
|
|
|
|
|
BENC_GS_APPEND(&pt->codec_opts); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// returns NULL if transform handler was successfully set up |
|
|
|
|
|
// returns empty string "" if no transform handler is requested |
|
|
|
|
|
// returns error string on error |
|
|
|
|
|
static const char *__make_transform_handler(struct codec_handler *handler) { |
|
|
|
|
|
__auto_type tcc = handler->tcc; |
|
|
|
|
|
if (!tcc) |
|
|
|
|
|
return ""; |
|
|
|
|
|
if (!tcc->transform.address.family) |
|
|
|
|
|
return ""; |
|
|
|
|
|
|
|
|
|
|
|
__auto_type tfh = handler->transform; |
|
|
|
|
|
__auto_type media = handler->media; |
|
|
|
|
|
__auto_type call = media->call; |
|
|
|
|
|
|
|
|
|
|
|
if (tfh) { |
|
|
|
|
|
if (handler->transform->tcc != tcc) { |
|
|
|
|
|
ilogs(codec, LOG_DEBUG, "Transform handler mismatched, resetting"); |
|
|
|
|
|
obj_release(handler->transform); |
|
|
|
|
|
tfh = handler->transform = NULL; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
ilogs(codec, LOG_DEBUG, "Leaving existing transform handler intact"); |
|
|
|
|
|
// restore reference in case it has been released |
|
|
|
|
|
obj_release(tfh->call); |
|
|
|
|
|
tfh->call = obj_get(call); |
|
|
|
|
|
__transform_subscriptions(handler); |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!handler->ssrc_hash) |
|
|
|
|
|
handler->ssrc_hash = create_ssrc_hash_full(__ssrc_handler_new, handler); |
|
|
|
|
|
|
|
|
|
|
|
if (!tfh) { |
|
|
|
|
|
ilogs(codec, LOG_DEBUG, "Creating new transform handler"); |
|
|
|
|
|
|
|
|
|
|
|
tfh = handler->transform = obj_alloc0(struct transform_handler, __transform_handler_destroy); |
|
|
|
|
|
tfh->call = obj_get(call); |
|
|
|
|
|
tfh->source_media = media; |
|
|
|
|
|
tfh->handler = handler; |
|
|
|
|
|
tfh->tcc = tcc; |
|
|
|
|
|
|
|
|
|
|
|
// create dedicated monologue and dedicated call_media to send media to the |
|
|
|
|
|
// remote rtpengine and forward received media to its designated destination |
|
|
|
|
|
tfh->transform_ml = call_get_or_create_monologue(call, STR_PTR("transform handler")); |
|
|
|
|
|
|
|
|
|
|
|
tfh->transform_media = call_make_transform_media(tfh->transform_ml, &media->type, media->type_id, |
|
|
|
|
|
&STR_NULL, &tcc->transform, &tcc->local_interface); |
|
|
|
|
|
|
|
|
|
|
|
codec_store_add_raw(&tfh->transform_media->codecs, rtp_payload_type_dup(&handler->dest_pt)); |
|
|
|
|
|
|
|
|
|
|
|
__transform_subscriptions(handler); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// manually construct the request |
|
|
|
|
|
g_autoptr(GString) req = g_string_new("d7:command9:transform5:mediald4:type"); |
|
|
|
|
|
BENC_GS_APPEND(&media->type); |
|
|
|
|
|
|
|
|
|
|
|
g_string_append(req, "5:codecld5:inputd5:codec"); |
|
|
|
|
|
append_pt(req, &handler->source_pt); |
|
|
|
|
|
|
|
|
|
|
|
g_string_append(req, "e6:outputd5:codec"); |
|
|
|
|
|
append_pt(req, &handler->dest_pt); |
|
|
|
|
|
|
|
|
|
|
|
__auto_type ps = tfh->transform_media->streams.head->data; |
|
|
|
|
|
__auto_type sock = &ps->selected_sfd->socket; |
|
|
|
|
|
|
|
|
|
|
|
g_string_append(req, "eee11:destinationd"); |
|
|
|
|
|
g_string_append(req, "6:family"); |
|
|
|
|
|
BENC_GS_APPEND(STR_PTR(sock->local.address.family->rfc_name)); |
|
|
|
|
|
g_string_append(req, "7:address"); |
|
|
|
|
|
BENC_GS_APPEND(STR_PTR(sockaddr_print_buf(&sock->local.address))); |
|
|
|
|
|
g_string_append_printf(req, "4:porti%ue", sock->local.port); |
|
|
|
|
|
|
|
|
|
|
|
g_string_append(req, "eee"); |
|
|
|
|
|
|
|
|
|
|
|
if (tcc->remote_interface.len) { |
|
|
|
|
|
g_string_append(req, "9:interface"); |
|
|
|
|
|
BENC_GS_APPEND(&tcc->remote_interface); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
g_string_append(req, "8:instance"); |
|
|
|
|
|
BENC_GS_APPEND(&rtpe_instance_id); |
|
|
|
|
|
|
|
|
|
|
|
g_string_append(req, "e"); |
|
|
|
|
|
|
|
|
|
|
|
// send out request |
|
|
|
|
|
__auto_type ret = ng_client_request(&tcc->transform, &STR_LEN(req->str, req->len), memory_arena); |
|
|
|
|
|
if (!ret) |
|
|
|
|
|
return "'transform' request to remote peer failed"; |
|
|
|
|
|
|
|
|
|
|
|
// process response |
|
|
|
|
|
str result; |
|
|
|
|
|
if (!bencode_dictionary_get_str(ret, "result", &result)) |
|
|
|
|
|
return "'transform' response didn't contain 'result'"; |
|
|
|
|
|
if (str_cmp(&result, "ok")) |
|
|
|
|
|
return "'transform' response didn't indicate success"; |
|
|
|
|
|
|
|
|
|
|
|
str s; |
|
|
|
|
|
|
|
|
|
|
|
if (!bencode_dictionary_get_str(ret, "call-id", &s)) |
|
|
|
|
|
return "'transform' response didn't contain 'call-id'"; |
|
|
|
|
|
tfh->call_id = call_str_cpy(&s); |
|
|
|
|
|
|
|
|
|
|
|
if (!bencode_dictionary_get_str(ret, "from-tag", &s)) |
|
|
|
|
|
return "'transform' response didn't contain 'from-tag'"; |
|
|
|
|
|
tfh->tag = call_str_cpy(&s); |
|
|
|
|
|
|
|
|
|
|
|
__auto_type remote_media = bencode_dictionary_get_expect(ret, "media", BENCODE_LIST); |
|
|
|
|
|
if (!remote_media) |
|
|
|
|
|
return "'transform' response didn't contain 'media'"; |
|
|
|
|
|
if (!remote_media->child) |
|
|
|
|
|
return "'transform' response contained empty 'media'"; |
|
|
|
|
|
|
|
|
|
|
|
__auto_type rm = remote_media->child; // just a single entry for now |
|
|
|
|
|
|
|
|
|
|
|
if (rm->type != BENCODE_DICTIONARY) |
|
|
|
|
|
return "incorrect type contained in 'media'"; |
|
|
|
|
|
str family, address; |
|
|
|
|
|
int port = bencode_dictionary_get_integer(rm, "port", 0); |
|
|
|
|
|
if (port <= 0 || port > 65535) |
|
|
|
|
|
return "invalid port"; |
|
|
|
|
|
if (!bencode_dictionary_get_str(rm, "family", &family)) |
|
|
|
|
|
return "'transform' response media didn't contain 'family'"; |
|
|
|
|
|
if (!bencode_dictionary_get_str(rm, "address", &address)) |
|
|
|
|
|
return "'transform' response media didn't contain 'address'"; |
|
|
|
|
|
__auto_type fam = get_socket_family_rfc(&family); |
|
|
|
|
|
if (!fam) |
|
|
|
|
|
return "'transform' response media contained invalid 'family'"; |
|
|
|
|
|
if (!sockaddr_parse_str(&tfh->remote.address, fam, &address)) |
|
|
|
|
|
return "'transform' response media contained invalid 'address'"; |
|
|
|
|
|
tfh->remote.port = port; |
|
|
|
|
|
|
|
|
|
|
|
ps = tfh->transform_media->streams.head->data; |
|
|
|
|
|
ps->advertised_endpoint = ps->endpoint = tfh->remote; |
|
|
|
|
|
PS_SET(ps, FILLED); |
|
|
|
|
|
|
|
|
|
|
|
// don't send the affected PT to the destination, and make sure |
|
|
|
|
|
// to only send the affected PT to the transform remote |
|
|
|
|
|
handler->handler_func = handler_func_blackhole; |
|
|
|
|
|
handler->kernelize = true; |
|
|
|
|
|
handler->blackhole = true; |
|
|
|
|
|
|
|
|
|
|
|
MEDIA_SET(tfh->transform_media, SELECT_PT); |
|
|
|
|
|
__auto_type tfm_handler = __get_pt_handler(handler->media, &handler->source_pt, tfh->transform_media); |
|
|
|
|
|
__make_passthrough(tfm_handler, -1, -1); |
|
|
|
|
|
|
|
|
|
|
|
handler->stats_chain_suffix = " (TFM)"; |
|
|
|
|
|
handler->stats_chain_suffix_brief = "_tfm"; |
|
|
|
|
|
|
|
|
|
|
|
__handler_stats_entry(handler); |
|
|
|
|
|
|
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#undef BENC_GS_APPEND |
|
|
|
|
|
|
|
|
static void __reset_sequencer(void *p, void *dummy) { |
|
|
static void __reset_sequencer(void *p, void *dummy) { |
|
|
struct ssrc_entry_call *s = p; |
|
|
struct ssrc_entry_call *s = p; |
|
|
if (s->sequencers) |
|
|
if (s->sequencers) |
|
|
@ -559,6 +794,15 @@ reset: |
|
|
if (handler->source_pt.codec_def && handler->source_pt.codec_def->dtmf) |
|
|
if (handler->source_pt.codec_def && handler->source_pt.codec_def->dtmf) |
|
|
handler->handler_func = handler_func_dtmf; |
|
|
handler->handler_func = handler_func_dtmf; |
|
|
|
|
|
|
|
|
|
|
|
handler->tcc = t_hash_table_lookup(rtpe_transcode_config, &handler->pi); |
|
|
|
|
|
const char *err = __make_transform_handler(handler); |
|
|
|
|
|
if (!err) |
|
|
|
|
|
return true; // delegated to transcode handler |
|
|
|
|
|
if (err[0] != '\0') |
|
|
|
|
|
ilogs(codec, LOG_ERR, "Failed to set up transform handler: %s", err); |
|
|
|
|
|
|
|
|
|
|
|
handler->ssrc_hash = create_ssrc_hash_full(ssrc_handler_new_func, handler); |
|
|
|
|
|
|
|
|
ilogs(codec, LOG_DEBUG, "Created transcode context for " STR_FORMAT "/" STR_FORMAT " (%i) -> " STR_FORMAT |
|
|
ilogs(codec, LOG_DEBUG, "Created transcode context for " STR_FORMAT "/" STR_FORMAT " (%i) -> " STR_FORMAT |
|
|
"/" STR_FORMAT " (%i) with DTMF output %i and CN output %i", |
|
|
"/" STR_FORMAT " (%i) with DTMF output %i and CN output %i", |
|
|
STR_FMT(&handler->source_pt.encoding_with_params), |
|
|
STR_FMT(&handler->source_pt.encoding_with_params), |
|
|
@ -569,8 +813,6 @@ reset: |
|
|
dest->payload_type, |
|
|
dest->payload_type, |
|
|
dtmf_payload_type, cn_payload_type); |
|
|
dtmf_payload_type, cn_payload_type); |
|
|
|
|
|
|
|
|
handler->ssrc_hash = create_ssrc_hash_full(ssrc_handler_new_func, handler); |
|
|
|
|
|
|
|
|
|
|
|
if (handler->ssrc_hash->precreat |
|
|
if (handler->ssrc_hash->precreat |
|
|
&& ((struct codec_ssrc_handler *) handler->ssrc_hash->precreat)->chain) |
|
|
&& ((struct codec_ssrc_handler *) handler->ssrc_hash->precreat)->chain) |
|
|
{ |
|
|
{ |
|
|
@ -585,6 +827,10 @@ reset: |
|
|
no_handler_reset: |
|
|
no_handler_reset: |
|
|
__delay_buffer_setup(&handler->delay_buffer, handler, handler->media->call, handler->media->buffer_delay); |
|
|
__delay_buffer_setup(&handler->delay_buffer, handler, handler->media->call, handler->media->buffer_delay); |
|
|
__dtx_restart(handler); |
|
|
__dtx_restart(handler); |
|
|
|
|
|
|
|
|
|
|
|
// double-check transform handler in case this comes after a no-op reset (shutdown + no_handler_reset) |
|
|
|
|
|
__make_transform_handler(handler); |
|
|
|
|
|
|
|
|
// check if we have multiple decoders transcoding to the same output PT |
|
|
// check if we have multiple decoders transcoding to the same output PT |
|
|
struct codec_handler *output_handler = NULL; |
|
|
struct codec_handler *output_handler = NULL; |
|
|
if (output_transcoders) |
|
|
if (output_transcoders) |
|
|
@ -1178,6 +1424,7 @@ void __codec_handlers_update(struct call_media *receiver, struct call_media *sin |
|
|
__generator_stop(receiver); |
|
|
__generator_stop(receiver); |
|
|
__generator_stop(sink); |
|
|
__generator_stop(sink); |
|
|
codec_handlers_stop(&receiver->codec_handlers_store, sink); |
|
|
codec_handlers_stop(&receiver->codec_handlers_store, sink); |
|
|
|
|
|
// XXX this can cause unnecessary shutdown/resets of codec handlers |
|
|
|
|
|
|
|
|
bool is_transcoding = false; |
|
|
bool is_transcoding = false; |
|
|
receiver->rtcp_handler = NULL; |
|
|
receiver->rtcp_handler = NULL; |
|
|
@ -1664,6 +1911,7 @@ bool codec_handler_transform(struct call_media *receiver, ng_codecs_q *q) { |
|
|
#ifdef WITH_TRANSCODING |
|
|
#ifdef WITH_TRANSCODING |
|
|
if (!codec_def_supported(input->codec_def) || !codec_def_supported(output->codec_def)) |
|
|
if (!codec_def_supported(input->codec_def) || !codec_def_supported(output->codec_def)) |
|
|
return false; |
|
|
return false; |
|
|
|
|
|
codec_store_add_raw(&receiver->codecs, rtp_payload_type_dup(input)); |
|
|
__make_transcoder(handler, output, NULL, -1, false, -1); |
|
|
__make_transcoder(handler, output, NULL, -1, false, -1); |
|
|
#else |
|
|
#else |
|
|
return false; |
|
|
return false; |
|
|
@ -3788,6 +4036,7 @@ void codec_handlers_stop(codec_handlers_q *q, struct call_media *sink) { |
|
|
delay_buffer_stop(&h->delay_buffer); |
|
|
delay_buffer_stop(&h->delay_buffer); |
|
|
} |
|
|
} |
|
|
ssrc_hash_foreach(h->ssrc_hash, __ssrc_handler_stop, NULL); |
|
|
ssrc_hash_foreach(h->ssrc_hash, __ssrc_handler_stop, NULL); |
|
|
|
|
|
__transform_handler_shutdown(h->transform); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|