@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include <sys/ioctl.h>
# include <errno.h>
# include <sys/mman.h>
# include <sys/poll.h>
# include "asterisk/lock.h"
# include "asterisk/translate.h"
@ -56,8 +57,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include "asterisk/utils.h"
# include "asterisk/linkedlists.h"
# include "asterisk/dahdi_compat.h"
# include "asterisk/frame.h"
# include "asterisk/ulaw.h"
# define BUFFER_SAMPLES 8000
# define BUFFER_SIZE 8000
# define G723_SAMPLES 240
# define G729_SAMPLES 160
static unsigned int global_useplc = 0 ;
@ -105,13 +111,53 @@ struct translator {
static AST_LIST_HEAD_STATIC ( translators , translator ) ;
struct pvt {
struct codec_dahdi_ pvt {
int fd ;
int fake ;
int samples ;
struct dahdi_transcoder_formats fmts ;
unsigned int softslin : 1 ;
unsigned int fake : 2 ;
uint16_t required_samples ;
uint16_t samples_in_buffer ;
uint8_t ulaw_buffer [ 1024 ] ;
} ;
/* Only used by a decoder */
static int ulawtolin ( struct ast_trans_pvt * pvt )
{
struct codec_dahdi_pvt * ztp = pvt - > pvt ;
int i = ztp - > required_samples ;
uint8_t * src = & ztp - > ulaw_buffer [ 0 ] ;
int16_t * dst = ( int16_t * ) pvt - > outbuf + pvt - > datalen ;
/* convert and copy in outbuf */
while ( i - - ) {
* dst + + = AST_MULAW ( * src + + ) ;
}
return 0 ;
}
/* Only used by an encoder. */
static int lintoulaw ( struct ast_trans_pvt * pvt , struct ast_frame * f )
{
struct codec_dahdi_pvt * ztp = pvt - > pvt ;
int i = f - > samples ;
uint8_t * dst = & ztp - > ulaw_buffer [ ztp - > samples_in_buffer ] ;
int16_t * src = ( int16_t * ) f - > data ;
if ( ztp - > samples_in_buffer + i > sizeof ( ztp - > ulaw_buffer ) ) {
ast_log ( LOG_ERROR , " Out of buffer space! \n " ) ;
return - i ;
}
while ( i - - ) {
* dst + + = AST_LIN2MU ( * src + + ) ;
}
ztp - > samples_in_buffer + = f - > samples ;
return 0 ;
}
static int transcoder_show ( int fd , int argc , char * * argv )
{
struct channel_usage copy ;
@ -126,68 +172,79 @@ static int transcoder_show(int fd, int argc, char **argv)
return RESULT_SUCCESS ;
}
static int zap_framein ( struct ast_trans_pvt * pvt , struct ast_frame * f )
static void dahdi_write_frame ( struct codec_dahdi_pvt * ztp , const uint8_t * buffer , const ssize_t count )
{
int res ;
struct pvt * ztp = pvt - > pvt ;
if ( f - > subclass ) {
/* Give the frame to the hardware transcoder... */
res = write ( ztp - > fd , f - > data , f - > datalen ) ;
struct pollfd p = { 0 } ;
if ( ! count ) return ;
res = write ( ztp - > fd , buffer , count ) ;
if ( option_verbose > 10 ) {
if ( - 1 = = res ) {
ast_log ( LOG_ERROR , " Failed to write to transcoder: %s \n " , strerror ( errno ) ) ;
}
if ( count ! = res ) {
ast_log ( LOG_ERROR , " Requested write of %zd bytes, but only wrote %d bytes. \n " , count , res ) ;
}
if ( f - > datalen ! = res ) {
ast_log ( LOG_ERROR , " Requested write of %d bytes, but only wrote %d bytes. \n " , f - > datalen , res ) ;
}
res = - 1 ;
pvt - > samples + = f - > samples ;
} else {
/* Fake a return frame for calculation purposes */
ztp - > fake = 2 ;
pvt - > samples = f - > samples ;
res = 0 ;
}
return res ;
p . fd = ztp - > fd ;
p . events = POLLOUT ;
res = poll ( & p , 1 , 50 ) ;
}
static struct ast_frame * zap_frameout ( struct ast_trans_pvt * pvt )
static int dahdi_encoder_framein ( struct ast_trans_pvt * pvt , struct ast_frame * f )
{
struct pvt * ztp = pvt - > pvt ;
struct codec_dahdi_pvt * ztp = pvt - > pvt ;
if ( 0 = = ztp - > fake ) {
int res ;
/* Let's check to see if there is a new frame for us.... */
res = read ( ztp - > fd , pvt - > outbuf + pvt - > datalen , pvt - > t - > buf_size - pvt - > datalen ) ;
if ( - 1 = = res ) {
if ( EWOULDBLOCK = = errno ) {
/* Nothing waiting... */
return NULL ;
} else {
ast_log ( LOG_ERROR , " Failed to read from transcoder: %s \n " , strerror ( errno ) ) ;
return NULL ;
}
} else {
pvt - > f . samples = ztp - > samples ;
pvt - > f . datalen = res ;
pvt - > datalen = 0 ;
pvt - > f . frametype = AST_FRAME_VOICE ;
pvt - > f . subclass = 1 < < ( pvt - > t - > dstfmt ) ;
pvt - > f . mallocd = 0 ;
pvt - > f . offset = AST_FRIENDLY_OFFSET ;
pvt - > f . src = pvt - > t - > name ;
pvt - > f . data = pvt - > outbuf ;
ast_set_flag ( & pvt - > f , AST_FRFLAG_FROM_TRANSLATOR ) ;
return & pvt - > f ;
if ( ! f - > subclass ) {
/* We're just faking a return for calculation purposes. */
ztp - > fake = 2 ;
pvt - > samples = f - > samples ;
return 0 ;
}
/* Buffer up the packets and send them to the hardware if we
* have enough samples set up . */
if ( ztp - > softslin ) {
if ( lintoulaw ( pvt , f ) ) {
return - 1 ;
}
} else {
/* NOTE: If softslin support is not needed, and the sample
* size is equal to the required sample size , we wouldn ' t
* need this copy operation . But at the time this was
* written , only softslin is supported . */
if ( ztp - > samples_in_buffer + f - > samples > sizeof ( ztp - > ulaw_buffer ) ) {
ast_log ( LOG_ERROR , " Out of buffer space. \n " ) ;
return - 1 ;
}
memcpy ( & ztp - > ulaw_buffer [ ztp - > samples_in_buffer ] , f - > data , f - > samples ) ;
ztp - > samples_in_buffer + = f - > samples ;
}
} else if ( 2 = = ztp - > fake ) {
while ( ztp - > samples_in_buffer > ztp - > required_samples ) {
dahdi_write_frame ( ztp , ztp - > ulaw_buffer , ztp - > required_samples ) ;
ztp - > samples_in_buffer - = ztp - > required_samples ;
if ( ztp - > samples_in_buffer ) {
/* Shift any remaining bytes down. */
memmove ( ztp - > ulaw_buffer , & ztp - > ulaw_buffer [ ztp - > required_samples ] ,
ztp - > samples_in_buffer ) ;
}
}
pvt - > samples + = f - > samples ;
pvt - > datalen = 0 ;
return - 1 ;
}
static struct ast_frame * dahdi_encoder_frameout ( struct ast_trans_pvt * pvt )
{
struct codec_dahdi_pvt * ztp = pvt - > pvt ;
int res ;
if ( 2 = = ztp - > fake ) {
ztp - > fake = 1 ;
pvt - > f . frametype = AST_FRAME_VOICE ;
pvt - > f . subclass = 0 ;
pvt - > f . samples = 160 ;
pvt - > f . samples = ztp - > required_samples ;
pvt - > f . data = NULL ;
pvt - > f . offset = 0 ;
pvt - > f . datalen = 0 ;
@ -198,17 +255,128 @@ static struct ast_frame *zap_frameout(struct ast_trans_pvt *pvt)
return & pvt - > f ;
} else if ( 1 = = ztp - > fake ) {
ztp - > fake = 0 ;
return NULL ;
}
res = read ( ztp - > fd , pvt - > outbuf + pvt - > datalen , pvt - > t - > buf_size - pvt - > datalen ) ;
if ( - 1 = = res ) {
if ( EWOULDBLOCK = = errno ) {
/* Nothing waiting... */
return NULL ;
} else {
ast_log ( LOG_ERROR , " Failed to read from transcoder: %s \n " , strerror ( errno ) ) ;
return NULL ;
}
} else {
pvt - > f . datalen = res ;
pvt - > f . samples = ztp - > required_samples ;
pvt - > f . frametype = AST_FRAME_VOICE ;
pvt - > f . subclass = 1 < < ( pvt - > t - > dstfmt ) ;
pvt - > f . mallocd = 0 ;
pvt - > f . offset = AST_FRIENDLY_OFFSET ;
pvt - > f . src = pvt - > t - > name ;
pvt - > f . data = pvt - > outbuf ;
ast_set_flag ( & pvt - > f , AST_FRFLAG_FROM_TRANSLATOR ) ;
pvt - > samples = 0 ;
pvt - > datalen = 0 ;
return & pvt - > f ;
}
/* Shouldn't get here... */
return NULL ;
}
static int dahdi_decoder_framein ( struct ast_trans_pvt * pvt , struct ast_frame * f )
{
struct codec_dahdi_pvt * ztp = pvt - > pvt ;
if ( ! f - > subclass ) {
/* We're just faking a return for calculation purposes. */
ztp - > fake = 2 ;
pvt - > samples = f - > samples ;
return 0 ;
}
if ( ! f - > datalen ) {
if ( f - > samples ! = ztp - > required_samples ) {
ast_log ( LOG_ERROR , " %d != %d %d \n " , f - > samples , ztp - > required_samples , f - > datalen ) ;
}
}
dahdi_write_frame ( ztp , f - > data , f - > datalen ) ;
pvt - > samples + = f - > samples ;
pvt - > datalen = 0 ;
return - 1 ;
}
static struct ast_frame * dahdi_decoder_frameout ( struct ast_trans_pvt * pvt )
{
int res ;
struct codec_dahdi_pvt * ztp = pvt - > pvt ;
if ( 2 = = ztp - > fake ) {
ztp - > fake = 1 ;
pvt - > f . frametype = AST_FRAME_VOICE ;
pvt - > f . subclass = 0 ;
pvt - > f . samples = ztp - > required_samples ;
pvt - > f . data = NULL ;
pvt - > f . offset = 0 ;
pvt - > f . datalen = 0 ;
pvt - > f . mallocd = 0 ;
ast_set_flag ( & pvt - > f , AST_FRFLAG_FROM_TRANSLATOR ) ;
pvt - > samples = 0 ;
return & pvt - > f ;
} else if ( 1 = = ztp - > fake ) {
pvt - > samples = 0 ;
ztp - > fake = 0 ;
return NULL ;
}
/* Let's check to see if there is a new frame for us.... */
if ( ztp - > softslin ) {
res = read ( ztp - > fd , ztp - > ulaw_buffer , sizeof ( ztp - > ulaw_buffer ) ) ;
} else {
res = read ( ztp - > fd , pvt - > outbuf + pvt - > datalen , pvt - > t - > buf_size - pvt - > datalen ) ;
}
if ( - 1 = = res ) {
if ( EWOULDBLOCK = = errno ) {
/* Nothing waiting... */
return NULL ;
} else {
ast_log ( LOG_ERROR , " Failed to read from transcoder: %s \n " , strerror ( errno ) ) ;
return NULL ;
}
} else {
if ( ztp - > softslin ) {
ulawtolin ( pvt ) ;
pvt - > f . datalen = res * 2 ;
} else {
pvt - > f . datalen = res ;
}
pvt - > datalen = 0 ;
pvt - > f . frametype = AST_FRAME_VOICE ;
pvt - > f . subclass = 1 < < ( pvt - > t - > dstfmt ) ;
pvt - > f . mallocd = 0 ;
pvt - > f . offset = AST_FRIENDLY_OFFSET ;
pvt - > f . src = pvt - > t - > name ;
pvt - > f . data = pvt - > outbuf ;
pvt - > f . samples = ztp - > required_samples ;
ast_set_flag ( & pvt - > f , AST_FRFLAG_FROM_TRANSLATOR ) ;
pvt - > samples = 0 ;
return & pvt - > f ;
}
/* Shouldn't get here... */
return NULL ;
}
static void zap_destroy ( struct ast_trans_pvt * pvt )
static void dahdi_destroy ( struct ast_trans_pvt * pvt )
{
struct pvt * ztp = pvt - > pvt ;
struct codec_dahdi_ pvt * ztp = pvt - > pvt ;
switch ( ztp - > fmts . dstfmt ) {
case AST_FORMAT_G729A :
@ -223,29 +391,57 @@ static void zap_destroy(struct ast_trans_pvt *pvt)
close ( ztp - > fd ) ;
}
static int zap _translate( struct ast_trans_pvt * pvt , int dest , int source )
static int dahdi _translate( struct ast_trans_pvt * pvt , int dest , int source )
{
/* Request translation through zap if possible */
int fd ;
struct pvt * ztp = pvt - > pvt ;
struct codec_dahdi_ pvt * ztp = pvt - > pvt ;
int flags ;
int tried_once = 0 ;
# ifdef HAVE_ZAPTEL
const char * dev_filename = " /dev/zap/transcode " ;
# else
const char * dev_filename = " /dev/dahdi/transcode " ;
# endif
if ( ( fd = open ( DAHDI_FILE_TRANSCODE , O_RDWR ) ) < 0 ) {
ast_log ( LOG_ERROR , " Failed to open " DAHDI_FILE_TRANSCODE " : %s \n " , strerror ( errno ) ) ;
if ( ( fd = open ( dev_filename , O_RDWR ) ) < 0 ) {
ast_log ( LOG_ERROR , " Failed to open %s: %s\n " , dev_filename , strerror ( errno ) ) ;
return - 1 ;
}
ztp - > fmts . srcfmt = ( 1 < < source ) ;
ztp - > fmts . dstfmt = ( 1 < < dest ) ;
ast_log ( LOG_ VERBOSE , " Opening transcoder channel from %d to %d. \n " , source , dest ) ;
ast_log ( LOG_ DEBUG , " Opening transcoder channel from %d to %d. \n " , source , dest ) ;
retry :
if ( ioctl ( fd , DAHDI_TC_ALLOCATE , & ztp - > fmts ) ) {
if ( ( ENODEV = = errno ) & & ! tried_once ) {
/* We requested to translate to/from an unsupported
* format . Most likely this is because signed linear
* was not supported by any hardware devices even
* though this module always registers signed linear
* support . In this case we ' ll retry , requesting
* support for ULAW instead of signed linear and then
* we ' ll just convert from ulaw to signed linear in
* software . */
if ( AST_FORMAT_SLINEAR = = ztp - > fmts . srcfmt ) {
ast_log ( LOG_DEBUG , " Using soft_slin support on source \n " ) ;
ztp - > softslin = 1 ;
ztp - > fmts . srcfmt = AST_FORMAT_ULAW ;
} else if ( AST_FORMAT_SLINEAR = = ztp - > fmts . dstfmt ) {
ast_log ( LOG_DEBUG , " Using soft_slin support on destination \n " ) ;
ztp - > softslin = 1 ;
ztp - > fmts . dstfmt = AST_FORMAT_ULAW ;
}
tried_once = 1 ;
goto retry ;
}
ast_log ( LOG_ERROR , " Unable to attach to transcoder: %s \n " , strerror ( errno ) ) ;
close ( fd ) ;
return - 1 ;
}
}
flags = fcntl ( fd , F_GETFL ) ;
if ( flags > - 1 ) {
@ -255,17 +451,7 @@ static int zap_translate(struct ast_trans_pvt *pvt, int dest, int source)
ztp - > fd = fd ;
switch ( ztp - > fmts . dstfmt ) {
case AST_FORMAT_G729A :
ztp - > samples = 160 ;
break ;
case AST_FORMAT_G723_1 :
ztp - > samples = 240 ;
break ;
default :
ztp - > samples = 160 ;
break ;
} ;
ztp - > required_samples = ( ( ztp - > fmts . dstfmt | ztp - > fmts . srcfmt ) & AST_FORMAT_G723_1 ) ? G723_SAMPLES : G729_SAMPLES ;
switch ( ztp - > fmts . dstfmt ) {
case AST_FORMAT_G729A :
@ -282,9 +468,9 @@ static int zap_translate(struct ast_trans_pvt *pvt, int dest, int source)
return 0 ;
}
static int zap _new( struct ast_trans_pvt * pvt )
static int dahdi _new( struct ast_trans_pvt * pvt )
{
return zap _translate( pvt , pvt - > t - > dstfmt , pvt - > t - > srcfmt ) ;
return dahdi _translate( pvt , pvt - > t - > dstfmt , pvt - > t - > srcfmt ) ;
}
static struct ast_frame * fakesrc_sample ( void )
@ -299,26 +485,58 @@ static struct ast_frame *fakesrc_sample(void)
return & f ;
}
static int is_encoder ( struct translator * zt )
{
if ( zt - > t . srcfmt & ( AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_SLINEAR ) ) {
return 1 ;
} else {
return 0 ;
}
}
static int register_translator ( int dst , int src )
{
struct translator * zt ;
int res ;
if ( ! ( zt = ast_calloc ( 1 , sizeof ( * zt ) ) ) )
if ( ! ( zt = ast_calloc ( 1 , sizeof ( * zt ) ) ) ) {
return - 1 ;
}
snprintf ( ( char * ) ( zt - > t . name ) , sizeof ( zt - > t . name ) , " zap%sto%s " ,
ast_getformatname ( ( 1 < < src ) ) , ast_getformatname ( ( 1 < < dst ) ) ) ;
zt - > t . srcfmt = ( 1 < < src ) ;
zt - > t . dstfmt = ( 1 < < dst ) ;
zt - > t . newpvt = zap_new ;
zt - > t . framein = zap_framein ;
zt - > t . frameout = zap_frameout ;
zt - > t . destroy = zap_destroy ;
zt - > t . buf_size = BUFFER_SIZE ;
if ( is_encoder ( zt ) ) {
zt - > t . framein = dahdi_encoder_framein ;
zt - > t . frameout = dahdi_encoder_frameout ;
#if 0
zt - > t . buffer_samples = 0 ;
# endif
} else {
zt - > t . framein = dahdi_decoder_framein ;
zt - > t . frameout = dahdi_decoder_frameout ;
#if 0
if ( AST_FORMAT_G723_1 = = zt - > t . srcfmt ) {
zt - > t . plc_samples = G723_SAMPLES ;
} else {
zt - > t . plc_samples = G729_SAMPLES ;
}
zt - > t . buffer_samples = zt - > t . plc_samples * 8 ;
# endif
}
zt - > t . destroy = dahdi_destroy ;
zt - > t . buffer_samples = 0 ;
zt - > t . newpvt = dahdi_new ;
zt - > t . sample = fakesrc_sample ;
#if 0
zt - > t . useplc = global_useplc ;
zt - > t . buf_size = BUFFER_SAMPLES * 2 ;
zt - > t . desc_size = sizeof ( struct pvt ) ;
# endif
zt - > t . useplc = 0 ;
zt - > t . native_plc = 0 ;
zt - > t . desc_size = sizeof ( struct codec_dahdi_pvt ) ;
if ( ( res = ast_register_translator ( & zt - > t ) ) ) {
free ( zt ) ;
return - 1 ;
@ -423,8 +641,25 @@ static int find_transcoders(void)
for ( info . tcnum = 0 ; ! ( res = ioctl ( fd , DAHDI_TC_GETINFO , & info ) ) ; info . tcnum + + ) {
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 " Found transcoder '%s'. \n " , info . name ) ;
/* Complex codecs need to support signed linear. If the
* hardware transcoder does not natively support signed linear
* format , we will emulate it in software directly in this
* module . Also , do not allow direct ulaw / alaw to complex
* codec translation , since that will prevent the generic PLC
* functions from working . */
if ( info . dstfmts & ( AST_FORMAT_ULAW | AST_FORMAT_ALAW ) ) {
info . dstfmts | = AST_FORMAT_SLINEAR ;
info . dstfmts & = ~ ( AST_FORMAT_ULAW | AST_FORMAT_ALAW ) ;
}
if ( info . srcfmts & ( AST_FORMAT_ULAW | AST_FORMAT_ALAW ) ) {
info . srcfmts | = AST_FORMAT_SLINEAR ;
info . srcfmts & = ~ ( AST_FORMAT_ULAW | AST_FORMAT_ALAW ) ;
}
build_translators ( & map , info . dstfmts , info . srcfmts ) ;
ast_atomic_fetchadd_int ( & channels . total , info . numchannels / 2 ) ;
}
close ( fd ) ;
@ -466,6 +701,7 @@ static int unload_module(void)
static int load_module ( void )
{
ast_ulaw_init ( ) ;
parse_config ( ) ;
find_transcoders ( ) ;
ast_cli_register_multiple ( cli , sizeof ( cli ) / sizeof ( cli [ 0 ] ) ) ;