Index: channels/chan_sip.c =================================================================== --- ../asterisk-1.4.4/channels/chan_sip.c 2007-04-27 16:04:07.000000000 +0200 +++ channels/chan_sip.c 2007-05-27 19:26:45.000000000 +0200 @@ -1210,6 +1211,7 @@ 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 *c, int option, void *data, int datalen); /*--- Transmitting responses and requests */ static int sipsock_read(int *id, int fd, short events, void *ignore); @@ -1543,11 +1546,7 @@ .write_video = sip_write, .indicate = sip_indicate, .transfer = sip_transfer, + .setoption = sip_setoption, .fixup = sip_fixup, .send_digit_begin = sip_senddigit_begin, .send_digit_end = sip_senddigit_end, @@ -3456,9 +3480,29 @@ struct sip_pvt *p = ast->tech_pvt; ast_mutex_lock(&p->lock); - if (ast->_state != AST_STATE_UP) { + if (ast->_state != AST_STATE_UP) + { try_suggested_sip_codec(p); + /* new mechanism based on prefcodecs to negotiate SDP + * in answers */ + if (p->prefcodec) + { + int afmt, vfmt; + + afmt = p->jointcapability & p->prefcodec & AST_FORMAT_AUDIO_MASK; + vfmt = p->jointcapability & p->prefcodec & AST_FORMAT_VIDEO_MASK; + if (afmt == 0) + { + ast_log(LOG_NOTICE, "No common audio codec with preferred codec, changing only video part to %08x. Audio is transcoded.\n", vfmt); + p->jointcapability &= (vfmt | AST_FORMAT_AUDIO_MASK); + } + else + { + ast_log(LOG_NOTICE, "Adjusting jointcapabilities based on preferred codec format %08x.\n", p->prefcodec); + p->jointcapability &= p->prefcodec; + } + } ast_setstate(ast, AST_STATE_UP); if (option_debug) ast_log(LOG_DEBUG, "SIP answering channel: %s\n", ast->name); @@ -3656,6 +3700,47 @@ return res; } +/* SIP set option. Mostly indicate the preferred codec to this channel. + * this is useful for app_dial and app_queue to close the codec negocialtion + * and send back a proper 200 OK or 183 Call Progress with a negociated SDP + * instead using capapbility to create the SDP */ +static int sip_setoption(struct ast_channel *c, int option, void *data, int datalen) +{ + struct sip_pvt *p = c->tech_pvt; + int * p_fmt, vfmt; + + switch (option) + { + case AST_OPTION_PREFCODECS: + if (datalen != sizeof(int)) + { + ast_log(LOG_WARNING,"chan %s - AST_OPTION_PREFCODEC data len incorrect. Ignoring.\n", c->name); + return -2; + } + + p_fmt = (int *) data; + /* check video compatibility */ + vfmt = (*p_fmt) & AST_FORMAT_VIDEO_MASK; + if ( vfmt != 0 + && + (vfmt & c->nativeformats & AST_FORMAT_VIDEO_MASK) == 0) + { + ast_log(LOG_WARNING,"chan %s - video format requested %08x is incompatble with channel native video format %08x.\n", c->name, vfmt, c->nativeformats & AST_FORMAT_VIDEO_MASK); + return -3; + } + ast_log(LOG_DEBUG,"chan %s - setting preferred codecs to %08x.\n", c->name, *p_fmt); + + p->prefcodec = *p_fmt; + break; + + + default: + ast_log(LOG_NOTICE, "chan %s - unsupported option %d rquested.\n", c->name, option); + return -1; + } + return 0; +} + /*! \brief Play indication to user * With SIP a lot of indications is sent as messages, letting the device play the indication - busy signal, congestion etc @@ -5222,14 +5307,19 @@ if (option_debug > 3) ast_log(LOG_DEBUG, "We have an owner, now see if we need to change this call\n"); - if (!(p->owner->nativeformats & p->jointcapability) && (p->jointcapability & AST_FORMAT_AUDIO_MASK)) { - if (debug) { - char s1[BUFSIZ], s2[BUFSIZ]; - ast_log(LOG_DEBUG, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n", - ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability), - ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats)); - } + /* if peer and incoming channel have no audio codec in common, + pickup new codecs to prepare translation + */ + if (!(p->owner->nativeformats & p->jointcapability & AST_FORMAT_AUDIO_MASK) + && + (p->jointcapability & AST_FORMAT_AUDIO_MASK)) + { + char s1[BUFSIZ], s2[BUFSIZ]; + ast_log(LOG_DEBUG, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n", + ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability), + ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats)); p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability); + ast_log(LOG_DEBUG, "New native format for chan %s is %08x\n", p->owner->name, p->owner->nativeformats); ast_set_read_format(p->owner, p->owner->readformat); ast_set_write_format(p->owner, p->owner->writeformat); } @@ -15440,6 +15599,7 @@ printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "", host); #endif p->prefcodec = oldformat; /* Format for this call */ + p->jointcapability = oldformat & p->capability; ast_mutex_lock(&p->lock); tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */ ast_mutex_unlock(&p->lock); Index: main/channel.c =================================================================== --- ../asterisk-1.4.4/main/channel.c 2007-04-26 05:19:51.000000000 +0200 +++ main/channel.c 2007-05-27 18:56:06.000000000 +0200 @@ -2863,13 +2863,16 @@ 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, oldfmt; int res; - + + oldfmt = fmt; /* Make sure we only consider audio */ fmt &= AST_FORMAT_AUDIO_MASK; - native = chan->nativeformats; + //native = AST_FORMAT_AUDIO_MASK & chan->nativeformats; + native = chan->nativeformats; + ast_log(LOG_DEBUG, "chan=%s format=%08x (with video = %08x) native=%08x in dir. %s\n", chan->name, fmt, oldfmt, native, direction ? "write" : "read"); /* Find a translation path from the native format to one of the desired formats */ if (!direction) /* reading */ @@ -3081,7 +3084,7 @@ fmt = format & AST_FORMAT_AUDIO_MASK; res = ast_translator_best_choice(&fmt, &capabilities); if (res < 0) { - ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %d) to %d\n", type, chan->tech->capabilities, format); + ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %08x) to %08x\n", type, chan->tech->capabilities, format); AST_LIST_UNLOCK(&channels); return NULL; } @@ -3212,55 +3215,116 @@ int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer) { - int src; - int dst; + int asrc, vsrc; + int adst, vdst; + + ast_log(LOG_DEBUG, "src chan %s with format %08x to dst chan %s with format %08x\n", chan->name, chan->nativeformats, peer->name, peer->nativeformats); - /* Set up translation from the chan to the peer */ - src = chan->nativeformats; - dst = peer->nativeformats; - if (ast_translator_best_choice(&dst, &src) < 0) { - ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, src, peer->name, dst); + /* Set up AUDIO translation from the chan to the peer */ + asrc = chan->nativeformats & AST_FORMAT_AUDIO_MASK; + adst = peer->nativeformats & AST_FORMAT_AUDIO_MASK; + if (ast_translator_best_choice(&adst, &asrc) < 0) { + ast_log(LOG_WARNING, "No path to translate AUDIO from chan=%s (%08x) to peer=%s (%08x)\n", chan->name, asrc, peer->name, adst); return -1; } + ast_log(LOG_DEBUG, "ast_translator_best_choice() selected AUDIO src format %08x and AUDIO dst format %08x\n", asrc, adst); /* if the best path is not 'pass through', then transcoding is needed; if desired, force transcode path to use SLINEAR between channels, but only if there is no direct conversion available */ - if ((src != dst) && ast_opt_transcode_via_slin && - (ast_translate_path_steps(dst, src) != 1)) - dst = AST_FORMAT_SLINEAR; - if (ast_set_read_format(chan, dst) < 0) { - ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, dst); - return -1; - } - if (ast_set_write_format(peer, dst) < 0) { - ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, dst); - return -1; - } - - /* Set up translation from the peer to the chan */ - src = peer->nativeformats; - dst = chan->nativeformats; - if (ast_translator_best_choice(&dst, &src) < 0) { - ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, src, chan->name, dst); - return -1; + if ((asrc != adst) + && + ast_opt_transcode_via_slin + && + (ast_translate_path_steps(adst, asrc) != 1)) + { + ast_log(LOG_NOTICE, "No direct AUDIO transl -> transcoding audio using SLINEAR format between channels\n"); + adst = AST_FORMAT_SLINEAR; + } + + /* Set up VIDEO translation from the chan to the peer */ + vsrc = chan->nativeformats & AST_FORMAT_VIDEO_MASK; + vdst = peer->nativeformats & AST_FORMAT_VIDEO_MASK; + if (ast_translator_best_choice(&vdst, &vsrc) < 0) { + ast_log(LOG_WARNING, "No path to translate VIDEO from chan=%s(%08x) to peer=%s(%08x)\n", chan->name, vsrc, peer->name, vdst); + return -2; + } + + ast_log(LOG_DEBUG, "after ast_translator_best_choice(), VIDEO src format is %08x and VIDEO dst format is %08x\n", vsrc, vdst); + + if ( vsrc != vdst && ast_translate_path_steps(vdst, vsrc) != 1) + { + ast_log(LOG_ERROR, "No direct VIDEO translation. Failed to make channels compatible"); + return -2; + } + + if (ast_set_read_format(chan, adst | vdst) < 0) { + ast_log(LOG_WARNING, "Unable to set read format on channel %s to %08x\n", chan->name, adst | vdst); + return -3; + } + + if (ast_set_write_format(peer, adst | vdst) < 0) { + ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, adst | vdst); + return -4; + } + + /* Set up translation from the PEER to the CHAN for AUDIO part*/ + asrc = peer->nativeformats & AST_FORMAT_AUDIO_MASK; + adst = chan->nativeformats & AST_FORMAT_AUDIO_MASK; + if (ast_translator_best_choice(&adst, &asrc) < 0) { + ast_log(LOG_WARNING, "No path to translate AUDIO from peer=%s(%08x) to chan=%s(%08x)\n", peer->name, asrc, chan->name, adst); + return -2; } /* if the best path is not 'pass through', then transcoding is needed; if desired, force transcode path to use SLINEAR between channels, but only if there is no direct conversion available */ - if ((src != dst) && ast_opt_transcode_via_slin && - (ast_translate_path_steps(dst, src) != 1)) - dst = AST_FORMAT_SLINEAR; - if (ast_set_read_format(peer, dst) < 0) { - ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, dst); - return -1; + if ((asrc != adst) + && + ast_opt_transcode_via_slin + && + (ast_translate_path_steps(adst, asrc) != 1)) + { + ast_log(LOG_NOTICE, "No direct AUDIO transl -> transcoding audio using SLINEAR format between channels\n"); + } - if (ast_set_write_format(chan, dst) < 0) { - ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, dst); - return -1; + + /* Set up translation from the PEER to the CHAN for VIDEO part*/ + vsrc = peer->nativeformats & AST_FORMAT_VIDEO_MASK; + vdst = chan->nativeformats & AST_FORMAT_VIDEO_MASK; + if (ast_translator_best_choice(&vdst, &vsrc) < 0) { + ast_log(LOG_WARNING, "No path to translate VIDEO from peer=%s(%08x) to chan=%s(%08x)\n", peer->name, asrc, chan->name, adst); + return -5; + } + + ast_log(LOG_DEBUG, "after ast_translator_best_choice(), VIDEO src format is %08x and AUDIO dst format is %08x\n", vsrc, vdst); + + if ( vsrc != vdst && ast_translate_path_steps(vdst, vsrc) != 1) + { + ast_log(LOG_ERROR, "No direct VIDEO translation. Failed to make channels compatible"); + return -5; + } + + if (ast_set_read_format(peer, adst | vdst) < 0) { + ast_log(LOG_WARNING, "Unable to set read format on peer channel %s to %d\n", peer->name, adst | vdst); + return -6; + } + if (ast_set_write_format(chan, adst | vdst) < 0) { + ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, adst | vdst); + return -7; + } + + /* new ! indicate preferred codec to incoming channel. Restrict here to + SIP channels because it requeires the siport of a new AST_OPTION but + the same mechanism could be used for IAX and later for H.324m or + other protocols which supports codec nego (skinny ? gtalk ?) + */ + if ( !strncmp(chan->name,"SIP",3) ) + { + int pcodec = adst | vdst; + ast_channel_setoption(chan, AST_OPTION_PREFCODECS, &pcodec, sizeof(pcodec), 0); } return 0; } Index: include/asterisk/frame.h =================================================================== --- ../asterisk-1.4.4/include/asterisk/frame.h 2006-10-30 17:27:34.000000000 +0100 +++ include/asterisk/frame.h 2007-05-27 18:01:05.000000000 +0200 @@ -329,6 +331,8 @@ /*! Explicitly enable or disable echo cancelation for the given channel */ #define AST_OPTION_ECHOCAN 8 +/*! indicate the given channel what are our preferred codecs */ +#define AST_OPTION_PREFCODECS 9 struct oprmode { struct ast_channel *peer; int mode;