diff --git a/channels/chan_sip.c b/channels/chan_sip.c index c3f62d8cd9..c255f5edc2 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -2272,6 +2272,7 @@ static int sip_transfer(struct ast_channel *ast, const char *dest); static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int sip_senddigit_begin(struct ast_channel *ast, char digit); static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); +static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen); static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen); static const char *sip_get_callid(struct ast_channel *chan); @@ -2680,6 +2681,7 @@ static const struct ast_channel_tech sip_tech = { .early_bridge = ast_rtp_instance_early_bridge, .send_text = sip_sendtext, /* called with chan locked */ .func_channel_read = acf_channel_read, + .setoption = sip_setoption, .queryoption = sip_queryoption, .get_pvt_uniqueid = sip_get_callid, }; @@ -3924,6 +3926,26 @@ static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittyp return res; } +/*! \brief Set an option on a SIP dialog */ +static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen) +{ + int res = -1; + struct sip_pvt *p = chan->tech_pvt; + + if (option == AST_OPTION_FORMAT_READ) { + int format = *(int *)data; + res = ast_rtp_instance_set_read_format(p->rtp, format); + } else if (option == AST_OPTION_FORMAT_WRITE) { + int format = *(int *)data; + res = ast_rtp_instance_set_write_format(p->rtp, format); + } else if (option == AST_OPTION_MAKE_COMPATIBLE) { + struct ast_channel *peer = data; + res = ast_rtp_instance_make_compatible(chan, p->rtp, peer); + } + + return res; +} + /*! \brief Query an option on a SIP dialog */ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen) { diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index f7e8b20add..a16cf804c3 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -391,6 +391,15 @@ enum ast_control_transfer { */ #define AST_OPTION_T38_STATE 10 +/*! Request that the channel driver deliver frames in a specific format */ +#define AST_OPTION_FORMAT_READ 11 + +/*! Request that the channel driver be prepared to accept frames in a specific format */ +#define AST_OPTION_FORMAT_WRITE 12 + +/*! Request that the channel driver make two channels of the same tech type compatible if possible */ +#define AST_OPTION_MAKE_COMPATIBLE 13 + struct oprmode { struct ast_channel *peer; int mode; diff --git a/main/channel.c b/main/channel.c index acf3f557bd..f3444eac11 100644 --- a/main/channel.c +++ b/main/channel.c @@ -3769,7 +3769,7 @@ done: static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *format, struct ast_trans_pvt **trans, const int direction) { - int native; + int native, native_fmt = ast_best_codec(fmt); int res; char from[200], to[200]; @@ -3780,7 +3780,19 @@ static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *fo if (!fmt || !native) /* No audio requested */ return 0; /* Let's try a call without any sounds (video, text) */ - + + /* See if the underlying channel driver is capable of performing transcoding for us */ + if (!ast_channel_setoption(chan, direction ? AST_OPTION_FORMAT_WRITE : AST_OPTION_FORMAT_READ, &native_fmt, sizeof(int*), 0)) { + ast_debug(1, "Channel driver natively set channel %s to %s format %s (%d)\n", chan->name, + direction ? "write" : "read", ast_getformatname(native_fmt), native_fmt); + chan->nativeformats = *rawformat = *format = native_fmt; + if (*trans) { + ast_translator_free_path(*trans); + } + *trans = NULL; + return 0; + } + /* Find a translation path from the native format to one of the desired formats */ if (!direction) /* reading */ @@ -4202,6 +4214,12 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a int src; int dst; + /* See if the channel driver can natively make these two channels compatible */ + if (from->tech->bridge && from->tech->bridge == to->tech->bridge && + !ast_channel_setoption(from, AST_OPTION_MAKE_COMPATIBLE, to, sizeof(struct ast_channel *), 0)) { + return 0; + } + if (from->readformat == to->writeformat && from->writeformat == to->readformat) { /* Already compatible! Moving on ... */ return 0;