mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-05-15 04:32:55 +00:00
related infrastructure work can take place around it. git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@13082 d0543943-73ff-0310-b7d9-9358b9ac24b2
348 lines
12 KiB
C
348 lines
12 KiB
C
/*
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2009, Steve Underwood <steveu@coppice.org>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Steve Underwood <steveu@coppice.org>
|
|
*
|
|
* mod_t38gateway.c -- A T.38 gateway
|
|
*
|
|
* This module uses the T.38 gateway engine of spandsp to create a T.38 gateway suitable for
|
|
* V.17, V.29, and V.27ter FAX operation.
|
|
*
|
|
*/
|
|
|
|
#include <switch.h>
|
|
|
|
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
|
|
#include <spandsp.h>
|
|
#include <spandsp/version.h>
|
|
|
|
#include "udptl.h"
|
|
|
|
/*! Syntax of the API call. */
|
|
#define T38GATEWAY_SYNTAX "<uuid> <command>"
|
|
|
|
/*! Number of expected parameters in api call. */
|
|
#define T38GATEWAY_PARAMS 2
|
|
|
|
/*! FreeSWITCH CUSTOM event types. */
|
|
#define T38GATEWAY_EVENT_CNG "t38gateway::cng"
|
|
#define T38GATEWAY_EVENT_CED "t38gateway::ced"
|
|
#define T38GATEWAY_EVENT_PAGE "t38gateway::page"
|
|
|
|
/* Prototypes */
|
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_t38gateway_shutdown);
|
|
SWITCH_STANDARD_API(t38gateway_api_main);
|
|
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_t38gateway_load);
|
|
SWITCH_MODULE_DEFINITION(mod_t38gateway, mod_t38gateway_load, NULL, NULL);
|
|
SWITCH_STANDARD_APP(t38gateway_start_function);
|
|
|
|
/*! Type that holds codec information. */
|
|
typedef struct {
|
|
/*! The sampling rate of the audio stream. */
|
|
int rate;
|
|
/*! The number of channels. */
|
|
int channels;
|
|
} t38gateway_codec_info_t;
|
|
|
|
/*! Type that holds session information pertinent to the t38gateway module. */
|
|
typedef struct {
|
|
/*! Internal FreeSWITCH session. */
|
|
switch_core_session_t *session;
|
|
/*! Codec information for the session. */
|
|
t38gateway_codec_info_t t38gateway_codec;
|
|
t38_gateway_state_t *t38;
|
|
t38_core_state_t *t38_core;
|
|
udptl_state_t *udptl;
|
|
} t38gateway_session_info_t;
|
|
|
|
static int tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count)
|
|
{
|
|
t38gateway_session_info_t *t;
|
|
|
|
t = (t38gateway_session_info_t *) user_data;
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static int rx_packet_handler(void *user_data, const uint8_t *buf, int len, int seq_no)
|
|
{
|
|
t38gateway_session_info_t *t;
|
|
|
|
t = (t38gateway_session_info_t *) user_data;
|
|
t38_core_rx_ifp_packet(t->t38_core, buf, len, seq_no);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
/*! \brief The callback function that is called when new audio data becomes available
|
|
*
|
|
* @author Steve Underwood
|
|
* @param bug A reference to the media bug.
|
|
* @param user_data The session information for this call.
|
|
* @param type The switch callback type.
|
|
* @return The success or failure of the function.
|
|
*/
|
|
static switch_bool_t t38gateway_callback(switch_media_bug_t * bug, void *user_data, switch_abc_type_t type)
|
|
{
|
|
t38gateway_session_info_t *t38gateway_info;
|
|
switch_codec_t *read_codec;
|
|
switch_frame_t *frame;
|
|
int16_t *amp;
|
|
|
|
t38gateway_info = (t38gateway_session_info_t *) user_data;
|
|
if (t38gateway_info == NULL) {
|
|
return SWITCH_FALSE;
|
|
}
|
|
|
|
switch (type) {
|
|
case SWITCH_ABC_TYPE_INIT:
|
|
read_codec = switch_core_session_get_read_codec(t38gateway_info->session);
|
|
t38gateway_info->t38gateway_codec.rate = read_codec->implementation->samples_per_second;
|
|
t38gateway_info->t38gateway_codec.channels = read_codec->implementation->number_of_channels;
|
|
break;
|
|
case SWITCH_ABC_TYPE_READ_PING:
|
|
case SWITCH_ABC_TYPE_CLOSE:
|
|
break;
|
|
case SWITCH_ABC_TYPE_READ:
|
|
frame = switch_core_media_bug_get_read_replace_frame(bug);
|
|
amp = (int16_t *) frame->data;
|
|
t38_gateway_rx(t38gateway_info->t38, amp, frame->samples);
|
|
break;
|
|
case SWITCH_ABC_TYPE_WRITE:
|
|
case SWITCH_ABC_TYPE_READ_REPLACE:
|
|
case SWITCH_ABC_TYPE_WRITE_REPLACE:
|
|
break;
|
|
}
|
|
|
|
return SWITCH_TRUE;
|
|
}
|
|
|
|
/*! \brief FreeSWITCH module loading function
|
|
*
|
|
* @author Steve Underwood
|
|
* @return Load success or failure.
|
|
*/
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_t38gateway_load)
|
|
{
|
|
switch_application_interface_t *app_interface;
|
|
switch_api_interface_t *api_interface;
|
|
/* connect my internal structure to the blank pointer passed to me */
|
|
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "T.38 gateway enabled\n");
|
|
|
|
SWITCH_ADD_APP(app_interface, "t38gateway", "T.38 gateway", "T.38 gateway", t38gateway_start_function, "[start] [stop]", SAF_NONE);
|
|
|
|
SWITCH_ADD_API(api_interface, "t38gateway", "T.38 gateway", t38gateway_api_main, T38GATEWAY_SYNTAX);
|
|
|
|
/* indicate that the module should continue to be loaded */
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*! \brief FreeSWITCH application handler function.
|
|
* This handles calls made from applications such as LUA and the dialplan
|
|
*
|
|
* @author Steve Underwood
|
|
* @return Success or failure of the function.
|
|
*/
|
|
SWITCH_STANDARD_APP(t38gateway_start_function)
|
|
{
|
|
switch_media_bug_t *bug;
|
|
switch_status_t status;
|
|
switch_channel_t *channel;
|
|
t38gateway_session_info_t *t38gateway_info;
|
|
|
|
if (session == NULL)
|
|
return;
|
|
|
|
channel = switch_core_session_get_channel(session);
|
|
|
|
/* Is this channel already set? */
|
|
bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_t38gateway_");
|
|
/* If yes */
|
|
if (bug != NULL) {
|
|
/* If we have a stop remove audio bug */
|
|
if (strcasecmp(data, "stop") == 0) {
|
|
switch_channel_set_private(channel, "_t38gateway_", NULL);
|
|
switch_core_media_bug_remove(session, &bug);
|
|
return;
|
|
}
|
|
|
|
/* We have already started */
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n");
|
|
|
|
return;
|
|
}
|
|
|
|
t38gateway_info = (t38gateway_session_info_t *) switch_core_session_alloc(session, sizeof(t38gateway_session_info_t));
|
|
|
|
t38gateway_info->session = session;
|
|
t38gateway_info->t38 = t38_gateway_init(NULL, tx_packet_handler, (void *) t38gateway_info);
|
|
t38gateway_info->t38_core = t38_gateway_get_t38_core_state(t38gateway_info->t38);
|
|
t38gateway_info->udptl = udptl_init(NULL, UDPTL_ERROR_CORRECTION_REDUNDANCY, 3, 3, rx_packet_handler, (void *) t38gateway_info);
|
|
|
|
status = switch_core_media_bug_add(session, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
|
|
|
|
if (status != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failure hooking to stream\n");
|
|
return;
|
|
}
|
|
|
|
switch_channel_set_private(channel, "_t38gateway_", bug);
|
|
}
|
|
|
|
/*! \brief Called when the module shuts down
|
|
*
|
|
* @author Steve Underwood
|
|
* @return The success or failure of the function.
|
|
*/
|
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_t38gateway_shutdown)
|
|
{
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "T.38 gateway disabled\n");
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*! \brief FreeSWITCH API handler function.
|
|
* This function handles API calls such as the ones from mod_event_socket and in some cases
|
|
* scripts such as LUA scripts.
|
|
*
|
|
* @author Steve Underwood
|
|
* @return The success or failure of the function.
|
|
*/
|
|
SWITCH_STANDARD_API(t38gateway_api_main)
|
|
{
|
|
switch_core_session_t *t38gateway_session;
|
|
switch_media_bug_t *bug;
|
|
t38gateway_session_info_t *t38gateway_info;
|
|
switch_channel_t *channel;
|
|
switch_status_t status;
|
|
int argc;
|
|
char *argv[T38GATEWAY_PARAMS];
|
|
char *ccmd;
|
|
char *uuid;
|
|
char *command;
|
|
|
|
/* No command? Display usage */
|
|
if (cmd == NULL) {
|
|
stream->write_function(stream, "-USAGE: %s\n", T38GATEWAY_SYNTAX);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Duplicated contents of original string */
|
|
ccmd = strdup(cmd);
|
|
/* Separate the arguments */
|
|
argc = switch_separate_string(ccmd, ' ', argv, T38GATEWAY_PARAMS);
|
|
|
|
/* If we don't have the expected number of parameters
|
|
* display usage */
|
|
if (argc != T38GATEWAY_PARAMS) {
|
|
stream->write_function(stream, "-USAGE: %s\n", T38GATEWAY_SYNTAX);
|
|
switch_safe_free(ccmd);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
uuid = argv[0];
|
|
command = argv[1];
|
|
|
|
/* using uuid locate a reference to the FreeSWITCH session */
|
|
t38gateway_session = switch_core_session_locate(uuid);
|
|
|
|
/* If the session was not found exit */
|
|
if (t38gateway_session == NULL) {
|
|
switch_safe_free(ccmd);
|
|
stream->write_function(stream, "-USAGE: %s\n", T38GATEWAY_SYNTAX);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
/* Get current channel of the session to tag the session
|
|
* This indicates that our module is present */
|
|
channel = switch_core_session_get_channel(t38gateway_session);
|
|
|
|
/* Is this channel already set? */
|
|
bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_t38gateway_");
|
|
/* If yes */
|
|
if (bug != NULL) {
|
|
/* If we have a stop remove audio bug */
|
|
if (strcasecmp(command, "stop") == 0) {
|
|
switch_channel_set_private(channel, "_t38gateway_", NULL);
|
|
switch_core_media_bug_remove(t38gateway_session, &bug);
|
|
switch_safe_free(ccmd);
|
|
stream->write_function(stream, "+OK\n");
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* We have already started */
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n");
|
|
|
|
switch_safe_free(ccmd);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
/* If we don't see the expected start exit */
|
|
if (strcasecmp(command, "start") != 0) {
|
|
switch_safe_free(ccmd);
|
|
stream->write_function(stream, "-USAGE: %s\n", T38GATEWAY_SYNTAX);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
/* Allocate memory attached to this FreeSWITCH session for
|
|
* use in the callback routine and to store state information */
|
|
t38gateway_info = (t38gateway_session_info_t *) switch_core_session_alloc(t38gateway_session, sizeof(t38gateway_session_info_t));
|
|
|
|
/* Set initial values and states */
|
|
t38gateway_info->session = t38gateway_session;
|
|
t38gateway_info->t38 = t38_gateway_init(NULL, tx_packet_handler, (void *) t38gateway_info);
|
|
t38gateway_info->t38_core = t38_gateway_get_t38_core_state(t38gateway_info->t38);
|
|
t38gateway_info->udptl = udptl_init(NULL, UDPTL_ERROR_CORRECTION_REDUNDANCY, 3, 3, rx_packet_handler, (void *) t38gateway_info);
|
|
|
|
/* Add a media bug that allows me to intercept the
|
|
* reading leg of the audio stream */
|
|
status = switch_core_media_bug_add(t38gateway_session, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
|
|
|
|
/* If adding a media bug fails exit */
|
|
if (status != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failure hooking to stream\n");
|
|
|
|
switch_safe_free(ccmd);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
/* Set the t38gateway tag to detect an existing t38gateway media bug */
|
|
switch_channel_set_private(channel, "_t38gateway_", bug);
|
|
|
|
/* Everything went according to plan! Notify the user */
|
|
stream->write_function(stream, "+OK\n");
|
|
|
|
switch_safe_free(ccmd);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* For Emacs:
|
|
* Local Variables:
|
|
* mode:c
|
|
* indent-tabs-mode:t
|
|
* tab-width:4
|
|
* c-basic-offset:4
|
|
* End:
|
|
* For VIM:
|
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
|
|
*/
|