Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
F
freeswitch
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
张华
freeswitch
Commits
5be9857a
提交
5be9857a
authored
5月 31, 2012
作者:
Ken Rice
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
remove mod_fax. this has been deprecated for quite a while
上级
be2ec292
显示空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
0 行增加
和
2984 行删除
+0
-2984
Makefile.am
src/mod/applications/mod_fax/Makefile.am
+0
-24
mod_fax.2008.vcproj
src/mod/applications/mod_fax/mod_fax.2008.vcproj
+0
-299
mod_fax.c
src/mod/applications/mod_fax/mod_fax.c
+0
-1945
udptl.c
src/mod/applications/mod_fax/udptl.c
+0
-563
udptl.h
src/mod/applications/mod_fax/udptl.h
+0
-153
没有找到文件。
src/mod/applications/mod_fax/Makefile.am
deleted
100644 → 0
浏览文件 @
be2ec292
include
$(top_srcdir)/build/modmake.rulesam
MODNAME
=
mod_fax
TIFF_DIR
=
$(switch_srcdir)
/libs/tiff-3.8.2
TIFF_BUILDDIR
=
$(switch_builddir)
/libs/tiff-3.8.2
TIFF_LA
=
$(TIFF_BUILDDIR)
/libtiff/libtiff.la
SPANDSP_DIR
=
$(switch_srcdir)
/libs/spandsp
SPANDSP_BUILDDIR
=
$(switch_builddir)
/libs/spandsp
SPANDSP_LA
=
$(SPANDSP_BUILDDIR)
/src/libspandsp.la
mod_LTLIBRARIES
=
mod_fax.la
mod_fax_la_SOURCES
=
mod_fax.c udptl.c
mod_fax_la_CFLAGS
=
$(AM_CFLAGS)
-I
$(SPANDSP_DIR)
/src
-I
$(TIFF_DIR)
/libtiff
-I
$(SPANDSP_BUILDDIR)
/src
-I
$(TIFF_BUILDDIR)
/libtiff
-I
.
mod_fax_la_LIBADD
=
$(switch_builddir)
/libfreeswitch.la
$(SPANDSP_LA)
$(TIFF_LA)
mod_fax_la_LDFLAGS
=
-avoid-version
-module
-no-undefined
-shared
-ljpeg
$(SPANDSP_LA)
:
$(TIFF_LA) $(SPANDSP_DIR) $(SPANDSP_DIR)/.update
cd
$(SPANDSP_BUILDDIR)
&&
$(MAKE)
-j1
$(TOUCH_TARGET)
$(TIFF_LA)
:
$(TIFF_DIR) $(TIFF_DIR)/.update
cd
$(TIFF_BUILDDIR)
&&
$(MAKE)
-j1
$(TOUCH_TARGET)
src/mod/applications/mod_fax/mod_fax.2008.vcproj
deleted
100644 → 0
浏览文件 @
be2ec292
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType=
"Visual C++"
Version=
"9.00"
Name=
"mod_fax"
ProjectGUID=
"{7877EFC8-4807-484B-B573-D7B7FD058FAA}"
RootNamespace=
"mod_fax"
Keyword=
"Win32Proj"
TargetFrameworkVersion=
"131072"
>
<Platforms>
<Platform
Name=
"Win32"
/>
<Platform
Name=
"x64"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name=
"Debug|Win32"
ConfigurationType=
"2"
InheritedPropertySheets=
"..\..\..\..\w32\module_debug.vsprops"
CharacterSet=
"0"
>
<Tool
Name=
"VCPreBuildEventTool"
/>
<Tool
Name=
"VCCustomBuildTool"
/>
<Tool
Name=
"VCXMLDataGeneratorTool"
/>
<Tool
Name=
"VCWebServiceProxyGeneratorTool"
/>
<Tool
Name=
"VCMIDLTool"
/>
<Tool
Name=
"VCCLCompilerTool"
AdditionalIncludeDirectories=
""$(InputDir)..\..\..\..\libs\spandsp\src\msvc";"$(InputDir)..\..\..\..\libs\spandsp\src";"$(InputDir)..\..\..\..\libs\tiff-3.8.2\libtiff""
UsePrecompiledHeader=
"0"
/>
<Tool
Name=
"VCManagedResourceCompilerTool"
/>
<Tool
Name=
"VCResourceCompilerTool"
/>
<Tool
Name=
"VCPreLinkEventTool"
/>
<Tool
Name=
"VCLinkerTool"
AdditionalDependencies=
"ws2_32.lib"
RandomizedBaseAddress=
"1"
DataExecutionPrevention=
"0"
/>
<Tool
Name=
"VCALinkTool"
/>
<Tool
Name=
"VCManifestTool"
/>
<Tool
Name=
"VCXDCMakeTool"
/>
<Tool
Name=
"VCBscMakeTool"
/>
<Tool
Name=
"VCFxCopTool"
/>
<Tool
Name=
"VCAppVerifierTool"
/>
<Tool
Name=
"VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name=
"Debug|x64"
ConfigurationType=
"2"
InheritedPropertySheets=
"..\..\..\..\w32\module_debug.vsprops"
CharacterSet=
"0"
>
<Tool
Name=
"VCPreBuildEventTool"
/>
<Tool
Name=
"VCCustomBuildTool"
/>
<Tool
Name=
"VCXMLDataGeneratorTool"
/>
<Tool
Name=
"VCWebServiceProxyGeneratorTool"
/>
<Tool
Name=
"VCMIDLTool"
TargetEnvironment=
"3"
/>
<Tool
Name=
"VCCLCompilerTool"
AdditionalIncludeDirectories=
""$(InputDir)..\..\..\..\libs\spandsp\src\msvc";"$(InputDir)..\..\..\..\libs\spandsp\src";"$(InputDir)..\..\..\..\libs\tiff-3.8.2\libtiff""
UsePrecompiledHeader=
"0"
/>
<Tool
Name=
"VCManagedResourceCompilerTool"
/>
<Tool
Name=
"VCResourceCompilerTool"
/>
<Tool
Name=
"VCPreLinkEventTool"
/>
<Tool
Name=
"VCLinkerTool"
AdditionalDependencies=
"ws2_32.lib"
OutputFile=
"$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
RandomizedBaseAddress=
"1"
DataExecutionPrevention=
"0"
TargetMachine=
"17"
/>
<Tool
Name=
"VCALinkTool"
/>
<Tool
Name=
"VCManifestTool"
/>
<Tool
Name=
"VCXDCMakeTool"
/>
<Tool
Name=
"VCBscMakeTool"
/>
<Tool
Name=
"VCFxCopTool"
/>
<Tool
Name=
"VCAppVerifierTool"
/>
<Tool
Name=
"VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name=
"Release|Win32"
ConfigurationType=
"2"
InheritedPropertySheets=
"..\..\..\..\w32\module_release.vsprops"
CharacterSet=
"0"
>
<Tool
Name=
"VCPreBuildEventTool"
/>
<Tool
Name=
"VCCustomBuildTool"
/>
<Tool
Name=
"VCXMLDataGeneratorTool"
/>
<Tool
Name=
"VCWebServiceProxyGeneratorTool"
/>
<Tool
Name=
"VCMIDLTool"
/>
<Tool
Name=
"VCCLCompilerTool"
AdditionalIncludeDirectories=
""$(InputDir)..\..\..\..\libs\spandsp\src\msvc";"$(InputDir)..\..\..\..\libs\spandsp\src";"$(InputDir)..\..\..\..\libs\tiff-3.8.2\libtiff""
UsePrecompiledHeader=
"0"
/>
<Tool
Name=
"VCManagedResourceCompilerTool"
/>
<Tool
Name=
"VCResourceCompilerTool"
/>
<Tool
Name=
"VCPreLinkEventTool"
/>
<Tool
Name=
"VCLinkerTool"
AdditionalDependencies=
"ws2_32.lib"
RandomizedBaseAddress=
"1"
DataExecutionPrevention=
"0"
/>
<Tool
Name=
"VCALinkTool"
/>
<Tool
Name=
"VCManifestTool"
/>
<Tool
Name=
"VCXDCMakeTool"
/>
<Tool
Name=
"VCBscMakeTool"
/>
<Tool
Name=
"VCFxCopTool"
/>
<Tool
Name=
"VCAppVerifierTool"
/>
<Tool
Name=
"VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name=
"Release|x64"
ConfigurationType=
"2"
InheritedPropertySheets=
"..\..\..\..\w32\module_release.vsprops"
CharacterSet=
"0"
>
<Tool
Name=
"VCPreBuildEventTool"
/>
<Tool
Name=
"VCCustomBuildTool"
/>
<Tool
Name=
"VCXMLDataGeneratorTool"
/>
<Tool
Name=
"VCWebServiceProxyGeneratorTool"
/>
<Tool
Name=
"VCMIDLTool"
TargetEnvironment=
"3"
/>
<Tool
Name=
"VCCLCompilerTool"
AdditionalIncludeDirectories=
""$(InputDir)..\..\..\..\libs\spandsp\src\msvc";"$(InputDir)..\..\..\..\libs\spandsp\src";"$(InputDir)..\..\..\..\libs\tiff-3.8.2\libtiff""
UsePrecompiledHeader=
"0"
/>
<Tool
Name=
"VCManagedResourceCompilerTool"
/>
<Tool
Name=
"VCResourceCompilerTool"
/>
<Tool
Name=
"VCPreLinkEventTool"
/>
<Tool
Name=
"VCLinkerTool"
AdditionalDependencies=
"ws2_32.lib"
OutputFile=
"$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
RandomizedBaseAddress=
"1"
DataExecutionPrevention=
"0"
TargetMachine=
"17"
/>
<Tool
Name=
"VCALinkTool"
/>
<Tool
Name=
"VCManifestTool"
/>
<Tool
Name=
"VCXDCMakeTool"
/>
<Tool
Name=
"VCBscMakeTool"
/>
<Tool
Name=
"VCFxCopTool"
/>
<Tool
Name=
"VCAppVerifierTool"
/>
<Tool
Name=
"VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath=
".\mod_fax.c"
>
</File>
<File
RelativePath=
".\udptl.c"
>
</File>
<File
RelativePath=
".\udptl.h"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>
src/mod/applications/mod_fax/mod_fax.c
deleted
100644 → 0
浏览文件 @
be2ec292
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.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.
*
* The Original Code is FreeSWITCH mod_fax.
*
* The Initial Developer of the Original Code is
* Massimo Cetra <devel@navynet.it>
*
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Brian West <brian@freeswitch.org>
* Anthony Minessale II <anthm@freeswitch.org>
* Steve Underwood <steveu@coppice.org>
* Antonio Gallo <agx@linux.it>
* mod_fax.c -- Fax applications provided by SpanDSP
*
*/
#include <switch.h>
#ifdef WIN32
#define FAX_INVALID_SOCKET INVALID_HANDLE_VALUE
typedef
HANDLE
zap_socket_t
;
#else
#define FAX_INVALID_SOCKET -1
typedef
int
zap_socket_t
;
#endif
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
#include <spandsp.h>
#include <spandsp/version.h>
#include "udptl.h"
#define LOCAL_FAX_MAX_DATAGRAM 400
#define MAX_FEC_ENTRIES 4
#define MAX_FEC_SPAN 4
/*****************************************************************************
OUR DEFINES AND STRUCTS
*****************************************************************************/
typedef
enum
{
FUNCTION_TX
,
FUNCTION_RX
,
FUNCTION_GW
}
application_mode_t
;
typedef
enum
{
T38_MODE
,
AUDIO_MODE
,
T38_GATEWAY_MODE
}
transport_mode_t
;
typedef
enum
{
T38_MODE_UNKNOWN
=
0
,
T38_MODE_NEGOTIATED
=
1
,
T38_MODE_REQUESTED
=
2
,
T38_MODE_REFUSED
=
-
1
,
}
t38_mode_t
;
/* The global stuff */
static
struct
{
switch_memory_pool_t
*
pool
;
switch_mutex_t
*
mutex
;
uint32_t
total_sessions
;
short
int
use_ecm
;
short
int
verbose
;
short
int
disable_v17
;
short
int
enable_t38
;
short
int
enable_t38_request
;
short
int
enable_t38_insist
;
char
ident
[
20
];
char
header
[
50
];
char
*
prepend_string
;
char
*
spool
;
}
globals
;
struct
pvt_s
{
switch_core_session_t
*
session
;
application_mode_t
app_mode
;
fax_state_t
*
fax_state
;
t38_terminal_state_t
*
t38_state
;
t38_gateway_state_t
*
t38_gateway_state
;
t38_core_state_t
*
t38_core
;
udptl_state_t
*
udptl_state
;
char
*
filename
;
char
*
ident
;
char
*
header
;
int
use_ecm
;
int
disable_v17
;
int
verbose
;
int
caller
;
int
tx_page_start
;
int
tx_page_end
;
int
done
;
t38_mode_t
t38_mode
;
struct
pvt_s
*
next
;
};
typedef
struct
pvt_s
pvt_t
;
static
void
launch_timer_thread
(
void
);
static
struct
{
pvt_t
*
head
;
switch_mutex_t
*
mutex
;
switch_thread_t
*
thread
;
int
thread_running
;
}
t38_state_list
;
static
int
add_pvt
(
pvt_t
*
pvt
)
{
int
r
=
0
;
uint32_t
sanity
=
50
;
switch_mutex_lock
(
t38_state_list
.
mutex
);
if
(
!
t38_state_list
.
thread_running
)
{
launch_timer_thread
();
while
(
--
sanity
&&
!
t38_state_list
.
thread_running
)
{
switch_yield
(
10000
);
}
}
switch_mutex_unlock
(
t38_state_list
.
mutex
);
if
(
t38_state_list
.
thread_running
)
{
switch_mutex_lock
(
t38_state_list
.
mutex
);
pvt
->
next
=
t38_state_list
.
head
;
t38_state_list
.
head
=
pvt
;
switch_mutex_unlock
(
t38_state_list
.
mutex
);
}
return
r
;
}
static
int
del_pvt
(
pvt_t
*
del_pvt
)
{
pvt_t
*
p
,
*
l
=
NULL
;
int
r
=
0
;
if
(
!
t38_state_list
.
thread_running
)
goto
end
;
switch_mutex_lock
(
t38_state_list
.
mutex
);
for
(
p
=
t38_state_list
.
head
;
p
;
p
=
p
->
next
)
{
if
(
p
==
del_pvt
)
{
if
(
l
)
{
l
->
next
=
p
->
next
;
}
else
{
t38_state_list
.
head
=
p
->
next
;
}
p
->
next
=
NULL
;
r
=
1
;
goto
end
;
}
l
=
p
;
}
end:
switch_mutex_unlock
(
t38_state_list
.
mutex
);
return
r
;
}
static
void
*
SWITCH_THREAD_FUNC
timer_thread_run
(
switch_thread_t
*
thread
,
void
*
obj
)
{
switch_timer_t
timer
=
{
0
};
pvt_t
*
pvt
;
int
samples
=
240
;
int
ms
=
30
;
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"timer thread started.
\n
"
);
if
(
switch_core_timer_init
(
&
timer
,
"soft"
,
ms
,
samples
,
NULL
)
!=
SWITCH_STATUS_SUCCESS
)
{
return
NULL
;
}
t38_state_list
.
thread_running
=
1
;
while
(
t38_state_list
.
thread_running
)
{
switch_mutex_lock
(
t38_state_list
.
mutex
);
if
(
!
t38_state_list
.
head
)
{
switch_mutex_unlock
(
t38_state_list
.
mutex
);
goto
end
;
}
for
(
pvt
=
t38_state_list
.
head
;
pvt
;
pvt
=
pvt
->
next
)
{
if
(
pvt
->
udptl_state
)
{
t38_terminal_send_timeout
(
pvt
->
t38_state
,
samples
);
}
}
switch_mutex_unlock
(
t38_state_list
.
mutex
);
switch_core_timer_next
(
&
timer
);
}
end:
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"timer thread ended.
\n
"
);
t38_state_list
.
thread_running
=
0
;
switch_core_timer_destroy
(
&
timer
);
return
NULL
;
}
static
void
launch_timer_thread
(
void
)
{
switch_threadattr_t
*
thd_attr
=
NULL
;
switch_threadattr_create
(
&
thd_attr
,
globals
.
pool
);
switch_threadattr_stacksize_set
(
thd_attr
,
SWITCH_THREAD_STACKSIZE
);
switch_thread_create
(
&
t38_state_list
.
thread
,
thd_attr
,
timer_thread_run
,
NULL
,
globals
.
pool
);
}
/*****************************************************************************
LOGGING AND HELPER FUNCTIONS
*****************************************************************************/
static
void
counter_increment
(
void
)
{
switch_mutex_lock
(
globals
.
mutex
);
globals
.
total_sessions
++
;
switch_mutex_unlock
(
globals
.
mutex
);
}
static
void
spanfax_log_message
(
int
level
,
const
char
*
msg
)
{
int
fs_log_level
;
switch
(
level
)
{
case
SPAN_LOG_NONE
:
return
;
case
SPAN_LOG_ERROR
:
case
SPAN_LOG_PROTOCOL_ERROR
:
fs_log_level
=
SWITCH_LOG_ERROR
;
break
;
case
SPAN_LOG_WARNING
:
case
SPAN_LOG_PROTOCOL_WARNING
:
fs_log_level
=
SWITCH_LOG_WARNING
;
break
;
case
SPAN_LOG_FLOW
:
case
SPAN_LOG_FLOW_2
:
case
SPAN_LOG_FLOW_3
:
default:
/* SPAN_LOG_DEBUG, SPAN_LOG_DEBUG_2, SPAN_LOG_DEBUG_3 */
fs_log_level
=
SWITCH_LOG_DEBUG
;
break
;
}
if
(
!
zstr
(
msg
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
fs_log_level
,
"%s"
,
msg
);
}
}
/*
* Called at the end of the document
*/
static
void
phase_e_handler
(
t30_state_t
*
s
,
void
*
user_data
,
int
result
)
{
t30_stats_t
t
;
const
char
*
local_ident
;
const
char
*
far_ident
;
switch_core_session_t
*
session
;
switch_channel_t
*
channel
;
pvt_t
*
pvt
;
char
*
tmp
;
pvt
=
(
pvt_t
*
)
user_data
;
switch_assert
(
pvt
);
session
=
pvt
->
session
;
switch_assert
(
session
);
channel
=
switch_core_session_get_channel
(
session
);
switch_assert
(
channel
);
t30_get_transfer_statistics
(
s
,
&
t
);
local_ident
=
switch_str_nil
(
t30_get_tx_ident
(
s
));
far_ident
=
switch_str_nil
(
t30_get_rx_ident
(
s
));
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"==============================================================================
\n
"
);
if
(
result
==
T30_ERR_OK
)
{
if
(
pvt
->
app_mode
==
FUNCTION_TX
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Fax successfully sent.
\n
"
);
}
else
if
(
pvt
->
app_mode
==
FUNCTION_RX
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Fax successfully received.
\n
"
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Fax successfully managed. How ?
\n
"
);
}
switch_channel_set_variable
(
channel
,
"fax_success"
,
"1"
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Fax processing not successful - result (%d) %s.
\n
"
,
result
,
t30_completion_code_to_str
(
result
));
switch_channel_set_variable
(
channel
,
"fax_success"
,
"0"
);
}
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Remote station id: %s
\n
"
,
far_ident
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Local station id: %s
\n
"
,
local_ident
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Pages transferred: %i
\n
"
,
pvt
->
app_mode
==
FUNCTION_TX
?
t
.
pages_tx
:
t
.
pages_rx
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Total fax pages: %i
\n
"
,
t
.
pages_in_file
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Image resolution: %ix%i
\n
"
,
t
.
x_resolution
,
t
.
y_resolution
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Transfer Rate: %i
\n
"
,
t
.
bit_rate
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"ECM status %s
\n
"
,
(
t
.
error_correcting_mode
)
?
"on"
:
"off"
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"remote country: %s
\n
"
,
switch_str_nil
(
t30_get_rx_country
(
s
)));
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"remote vendor: %s
\n
"
,
switch_str_nil
(
t30_get_rx_vendor
(
s
)));
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"remote model: %s
\n
"
,
switch_str_nil
(
t30_get_rx_model
(
s
)));
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"==============================================================================
\n
"
);
/*
Set our channel variables
*/
tmp
=
switch_mprintf
(
"%i"
,
result
);
if
(
tmp
)
{
switch_channel_set_variable
(
channel
,
"fax_result_code"
,
tmp
);
switch_safe_free
(
tmp
);
}
switch_channel_set_variable
(
channel
,
"fax_result_text"
,
t30_completion_code_to_str
(
result
));
switch_channel_set_variable
(
channel
,
"fax_ecm_used"
,
(
t
.
error_correcting_mode
)
?
"on"
:
"off"
);
switch_channel_set_variable
(
channel
,
"fax_local_station_id"
,
local_ident
);
switch_channel_set_variable
(
channel
,
"fax_remote_station_id"
,
far_ident
);
tmp
=
switch_mprintf
(
"%i"
,
pvt
->
app_mode
==
FUNCTION_TX
?
t
.
pages_tx
:
t
.
pages_rx
);
if
(
tmp
)
{
switch_channel_set_variable
(
channel
,
"fax_document_transferred_pages"
,
tmp
);
switch_safe_free
(
tmp
);
}
tmp
=
switch_mprintf
(
"%i"
,
t
.
pages_in_file
);
if
(
tmp
)
{
switch_channel_set_variable
(
channel
,
"fax_document_total_pages"
,
tmp
);
switch_safe_free
(
tmp
);
}
tmp
=
switch_mprintf
(
"%ix%i"
,
t
.
x_resolution
,
t
.
y_resolution
);
if
(
tmp
)
{
switch_channel_set_variable
(
channel
,
"fax_image_resolution"
,
tmp
);
switch_safe_free
(
tmp
);
}
tmp
=
switch_mprintf
(
"%d"
,
t
.
image_size
);
if
(
tmp
)
{
switch_channel_set_variable
(
channel
,
"fax_image_size"
,
tmp
);
switch_safe_free
(
tmp
);
}
tmp
=
switch_mprintf
(
"%d"
,
t
.
bad_rows
);
if
(
tmp
)
{
switch_channel_set_variable
(
channel
,
"fax_bad_rows"
,
tmp
);
switch_safe_free
(
tmp
);
}
tmp
=
switch_mprintf
(
"%i"
,
t
.
bit_rate
);
if
(
tmp
)
{
switch_channel_set_variable
(
channel
,
"fax_transfer_rate"
,
tmp
);
switch_safe_free
(
tmp
);
}
/* switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); */
pvt
->
done
=
1
;
/*
TODO Fire events
*/
}
static
int
t38_tx_packet_handler
(
t38_core_state_t
*
s
,
void
*
user_data
,
const
uint8_t
*
buf
,
int
len
,
int
count
)
{
switch_frame_t
out_frame
=
{
0
};
switch_core_session_t
*
session
;
switch_channel_t
*
channel
;
pvt_t
*
pvt
;
uint8_t
pkt
[
LOCAL_FAX_MAX_DATAGRAM
];
int
x
;
int
r
=
0
;
pvt
=
(
pvt_t
*
)
user_data
;
session
=
pvt
->
session
;
channel
=
switch_core_session_get_channel
(
session
);
/* we need to build a real packet here and make write_frame.packet and write_frame.packetlen point to it */
out_frame
.
flags
=
SFF_UDPTL_PACKET
|
SFF_PROXY_PACKET
;
out_frame
.
packet
=
pkt
;
out_frame
.
packetlen
=
udptl_build_packet
(
pvt
->
udptl_state
,
pkt
,
buf
,
len
);
//switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "WRITE %d udptl bytes\n", out_frame.packetlen);
for
(
x
=
0
;
x
<
count
;
x
++
)
{
if
(
switch_core_session_write_frame
(
session
,
&
out_frame
,
SWITCH_IO_FLAG_NONE
,
0
)
!=
SWITCH_STATUS_SUCCESS
)
{
r
=
-
1
;
break
;
}
}
return
r
;
}
static
switch_status_t
spanfax_init
(
pvt_t
*
pvt
,
transport_mode_t
trans_mode
)
{
switch_core_session_t
*
session
;
switch_channel_t
*
channel
;
fax_state_t
*
fax
;
t38_terminal_state_t
*
t38
;
t30_state_t
*
t30
;
session
=
(
switch_core_session_t
*
)
pvt
->
session
;
switch_assert
(
session
);
channel
=
switch_core_session_get_channel
(
session
);
switch_assert
(
channel
);
switch
(
trans_mode
)
{
case
AUDIO_MODE
:
if
(
pvt
->
fax_state
==
NULL
)
{
pvt
->
fax_state
=
(
fax_state_t
*
)
switch_core_session_alloc
(
pvt
->
session
,
sizeof
(
fax_state_t
));
}
if
(
pvt
->
fax_state
==
NULL
)
{
return
SWITCH_STATUS_FALSE
;
}
fax
=
pvt
->
fax_state
;
t30
=
fax_get_t30_state
(
fax
);
memset
(
fax
,
0
,
sizeof
(
fax_state_t
));
if
(
fax_init
(
fax
,
pvt
->
caller
)
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot initialize my fax structs
\n
"
);
return
SWITCH_STATUS_FALSE
;
}
fax_set_transmit_on_idle
(
fax
,
TRUE
);
span_log_set_message_handler
(
&
fax
->
logging
,
spanfax_log_message
);
span_log_set_message_handler
(
&
t30
->
logging
,
spanfax_log_message
);
if
(
pvt
->
verbose
)
{
span_log_set_level
(
&
fax
->
logging
,
SPAN_LOG_SHOW_SEVERITY
|
SPAN_LOG_SHOW_PROTOCOL
|
SPAN_LOG_FLOW
);
span_log_set_level
(
&
t30
->
logging
,
SPAN_LOG_SHOW_SEVERITY
|
SPAN_LOG_SHOW_PROTOCOL
|
SPAN_LOG_FLOW
);
}
break
;
case
T38_MODE
:
if
(
pvt
->
t38_state
==
NULL
)
{
pvt
->
t38_state
=
(
t38_terminal_state_t
*
)
switch_core_session_alloc
(
pvt
->
session
,
sizeof
(
t38_terminal_state_t
));
}
if
(
pvt
->
t38_state
==
NULL
)
{
return
SWITCH_STATUS_FALSE
;
}
if
(
pvt
->
udptl_state
==
NULL
)
{
pvt
->
udptl_state
=
(
udptl_state_t
*
)
switch_core_session_alloc
(
pvt
->
session
,
sizeof
(
udptl_state_t
));
}
if
(
pvt
->
udptl_state
==
NULL
)
{
t38_terminal_free
(
pvt
->
t38_state
);
pvt
->
t38_state
=
NULL
;
return
SWITCH_STATUS_FALSE
;
}
/* add to timer thread processing */
add_pvt
(
pvt
);
t38
=
pvt
->
t38_state
;
t30
=
t38_terminal_get_t30_state
(
t38
);
memset
(
t38
,
0
,
sizeof
(
t38_terminal_state_t
));
if
(
t38_terminal_init
(
t38
,
pvt
->
caller
,
t38_tx_packet_handler
,
pvt
)
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot initialize my T.38 structs
\n
"
);
return
SWITCH_STATUS_FALSE
;
}
pvt
->
t38_core
=
t38_terminal_get_t38_core_state
(
pvt
->
t38_state
);
if
(
udptl_init
(
pvt
->
udptl_state
,
UDPTL_ERROR_CORRECTION_REDUNDANCY
,
3
,
3
,
(
udptl_rx_packet_handler_t
*
)
t38_core_rx_ifp_packet
,
(
void
*
)
pvt
->
t38_core
)
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot initialize my UDPTL structs
\n
"
);
return
SWITCH_STATUS_FALSE
;
}
span_log_set_message_handler
(
&
t38
->
logging
,
spanfax_log_message
);
span_log_set_message_handler
(
&
t30
->
logging
,
spanfax_log_message
);
if
(
pvt
->
verbose
)
{
span_log_set_level
(
&
t38
->
logging
,
SPAN_LOG_SHOW_SEVERITY
|
SPAN_LOG_SHOW_PROTOCOL
|
SPAN_LOG_FLOW
);
span_log_set_level
(
&
t30
->
logging
,
SPAN_LOG_SHOW_SEVERITY
|
SPAN_LOG_SHOW_PROTOCOL
|
SPAN_LOG_FLOW
);
}
break
;
case
T38_GATEWAY_MODE
:
if
(
pvt
->
t38_gateway_state
==
NULL
)
{
pvt
->
t38_gateway_state
=
(
t38_gateway_state_t
*
)
switch_core_session_alloc
(
pvt
->
session
,
sizeof
(
t38_gateway_state_t
));
}
if
(
pvt
->
udptl_state
==
NULL
)
{
pvt
->
udptl_state
=
(
udptl_state_t
*
)
switch_core_session_alloc
(
pvt
->
session
,
sizeof
(
udptl_state_t
));
}
if
(
t38_gateway_init
(
pvt
->
t38_gateway_state
,
t38_tx_packet_handler
,
pvt
)
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot initialize my T.38 structs
\n
"
);
t38_gateway_free
(
pvt
->
t38_gateway_state
);
pvt
->
t38_gateway_state
=
NULL
;
return
SWITCH_STATUS_FALSE
;
}
pvt
->
t38_core
=
t38_gateway_get_t38_core_state
(
pvt
->
t38_gateway_state
);
if
(
udptl_init
(
pvt
->
udptl_state
,
UDPTL_ERROR_CORRECTION_REDUNDANCY
,
3
,
3
,
(
udptl_rx_packet_handler_t
*
)
t38_core_rx_ifp_packet
,
(
void
*
)
pvt
->
t38_core
)
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot initialize my UDPTL structs
\n
"
);
t38_gateway_free
(
pvt
->
t38_gateway_state
);
udptl_release
(
pvt
->
udptl_state
);
pvt
->
udptl_state
=
NULL
;
return
SWITCH_STATUS_FALSE
;
}
t38_gateway_set_transmit_on_idle
(
pvt
->
t38_gateway_state
,
TRUE
);
if
(
switch_true
(
switch_channel_get_variable
(
channel
,
"fax_v17_disabled"
)))
{
t38_gateway_set_supported_modems
(
pvt
->
t38_gateway_state
,
T30_SUPPORT_V29
|
T30_SUPPORT_V27TER
);
}
else
{
t38_gateway_set_supported_modems
(
pvt
->
t38_gateway_state
,
T30_SUPPORT_V17
|
T30_SUPPORT_V29
|
T30_SUPPORT_V27TER
);
}
t38_gateway_set_ecm_capability
(
pvt
->
t38_gateway_state
,
pvt
->
use_ecm
);
switch_channel_set_variable
(
channel
,
"fax_ecm_requested"
,
pvt
->
use_ecm
?
"true"
:
"false"
);
if
(
switch_true
(
switch_channel_get_variable
(
channel
,
"FAX_DISABLE_ECM"
)))
{
t38_gateway_set_ecm_capability
(
pvt
->
t38_gateway_state
,
FALSE
);
}
else
{
t38_gateway_set_ecm_capability
(
pvt
->
t38_gateway_state
,
TRUE
);
}
span_log_set_message_handler
(
&
pvt
->
t38_gateway_state
->
logging
,
spanfax_log_message
);
span_log_set_message_handler
(
&
pvt
->
t38_core
->
logging
,
spanfax_log_message
);
if
(
pvt
->
verbose
)
{
span_log_set_level
(
&
pvt
->
t38_gateway_state
->
logging
,
SPAN_LOG_SHOW_SEVERITY
|
SPAN_LOG_SHOW_PROTOCOL
|
SPAN_LOG_FLOW
);
span_log_set_level
(
&
pvt
->
t38_core
->
logging
,
SPAN_LOG_SHOW_SEVERITY
|
SPAN_LOG_SHOW_PROTOCOL
|
SPAN_LOG_FLOW
);
}
t38_set_t38_version
(
pvt
->
t38_core
,
0
);
t38_gateway_set_ecm_capability
(
pvt
->
t38_gateway_state
,
1
);
return
SWITCH_STATUS_SUCCESS
;
default:
assert
(
0
);
/* What? */
return
SWITCH_STATUS_SUCCESS
;
}
/* Switch trans mode */
/* All the things which are common to audio and T.38 FAX setup */
t30_set_tx_ident
(
t30
,
pvt
->
ident
);
t30_set_tx_page_header_info
(
t30
,
pvt
->
header
);
t30_set_phase_e_handler
(
t30
,
phase_e_handler
,
pvt
);
t30_set_supported_image_sizes
(
t30
,
T30_SUPPORT_US_LETTER_LENGTH
|
T30_SUPPORT_US_LEGAL_LENGTH
|
T30_SUPPORT_UNLIMITED_LENGTH
|
T30_SUPPORT_215MM_WIDTH
|
T30_SUPPORT_255MM_WIDTH
|
T30_SUPPORT_303MM_WIDTH
);
t30_set_supported_resolutions
(
t30
,
T30_SUPPORT_STANDARD_RESOLUTION
|
T30_SUPPORT_FINE_RESOLUTION
|
T30_SUPPORT_SUPERFINE_RESOLUTION
|
T30_SUPPORT_R8_RESOLUTION
|
T30_SUPPORT_R16_RESOLUTION
);
if
(
pvt
->
disable_v17
)
{
t30_set_supported_modems
(
t30
,
T30_SUPPORT_V29
|
T30_SUPPORT_V27TER
);
switch_channel_set_variable
(
channel
,
"fax_v17_disabled"
,
"1"
);
}
else
{
t30_set_supported_modems
(
t30
,
T30_SUPPORT_V29
|
T30_SUPPORT_V27TER
|
T30_SUPPORT_V17
);
switch_channel_set_variable
(
channel
,
"fax_v17_disabled"
,
"0"
);
}
if
(
pvt
->
use_ecm
)
{
t30_set_supported_compressions
(
t30
,
T30_SUPPORT_T4_1D_COMPRESSION
|
T30_SUPPORT_T4_2D_COMPRESSION
|
T30_SUPPORT_T6_COMPRESSION
);
t30_set_ecm_capability
(
t30
,
TRUE
);
switch_channel_set_variable
(
channel
,
"fax_ecm_requested"
,
"1"
);
}
else
{
t30_set_supported_compressions
(
t30
,
T30_SUPPORT_T4_1D_COMPRESSION
|
T30_SUPPORT_T4_2D_COMPRESSION
);
switch_channel_set_variable
(
channel
,
"fax_ecm_requested"
,
"0"
);
}
if
(
pvt
->
app_mode
==
FUNCTION_TX
)
{
t30_set_tx_file
(
t30
,
pvt
->
filename
,
pvt
->
tx_page_start
,
pvt
->
tx_page_end
);
}
else
{
t30_set_rx_file
(
t30
,
pvt
->
filename
,
-
1
);
}
switch_channel_set_variable
(
channel
,
"fax_filename"
,
pvt
->
filename
);
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
spanfax_destroy
(
pvt_t
*
pvt
)
{
int
terminate
;
t30_state_t
*
t30
;
if
(
!
pvt
)
return
SWITCH_STATUS_FALSE
;
if
(
pvt
->
fax_state
)
{
if
(
pvt
->
t38_state
)
{
terminate
=
0
;
}
else
{
terminate
=
1
;
}
t30
=
fax_get_t30_state
(
pvt
->
fax_state
);
if
(
terminate
&&
t30
)
{
t30_terminate
(
t30
);
}
fax_release
(
pvt
->
fax_state
);
}
if
(
pvt
->
t38_state
)
{
/* remove from timer thread processing */
del_pvt
(
pvt
);
if
(
pvt
->
t38_state
)
{
terminate
=
1
;
}
else
{
terminate
=
0
;
}
t30
=
t38_terminal_get_t30_state
(
pvt
->
t38_state
);
if
(
terminate
&&
t30
)
{
t30_terminate
(
t30
);
}
t38_terminal_release
(
pvt
->
t38_state
);
}
if
(
pvt
->
t38_gateway_state
)
{
t38_gateway_release
(
pvt
->
t38_gateway_state
);
}
if
(
pvt
->
udptl_state
)
{
udptl_release
(
pvt
->
udptl_state
);
}
return
SWITCH_STATUS_SUCCESS
;
}
static
t38_mode_t
configure_t38
(
pvt_t
*
pvt
)
{
switch_core_session_t
*
session
=
pvt
->
session
;
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_t38_options_t
*
t38_options
=
switch_channel_get_private
(
channel
,
"t38_options"
);
int
method
=
2
;
if
(
!
t38_options
||
!
pvt
||
!
pvt
->
t38_core
)
{
pvt
->
t38_mode
=
T38_MODE_REFUSED
;
return
pvt
->
t38_mode
;
}
t38_set_t38_version
(
pvt
->
t38_core
,
t38_options
->
T38FaxVersion
);
t38_set_max_buffer_size
(
pvt
->
t38_core
,
t38_options
->
T38FaxMaxBuffer
);
t38_set_fastest_image_data_rate
(
pvt
->
t38_core
,
t38_options
->
T38MaxBitRate
);
t38_set_fill_bit_removal
(
pvt
->
t38_core
,
t38_options
->
T38FaxFillBitRemoval
);
t38_set_mmr_transcoding
(
pvt
->
t38_core
,
t38_options
->
T38FaxTranscodingMMR
);
t38_set_jbig_transcoding
(
pvt
->
t38_core
,
t38_options
->
T38FaxTranscodingJBIG
);
t38_set_max_datagram_size
(
pvt
->
t38_core
,
t38_options
->
T38FaxMaxDatagram
);
if
(
t38_options
->
T38FaxRateManagement
)
{
if
(
!
strcasecmp
(
t38_options
->
T38FaxRateManagement
,
"transferredTCF"
))
{
method
=
2
;
}
else
{
method
=
1
;
}
}
t38_set_data_rate_management_method
(
pvt
->
t38_core
,
method
);
//t38_set_data_transport_protocol(pvt->t38_core, int data_transport_protocol);
//t38_set_redundancy_control(pvt->t38_core, int category, int setting);
return
pvt
->
t38_mode
;
}
static
t38_mode_t
negotiate_t38
(
pvt_t
*
pvt
)
{
switch_core_session_t
*
session
=
pvt
->
session
;
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_core_session_message_t
msg
=
{
0
};
switch_t38_options_t
*
t38_options
=
switch_channel_get_private
(
channel
,
"t38_options"
);
int
enabled
=
0
,
insist
=
0
;
const
char
*
v
;
pvt
->
t38_mode
=
T38_MODE_REFUSED
;
if
(
pvt
->
app_mode
==
FUNCTION_GW
)
{
enabled
=
1
;
}
else
if
((
v
=
switch_channel_get_variable
(
channel
,
"fax_enable_t38"
)))
{
enabled
=
switch_true
(
v
);
}
else
{
enabled
=
globals
.
enable_t38
;
}
if
(
!
(
enabled
&&
t38_options
))
{
/* if there is no t38_options the endpoint will refuse the transition */
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_WARNING
,
"%s NO T38 options detected.
\n
"
,
switch_channel_get_name
(
channel
));
switch_channel_set_private
(
channel
,
"t38_options"
,
NULL
);
}
else
{
pvt
->
t38_mode
=
T38_MODE_NEGOTIATED
;
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxVersion = %d
\n
"
,
t38_options
->
T38FaxVersion
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38MaxBitRate = %d
\n
"
,
t38_options
->
T38MaxBitRate
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxFillBitRemoval = %d
\n
"
,
t38_options
->
T38FaxFillBitRemoval
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxTranscodingMMR = %d
\n
"
,
t38_options
->
T38FaxTranscodingMMR
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxTranscodingJBIG = %d
\n
"
,
t38_options
->
T38FaxTranscodingJBIG
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxRateManagement = '%s'
\n
"
,
t38_options
->
T38FaxRateManagement
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxMaxBuffer = %d
\n
"
,
t38_options
->
T38FaxMaxBuffer
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxMaxDatagram = %d
\n
"
,
t38_options
->
T38FaxMaxDatagram
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38FaxUdpEC = '%s'
\n
"
,
t38_options
->
T38FaxUdpEC
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"T38VendorInfo = '%s'
\n
"
,
switch_str_nil
(
t38_options
->
T38VendorInfo
));
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"ip = '%s'
\n
"
,
t38_options
->
remote_ip
?
t38_options
->
remote_ip
:
"Not specified"
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"port = %d
\n
"
,
t38_options
->
remote_port
);
/* Time to practice our negotiating skills, by editing the t38_options */
if
(
t38_options
->
T38FaxVersion
>
3
)
{
t38_options
->
T38FaxVersion
=
3
;
}
t38_options
->
T38MaxBitRate
=
(
pvt
->
disable_v17
)
?
9600
:
14400
;
t38_options
->
T38FaxFillBitRemoval
=
1
;
t38_options
->
T38FaxTranscodingMMR
=
0
;
t38_options
->
T38FaxTranscodingJBIG
=
0
;
t38_options
->
T38FaxRateManagement
=
"transferredTCF"
;
t38_options
->
T38FaxMaxBuffer
=
2000
;
t38_options
->
T38FaxMaxDatagram
=
LOCAL_FAX_MAX_DATAGRAM
;
if
(
strcasecmp
(
t38_options
->
T38FaxUdpEC
,
"t38UDPRedundancy"
)
==
0
||
strcasecmp
(
t38_options
->
T38FaxUdpEC
,
"t38UDPFEC"
)
==
0
)
{
t38_options
->
T38FaxUdpEC
=
"t38UDPRedundancy"
;
}
else
{
t38_options
->
T38FaxUdpEC
=
NULL
;
}
t38_options
->
T38VendorInfo
=
"0 0 0"
;
}
if
((
v
=
switch_channel_get_variable
(
channel
,
"fax_enable_t38_insist"
)))
{
insist
=
switch_true
(
v
);
}
else
{
insist
=
globals
.
enable_t38_insist
;
}
/* This will send the options back in a response */
msg
.
from
=
__FILE__
;
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION
;
msg
.
numeric_arg
=
insist
;
switch_core_session_receive_message
(
session
,
&
msg
);
return
pvt
->
t38_mode
;
}
static
t38_mode_t
request_t38
(
pvt_t
*
pvt
)
{
switch_core_session_t
*
session
=
pvt
->
session
;
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_core_session_message_t
msg
=
{
0
};
switch_t38_options_t
*
t38_options
=
NULL
;
int
enabled
=
0
,
insist
=
0
;
const
char
*
v
;
pvt
->
t38_mode
=
T38_MODE_UNKNOWN
;
if
(
pvt
->
app_mode
==
FUNCTION_GW
)
{
enabled
=
1
;
}
else
if
((
v
=
switch_channel_get_variable
(
channel
,
"fax_enable_t38"
)))
{
enabled
=
switch_true
(
v
);
}
else
{
enabled
=
globals
.
enable_t38
;
}
if
(
enabled
)
{
if
((
v
=
switch_channel_get_variable
(
channel
,
"fax_enable_t38_request"
)))
{
enabled
=
switch_true
(
v
);
}
else
{
enabled
=
globals
.
enable_t38_request
;
}
}
if
((
v
=
switch_channel_get_variable
(
channel
,
"fax_enable_t38_insist"
)))
{
insist
=
switch_true
(
v
);
}
else
{
insist
=
globals
.
enable_t38_insist
;
}
if
(
enabled
)
{
t38_options
=
switch_core_session_alloc
(
session
,
sizeof
(
*
t38_options
));
t38_options
->
T38MaxBitRate
=
(
pvt
->
disable_v17
)
?
9600
:
14400
;
t38_options
->
T38FaxVersion
=
0
;
t38_options
->
T38FaxFillBitRemoval
=
1
;
t38_options
->
T38FaxTranscodingMMR
=
0
;
t38_options
->
T38FaxTranscodingJBIG
=
0
;
t38_options
->
T38FaxRateManagement
=
"transferredTCF"
;
t38_options
->
T38FaxMaxBuffer
=
2000
;
t38_options
->
T38FaxMaxDatagram
=
LOCAL_FAX_MAX_DATAGRAM
;
t38_options
->
T38FaxUdpEC
=
"t38UDPRedundancy"
;
t38_options
->
T38VendorInfo
=
"0 0 0"
;
switch_channel_set_private
(
channel
,
"t38_options"
,
t38_options
);
pvt
->
t38_mode
=
T38_MODE_REQUESTED
;
/* This will send a request for t.38 mode */
msg
.
from
=
__FILE__
;
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA
;
msg
.
numeric_arg
=
insist
;
switch_core_session_receive_message
(
session
,
&
msg
);
}
return
pvt
->
t38_mode
;
}
/*****************************************************************************
MAIN FAX PROCESSING
*****************************************************************************/
static
pvt_t
*
pvt_init
(
switch_core_session_t
*
session
,
application_mode_t
app_mode
)
{
switch_channel_t
*
channel
;
pvt_t
*
pvt
=
NULL
;
const
char
*
tmp
;
/* Make sure we have a valid channel when starting the FAX application */
channel
=
switch_core_session_get_channel
(
session
);
switch_assert
(
channel
!=
NULL
);
if
(
!
switch_channel_media_ready
(
channel
))
{
switch_channel_answer
(
channel
);
}
/* Allocate our structs */
pvt
=
switch_core_session_alloc
(
session
,
sizeof
(
pvt_t
));
pvt
->
session
=
session
;
pvt
->
app_mode
=
app_mode
;
pvt
->
tx_page_start
=
-
1
;
pvt
->
tx_page_end
=
-
1
;
switch
(
pvt
->
app_mode
)
{
case
FUNCTION_TX
:
pvt
->
caller
=
1
;
break
;
case
FUNCTION_RX
:
pvt
->
caller
=
0
;
break
;
case
FUNCTION_GW
:
break
;
}
/* Retrieving our settings from the channel variables */
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_use_ecm"
)))
{
pvt
->
use_ecm
=
switch_true
(
tmp
);
}
else
{
pvt
->
use_ecm
=
globals
.
use_ecm
;
}
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_disable_v17"
)))
{
pvt
->
disable_v17
=
switch_true
(
tmp
);
}
else
{
pvt
->
disable_v17
=
globals
.
disable_v17
;
}
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_verbose"
)))
{
pvt
->
verbose
=
switch_true
(
tmp
);
}
else
{
pvt
->
verbose
=
globals
.
verbose
;
}
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_force_caller"
)))
{
if
(
switch_true
(
tmp
))
{
pvt
->
caller
=
1
;
}
else
{
pvt
->
caller
=
0
;
}
}
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_ident"
)))
{
pvt
->
ident
=
switch_core_session_strdup
(
session
,
tmp
);
}
else
{
pvt
->
ident
=
switch_core_session_strdup
(
session
,
globals
.
ident
);
}
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_header"
)))
{
pvt
->
header
=
switch_core_session_strdup
(
session
,
tmp
);
}
else
{
pvt
->
header
=
switch_core_session_strdup
(
session
,
globals
.
header
);
}
if
(
pvt
->
app_mode
==
FUNCTION_TX
)
{
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_start_page"
)))
{
pvt
->
tx_page_start
=
atoi
(
tmp
);
}
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"fax_end_page"
)))
{
pvt
->
tx_page_end
=
atoi
(
tmp
);
}
if
(
pvt
->
tx_page_end
<
-
1
)
{
pvt
->
tx_page_end
=
-
1
;
}
if
(
pvt
->
tx_page_start
<
-
1
)
{
pvt
->
tx_page_start
=
-
1
;
}
if
((
pvt
->
tx_page_end
<
pvt
->
tx_page_start
)
&&
(
pvt
->
tx_page_end
!=
-
1
))
{
pvt
->
tx_page_end
=
pvt
->
tx_page_start
;
}
}
return
pvt
;
}
void
process_fax
(
switch_core_session_t
*
session
,
const
char
*
data
,
application_mode_t
app_mode
)
{
pvt_t
*
pvt
;
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_codec_t
read_codec
=
{
0
};
switch_codec_t
write_codec
=
{
0
};
switch_frame_t
*
read_frame
=
{
0
};
switch_frame_t
write_frame
=
{
0
};
switch_codec_implementation_t
read_impl
=
{
0
};
int16_t
*
buf
=
NULL
;
switch_core_session_get_read_impl
(
session
,
&
read_impl
);
counter_increment
();
pvt
=
pvt_init
(
session
,
app_mode
);
buf
=
switch_core_session_alloc
(
session
,
SWITCH_RECOMMENDED_BUFFER_SIZE
);
if
(
!
zstr
(
data
))
{
pvt
->
filename
=
switch_core_session_strdup
(
session
,
data
);
if
(
pvt
->
app_mode
==
FUNCTION_TX
)
{
if
((
switch_file_exists
(
pvt
->
filename
,
switch_core_session_get_pool
(
session
))
!=
SWITCH_STATUS_SUCCESS
))
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot send inexistant fax file [%s]
\n
"
,
switch_str_nil
(
pvt
->
filename
));
goto
done
;
}
}
}
else
{
if
(
pvt
->
app_mode
==
FUNCTION_TX
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Fax TX filename not set.
\n
"
);
goto
done
;
}
else
if
(
pvt
->
app_mode
==
FUNCTION_RX
)
{
char
*
fname
;
const
char
*
prefix
;
switch_time_t
time
;
time
=
switch_time_now
();
if
(
!
(
prefix
=
switch_channel_get_variable
(
channel
,
"fax_prefix"
)))
{
prefix
=
globals
.
prepend_string
;
}
fname
=
switch_mprintf
(
"%s/%s-%ld-%ld.tif"
,
globals
.
spool
,
prefix
,
globals
.
total_sessions
,
time
);
if
(
fname
)
{
pvt
->
filename
=
switch_core_session_strdup
(
session
,
fname
);
switch_safe_free
(
fname
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot automatically set fax RX destination file
\n
"
);
goto
done
;
}
}
else
{
assert
(
0
);
/* UH ?? */
}
}
/*
*** Initialize the SpanDSP elements ***
Note: we could analyze if a fax was already detected in previous stages
and if so, when T.38 will be supported, send a reinvite in T38_MODE,
bypassing AUDIO_MODE.
*/
if
((
spanfax_init
(
pvt
,
AUDIO_MODE
)
!=
SWITCH_STATUS_SUCCESS
))
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Cannot initialize Fax engine
\n
"
);
return
;
}
/*
Note: Disable echocan on the channel, remember to call app "disable_ec" in the dialplan
before invoking fax applications
*/
/*
Note: we are disabling the Jitterbuffer, here, before we answer.
If you have set it to something else and the channel is pre-answered,
it will have no effect. Make sure that if you want more reliable
faxes, it is disabled.
*/
switch_channel_set_variable
(
channel
,
"jitterbuffer_msec"
,
"0"
);
/* We store the original channel codec before switching both
* legs of the calls to a linear 16 bit codec that is the one
* used internally by spandsp and FS will do the transcoding
* from G.711 or any other original codec
*/
if
(
switch_core_codec_init
(
&
read_codec
,
"L16"
,
NULL
,
read_impl
.
samples_per_second
,
read_impl
.
microseconds_per_packet
/
1000
,
1
,
SWITCH_CODEC_FLAG_ENCODE
|
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
session
))
==
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Raw read codec activation Success L16 %u
\n
"
,
read_codec
.
implementation
->
microseconds_per_packet
);
switch_core_session_set_read_codec
(
session
,
&
read_codec
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Raw read codec activation Failed L16
\n
"
);
goto
done
;
}
if
(
switch_core_codec_init
(
&
write_codec
,
"L16"
,
NULL
,
read_impl
.
samples_per_second
,
read_impl
.
microseconds_per_packet
/
1000
,
1
,
SWITCH_CODEC_FLAG_ENCODE
|
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
session
))
==
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Raw write codec activation Success L16
\n
"
);
write_frame
.
codec
=
&
write_codec
;
write_frame
.
data
=
buf
;
write_frame
.
buflen
=
SWITCH_RECOMMENDED_BUFFER_SIZE
;
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Raw write codec activation Failed L16
\n
"
);
goto
done
;
}
switch_ivr_sleep
(
session
,
250
,
SWITCH_TRUE
,
NULL
);
/* If you have the means, I highly recommend picking one up. ...*/
request_t38
(
pvt
);
while
(
switch_channel_ready
(
channel
))
{
int
tx
=
0
;
switch_status_t
status
;
/*
if we are in T.38 mode, we should: 1- initialize the ptv->t38_state stuff, if not done
and then set some callbacks when reading frames.
The only thing we need, then, in this loop, is:
- read a frame without blocking
- eventually feed that frame in spandsp,
- call t38_terminal_send_timeout(), sleep for a while
The T.38 stuff can be placed here (and the audio stuff can be skipped)
*/
/* read new audio frame from the channel */
status
=
switch_core_session_read_frame
(
session
,
&
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
);
if
(
!
SWITCH_READ_ACCEPTABLE
(
status
)
||
pvt
->
done
)
{
/* Our duty is over */
goto
done
;
}
switch
(
pvt
->
t38_mode
)
{
case
T38_MODE_REQUESTED
:
{
if
(
switch_channel_test_app_flag_key
(
"T38"
,
channel
,
CF_APP_T38
))
{
switch_core_session_message_t
msg
=
{
0
};
pvt
->
t38_mode
=
T38_MODE_NEGOTIATED
;
spanfax_init
(
pvt
,
T38_MODE
);
configure_t38
(
pvt
);
/* This will change the rtp stack to udptl mode */
msg
.
from
=
__FILE__
;
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_UDPTL_MODE
;
switch_core_session_receive_message
(
session
,
&
msg
);
}
continue
;
}
break
;
case
T38_MODE_UNKNOWN
:
{
if
(
switch_channel_test_app_flag_key
(
"T38"
,
channel
,
CF_APP_T38
))
{
if
(
negotiate_t38
(
pvt
)
==
T38_MODE_NEGOTIATED
)
{
/* is is safe to call this again, it was already called above in AUDIO_MODE */
/* but this is the only way to set up the t38 stuff */
spanfax_init
(
pvt
,
T38_MODE
);
continue
;
}
}
}
break
;
case
T38_MODE_NEGOTIATED
:
{
/* do what we need to do when we are in t38 mode */
if
(
switch_test_flag
(
read_frame
,
SFF_CNG
))
{
/* dunno what to do, most likely you will not get too many of these since we turn off the timer in udptl mode */
continue
;
}
if
(
switch_test_flag
(
read_frame
,
SFF_UDPTL_PACKET
))
{
/* now we know we can cast frame->packet to a udptl structure */
//switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "READ %d udptl bytes\n", read_frame->packetlen);
udptl_rx_packet
(
pvt
->
udptl_state
,
read_frame
->
packet
,
read_frame
->
packetlen
);
}
}
continue
;
default:
break
;
}
/* Skip CNG frames (auto-generated by FreeSWITCH, usually) */
if
(
switch_test_flag
(
read_frame
,
SFF_CNG
))
{
/* We have no real signal data for the FAX software, but we have a space in time if we have a CNG indication.
Do a fill-in operation in the FAX machine, to keep things rolling along. */
if
(
fax_rx_fillin
(
pvt
->
fax_state
,
read_frame
->
samples
))
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"fax_rx_fillin reported an error
\n
"
);
goto
done
;
}
}
else
{
/* Pass the new incoming audio frame to the fax_rx function */
if
(
fax_rx
(
pvt
->
fax_state
,
(
int16_t
*
)
read_frame
->
data
,
read_frame
->
samples
))
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"fax_rx reported an error
\n
"
);
goto
done
;
}
}
if
((
tx
=
fax_tx
(
pvt
->
fax_state
,
buf
,
write_codec
.
implementation
->
samples_per_packet
))
<
0
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"fax_tx reported an error
\n
"
);
goto
done
;
}
if
(
!
tx
)
{
/* switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "No audio samples to send\n"); */
continue
;
}
else
{
/* Set our write_frame data */
write_frame
.
datalen
=
tx
*
sizeof
(
int16_t
);
write_frame
.
samples
=
tx
;
}
if
(
switch_core_session_write_frame
(
session
,
&
write_frame
,
SWITCH_IO_FLAG_NONE
,
0
)
!=
SWITCH_STATUS_SUCCESS
)
{
goto
done
;
}
}
done:
/* Destroy the SpanDSP structures */
spanfax_destroy
(
pvt
);
/* restore the original codecs over the channel */
switch_core_session_set_read_codec
(
session
,
NULL
);
if
(
switch_core_codec_ready
(
&
read_codec
))
{
switch_core_codec_destroy
(
&
read_codec
);
}
if
(
switch_core_codec_ready
(
&
write_codec
))
{
switch_core_codec_destroy
(
&
write_codec
);
}
}
/* **************************************************************************
CONFIGURATION
************************************************************************* */
void
load_configuration
(
switch_bool_t
reload
)
{
switch_xml_t
xml
=
NULL
,
x_lists
=
NULL
,
x_list
=
NULL
,
cfg
=
NULL
;
if
((
xml
=
switch_xml_open_cfg
(
"fax.conf"
,
&
cfg
,
NULL
)))
{
if
((
x_lists
=
switch_xml_child
(
cfg
,
"settings"
)))
{
for
(
x_list
=
switch_xml_child
(
x_lists
,
"param"
);
x_list
;
x_list
=
x_list
->
next
)
{
const
char
*
name
=
switch_xml_attr
(
x_list
,
"name"
);
const
char
*
value
=
switch_xml_attr
(
x_list
,
"value"
);
if
(
zstr
(
name
))
{
continue
;
}
if
(
zstr
(
value
))
{
continue
;
}
if
(
!
strcmp
(
name
,
"use-ecm"
))
{
if
(
switch_true
(
value
))
globals
.
use_ecm
=
1
;
else
globals
.
use_ecm
=
0
;
}
else
if
(
!
strcmp
(
name
,
"verbose"
))
{
if
(
switch_true
(
value
))
globals
.
verbose
=
1
;
else
globals
.
verbose
=
0
;
}
else
if
(
!
strcmp
(
name
,
"disable-v17"
))
{
if
(
switch_true
(
value
))
globals
.
disable_v17
=
1
;
else
globals
.
disable_v17
=
0
;
}
else
if
(
!
strcmp
(
name
,
"enable-t38"
))
{
if
(
switch_true
(
value
))
{
globals
.
enable_t38
=
1
;
}
else
{
globals
.
enable_t38
=
0
;
}
}
else
if
(
!
strcmp
(
name
,
"enable-t38-request"
))
{
if
(
switch_true
(
value
))
{
globals
.
enable_t38_request
=
1
;
}
else
{
globals
.
enable_t38_request
=
0
;
}
}
else
if
(
!
strcmp
(
name
,
"ident"
))
{
strncpy
(
globals
.
ident
,
value
,
sizeof
(
globals
.
ident
)
-
1
);
}
else
if
(
!
strcmp
(
name
,
"header"
))
{
strncpy
(
globals
.
header
,
value
,
sizeof
(
globals
.
header
)
-
1
);
}
else
if
(
!
strcmp
(
name
,
"spool-dir"
))
{
globals
.
spool
=
switch_core_strdup
(
globals
.
pool
,
value
);
}
else
if
(
!
strcmp
(
name
,
"file-prefix"
))
{
globals
.
prepend_string
=
switch_core_strdup
(
globals
.
pool
,
value
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_WARNING
,
"Unknown parameter %s
\n
"
,
name
);
}
}
}
switch_xml_free
(
xml
);
}
}
static
void
event_handler
(
switch_event_t
*
event
)
{
load_configuration
(
1
);
}
typedef
struct
{
switch_core_session_t
*
session
;
dtmf_rx_state_t
*
dtmf_detect
;
}
switch_inband_dtmf_t
;
static
switch_bool_t
inband_dtmf_callback
(
switch_media_bug_t
*
bug
,
void
*
user_data
,
switch_abc_type_t
type
)
{
switch_inband_dtmf_t
*
pvt
=
(
switch_inband_dtmf_t
*
)
user_data
;
switch_frame_t
*
frame
=
NULL
;
char
digit_str
[
80
];
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
pvt
->
session
);
switch
(
type
)
{
case
SWITCH_ABC_TYPE_INIT
:
pvt
->
dtmf_detect
=
dtmf_rx_init
(
NULL
,
NULL
,
NULL
);
break
;
case
SWITCH_ABC_TYPE_CLOSE
:
if
(
pvt
->
dtmf_detect
)
{
dtmf_rx_free
(
pvt
->
dtmf_detect
);
}
break
;
case
SWITCH_ABC_TYPE_READ_REPLACE
:
if
((
frame
=
switch_core_media_bug_get_read_replace_frame
(
bug
)))
{
dtmf_rx
(
pvt
->
dtmf_detect
,
frame
->
data
,
frame
->
samples
);
dtmf_rx_get
(
pvt
->
dtmf_detect
,
digit_str
,
sizeof
(
digit_str
));
if
(
digit_str
[
0
])
{
char
*
p
=
digit_str
;
while
(
p
&&
*
p
)
{
switch_dtmf_t
dtmf
=
{
0
};
dtmf
.
digit
=
*
p
;
dtmf
.
duration
=
switch_core_default_dtmf_duration
(
0
);
switch_channel_queue_dtmf
(
channel
,
&
dtmf
);
p
++
;
}
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
switch_core_media_bug_get_session
(
bug
)),
SWITCH_LOG_DEBUG
,
"DTMF DETECTED: [%s]
\n
"
,
digit_str
);
}
switch_core_media_bug_set_read_replace_frame
(
bug
,
frame
);
}
break
;
case
SWITCH_ABC_TYPE_WRITE
:
default:
break
;
}
return
SWITCH_TRUE
;
}
switch_status_t
spandsp_stop_inband_dtmf_session
(
switch_core_session_t
*
session
)
{
switch_media_bug_t
*
bug
;
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
if
((
bug
=
switch_channel_get_private
(
channel
,
"dtmf"
)))
{
switch_channel_set_private
(
channel
,
"dtmf"
,
NULL
);
switch_core_media_bug_remove
(
session
,
&
bug
);
return
SWITCH_STATUS_SUCCESS
;
}
return
SWITCH_STATUS_FALSE
;
}
switch_status_t
spandsp_inband_dtmf_session
(
switch_core_session_t
*
session
)
{
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_media_bug_t
*
bug
;
switch_status_t
status
;
switch_inband_dtmf_t
*
pvt
;
switch_codec_implementation_t
read_impl
=
{
0
};
switch_core_session_get_read_impl
(
session
,
&
read_impl
);
if
(
!
(
pvt
=
switch_core_session_alloc
(
session
,
sizeof
(
*
pvt
))))
{
return
SWITCH_STATUS_MEMERR
;
}
pvt
->
session
=
session
;
if
(
switch_channel_pre_answer
(
channel
)
!=
SWITCH_STATUS_SUCCESS
)
{
return
SWITCH_STATUS_FALSE
;
}
if
((
status
=
switch_core_media_bug_add
(
session
,
"spandsp_dtmf_detect"
,
NULL
,
inband_dtmf_callback
,
pvt
,
0
,
SMBF_READ_REPLACE
,
&
bug
))
!=
SWITCH_STATUS_SUCCESS
)
{
return
status
;
}
switch_channel_set_private
(
channel
,
"dtmf"
,
bug
);
return
SWITCH_STATUS_SUCCESS
;
}
/* **************************************************************************
FREESWITCH MODULE DEFINITIONS
************************************************************************* */
#define SPANFAX_RX_USAGE "<filename>"
#define SPANFAX_TX_USAGE "<filename>"
SWITCH_MODULE_LOAD_FUNCTION
(
mod_fax_init
);
SWITCH_MODULE_SHUTDOWN_FUNCTION
(
mod_fax_shutdown
);
SWITCH_MODULE_DEFINITION
(
mod_fax
,
mod_fax_init
,
mod_fax_shutdown
,
NULL
);
static
switch_event_node_t
*
NODE
=
NULL
;
SWITCH_STANDARD_APP
(
spanfax_tx_function
)
{
process_fax
(
session
,
data
,
FUNCTION_TX
);
}
SWITCH_STANDARD_APP
(
spanfax_rx_function
)
{
process_fax
(
session
,
data
,
FUNCTION_RX
);
}
SWITCH_STANDARD_APP
(
dtmf_session_function
)
{
spandsp_inband_dtmf_session
(
session
);
}
SWITCH_STANDARD_APP
(
stop_dtmf_session_function
)
{
spandsp_stop_inband_dtmf_session
(
session
);
}
static
const
switch_state_handler_table_t
t38_gateway_state_handlers
;
static
switch_status_t
t38_gateway_on_soft_execute
(
switch_core_session_t
*
session
)
{
switch_core_session_t
*
other_session
;
switch_channel_t
*
other_channel
,
*
channel
=
switch_core_session_get_channel
(
session
);
pvt_t
*
pvt
;
const
char
*
peer_uuid
=
switch_channel_get_variable
(
channel
,
"t38_peer"
);
switch_core_session_message_t
msg
=
{
0
};
switch_status_t
status
;
switch_frame_t
*
read_frame
=
{
0
};
if
(
!
(
other_session
=
switch_core_session_locate
(
peer_uuid
)))
{
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_WARNING
,
"%s Cannot locate channel with uuid %s"
,
switch_channel_get_name
(
channel
),
peer_uuid
);
goto
end
;
}
other_channel
=
switch_core_session_get_channel
(
other_session
);
pvt
=
pvt_init
(
session
,
FUNCTION_GW
);
request_t38
(
pvt
);
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_BRIDGE
;
msg
.
from
=
__FILE__
;
msg
.
string_arg
=
peer_uuid
;
switch_core_session_receive_message
(
session
,
&
msg
);
while
(
switch_channel_ready
(
channel
)
&&
switch_channel_up
(
other_channel
)
&&
!
switch_channel_test_app_flag_key
(
"T38"
,
channel
,
CF_APP_T38
))
{
status
=
switch_core_session_read_frame
(
session
,
&
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
);
if
(
!
SWITCH_READ_ACCEPTABLE
(
status
)
||
pvt
->
done
)
{
/* Our duty is over */
goto
end_unlock
;
}
if
(
switch_test_flag
(
read_frame
,
SFF_CNG
))
{
continue
;
}
if
(
switch_core_session_write_frame
(
other_session
,
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
)
!=
SWITCH_STATUS_SUCCESS
)
{
goto
end_unlock
;
}
}
if
(
!
(
switch_channel_ready
(
channel
)
&&
switch_channel_up
(
other_channel
)))
{
goto
end_unlock
;
}
if
(
!
switch_channel_test_app_flag_key
(
"T38"
,
channel
,
CF_APP_T38
))
{
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_WARNING
,
"%s Could not negotiate T38
\n
"
,
switch_channel_get_name
(
channel
));
goto
end_unlock
;
}
if
(
pvt
->
t38_mode
==
T38_MODE_REQUESTED
)
{
spanfax_init
(
pvt
,
T38_GATEWAY_MODE
);
configure_t38
(
pvt
);
pvt
->
t38_mode
=
T38_MODE_NEGOTIATED
;
}
else
{
if
(
negotiate_t38
(
pvt
)
!=
T38_MODE_NEGOTIATED
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_WARNING
,
"%s Could not negotiate T38
\n
"
,
switch_channel_get_name
(
channel
));
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
goto
end_unlock
;
}
spanfax_init
(
pvt
,
T38_GATEWAY_MODE
);
}
/* This will change the rtp stack to udptl mode */
msg
.
from
=
__FILE__
;
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_UDPTL_MODE
;
switch_core_session_receive_message
(
session
,
&
msg
);
/* wake up the audio side */
switch_channel_set_private
(
channel
,
"_t38_pvt"
,
pvt
);
switch_channel_set_app_flag_key
(
"T38"
,
other_channel
,
CF_APP_T38
);
while
(
switch_channel_ready
(
channel
)
&&
switch_channel_up
(
other_channel
))
{
status
=
switch_core_session_read_frame
(
session
,
&
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
);
if
(
!
SWITCH_READ_ACCEPTABLE
(
status
)
||
pvt
->
done
)
{
/* Our duty is over */
goto
end_unlock
;
}
if
(
switch_test_flag
(
read_frame
,
SFF_CNG
))
{
continue
;
}
if
(
switch_test_flag
(
read_frame
,
SFF_UDPTL_PACKET
))
{
udptl_rx_packet
(
pvt
->
udptl_state
,
read_frame
->
packet
,
read_frame
->
packetlen
);
}
}
end_unlock:
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_UNBRIDGE
;
msg
.
from
=
__FILE__
;
msg
.
string_arg
=
peer_uuid
;
switch_core_session_receive_message
(
session
,
&
msg
);
switch_channel_hangup
(
other_channel
,
SWITCH_CAUSE_NORMAL_CLEARING
);
switch_core_session_rwunlock
(
other_session
);
end:
switch_channel_clear_state_handler
(
channel
,
&
t38_gateway_state_handlers
);
switch_channel_set_variable
(
channel
,
"t38_peer"
,
NULL
);
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_NORMAL_CLEARING
);
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
t38_gateway_on_consume_media
(
switch_core_session_t
*
session
)
{
switch_core_session_t
*
other_session
;
switch_channel_t
*
other_channel
,
*
channel
=
switch_core_session_get_channel
(
session
);
const
char
*
peer_uuid
=
switch_channel_get_variable
(
channel
,
"t38_peer"
);
pvt_t
*
pvt
=
NULL
;
switch_codec_t
read_codec
=
{
0
};
switch_codec_t
write_codec
=
{
0
};
switch_frame_t
*
read_frame
=
{
0
};
switch_frame_t
write_frame
=
{
0
};
switch_codec_implementation_t
read_impl
=
{
0
};
int16_t
*
buf
=
NULL
;
switch_status_t
status
;
switch_size_t
tx
;
const
char
*
t38_trace
=
switch_channel_get_variable
(
channel
,
"t38_trace"
);
char
*
trace_read
,
*
trace_write
;
zap_socket_t
read_fd
=
FAX_INVALID_SOCKET
,
write_fd
=
FAX_INVALID_SOCKET
;
switch_core_session_message_t
msg
=
{
0
};
switch_event_t
*
event
;
switch_core_session_get_read_impl
(
session
,
&
read_impl
);
buf
=
switch_core_session_alloc
(
session
,
SWITCH_RECOMMENDED_BUFFER_SIZE
);
if
(
!
(
other_session
=
switch_core_session_locate
(
peer_uuid
)))
{
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
goto
end
;
}
other_channel
=
switch_core_session_get_channel
(
other_session
);
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_BRIDGE
;
msg
.
from
=
__FILE__
;
msg
.
string_arg
=
peer_uuid
;
switch_core_session_receive_message
(
session
,
&
msg
);
if
(
switch_event_create
(
&
event
,
SWITCH_EVENT_CHANNEL_BRIDGE
)
==
SWITCH_STATUS_SUCCESS
)
{
switch_channel_event_set_data
(
channel
,
event
);
switch_event_fire
(
&
event
);
}
while
(
switch_channel_ready
(
channel
)
&&
switch_channel_up
(
other_channel
)
&&
!
switch_channel_test_app_flag_key
(
"T38"
,
channel
,
CF_APP_T38
))
{
status
=
switch_core_session_read_frame
(
session
,
&
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
);
if
(
!
SWITCH_READ_ACCEPTABLE
(
status
))
{
/* Our duty is over */
goto
end_unlock
;
}
if
(
switch_test_flag
(
read_frame
,
SFF_CNG
))
{
continue
;
}
if
(
switch_core_session_write_frame
(
other_session
,
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
)
!=
SWITCH_STATUS_SUCCESS
)
{
goto
end_unlock
;
}
}
if
(
!
(
switch_channel_ready
(
channel
)
&&
switch_channel_up
(
other_channel
)))
{
goto
end_unlock
;
}
if
(
!
switch_channel_test_app_flag_key
(
"T38"
,
channel
,
CF_APP_T38
))
{
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
goto
end_unlock
;
}
if
(
!
(
pvt
=
switch_channel_get_private
(
other_channel
,
"_t38_pvt"
)))
{
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
goto
end_unlock
;
}
if
(
switch_core_codec_init
(
&
read_codec
,
"L16"
,
NULL
,
read_impl
.
samples_per_second
,
read_impl
.
microseconds_per_packet
/
1000
,
1
,
SWITCH_CODEC_FLAG_ENCODE
|
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
session
))
==
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Raw read codec activation Success L16 %u
\n
"
,
read_codec
.
implementation
->
microseconds_per_packet
);
switch_core_session_set_read_codec
(
session
,
&
read_codec
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Raw read codec activation Failed L16
\n
"
);
goto
end_unlock
;
}
if
(
switch_core_codec_init
(
&
write_codec
,
"L16"
,
NULL
,
read_impl
.
samples_per_second
,
read_impl
.
microseconds_per_packet
/
1000
,
1
,
SWITCH_CODEC_FLAG_ENCODE
|
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
session
))
==
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Raw write codec activation Success L16
\n
"
);
write_frame
.
codec
=
&
write_codec
;
write_frame
.
data
=
buf
;
write_frame
.
buflen
=
SWITCH_RECOMMENDED_BUFFER_SIZE
;
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Raw write codec activation Failed L16
\n
"
);
goto
end_unlock
;
}
switch_ivr_sleep
(
session
,
0
,
SWITCH_TRUE
,
NULL
);
if
(
switch_true
(
t38_trace
))
{
trace_read
=
switch_core_session_sprintf
(
session
,
"%s%s%s_read.raw"
,
SWITCH_GLOBAL_dirs
.
temp_dir
,
SWITCH_PATH_SEPARATOR
,
switch_core_session_get_uuid
(
session
));
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_WARNING
,
"Tracing inbound audio to %s
\n
"
,
trace_read
);
switch_channel_set_variable
(
channel
,
"t38_trace_read"
,
trace_read
);
trace_write
=
switch_core_session_sprintf
(
session
,
"%s%s%s_write.raw"
,
SWITCH_GLOBAL_dirs
.
temp_dir
,
SWITCH_PATH_SEPARATOR
,
switch_core_session_get_uuid
(
session
));
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_WARNING
,
"Tracing outbound audio to %s
\n
"
,
trace_write
);
switch_channel_set_variable
(
channel
,
"t38_trace_read"
,
trace_write
);
if
((
write_fd
=
open
(
trace_read
,
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
S_IRUSR
|
S_IWUSR
))
!=
FAX_INVALID_SOCKET
)
{
if
((
read_fd
=
open
(
trace_write
,
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
S_IRUSR
|
S_IWUSR
))
==
FAX_INVALID_SOCKET
)
{
close
(
write_fd
);
write_fd
=
FAX_INVALID_SOCKET
;
}
}
}
while
(
switch_channel_ready
(
channel
)
&&
switch_channel_up
(
other_channel
))
{
status
=
switch_core_session_read_frame
(
session
,
&
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
);
if
(
!
SWITCH_READ_ACCEPTABLE
(
status
)
||
pvt
->
done
)
{
/* Our duty is over */
goto
end_unlock
;
}
/* Skip CNG frames (auto-generated by FreeSWITCH, usually) */
if
(
!
switch_test_flag
(
read_frame
,
SFF_CNG
))
{
if
(
read_fd
!=
FAX_INVALID_SOCKET
)
{
int
w
=
write
(
read_fd
,
read_frame
->
data
,
read_frame
->
datalen
);
if
(
w
<=
0
)
{
close
(
read_fd
);
read_fd
=
FAX_INVALID_SOCKET
;
}
}
if
(
t38_gateway_rx
(
pvt
->
t38_gateway_state
,
(
int16_t
*
)
read_frame
->
data
,
read_frame
->
samples
))
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"fax_rx reported an error
\n
"
);
goto
end_unlock
;
}
}
if
((
tx
=
t38_gateway_tx
(
pvt
->
t38_gateway_state
,
buf
,
write_codec
.
implementation
->
samples_per_packet
))
<
0
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"fax_tx reported an error
\n
"
);
goto
end_unlock
;
}
if
(
!
tx
)
{
/* switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "No audio samples to send\n"); */
continue
;
}
else
{
/* Set our write_frame data */
write_frame
.
datalen
=
tx
*
sizeof
(
int16_t
);
write_frame
.
samples
=
tx
;
}
if
(
write_fd
!=
FAX_INVALID_SOCKET
)
{
int
w
=
write
(
write_fd
,
write_frame
.
data
,
write_frame
.
datalen
);
if
(
w
<=
0
)
{
close
(
write_fd
);
write_fd
=
FAX_INVALID_SOCKET
;
}
}
if
(
switch_core_session_write_frame
(
session
,
&
write_frame
,
SWITCH_IO_FLAG_NONE
,
0
)
!=
SWITCH_STATUS_SUCCESS
)
{
goto
end_unlock
;
}
}
end_unlock:
msg
.
message_id
=
SWITCH_MESSAGE_INDICATE_UNBRIDGE
;
msg
.
from
=
__FILE__
;
msg
.
string_arg
=
peer_uuid
;
switch_core_session_receive_message
(
session
,
&
msg
);
if
(
switch_event_create
(
&
event
,
SWITCH_EVENT_CHANNEL_UNBRIDGE
)
==
SWITCH_STATUS_SUCCESS
)
{
switch_channel_event_set_data
(
channel
,
event
);
switch_event_fire
(
&
event
);
}
if
(
read_fd
!=
FAX_INVALID_SOCKET
)
{
close
(
read_fd
);
read_fd
=
FAX_INVALID_SOCKET
;
}
if
(
write_fd
!=
FAX_INVALID_SOCKET
)
{
close
(
write_fd
);
write_fd
=
FAX_INVALID_SOCKET
;
}
switch_channel_hangup
(
other_channel
,
SWITCH_CAUSE_NORMAL_CLEARING
);
switch_core_session_rwunlock
(
other_session
);
switch_core_session_set_read_codec
(
session
,
NULL
);
if
(
switch_core_codec_ready
(
&
read_codec
))
{
switch_core_codec_destroy
(
&
read_codec
);
}
if
(
switch_core_codec_ready
(
&
write_codec
))
{
switch_core_codec_destroy
(
&
write_codec
);
}
end:
switch_channel_clear_state_handler
(
channel
,
&
t38_gateway_state_handlers
);
switch_channel_set_variable
(
channel
,
"t38_peer"
,
NULL
);
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_NORMAL_CLEARING
);
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
t38_gateway_on_reset
(
switch_core_session_t
*
session
)
{
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_channel_set_variable
(
channel
,
"rtp_autoflush_during_bridge"
,
"false"
);
switch_channel_clear_flag
(
channel
,
CF_REDIRECT
);
if
(
switch_channel_test_app_flag
(
channel
,
CF_APP_TAGGED
))
{
switch_channel_clear_app_flag
(
channel
,
CF_APP_TAGGED
);
switch_channel_set_state
(
channel
,
CS_CONSUME_MEDIA
);
}
else
{
switch_channel_set_state
(
channel
,
CS_SOFT_EXECUTE
);
}
return
SWITCH_STATUS_SUCCESS
;
}
static
const
switch_state_handler_table_t
t38_gateway_state_handlers
=
{
/*.on_init */
NULL
,
/*.on_routing */
NULL
,
/*.on_execute */
NULL
,
/*.on_hangup */
NULL
,
/*.on_exchange_media */
NULL
,
/*.on_soft_execute */
t38_gateway_on_soft_execute
,
/*.on_consume_media */
t38_gateway_on_consume_media
,
/*.on_hibernate */
NULL
,
/*.on_reset */
t38_gateway_on_reset
,
/*.on_park */
NULL
,
/*.on_reporting */
NULL
,
/*.on_destroy */
NULL
,
SSH_FLAG_STICKY
};
static
switch_bool_t
t38_gateway_start
(
switch_core_session_t
*
session
,
const
char
*
app
,
const
char
*
data
)
{
switch_channel_t
*
other_channel
=
NULL
,
*
channel
=
switch_core_session_get_channel
(
session
);
switch_core_session_t
*
other_session
=
NULL
;
int
peer
=
app
&&
!
strcasecmp
(
app
,
"peer"
);
if
(
switch_core_session_get_partner
(
session
,
&
other_session
)
==
SWITCH_STATUS_SUCCESS
)
{
other_channel
=
switch_core_session_get_channel
(
other_session
);
switch_channel_set_variable
(
channel
,
"t38_peer"
,
switch_core_session_get_uuid
(
other_session
));
switch_channel_set_variable
(
other_channel
,
"t38_peer"
,
switch_core_session_get_uuid
(
session
));
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"%s starting gateway mode to %s
\n
"
,
switch_channel_get_name
(
peer
?
channel
:
other_channel
),
switch_channel_get_name
(
peer
?
other_channel
:
channel
));
switch_channel_clear_state_handler
(
channel
,
NULL
);
switch_channel_clear_state_handler
(
other_channel
,
NULL
);
switch_channel_add_state_handler
(
channel
,
&
t38_gateway_state_handlers
);
switch_channel_add_state_handler
(
other_channel
,
&
t38_gateway_state_handlers
);
switch_channel_set_app_flag
(
peer
?
channel
:
other_channel
,
CF_APP_TAGGED
);
switch_channel_clear_app_flag
(
peer
?
other_channel
:
channel
,
CF_APP_TAGGED
);
switch_channel_set_flag
(
channel
,
CF_REDIRECT
);
switch_channel_set_state
(
channel
,
CS_RESET
);
switch_channel_set_flag
(
other_channel
,
CF_REDIRECT
);
switch_channel_set_state
(
other_channel
,
CS_RESET
);
switch_core_session_rwunlock
(
other_session
);
}
return
SWITCH_FALSE
;
}
SWITCH_STANDARD_APP
(
t38_gateway_function
)
{
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
time_t
timeout
=
switch_epoch_time_now
(
NULL
)
+
20
;
const
char
*
var
;
if
(
zstr
(
data
)
||
strcasecmp
(
data
,
"self"
))
{
data
=
"peer"
;
}
switch_channel_set_variable
(
channel
,
"t38_leg"
,
data
);
if
((
var
=
switch_channel_get_variable
(
channel
,
"t38_gateway_detect_timeout"
)))
{
long
to
=
atol
(
var
);
if
(
to
>
-
1
)
{
timeout
=
(
time_t
)
(
switch_epoch_time_now
(
NULL
)
+
to
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_WARNING
,
"%s invalid timeout value.
\n
"
,
switch_channel_get_name
(
channel
));
}
}
switch_ivr_tone_detect_session
(
session
,
"t38"
,
"1100.0"
,
"rw"
,
timeout
,
1
,
data
,
NULL
,
t38_gateway_start
);
}
SWITCH_MODULE_LOAD_FUNCTION
(
mod_fax_init
)
{
switch_application_interface_t
*
app_interface
;
*
module_interface
=
switch_loadable_module_create_module_interface
(
pool
,
modname
);
SWITCH_ADD_APP
(
app_interface
,
"t38_gateway"
,
"Convert to T38 Gateway if tones are heard"
,
"Convert to T38 Gateway if tones are heard"
,
t38_gateway_function
,
""
,
SAF_MEDIA_TAP
);
SWITCH_ADD_APP
(
app_interface
,
"rxfax"
,
"FAX Receive Application"
,
"FAX Receive Application"
,
spanfax_rx_function
,
SPANFAX_RX_USAGE
,
SAF_SUPPORT_NOMEDIA
);
SWITCH_ADD_APP
(
app_interface
,
"txfax"
,
"FAX Transmit Application"
,
"FAX Transmit Application"
,
spanfax_tx_function
,
SPANFAX_TX_USAGE
,
SAF_SUPPORT_NOMEDIA
);
SWITCH_ADD_APP
(
app_interface
,
"spandsp_stop_dtmf"
,
"stop inband dtmf"
,
"Stop detecting inband dtmf."
,
stop_dtmf_session_function
,
""
,
SAF_NONE
);
SWITCH_ADD_APP
(
app_interface
,
"spandsp_start_dtmf"
,
"Detect dtmf"
,
"Detect inband dtmf on the session"
,
dtmf_session_function
,
""
,
SAF_MEDIA_TAP
);
memset
(
&
globals
,
0
,
sizeof
(
globals
));
memset
(
&
t38_state_list
,
0
,
sizeof
(
t38_state_list
));
switch_core_new_memory_pool
(
&
globals
.
pool
);
switch_mutex_init
(
&
globals
.
mutex
,
SWITCH_MUTEX_NESTED
,
globals
.
pool
);
switch_mutex_init
(
&
t38_state_list
.
mutex
,
SWITCH_MUTEX_NESTED
,
globals
.
pool
);
globals
.
enable_t38
=
1
;
globals
.
total_sessions
=
0
;
globals
.
verbose
=
1
;
globals
.
use_ecm
=
1
;
globals
.
disable_v17
=
0
;
globals
.
prepend_string
=
switch_core_strdup
(
globals
.
pool
,
"fax"
);
globals
.
spool
=
switch_core_strdup
(
globals
.
pool
,
"/tmp"
);
strncpy
(
globals
.
ident
,
"SpanDSP Fax Ident"
,
sizeof
(
globals
.
ident
)
-
1
);
strncpy
(
globals
.
header
,
"SpanDSP Fax Header"
,
sizeof
(
globals
.
header
)
-
1
);
load_configuration
(
0
);
if
((
switch_event_bind_removable
(
modname
,
SWITCH_EVENT_RELOADXML
,
NULL
,
event_handler
,
NULL
,
&
NODE
)
!=
SWITCH_STATUS_SUCCESS
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Couldn't bind our reloadxml handler!
\n
"
);
/* Not such severe to prevent loading */
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"mod_fax loaded, using spandsp library version [%s]
\n
"
,
SPANDSP_RELEASE_DATETIME_STRING
);
return
SWITCH_STATUS_SUCCESS
;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION
(
mod_fax_shutdown
)
{
switch_memory_pool_t
*
pool
=
globals
.
pool
;
switch_event_unbind
(
&
NODE
);
switch_core_destroy_memory_pool
(
&
pool
);
memset
(
&
globals
,
0
,
sizeof
(
globals
));
return
SWITCH_STATUS_UNLOAD
;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:nil
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/
src/mod/applications/mod_fax/udptl.c
deleted
100644 → 0
浏览文件 @
be2ec292
//#define UDPTL_DEBUG
/*
* 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>
*
* udptl.c -- UDPTL handling for T.38
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <inttypes.h>
#include <memory.h>
#include "udptl.h"
#define FALSE 0
#define TRUE (!FALSE)
static
int
decode_length
(
const
uint8_t
*
buf
,
int
limit
,
int
*
len
,
int
*
pvalue
)
{
if
(
*
len
>=
limit
)
return
-
1
;
if
((
buf
[
*
len
]
&
0x80
)
==
0
)
{
*
pvalue
=
buf
[(
*
len
)
++
];
return
0
;
}
if
((
buf
[
*
len
]
&
0x40
)
==
0
)
{
if
(
*
len
>=
limit
-
1
)
return
-
1
;
*
pvalue
=
(
buf
[(
*
len
)
++
]
&
0x3F
)
<<
8
;
*
pvalue
|=
buf
[(
*
len
)
++
];
return
0
;
}
*
pvalue
=
(
buf
[(
*
len
)
++
]
&
0x3F
)
<<
14
;
/* Indicate we have a fragment */
return
1
;
}
/*- End of function --------------------------------------------------------*/
static
int
decode_open_type
(
const
uint8_t
*
buf
,
int
limit
,
int
*
len
,
const
uint8_t
**
p_object
,
int
*
p_num_octets
)
{
int
octet_cnt
;
int
octet_idx
;
int
stat
;
int
i
;
const
uint8_t
**
pbuf
;
for
(
octet_idx
=
0
,
*
p_num_octets
=
0
;;
octet_idx
+=
octet_cnt
)
{
if
((
stat
=
decode_length
(
buf
,
limit
,
len
,
&
octet_cnt
))
<
0
)
return
-
1
;
if
(
octet_cnt
>
0
)
{
*
p_num_octets
+=
octet_cnt
;
pbuf
=
&
p_object
[
octet_idx
];
i
=
0
;
/* Make sure the buffer contains at least the number of bits requested */
if
((
*
len
+
octet_cnt
)
>
limit
)
return
-
1
;
*
pbuf
=
&
buf
[
*
len
];
*
len
+=
octet_cnt
;
}
if
(
stat
==
0
)
break
;
}
return
0
;
}
/*- End of function --------------------------------------------------------*/
static
int
encode_length
(
uint8_t
*
buf
,
int
*
len
,
int
value
)
{
int
multiplier
;
if
(
value
<
0x80
)
{
/* 1 octet */
buf
[(
*
len
)
++
]
=
value
;
return
value
;
}
if
(
value
<
0x4000
)
{
/* 2 octets */
/* Set the first bit of the first octet */
buf
[(
*
len
)
++
]
=
((
0x8000
|
value
)
>>
8
)
&
0xFF
;
buf
[(
*
len
)
++
]
=
value
&
0xFF
;
return
value
;
}
/* Fragmentation */
multiplier
=
(
value
<
0x10000
)
?
(
value
>>
14
)
:
4
;
/* Set the first 2 bits of the octet */
buf
[(
*
len
)
++
]
=
0xC0
|
multiplier
;
return
multiplier
<<
14
;
}
/*- End of function --------------------------------------------------------*/
static
int
encode_open_type
(
uint8_t
*
buf
,
int
*
len
,
const
uint8_t
*
data
,
int
num_octets
)
{
int
enclen
;
int
octet_idx
;
uint8_t
zero_byte
;
/* If open type is of zero length, add a single zero byte (10.1) */
if
(
num_octets
==
0
)
{
zero_byte
=
0
;
data
=
&
zero_byte
;
num_octets
=
1
;
}
/* Encode the open type */
for
(
octet_idx
=
0
;;
num_octets
-=
enclen
,
octet_idx
+=
enclen
)
{
if
((
enclen
=
encode_length
(
buf
,
len
,
num_octets
))
<
0
)
return
-
1
;
if
(
enclen
>
0
)
{
memcpy
(
&
buf
[
*
len
],
&
data
[
octet_idx
],
enclen
);
*
len
+=
enclen
;
}
if
(
enclen
>=
num_octets
)
break
;
}
return
0
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_rx_packet
(
udptl_state_t
*
s
,
const
uint8_t
buf
[],
int
len
)
{
int
stat
;
int
stat2
;
int
i
;
int
j
;
int
k
;
int
l
;
int
m
;
int
x
;
int
limit
;
int
which
;
int
ptr
;
int
count
;
int
total_count
;
int
seq_no
;
const
uint8_t
*
msg
;
const
uint8_t
*
data
;
int
msg_len
;
int
repaired
[
16
];
const
uint8_t
*
bufs
[
16
];
int
lengths
[
16
];
int
span
;
int
entries
;
ptr
=
0
;
/* Decode seq_number */
if
(
ptr
+
2
>
len
)
return
-
1
;
seq_no
=
(
buf
[
0
]
<<
8
)
|
buf
[
1
];
ptr
+=
2
;
/* Break out the primary packet */
if
((
stat
=
decode_open_type
(
buf
,
len
,
&
ptr
,
&
msg
,
&
msg_len
))
!=
0
)
return
-
1
;
/* Decode error_recovery */
if
(
ptr
+
1
>
len
)
return
-
1
;
/* Our buffers cannot tolerate overlength packets */
if
(
msg_len
>
LOCAL_FAX_MAX_DATAGRAM
)
return
-
1
;
/* Update any missed slots in the buffer */
for
(
i
=
s
->
rx_seq_no
;
seq_no
>
i
;
i
++
)
{
x
=
i
&
UDPTL_BUF_MASK
;
s
->
rx
[
x
].
buf_len
=
-
1
;
s
->
rx
[
x
].
fec_len
[
0
]
=
0
;
s
->
rx
[
x
].
fec_span
=
0
;
s
->
rx
[
x
].
fec_entries
=
0
;
}
/* Save the new packet. Pure redundancy mode won't use this, but some systems will switch
into FEC mode after sending some redundant packets. */
x
=
seq_no
&
UDPTL_BUF_MASK
;
memcpy
(
s
->
rx
[
x
].
buf
,
msg
,
msg_len
);
s
->
rx
[
x
].
buf_len
=
msg_len
;
s
->
rx
[
x
].
fec_len
[
0
]
=
0
;
s
->
rx
[
x
].
fec_span
=
0
;
s
->
rx
[
x
].
fec_entries
=
0
;
if
((
buf
[
ptr
++
]
&
0x80
)
==
0
)
{
/* Secondary packet mode for error recovery */
/* We might have the packet we want, but we need to check through
the redundant stuff, and verify the integrity of the UDPTL.
This greatly reduces our chances of accepting garbage. */
total_count
=
0
;
do
{
if
((
stat2
=
decode_length
(
buf
,
len
,
&
ptr
,
&
count
))
<
0
)
return
-
1
;
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
((
stat
=
decode_open_type
(
buf
,
len
,
&
ptr
,
&
bufs
[
total_count
+
i
],
&
lengths
[
total_count
+
i
]))
!=
0
)
return
-
1
;
}
total_count
+=
count
;
}
while
(
stat2
>
0
);
/* We should now be exactly at the end of the packet. If not, this is a fault. */
if
(
ptr
!=
len
)
return
-
1
;
if
(
seq_no
>
s
->
rx_seq_no
)
{
/* We received a later packet than we expected, so we need to check if we can fill in the gap from the
secondary packets. */
/* Step through in reverse order, so we go oldest to newest */
for
(
i
=
total_count
;
i
>
0
;
i
--
)
{
if
(
seq_no
-
i
>=
s
->
rx_seq_no
)
{
/* This one wasn't seen before */
/* Decode the secondary packet */
#if defined(UDPTL_DEBUG)
fprintf
(
stderr
,
"Secondary %d, len %d
\n
"
,
seq_no
-
i
,
lengths
[
i
-
1
]);
#endif
/* Save the new packet. Redundancy mode won't use this, but some systems will switch into
FEC mode after sending some redundant packets, and this may then be important. */
x
=
(
seq_no
-
i
)
&
UDPTL_BUF_MASK
;
memcpy
(
s
->
rx
[
x
].
buf
,
bufs
[
i
-
1
],
lengths
[
i
-
1
]);
s
->
rx
[
x
].
buf_len
=
lengths
[
i
-
1
];
s
->
rx
[
x
].
fec_len
[
0
]
=
0
;
s
->
rx
[
x
].
fec_span
=
0
;
s
->
rx
[
x
].
fec_entries
=
0
;
if
(
s
->
rx_packet_handler
(
s
->
user_data
,
bufs
[
i
-
1
],
lengths
[
i
-
1
],
seq_no
-
i
)
<
0
)
fprintf
(
stderr
,
"Bad IFP
\n
"
);
}
}
}
}
else
{
/* FEC mode for error recovery */
/* Decode the FEC packets */
/* The span is defined as an unconstrained integer, but will never be more
than a small value. */
if
(
ptr
+
2
>
len
)
return
-
1
;
if
(
buf
[
ptr
++
]
!=
1
)
return
-
1
;
span
=
buf
[
ptr
++
];
x
=
seq_no
&
UDPTL_BUF_MASK
;
s
->
rx
[
x
].
fec_span
=
span
;
memset
(
repaired
,
0
,
sizeof
(
repaired
));
repaired
[
x
]
=
TRUE
;
/* The number of entries is defined as a length, but will only ever be a small
value. Treat it as such. */
if
(
ptr
+
1
>
len
)
return
-
1
;
entries
=
buf
[
ptr
++
];
s
->
rx
[
x
].
fec_entries
=
entries
;
/* Decode the elements */
for
(
i
=
0
;
i
<
entries
;
i
++
)
{
if
((
stat
=
decode_open_type
(
buf
,
len
,
&
ptr
,
&
data
,
&
s
->
rx
[
x
].
fec_len
[
i
]))
!=
0
)
return
-
1
;
if
(
s
->
rx
[
x
].
fec_len
[
i
]
>
LOCAL_FAX_MAX_DATAGRAM
)
return
-
1
;
/* Save the new FEC data */
memcpy
(
s
->
rx
[
x
].
fec
[
i
],
data
,
s
->
rx
[
x
].
fec_len
[
i
]);
#if 0
fprintf(stderr, "FEC: ");
for (j = 0; j < s->rx[x].fec_len[i]; j++)
fprintf(stderr, "%02X ", data[j]);
fprintf(stderr, "\n");
#endif
}
/* We should now be exactly at the end of the packet. If not, this is a fault. */
if
(
ptr
!=
len
)
return
-
1
;
/* See if we can reconstruct anything which is missing */
/* TODO: this does not comprehensively hunt back and repair everything that is possible */
for
(
l
=
x
;
l
!=
((
x
-
(
16
-
span
*
entries
))
&
UDPTL_BUF_MASK
);
l
=
(
l
-
1
)
&
UDPTL_BUF_MASK
)
{
if
(
s
->
rx
[
l
].
fec_len
[
0
]
<=
0
)
continue
;
for
(
m
=
0
;
m
<
s
->
rx
[
l
].
fec_entries
;
m
++
)
{
limit
=
(
l
+
m
)
&
UDPTL_BUF_MASK
;
for
(
which
=
-
1
,
k
=
(
limit
-
s
->
rx
[
l
].
fec_span
*
s
->
rx
[
l
].
fec_entries
)
&
UDPTL_BUF_MASK
;
k
!=
limit
;
k
=
(
k
+
s
->
rx
[
l
].
fec_entries
)
&
UDPTL_BUF_MASK
)
{
if
(
s
->
rx
[
k
].
buf_len
<=
0
)
which
=
(
which
==
-
1
)
?
k
:
-
2
;
}
if
(
which
>=
0
)
{
/* Repairable */
for
(
j
=
0
;
j
<
s
->
rx
[
l
].
fec_len
[
m
];
j
++
)
{
s
->
rx
[
which
].
buf
[
j
]
=
s
->
rx
[
l
].
fec
[
m
][
j
];
for
(
k
=
(
limit
-
s
->
rx
[
l
].
fec_span
*
s
->
rx
[
l
].
fec_entries
)
&
UDPTL_BUF_MASK
;
k
!=
limit
;
k
=
(
k
+
s
->
rx
[
l
].
fec_entries
)
&
UDPTL_BUF_MASK
)
s
->
rx
[
which
].
buf
[
j
]
^=
(
s
->
rx
[
k
].
buf_len
>
j
)
?
s
->
rx
[
k
].
buf
[
j
]
:
0
;
}
s
->
rx
[
which
].
buf_len
=
s
->
rx
[
l
].
fec_len
[
m
];
repaired
[
which
]
=
TRUE
;
}
}
}
/* Now play any new packets forwards in time */
for
(
l
=
(
x
+
1
)
&
UDPTL_BUF_MASK
,
j
=
seq_no
-
UDPTL_BUF_MASK
;
l
!=
x
;
l
=
(
l
+
1
)
&
UDPTL_BUF_MASK
,
j
++
)
{
if
(
repaired
[
l
])
{
#if defined(UDPTL_DEBUG)
fprintf
(
stderr
,
"Fixed packet %d, len %d
\n
"
,
j
,
l
);
#endif
if
(
s
->
rx_packet_handler
(
s
->
user_data
,
s
->
rx
[
l
].
buf
,
s
->
rx
[
l
].
buf_len
,
j
)
<
0
)
fprintf
(
stderr
,
"Bad IFP
\n
"
);
}
}
}
/* If packets are received out of sequence, we may have already processed this packet from the error
recovery information in a packet already received. */
if
(
seq_no
>=
s
->
rx_seq_no
)
{
/* Decode the primary packet */
#if defined(UDPTL_DEBUG)
fprintf
(
stderr
,
"Primary packet %d, len %d
\n
"
,
seq_no
,
msg_len
);
#endif
if
(
s
->
rx_packet_handler
(
s
->
user_data
,
msg
,
msg_len
,
seq_no
)
<
0
)
fprintf
(
stderr
,
"Bad IFP
\n
"
);
}
s
->
rx_seq_no
=
(
seq_no
+
1
)
&
0xFFFF
;
return
0
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_build_packet
(
udptl_state_t
*
s
,
uint8_t
buf
[],
const
uint8_t
msg
[],
int
msg_len
)
{
uint8_t
fec
[
LOCAL_FAX_MAX_DATAGRAM
];
int
i
;
int
j
;
int
seq
;
int
entry
;
int
entries
;
int
span
;
int
m
;
int
len
;
int
limit
;
int
high_tide
;
/* UDPTL cannot cope with zero length messages, and our buffering for redundancy limits their
maximum length. */
if
(
msg_len
<
1
||
msg_len
>
LOCAL_FAX_MAX_DATAGRAM
)
return
-
1
;
seq
=
s
->
tx_seq_no
&
0xFFFF
;
/* Map the sequence number to an entry in the circular buffer */
entry
=
seq
&
UDPTL_BUF_MASK
;
/* We save the message in a circular buffer, for generating FEC or
redundancy sets later on. */
s
->
tx
[
entry
].
buf_len
=
msg_len
;
memcpy
(
s
->
tx
[
entry
].
buf
,
msg
,
msg_len
);
/* Build the UDPTL packet */
len
=
0
;
/* Encode the sequence number */
buf
[
len
++
]
=
(
seq
>>
8
)
&
0xFF
;
buf
[
len
++
]
=
seq
&
0xFF
;
/* Encode the primary packet */
if
(
encode_open_type
(
buf
,
&
len
,
msg
,
msg_len
)
<
0
)
return
-
1
;
/* Encode the appropriate type of error recovery information */
switch
(
s
->
error_correction_scheme
)
{
case
UDPTL_ERROR_CORRECTION_NONE
:
/* Encode the error recovery type */
buf
[
len
++
]
=
0x00
;
/* The number of entries will always be zero, so it is pointless allowing
for the fragmented case here. */
if
(
encode_length
(
buf
,
&
len
,
0
)
<
0
)
return
-
1
;
break
;
case
UDPTL_ERROR_CORRECTION_REDUNDANCY
:
/* Encode the error recovery type */
buf
[
len
++
]
=
0x00
;
if
(
s
->
tx_seq_no
>
s
->
error_correction_entries
)
entries
=
s
->
error_correction_entries
;
else
entries
=
s
->
tx_seq_no
;
/* The number of entries will always be small, so it is pointless allowing
for the fragmented case here. */
if
(
encode_length
(
buf
,
&
len
,
entries
)
<
0
)
return
-
1
;
/* Encode the elements */
for
(
i
=
0
;
i
<
entries
;
i
++
)
{
j
=
(
entry
-
i
-
1
)
&
UDPTL_BUF_MASK
;
if
(
encode_open_type
(
buf
,
&
len
,
s
->
tx
[
j
].
buf
,
s
->
tx
[
j
].
buf_len
)
<
0
)
return
-
1
;
}
break
;
case
UDPTL_ERROR_CORRECTION_FEC
:
span
=
s
->
error_correction_span
;
entries
=
s
->
error_correction_entries
;
if
(
seq
<
s
->
error_correction_span
*
s
->
error_correction_entries
)
{
/* In the initial stages, wind up the FEC smoothly */
entries
=
seq
/
s
->
error_correction_span
;
if
(
seq
<
s
->
error_correction_span
)
span
=
0
;
}
/* Encode the error recovery type */
buf
[
len
++
]
=
0x80
;
/* Span is defined as an inconstrained integer, which it dumb. It will only
ever be a small value. Treat it as such. */
buf
[
len
++
]
=
1
;
buf
[
len
++
]
=
span
;
/* The number of entries is defined as a length, but will only ever be a small
value. Treat it as such. */
buf
[
len
++
]
=
entries
;
for
(
m
=
0
;
m
<
entries
;
m
++
)
{
/* Make an XOR'ed entry the maximum length */
limit
=
(
entry
+
m
)
&
UDPTL_BUF_MASK
;
high_tide
=
0
;
for
(
i
=
(
limit
-
span
*
entries
)
&
UDPTL_BUF_MASK
;
i
!=
limit
;
i
=
(
i
+
entries
)
&
UDPTL_BUF_MASK
)
{
if
(
high_tide
<
s
->
tx
[
i
].
buf_len
)
{
for
(
j
=
0
;
j
<
high_tide
;
j
++
)
fec
[
j
]
^=
s
->
tx
[
i
].
buf
[
j
];
for
(;
j
<
s
->
tx
[
i
].
buf_len
;
j
++
)
fec
[
j
]
=
s
->
tx
[
i
].
buf
[
j
];
high_tide
=
s
->
tx
[
i
].
buf_len
;
}
else
{
for
(
j
=
0
;
j
<
s
->
tx
[
i
].
buf_len
;
j
++
)
fec
[
j
]
^=
s
->
tx
[
i
].
buf
[
j
];
}
}
if
(
encode_open_type
(
buf
,
&
len
,
fec
,
high_tide
)
<
0
)
return
-
1
;
}
break
;
}
if
(
s
->
verbose
)
fprintf
(
stderr
,
"
\n
"
);
s
->
tx_seq_no
++
;
return
len
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_set_error_correction
(
udptl_state_t
*
s
,
int
ec_scheme
,
int
span
,
int
entries
)
{
switch
(
ec_scheme
)
{
case
UDPTL_ERROR_CORRECTION_FEC
:
case
UDPTL_ERROR_CORRECTION_REDUNDANCY
:
case
UDPTL_ERROR_CORRECTION_NONE
:
s
->
error_correction_scheme
=
ec_scheme
;
break
;
case
-
1
:
/* Just don't change the scheme */
break
;
default:
return
-
1
;
}
if
(
span
>=
0
)
s
->
error_correction_span
=
span
;
if
(
entries
>=
0
)
s
->
error_correction_entries
=
entries
;
return
0
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_get_error_correction
(
udptl_state_t
*
s
,
int
*
ec_scheme
,
int
*
span
,
int
*
entries
)
{
if
(
ec_scheme
)
*
ec_scheme
=
s
->
error_correction_scheme
;
if
(
span
)
*
span
=
s
->
error_correction_span
;
if
(
entries
)
*
entries
=
s
->
error_correction_entries
;
return
0
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_set_local_max_datagram
(
udptl_state_t
*
s
,
int
max_datagram
)
{
s
->
local_max_datagram_size
=
max_datagram
;
return
0
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_get_local_max_datagram
(
udptl_state_t
*
s
)
{
return
s
->
local_max_datagram_size
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_set_far_max_datagram
(
udptl_state_t
*
s
,
int
max_datagram
)
{
s
->
far_max_datagram_size
=
max_datagram
;
return
0
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_get_far_max_datagram
(
udptl_state_t
*
s
)
{
return
s
->
far_max_datagram_size
;
}
/*- End of function --------------------------------------------------------*/
udptl_state_t
*
udptl_init
(
udptl_state_t
*
s
,
int
ec_scheme
,
int
span
,
int
entries
,
udptl_rx_packet_handler_t
rx_packet_handler
,
void
*
user_data
)
{
int
i
;
if
(
rx_packet_handler
==
NULL
)
return
NULL
;
if
(
s
==
NULL
)
{
if
((
s
=
(
udptl_state_t
*
)
malloc
(
sizeof
(
*
s
)))
==
NULL
)
return
NULL
;
}
memset
(
s
,
0
,
sizeof
(
*
s
));
s
->
error_correction_scheme
=
ec_scheme
;
s
->
error_correction_span
=
span
;
s
->
error_correction_entries
=
entries
;
s
->
far_max_datagram_size
=
LOCAL_FAX_MAX_DATAGRAM
;
s
->
local_max_datagram_size
=
LOCAL_FAX_MAX_DATAGRAM
;
memset
(
&
s
->
rx
,
0
,
sizeof
(
s
->
rx
));
memset
(
&
s
->
tx
,
0
,
sizeof
(
s
->
tx
));
for
(
i
=
0
;
i
<=
UDPTL_BUF_MASK
;
i
++
)
{
s
->
rx
[
i
].
buf_len
=
-
1
;
s
->
tx
[
i
].
buf_len
=
-
1
;
}
s
->
rx_packet_handler
=
rx_packet_handler
;
s
->
user_data
=
user_data
;
return
s
;
}
/*- End of function --------------------------------------------------------*/
int
udptl_release
(
udptl_state_t
*
s
)
{
return
0
;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
src/mod/applications/mod_fax/udptl.h
deleted
100644 → 0
浏览文件 @
be2ec292
/*
* 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>
*
* udptl.h -- UDPTL handling for T.38
*
*/
#if !defined(FREESWITCH_UDPTL_H)
#define FREESWITCH_UDPTL_H
#define LOCAL_FAX_MAX_DATAGRAM 400
#define LOCAL_FAX_MAX_FEC_PACKETS 5
#define UDPTL_BUF_MASK 15
typedef
int
(
udptl_rx_packet_handler_t
)
(
void
*
user_data
,
const
uint8_t
msg
[],
int
len
,
int
seq_no
);
typedef
struct
{
int
buf_len
;
uint8_t
buf
[
LOCAL_FAX_MAX_DATAGRAM
];
}
udptl_fec_tx_buffer_t
;
typedef
struct
{
int
buf_len
;
uint8_t
buf
[
LOCAL_FAX_MAX_DATAGRAM
];
int
fec_len
[
LOCAL_FAX_MAX_FEC_PACKETS
];
uint8_t
fec
[
LOCAL_FAX_MAX_FEC_PACKETS
][
LOCAL_FAX_MAX_DATAGRAM
];
int
fec_span
;
int
fec_entries
;
}
udptl_fec_rx_buffer_t
;
struct
udptl_state_s
{
udptl_rx_packet_handler_t
*
rx_packet_handler
;
void
*
user_data
;
/*! This option indicates the error correction scheme used in transmitted UDPTL
packets. */
int
error_correction_scheme
;
/*! This option indicates the number of error correction entries transmitted in
UDPTL packets. */
int
error_correction_entries
;
/*! This option indicates the span of the error correction entries in transmitted
UDPTL packets (FEC only). */
int
error_correction_span
;
/*! This option indicates the maximum size of a datagram that can be accepted by
the remote device. */
int
far_max_datagram_size
;
/*! This option indicates the maximum size of a datagram that we are prepared to
accept. */
int
local_max_datagram_size
;
int
verbose
;
int
tx_seq_no
;
int
rx_seq_no
;
int
rx_expected_seq_no
;
udptl_fec_tx_buffer_t
tx
[
UDPTL_BUF_MASK
+
1
];
udptl_fec_rx_buffer_t
rx
[
UDPTL_BUF_MASK
+
1
];
};
enum
{
UDPTL_ERROR_CORRECTION_NONE
,
UDPTL_ERROR_CORRECTION_FEC
,
UDPTL_ERROR_CORRECTION_REDUNDANCY
};
typedef
struct
udptl_state_s
udptl_state_t
;
#if defined(__cplusplus)
extern
"C"
{
#endif
/*! \brief Process an arriving UDPTL packet.
\param s The UDPTL context.
\param buf The UDPTL packet buffer.
\param len The length of the packet.
\return 0 for OK. */
int
udptl_rx_packet
(
udptl_state_t
*
s
,
const
uint8_t
buf
[],
int
len
);
/*! \brief Construct a UDPTL packet, ready for transmission.
\param s The UDPTL context.
\param buf The UDPTL packet buffer.
\param msg The primary packet.
\param len The length of the primary packet.
\return The length of the constructed UDPTL packet. */
int
udptl_build_packet
(
udptl_state_t
*
s
,
uint8_t
buf
[],
const
uint8_t
msg
[],
int
msg_len
);
/*! \brief Change the error correction settings of a UDPTL context.
\param s The UDPTL context.
\param ec_scheme One of the optional error correction schemes.
\param span The packet span over which error correction should be applied.
\param entries The number of error correction entries to include in packets.
\return 0 for OK. */
int
udptl_set_error_correction
(
udptl_state_t
*
s
,
int
ec_scheme
,
int
span
,
int
entries
);
/*! \brief Check the error correction settings of a UDPTL context.
\param s The UDPTL context.
\param ec_scheme One of the optional error correction schemes.
\param span The packet span over which error correction is being applied.
\param entries The number of error correction being included in packets.
\return 0 for OK. */
int
udptl_get_error_correction
(
udptl_state_t
*
s
,
int
*
ec_scheme
,
int
*
span
,
int
*
entries
);
int
udptl_set_local_max_datagram
(
udptl_state_t
*
s
,
int
max_datagram
);
int
udptl_get_local_max_datagram
(
udptl_state_t
*
s
);
int
udptl_set_far_max_datagram
(
udptl_state_t
*
s
,
int
max_datagram
);
int
udptl_get_far_max_datagram
(
udptl_state_t
*
s
);
/*! \brief Initialise a UDPTL context.
\param s The UDPTL context.
\param ec_scheme One of the optional error correction schemes.
\param span The packet span over which error correction should be applied.
\param entries The number of error correction entries to include in packets.
\param rx_packet_handler The callback function, used to report arriving IFP packets.
\param user_data An opaque pointer supplied to rx_packet_handler.
\return A pointer to the UDPTL context, or NULL if there was a problem. */
udptl_state_t
*
udptl_init
(
udptl_state_t
*
s
,
int
ec_scheme
,
int
span
,
int
entries
,
udptl_rx_packet_handler_t
rx_packet_handler
,
void
*
user_data
);
/*! \brief Release a UDPTL context.
\param s The UDPTL context.
\return 0 for OK. */
int
udptl_release
(
udptl_state_t
*
s
);
#if defined(__cplusplus)
}
#endif
#endif
/*- End of file ------------------------------------------------------------*/
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论