Tea for two (or maybe thirty eight?)

This commit is contained in:
Anthony Minessale 2010-05-12 21:25:54 -05:00 committed by Brian West
parent 7cfe6bb222
commit 7b3d3f7ef2
17 changed files with 1961 additions and 217 deletions

View File

@ -874,6 +874,7 @@ AC_CONFIG_FILES([Makefile
src/mod/Makefile
src/mod/applications/mod_enum/Makefile
src/mod/applications/mod_expr/Makefile
src/mod/applications/mod_fax/Makefile
src/mod/applications/mod_stress/Makefile
src/mod/applications/mod_t38gateway/Makefile
src/mod/endpoints/mod_portaudio/Makefile

View File

@ -176,6 +176,8 @@ SWITCH_DECLARE(switch_port_t) switch_rtp_get_remote_port(switch_rtp_t *rtp_sessi
SWITCH_DECLARE(void) switch_rtp_reset_media_timer(switch_rtp_t *rtp_session);
SWITCH_DECLARE(void) switch_rtp_set_max_missed_packets(switch_rtp_t *rtp_session, uint32_t max);
SWITCH_DECLARE(switch_status_t) switch_rtp_udptl_mode(switch_rtp_t *rtp_session);
/*!
\brief Assign a local address to the RTP session
\param rtp_session an RTP session to assign the local address to

View File

@ -499,7 +499,6 @@ typedef enum {
SWITCH_RTP_FLAG_GOOGLEHACK - Convert payload from 102 to 97
SWITCH_RTP_FLAG_VAD - Enable VAD
SWITCH_RTP_FLAG_BREAK - Stop what you are doing and return SWITCH_STATUS_BREAK
SWITCH_RTP_FLAG_MINI - Use mini RTP when possible
SWITCH_RTP_FLAG_DATAWAIT - Do not return from reads unless there is data even when non blocking
SWITCH_RTP_FLAG_BUGGY_2833 - Emulate the bug in cisco equipment to allow interop
SWITCH_RTP_FLAG_PASS_RFC2833 - Pass 2833 (ignore it)
@ -518,7 +517,7 @@ typedef enum {
SWITCH_RTP_FLAG_GOOGLEHACK = (1 << 8),
SWITCH_RTP_FLAG_VAD = (1 << 9),
SWITCH_RTP_FLAG_BREAK = (1 << 10),
SWITCH_RTP_FLAG_MINI = (1 << 11),
SWITCH_RTP_FLAG_UDPTL = (1 << 11),
SWITCH_RTP_FLAG_DATAWAIT = (1 << 12),
SWITCH_RTP_FLAG_BUGGY_2833 = (1 << 13),
SWITCH_RTP_FLAG_PASS_RFC2833 = (1 << 14),
@ -725,10 +724,13 @@ typedef enum {
SWITCH_MESSAGE_INDICATE_APPLICATION_EXEC,
SWITCH_MESSAGE_INDICATE_APPLICATION_EXEC_COMPLETE,
SWITCH_MESSAGE_INDICATE_PHONE_EVENT,
SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION,
SWITCH_MESSAGE_INDICATE_UDPTL_MODE,
SWITCH_MESSAGE_INVALID
} switch_core_session_message_types_t;
typedef struct {
uint16_t T38FaxVersion;
uint32_t T38MaxBitRate;
switch_bool_t T38FaxFillBitRemoval;
switch_bool_t T38FaxTranscodingMMR;
@ -1008,7 +1010,8 @@ typedef enum {
typedef enum {
CF_APP_TAGGED = (1 << 0)
CF_APP_TAGGED = (1 << 0),
CF_APP_T38 = (1 << 1)
} switch_channel_app_flag_t;
@ -1034,7 +1037,8 @@ typedef enum {
SFF_RFC2833 = (1 << 4),
SFF_PROXY_PACKET = (1 << 5),
SFF_DYNAMIC = (1 << 6),
SFF_ZRTP = (1 << 7)
SFF_ZRTP = (1 << 7),
SFF_UDPTL_PACKET = (1 << 8)
} switch_frame_flag_enum_t;
typedef uint32_t switch_frame_flag_t;

2
src/mod/.gitignore vendored
View File

@ -1,5 +1,7 @@
/Makefile
/Makefile.in
/applications/mod_fax/Makefile
/applications/mod_fax/Makefile.in
/applications/mod_commands/Makefile
/applications/mod_conference/Makefile
/applications/mod_dptools/Makefile

View File

@ -1,26 +0,0 @@
BASE=../../../..
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
LOCAL_CFLAGS=-I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff
LOCAL_LIBADD=$(SPANDSP_LA) $(TIFF_LA)
LOCAL_LDFLAGS=-ljpeg
include $(BASE)/build/modmake.rules
$(MODNAME).lo: $(SPANDSP_LA) $(TIFF_LA)
$(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)

View File

@ -0,0 +1,17 @@
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 ../mod_t38gateway/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_t38gateway
mod_fax_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA)
mod_fax_la_LDFLAGS = -avoid-version -module -no-undefined -shared -ljpeg

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,563 @@
//#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 ------------------------------------------------------------*/

View File

@ -0,0 +1,153 @@
/*
* 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(_UDPTL_H_)
#define _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 ------------------------------------------------------------*/

View File

@ -1,8 +1,17 @@
include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_t38gateway
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_t38gateway.la
mod_t38gateway_la_SOURCES = mod_t38gateway.c udptl.c
mod_t38gateway_la_CFLAGS = $(AM_CFLAGS)
mod_t38gateway_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_t38gateway_la_LDFLAGS = -avoid-version -module -no-undefined -shared
mod_t38gateway_la_CFLAGS = $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff
mod_t38gateway_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA)
mod_t38gateway_la_LDFLAGS = -avoid-version -module -no-undefined -shared -ljpeg

View File

@ -199,7 +199,7 @@ SWITCH_STANDARD_APP(t38gateway_start_function)
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);
status = switch_core_media_bug_add(session, "T.38 gateway", NULL, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
if (status != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure hooking to stream\n");
@ -316,7 +316,7 @@ SWITCH_STANDARD_API(t38gateway_api_main)
/* 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);
status = switch_core_media_bug_add(t38gateway_session, "T.38 gateway", NULL, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
/* If adding a media bug fails exit */
if (status != SWITCH_STATUS_SUCCESS) {

View File

@ -996,19 +996,23 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
}
tech_pvt->read_frame.datalen = 0;
if (codec_ms != tech_pvt->codec_ms) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"We were told to use ptime %d but what they meant to say was %d\n"
"This issue has so far been identified to happen on the following broken platforms/devices:\n"
"Linksys/Sipura aka Cisco\n"
"ShoreTel\n"
"Sonus/L3\n"
"We will try to fix it but some of the devices on this list are so broken who knows what will happen..\n",
"We will try to fix it but some of the devices on this list are so broken,\n"
"who knows what will happen..\n",
(int) tech_pvt->codec_ms, (int) codec_ms);
switch_channel_set_variable_printf(channel, "sip_h_X-Broken-PTIME", "Adv=%d;Sent=%d",
(int) tech_pvt->codec_ms, (int) codec_ms);
tech_pvt->codec_ms = codec_ms;
}
if (sofia_glue_tech_set_codec(tech_pvt, 2) != SWITCH_STATUS_SUCCESS) {
*frame = NULL;
@ -1049,6 +1053,7 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
tech_pvt->last_ts = 0;
/* inform them of the codec they are actually sending */
if (++tech_pvt->codec_reinvites > 2) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"Ok, some devices *cough* X-lite *cough*\n"
@ -1065,7 +1070,10 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
} else {
tech_pvt->mismatch_count = 0;
}
tech_pvt->last_ts = tech_pvt->read_frame.timestamp;
} else {
tech_pvt->mismatch_count = 0;
tech_pvt->last_ts = 0;
@ -1512,15 +1520,69 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
}
break;
case SWITCH_MESSAGE_INDICATE_UDPTL_MODE:
{
switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
if (!t38_options) {
nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
goto end_lock;
}
if (switch_rtp_ready(tech_pvt->rtp_session)) {
switch_rtp_udptl_mode(tech_pvt->rtp_session);
}
}
break;
case SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION:
{
switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
if (!t38_options) {
nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
goto end_lock;
}
if (switch_rtp_ready(tech_pvt->rtp_session)) {
switch_rtp_udptl_mode(tech_pvt->rtp_session);
}
sofia_glue_set_image_sdp(tech_pvt, t38_options, msg->numeric_arg);
if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) {
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_RESPONSE_HEADER_PREFIX);
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_AUTOANSWER(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CALL_INFO_STR(switch_channel_get_variable(tech_pvt->channel, SOFIA_SIP_HEADER_PREFIX "call_info")),
SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str),
SOATAG_REUSE_REJECTED(1), SOATAG_ORDERED_USER(1), SOATAG_AUDIO_AUX("cn telephone-event"), NUTAG_INCLUDE_EXTRA_SDP(1),
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_AUTOANSWER(0),
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CALL_INFO_STR(switch_channel_get_variable(tech_pvt->channel, SOFIA_SIP_HEADER_PREFIX "call_info")),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(tech_pvt->local_sdp_str),
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
TAG_END());
}
switch_safe_free(extra_headers);
sofia_set_flag_locked(tech_pvt, TFLAG_ANS);
}
}
break;
case SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA:
{
switch_t38_options_t *t38_options = (switch_t38_options_t *) msg->pointer_arg;
switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
sofia_glue_set_image_sdp(tech_pvt, t38_options);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Sending request for image media. %s\n",
switch_channel_get_name(channel), tech_pvt->local_sdp_str);
sofia_glue_set_image_sdp(tech_pvt, t38_options, msg->numeric_arg);
switch_channel_set_flag(channel, CF_REQ_MEDIA);
sofia_set_flag_locked(tech_pvt, TFLAG_SENT_UPDATE);

View File

@ -930,7 +930,7 @@ void sofia_glue_toggle_hold(private_object_t *tech_pvt, int sendonly);
const char *sofia_state_string(int state);
switch_status_t sofia_glue_tech_set_codec(private_object_t *tech_pvt, int force);
void sofia_wait_for_reply(struct private_object *tech_pvt, nua_event_t event, uint32_t timeout);
void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options);
void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options, int insist);
/*
* SLA (shared line appearance) entrypoints

View File

@ -4578,6 +4578,11 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
sdp_parser_free(parser);
}
}
if (match && switch_channel_test_app_flag(tech_pvt->channel, CF_APP_T38)) {
goto done;
}
if (match) {
if (sofia_glue_tech_choose_port(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) {
goto done;

View File

@ -37,7 +37,7 @@
#include <switch_stun.h>
void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options)
void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options, int insist)
{
char buf[2048];
const char *ip = t38_options->ip;
@ -45,6 +45,9 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
const char *family = "IP4";
const char *username = tech_pvt->profile->username;
//sofia_clear_flag(tech_pvt, TFLAG_ENABLE_SOA);
if (!ip) {
if (!(ip = tech_pvt->adv_sdp_audio_ip)) {
ip = tech_pvt->proxy_sdp_audio_ip;
@ -78,13 +81,27 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
tech_pvt->session_id++;
family = strchr(ip, ':') ? "IP6" : "IP4";
switch_snprintf(buf, sizeof(buf),
"v=0\n"
"o=%s %010u %010u IN %s %s\n"
"s=%s\n"
"c=IN %s %s\n"
"t=0 0\n"
"t=0 0\n",
username,
tech_pvt->owner_id,
tech_pvt->session_id,
family,
ip,
username,
family,
ip);
switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
"m=image %d udptl t38\n"
"a=T38FaxVersion:%d\n"
"a=T38MaxBitRate:%d\n"
"%s"
"%s"
@ -94,23 +111,32 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
"a=T38FaxMaxDatagram:%d\n"
"a=T38FaxUdpEC:%s\n"
"a=T38VendorInfo:%s\n",
username,
tech_pvt->owner_id,
tech_pvt->session_id,
family,
ip,
username,
family,
ip,
port,
t38_options->T38FaxVersion,
t38_options->T38MaxBitRate,
t38_options->T38FaxFillBitRemoval ? "a=T38FaxFillBitRemoval\n" : "",
t38_options->T38FaxTranscodingMMR ? "a=T38FaxTranscodingMMR\n" : "",
t38_options->T38FaxTranscodingJBIG ? "a=T38FaxTranscodingJBIG\n" : "",
t38_options->T38FaxRateManagement,
t38_options->T38FaxMaxBuffer, t38_options->T38FaxMaxDatagram, t38_options->T38FaxUdpEC, t38_options->T38VendorInfo);
t38_options->T38FaxMaxBuffer,
t38_options->T38FaxMaxDatagram,
t38_options->T38FaxUdpEC,
t38_options->T38VendorInfo);
if (insist) {
switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
"m=audio 0 RTP/AVP 19\n");
}
sofia_glue_tech_set_local_sdp(tech_pvt, buf, SWITCH_TRUE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "%s image media sdp:\n%s\n",
switch_channel_get_name(tech_pvt->channel), tech_pvt->local_sdp_str);
}
void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32_t port, const char *sr, int force)
@ -1582,7 +1608,9 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
return status;
}
if (!switch_channel_get_private(tech_pvt->channel, "t38_options") || zstr(tech_pvt->local_sdp_str)) {
sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0);
}
sofia_set_flag_locked(tech_pvt, TFLAG_READY);
@ -3417,10 +3445,24 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t *
}
if (got_udptl && m->m_type == sdp_media_image && m->m_port) {
switch_t38_options_t *t38_options = switch_core_session_alloc(tech_pvt->session, sizeof(switch_t38_options_t));
switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
if (!t38_options) {
t38_options = switch_core_session_alloc(tech_pvt->session, sizeof(switch_t38_options_t));
}
t38_options->port = m->m_port;
if (m->m_connections) {
t38_options->ip = switch_core_session_strdup(tech_pvt->session, m->m_connections->c_address);
} else if (sdp && sdp->sdp_connection) {
t38_options->ip = switch_core_session_strdup(tech_pvt->session, sdp->sdp_connection->c_address);
}
for (attr = m->m_attributes; attr; attr = attr->a_next) {
if (!strcasecmp(attr->a_name, "T38MaxBitRate") && attr->a_value) {
if (!strcasecmp(attr->a_name, "T38FaxVersion") && attr->a_value) {
t38_options->T38FaxVersion = (uint16_t) atoi(attr->a_value);
} else if (!strcasecmp(attr->a_name, "T38MaxBitRate") && attr->a_value) {
t38_options->T38MaxBitRate = (uint32_t) atoi(attr->a_value);
} else if (!strcasecmp(attr->a_name, "T38FaxFillBitRemoval")) {
t38_options->T38FaxFillBitRemoval = SWITCH_TRUE;
@ -3443,10 +3485,10 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t *
switch_channel_set_variable(tech_pvt->channel, "has_t38", "true");
switch_channel_set_private(tech_pvt->channel, "t38_options", t38_options);
//switch_channel_set_flag(tech_pvt->channel, CF_PROXY_MEDIA);
//switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA);
switch_channel_set_app_flag(tech_pvt->channel, CF_APP_T38);
/* do nothing here, mod_fax will trigger a response (if it's listening =/)*/
match = 1;
goto done;
} else if (m->m_type == sdp_media_audio && m->m_port && !got_audio) {
sdp_rtpmap_t *map;
for (attr = m->m_attributes; attr; attr = attr->a_next) {

View File

@ -556,6 +556,7 @@ static const char *message_names[] = {
"APPLICATION_EXEC",
"APPLICATION_EXEC_COMPLETE",
"PHONE_EVENT",
"T38_DESCRIPTION"
"INVALID"
};

View File

@ -1025,6 +1025,33 @@ SWITCH_DECLARE(switch_port_t) switch_rtp_get_remote_port(switch_rtp_t *rtp_sessi
}
SWITCH_DECLARE(switch_status_t) switch_rtp_udptl_mode(switch_rtp_t *rtp_session)
{
READ_INC(rtp_session);
WRITE_INC(rtp_session);
if (rtp_session->timer.timer_interface) {
switch_core_timer_destroy(&rtp_session->timer);
memset(&rtp_session->timer, 0, sizeof(rtp_session->timer));
}
switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_UDPTL);
switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA);
switch_socket_opt_set(rtp_session->sock_input, SWITCH_SO_NONBLOCK, FALSE);
switch_clear_flag(rtp_session, SWITCH_RTP_FLAG_USE_TIMER);
switch_clear_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK);
WRITE_DEC(rtp_session);
READ_DEC(rtp_session);
switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_FLUSH);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_session, const char *host, switch_port_t port, switch_port_t remote_rtcp_port,
switch_bool_t change_adv_addr, const char **err)
{
@ -1227,7 +1254,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_change_interval(switch_rtp_t *rtp_ses
rtp_session->timer_name, ms_per_packet / 1000,
samples_per_interval, rtp_session->pool)) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"RE-Starting timer [%s] %d bytes per %dms\n", rtp_session->timer_name, samples_per_interval, ms_per_packet);
"RE-Starting timer [%s] %d bytes per %dms\n", rtp_session->timer_name, samples_per_interval, ms_per_packet / 1000);
} else {
memset(&rtp_session->timer, 0, sizeof(rtp_session->timer));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
@ -2451,9 +2478,14 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
}
}
if (bytes && switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA)) {
if (bytes && (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_UDPTL))) {
/* Fast PASS! */
*flags |= SFF_PROXY_PACKET;
if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_UDPTL)) {
*flags |= SFF_UDPTL_PACKET;
}
ret = (int) bytes;
goto end;
}
@ -3475,12 +3507,12 @@ SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_fra
return -1;
}
if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA)) {
if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_UDPTL)) {
switch_size_t bytes;
char bufa[30];
const char *tx_host;
/* Fast PASS! */
if (!switch_test_flag(frame, SFF_PROXY_PACKET)) {
if (!switch_test_flag(frame, SFF_PROXY_PACKET) && !switch_test_flag(frame, SFF_UDPTL_PACKET)) {
return 0;
}
bytes = frame->packetlen;