Index: addons/chan_ooh323.c =================================================================== --- addons/chan_ooh323.c (revision 316874) +++ addons/chan_ooh323.c (working copy) @@ -138,6 +138,7 @@ static struct ooh323_pvt { struct ast_rtp_instance *vrtp; /* Placeholder for now */ int t38support; /* T.38 mode - disable, transparent, faxgw */ + int faxdetect; int rtptimeout; struct ast_udptl *udptl; int faxmode; @@ -209,6 +210,7 @@ struct ooh323_user{ struct ast_codec_pref prefs; int dtmfmode; int dtmfcodec; + int faxdetect; int t38support; int rtptimeout; int mUseIP; /* Use IP address or H323-ID to search user */ @@ -233,6 +235,7 @@ struct ooh323_peer{ int amaflags; int dtmfmode; int dtmfcodec; + int faxdetect; int t38support; int mFriend; /* indicates defined as friend */ char ip[4*8+7+2]; /* Max for IPv6 - 2 brackets, 8 4hex, 7 - : */ @@ -295,6 +298,7 @@ static struct ast_format_cap *gCap; static struct ast_codec_pref gPrefs; static int gDTMFMode = H323_DTMF_RFC2833; static int gDTMFCodec = 101; +static int gFAXdetect = 1; static int gT38Support = T38_FAXGW; static char gGatekeeper[100]; static enum RasGatekeeperMode gRasGkMode = RasNoGatekeeper; @@ -388,18 +392,25 @@ static struct ast_channel *ooh323_new(struct ooh32 ast_module_ref(myself); /* Allocate dsp for in-band DTMF support */ + if ((i->dtmfmode & H323_DTMF_INBAND) || i->faxdetect) { + i->vad = ast_dsp_new(); + } if (i->dtmfmode & H323_DTMF_INBAND) { - i->vad = ast_dsp_new(); ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_FAX_DETECT); - ast_dsp_set_faxmode(i->vad, - DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED); - - if (i->dtmfmode & H323_DTMF_INBANDRELAX) + if (i->dtmfmode & H323_DTMF_INBANDRELAX) { ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); + } + } else if (i->faxdetect) { + ast_dsp_set_features(i->vad, DSP_FEATURE_FAX_DETECT); } + if (i->faxdetect) { + ast_dsp_set_faxmode(i->vad, + DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED); + } + ast_mutex_lock(&usecnt_lock); usecnt++; ast_mutex_unlock(&usecnt_lock); @@ -484,6 +495,8 @@ static struct ooh323_pvt *ooh323_alloc(int callref ast_mutex_lock(&pvt->lock); pvt->faxmode = 0; + pvt->chmodepend = 0; + pvt->faxdetect = gFAXdetect; pvt->t38support = gT38Support; pvt->rtptimeout = gRTPTimeout; pvt->rtdrinterval = gRTDRInterval; @@ -610,6 +623,7 @@ static struct ast_channel *ooh323_request(const ch p->g729onlyA = peer->g729onlyA; p->dtmfmode |= peer->dtmfmode; p->dtmfcodec = peer->dtmfcodec; + p->faxdetect = peer->faxdetect; p->t38support = peer->t38support; p->rtptimeout = peer->rtptimeout; p->faststart = peer->faststart; @@ -639,6 +653,7 @@ static struct ast_channel *ooh323_request(const ch p->g729onlyA = g729onlyA; p->dtmfmode = gDTMFMode; p->dtmfcodec = gDTMFCodec; + p->faxdetect = gFAXdetect; p->t38support = gT38Support; p->rtptimeout = gRTPTimeout; ast_format_cap_copy(p->cap, gCap); @@ -828,17 +843,7 @@ static int ooh323_digit_begin(struct ast_channel * } ast_mutex_lock(&p->lock); - - if (digit == 'e' && !p->faxmode && p->t38support != T38_DISABLED) { - if (!p->chmodepend) { - if (gH323Debug) - ast_verbose("request to change %s to t.38 because fax cng\n", - p->callToken); - p->chmodepend = 1; - ooRequestChangeMode(p->callToken, 1); - } - - } else if (p->rtp && ((p->dtmfmode & H323_DTMF_RFC2833) || (p->dtmfmode & H323_DTMF_CISCO))) { + if (p->rtp && ((p->dtmfmode & H323_DTMF_RFC2833) || (p->dtmfmode & H323_DTMF_CISCO))) { ast_rtp_instance_dtmf_begin(p->rtp, digit); } else if (((p->dtmfmode & H323_DTMF_Q931) || (p->dtmfmode & H323_DTMF_H245ALPHANUMERIC) || @@ -1258,27 +1263,40 @@ static int ooh323_indicate(struct ast_channel *ast (int)sizeof(enum ast_control_t38), (int)datalen); } else { const struct ast_control_t38_parameters *parameters = data; + struct ast_control_t38_parameters our_parameters; enum ast_control_t38 message = parameters->request_response; switch (message) { + case AST_T38_NEGOTIATED: case AST_T38_REQUEST_NEGOTIATE: if (!p->chmodepend && !p->faxmode) { + p->chmodepend = 1; ooRequestChangeMode(p->callToken, 1); - p->chmodepend = 1; res = 0; } + our_parameters.request_response = AST_T38_NEGOTIATED; + our_parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl); + our_parameters.rate = AST_T38_RATE_14400; + ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters)); break; case AST_T38_REQUEST_TERMINATE: if (!p->chmodepend && p->faxmode) { + p->chmodepend = 1; ooRequestChangeMode(p->callToken, 0); - p->chmodepend = 1; res = 0; } break; + case AST_T38_REQUEST_PARMS: + our_parameters.request_response = AST_T38_REQUEST_PARMS; + our_parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl); + our_parameters.rate = AST_T38_RATE_14400; + ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters)); + res = AST_T38_REQUEST_PARMS; + break; default: ; @@ -1328,14 +1346,15 @@ static int ooh323_queryoption(struct ast_channel * " Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen); break; } - if (p->t38support != T38_DISABLED) + + if (p->faxmode) { + state = T38_STATE_NEGOTIATED; + } else if (p->chmodepend) { + state = T38_STATE_NEGOTIATED; + } else if (p->t38support != T38_DISABLED) { state = T38_STATE_UNKNOWN; - if (p->faxmode) - state = (p->chmodepend) ? T38_STATE_UNKNOWN : T38_STATE_NEGOTIATED; - else if (p->chmodepend) - state = T38_STATE_NEGOTIATING; + } - *((enum ast_t38_state *) data) = state; res = 0; break; @@ -1750,6 +1769,7 @@ int ooh323_onReceivedSetup(ooCallData *call, Q931M memcpy(&p->prefs, &user->prefs, sizeof(struct ast_codec_pref)); p->dtmfmode |= user->dtmfmode; p->dtmfcodec = user->dtmfcodec; + p->faxdetect = user->faxdetect; p->t38support = user->t38support; p->rtptimeout = user->rtptimeout; p->h245tunneling = user->h245tunneling; @@ -2190,6 +2210,7 @@ static struct ooh323_user *build_user(const char * user->rtptimeout = gRTPTimeout; user->dtmfmode = gDTMFMode; user->dtmfcodec = gDTMFCodec; + user->faxdetect = gFAXdetect; user->t38support = gT38Support; user->faststart = gFastStart; user->h245tunneling = gTunneling; @@ -2267,6 +2288,12 @@ static struct ooh323_user *build_user(const char * user->dtmfmode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0; } else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) { user->dtmfcodec = atoi(v->value); + } else if (!strcasecmp(v->name, "faxdetect")) { + if (ast_true(v->value)) { + user->faxdetect = 1; + } else { + user->faxdetect = 0; + } } else if (!strcasecmp(v->name, "t38support")) { if (!strcasecmp(v->value, "disabled")) user->t38support = T38_DISABLED; @@ -2310,6 +2337,7 @@ static struct ooh323_peer *build_peer(const char * peer->amaflags = gAMAFLAGS; peer->dtmfmode = gDTMFMode; peer->dtmfcodec = gDTMFCodec; + peer->faxdetect = gFAXdetect; peer->t38support = gT38Support; peer->faststart = gFastStart; peer->h245tunneling = gTunneling; @@ -2414,6 +2442,12 @@ static struct ooh323_peer *build_peer(const char * peer->dtmfmode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0; } else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) { peer->dtmfcodec = atoi(v->value); + } else if (!strcasecmp(v->name, "faxdetect")) { + if (ast_true(v->value)) { + peer->faxdetect = 1; + } else { + peer->faxdetect = 0; + } } else if (!strcasecmp(v->name, "t38support")) { if (!strcasecmp(v->value, "disabled")) peer->t38support = T38_DISABLED; @@ -2536,6 +2570,7 @@ int reload_config(int reload) memset(&gPrefs, 0, sizeof(struct ast_codec_pref)); gDTMFMode = H323_DTMF_RFC2833; gDTMFCodec = 101; + gFAXdetect = 1; gT38Support = T38_FAXGW; gTRCLVL = OOTRCLVLERR; gRasGkMode = RasNoGatekeeper; @@ -2739,6 +2774,12 @@ int reload_config(int reload) gDTMFMode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0; } else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) { gDTMFCodec = atoi(v->value); + } else if (!strcasecmp(v->name, "faxdetect")) { + if (ast_true(v->value)) { + gFAXdetect = 1; + } else { + gFAXdetect = 0; + } } else if (!strcasecmp(v->name, "t38support")) { if (!strcasecmp(v->value, "disabled")) gT38Support = T38_DISABLED; @@ -2873,6 +2914,7 @@ static char *handle_cli_ooh323_show_peer(struct as ast_cli(a->fd, "%s\n", "disabled"); else if (peer->t38support == T38_FAXGW) ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); + ast_cli(a->fd,"%-15s%s\n", "FAX Detect:", (peer->faxdetect) ? "Yes" : "No"); ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode); ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", @@ -3018,6 +3060,7 @@ static char *handle_cli_ooh323_show_user(struct as ast_cli(a->fd, "%s\n", "disabled"); else if (user->t38support == T38_FAXGW) ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); + ast_cli(a->fd,"%-15s%s\n", "FAX Detect:", (user->faxdetect) ? "Yes" : "No"); ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode); ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", @@ -3174,10 +3217,10 @@ static char *handle_cli_ooh323_show_config(struct ast_cli(a->fd, "%-20s", "DTMF Mode: "); if (gDTMFMode & H323_DTMF_CISCO) { ast_cli(a->fd, "%s\n", "cisco"); - ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", gDTMFCodec); + ast_cli(a->fd, "%-20.15s%d\n", "DTMF Codec: ", gDTMFCodec); } else if (gDTMFMode & H323_DTMF_RFC2833) { ast_cli(a->fd, "%s\n", "rfc2833"); - ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", gDTMFCodec); + ast_cli(a->fd, "%-20.15s%d\n", "DTMF Codec: ", gDTMFCodec); } else if (gDTMFMode & H323_DTMF_Q931) ast_cli(a->fd, "%s\n", "q931keypad"); else if (gDTMFMode & H323_DTMF_H245ALPHANUMERIC) @@ -3196,9 +3239,10 @@ static char *handle_cli_ooh323_show_config(struct ast_cli(a->fd, "%s\n", "disabled"); else if (gT38Support == T38_FAXGW) ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); + ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", (gFAXdetect) ? "Yes" : "No"); if (gRTDRCount && gRTDRInterval) - ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", gRTDRCount, gRTDRInterval); + ast_cli(a->fd, "%-20.15s%d,%d\n", "RoundTrip: ", gRTDRCount, gRTDRInterval); ast_cli(a->fd, "%-20s%ld\n", "Call counter: ", callnumber); ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode); @@ -4199,7 +4243,7 @@ void setup_udptl_connection(ooCallData *call, cons p->lastTxT38 = time(NULL); if (p->t38support == T38_ENABLED) { struct ast_control_t38_parameters parameters = { .request_response = 0 }; - parameters.request_response = AST_T38_NEGOTIATED; + parameters.request_response = AST_T38_REQUEST_NEGOTIATE; parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl); parameters.rate = AST_T38_RATE_14400; ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters)); @@ -4298,6 +4342,7 @@ struct ast_frame *ooh323_rtp_read(struct ast_chann { /* Retrieve audio/etc from channel. Assumes p->lock is already held. */ struct ast_frame *f; + struct ast_frame *dfr = NULL; static struct ast_frame null_frame = { AST_FRAME_NULL, }; switch (ast->fdno) { case 0: @@ -4336,14 +4381,42 @@ struct ast_frame *ooh323_rtp_read(struct ast_chann if ((p->dtmfmode & H323_DTMF_INBAND) && p->vad && (f->subclass.format.id == AST_FORMAT_SLINEAR || f->subclass.format.id == AST_FORMAT_ALAW || - f->subclass.format.id == AST_FORMAT_ULAW)) { - f = ast_dsp_process(p->owner, p->vad, f); - if (f && (f->frametype == AST_FRAME_DTMF)) { - ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass.integer); + f->subclass.format.id == AST_FORMAT_ULAW)) { + dfr = ast_dsp_process(p->owner, p->vad, f); } } + } else { + return f; } - } + +/* process INBAND DTMF*/ + if (dfr && (dfr->frametype == AST_FRAME_DTMF)) { + if (p->faxdetect && ((dfr->subclass.integer == 'f') || (dfr->subclass.integer == 'e'))) { + ast_debug(1, "* Detected FAX Tone %s\n", (dfr->subclass.integer == 'e') ? "CED" : "CNG"); + /* Switch to T.38 ON CED*/ + if (!p->faxmode && !p->chmodepend && (dfr->subclass.integer == 'e') && (p->t38support != T38_DISABLED)) { + if (gH323Debug) + ast_verbose("request to change %s to t.38 because fax ced\n", p->callToken); + p->chmodepend = 1; + ooRequestChangeMode(p->callToken, 1); + } else if (dfr->subclass.integer == 'f') { + char *target_context = S_OR(p->owner->macrocontext, p->owner->context); + if ((strcmp(p->owner->exten, "fax")) && + (ast_exists_extension(p->owner, target_context, "fax", 1, + S_COR(p->owner->caller.id.number.valid, p->owner->caller.id.number.str, NULL)))) { + ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n", p->owner->name); + pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten); + if (ast_async_goto(p->owner, target_context, "fax", 1)) { + ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name,target_context); + } + return &ast_null_frame; + } + } + } else if ((p->dtmfmode & H323_DTMF_INBAND) && !p->faxdetect) { + ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass.integer); + return dfr; + } + } return f; } @@ -4365,6 +4438,7 @@ void onModeChanged(ooCallData *call, int t38mode) if (gH323Debug) ast_debug(1, "mode for %s is already %d\n", call->callToken, t38mode); + p->chmodepend = 0; ast_mutex_unlock(&p->lock); return; } @@ -4375,11 +4449,13 @@ void onModeChanged(ooCallData *call, int t38mode) DEADLOCK_AVOIDANCE(&p->lock); } if (!p->owner) { + p->chmodepend = 0; ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Channel has no owner\n"); return; } } else { + p->chmodepend = 0; ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Channel has no owner\n"); return;