2003-06-28 18:35:58 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
1999-12-02 20:24:59 +00:00
*
2006-01-03 22:16:23 +00:00
* Copyright (C) 1999 - 2006, Digium, Inc.
2005-09-14 20:46:50 +00:00
*
* Mark Spencer <markster@digium.com>
1999-12-02 20:24:59 +00:00
*
2005-09-14 20:46:50 +00:00
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
1999-12-02 20:24:59 +00:00
*
* This program is free software, distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \brief Generic File Format Support.
2005-12-30 21:18:06 +00:00
*
2012-03-22 19:51:16 +00:00
* \author Mark Spencer <markster@digium.com>
1999-12-02 20:24:59 +00:00
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2006-06-07 18:54:56 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2005-04-22 13:11:34 +00:00
#include <dirent.h>
#include <sys/stat.h>
2011-07-05 13:38:37 +00:00
#include <sys/wait.h>
2008-06-13 12:45:50 +00:00
#include <math.h>
2005-04-22 13:11:34 +00:00
2007-11-22 00:53:49 +00:00
#include "asterisk/_private.h" /* declare ast_file_init() */
2007-11-20 23:16:15 +00:00
#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
2007-11-22 00:53:49 +00:00
#include "asterisk/mod_format.h"
2005-04-21 06:02:45 +00:00
#include "asterisk/cli.h"
#include "asterisk/channel.h"
#include "asterisk/sched.h"
#include "asterisk/translate.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
2005-07-12 01:34:06 +00:00
#include "asterisk/pbx.h"
2006-01-09 21:30:46 +00:00
#include "asterisk/linkedlists.h"
2006-06-07 18:54:56 +00:00
#include "asterisk/module.h"
2008-11-20 18:20:00 +00:00
#include "asterisk/astobj2.h"
2010-02-04 22:43:33 +00:00
#include "asterisk/test.h"
1999-12-02 20:24:59 +00:00
2010-08-30 09:32:17 +00:00
/*! \brief
2006-04-04 12:59:25 +00:00
* The following variable controls the layout of localized sound files.
* If 0, use the historical layout with prefix just before the filename
* (i.e. digits/en/1.gsm , digits/it/1.gsm or default to digits/1.gsm),
* if 1 put the prefix at the beginning of the filename
* (i.e. en/digits/1.gsm, it/digits/1.gsm or default to digits/1.gsm).
* The latter permits a language to be entirely in one directory.
2010-08-30 09:32:17 +00:00
*
* This is settable in asterisk.conf.
2006-04-04 12:59:25 +00:00
*/
2006-12-27 21:18:27 +00:00
int ast_language_is_prefix = 1 ;
1999-12-02 20:24:59 +00:00
2011-02-03 16:22:10 +00:00
static AST_RWLIST_HEAD_STATIC ( formats , ast_format_def );
1999-12-02 20:24:59 +00:00
2011-02-03 16:22:10 +00:00
int __ast_format_def_register ( const struct ast_format_def * f , struct ast_module * mod )
1999-12-02 20:24:59 +00:00
{
2011-02-03 16:22:10 +00:00
struct ast_format_def * tmp ;
2006-04-04 12:59:25 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_WRLOCK ( & formats );
AST_RWLIST_TRAVERSE ( & formats , tmp , list ) {
2006-04-04 12:59:25 +00:00
if ( ! strcasecmp ( f -> name , tmp -> name )) {
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats );
2006-04-04 12:59:25 +00:00
ast_log ( LOG_WARNING , "Tried to register '%s' format, already registered \n " , f -> name );
1999-12-02 20:24:59 +00:00
return - 1 ;
}
2006-04-04 12:59:25 +00:00
}
2006-08-21 02:11:39 +00:00
if ( ! ( tmp = ast_calloc ( 1 , sizeof ( * tmp )))) {
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats );
1999-12-02 20:24:59 +00:00
return - 1 ;
}
2006-04-04 12:59:25 +00:00
* tmp = * f ;
2006-08-21 02:11:39 +00:00
tmp -> module = mod ;
2006-04-04 12:59:25 +00:00
if ( tmp -> buf_size ) {
/*
* Align buf_size properly, rounding up to the machine-specific
* alignment for pointers.
*/
struct _test_align { void * a , * b ; } p ;
int align = ( char * ) & p . b - ( char * ) & p . a ;
2008-03-04 23:04:29 +00:00
tmp -> buf_size = (( f -> buf_size + align - 1 ) / align ) * align ;
2006-04-04 12:59:25 +00:00
}
2012-03-22 19:51:16 +00:00
2006-04-04 12:59:25 +00:00
memset ( & tmp -> list , 0 , sizeof ( tmp -> list ));
2007-01-26 22:55:06 +00:00
AST_RWLIST_INSERT_HEAD ( & formats , tmp , list );
AST_RWLIST_UNLOCK ( & formats );
2007-07-26 15:49:18 +00:00
ast_verb ( 2 , "Registered file format %s, extension(s) %s \n " , f -> name , f -> exts );
2006-08-21 02:11:39 +00:00
1999-12-02 20:24:59 +00:00
return 0 ;
}
2011-02-03 16:22:10 +00:00
int ast_format_def_unregister ( const char * name )
1999-12-02 20:24:59 +00:00
{
2011-02-03 16:22:10 +00:00
struct ast_format_def * tmp ;
2006-01-09 21:30:46 +00:00
int res = - 1 ;
2007-01-26 22:55:06 +00:00
AST_RWLIST_WRLOCK ( & formats );
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & formats , tmp , list ) {
1999-12-02 20:24:59 +00:00
if ( ! strcasecmp ( name , tmp -> name )) {
2007-11-08 05:28:47 +00:00
AST_RWLIST_REMOVE_CURRENT ( list );
2007-06-06 21:20:11 +00:00
ast_free ( tmp );
2006-01-09 21:30:46 +00:00
res = 0 ;
1999-12-02 20:24:59 +00:00
}
}
2007-11-08 05:28:47 +00:00
AST_RWLIST_TRAVERSE_SAFE_END ;
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats );
2006-01-09 21:30:46 +00:00
2007-07-26 15:49:18 +00:00
if ( ! res )
ast_verb ( 2 , "Unregistered format %s \n " , name );
else
2006-01-09 21:30:46 +00:00
ast_log ( LOG_WARNING , "Tried to unregister format %s, already unregistered \n " , name );
return res ;
1999-12-02 20:24:59 +00:00
}
int ast_stopstream ( struct ast_channel * tmp )
{
2007-11-29 00:07:32 +00:00
ast_channel_lock ( tmp );
1999-12-02 20:24:59 +00:00
/* Stop a running stream if there is one */
2012-02-20 23:43:27 +00:00
if ( ast_channel_stream ( tmp )) {
ast_closestream ( ast_channel_stream ( tmp ));
ast_channel_stream_set ( tmp , NULL );
2012-02-24 00:32:20 +00:00
if ( ast_channel_oldwriteformat ( tmp ) -> id && ast_set_write_format ( tmp , ast_channel_oldwriteformat ( tmp )))
ast_log ( LOG_WARNING , "Unable to restore format back to %s \n " , ast_getformatname ( ast_channel_oldwriteformat ( tmp )));
2003-06-29 03:24:39 +00:00
}
2007-05-06 13:11:15 +00:00
/* Stop the video stream too */
2012-02-20 23:43:27 +00:00
if ( ast_channel_vstream ( tmp ) != NULL ) {
ast_closestream ( ast_channel_vstream ( tmp ));
ast_channel_vstream_set ( tmp , NULL );
2007-05-06 13:11:15 +00:00
}
2007-11-29 00:07:32 +00:00
ast_channel_unlock ( tmp );
1999-12-02 20:24:59 +00:00
return 0 ;
}
int ast_writestream ( struct ast_filestream * fs , struct ast_frame * f )
{
int res = - 1 ;
2003-06-29 03:24:39 +00:00
if ( f -> frametype == AST_FRAME_VIDEO ) {
2011-02-03 16:22:10 +00:00
if ( AST_FORMAT_GET_TYPE ( fs -> fmt -> format . id ) == AST_FORMAT_TYPE_AUDIO ) {
2003-06-29 03:24:39 +00:00
/* This is the audio portion. Call the video one... */
if ( ! fs -> vfs && fs -> filename ) {
2011-02-03 16:22:10 +00:00
const char * type = ast_getformatname ( & f -> subclass . format );
2003-06-29 03:24:39 +00:00
fs -> vfs = ast_writefile ( fs -> filename , type , NULL , fs -> flags , 0 , fs -> mode );
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Opened video output file \n " );
2003-06-29 03:24:39 +00:00
}
if ( fs -> vfs )
return ast_writestream ( fs -> vfs , f );
2006-04-04 12:59:25 +00:00
/* else ignore */
2012-03-22 19:51:16 +00:00
return 0 ;
2003-06-29 03:24:39 +00:00
}
} else if ( f -> frametype != AST_FRAME_VOICE ) {
1999-12-02 20:24:59 +00:00
ast_log ( LOG_WARNING , "Tried to write non-voice frame \n " );
return - 1 ;
}
2011-02-03 16:22:10 +00:00
if ( ast_format_cmp ( & f -> subclass . format , & fs -> fmt -> format ) != AST_FORMAT_CMP_NOT_EQUAL ) {
2000-01-02 23:51:30 +00:00
res = fs -> fmt -> write ( fs , f );
2012-03-22 19:51:16 +00:00
if ( res < 0 )
2000-01-02 23:51:30 +00:00
ast_log ( LOG_WARNING , "Natural write failed \n " );
2006-04-04 12:59:25 +00:00
else if ( res > 0 )
2000-01-02 23:51:30 +00:00
ast_log ( LOG_WARNING , "Huh?? \n " );
} else {
1999-12-02 20:24:59 +00:00
/* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
the one we've setup a translator for, we do the "wrong thing" XXX */
2011-02-03 16:22:10 +00:00
if ( fs -> trans && ( ast_format_cmp ( & f -> subclass . format , & fs -> lastwriteformat ) != AST_FORMAT_CMP_EQUAL )) {
2003-05-28 19:45:07 +00:00
ast_translator_free_path ( fs -> trans );
fs -> trans = NULL ;
}
2012-03-22 19:51:16 +00:00
if ( ! fs -> trans )
2011-02-03 16:22:10 +00:00
fs -> trans = ast_translator_build_path ( & fs -> fmt -> format , & f -> subclass . format );
1999-12-02 20:24:59 +00:00
if ( ! fs -> trans )
2006-04-04 12:59:25 +00:00
ast_log ( LOG_WARNING , "Unable to translate to format %s, source format %s \n " ,
2011-02-03 16:22:10 +00:00
fs -> fmt -> name , ast_getformatname ( & f -> subclass . format ));
1999-12-02 20:24:59 +00:00
else {
2006-04-04 12:59:25 +00:00
struct ast_frame * trf ;
2011-02-03 16:22:10 +00:00
ast_format_copy ( & fs -> lastwriteformat , & f -> subclass . format );
2001-03-10 19:12:11 +00:00
/* Get the translated frame but don't consume the original in case they're using it on another stream */
2009-06-16 18:54:30 +00:00
if (( trf = ast_translate ( fs -> trans , f , 0 ))) {
struct ast_frame * cur ;
/* the translator may have returned multiple frames, so process them */
for ( cur = trf ; cur ; cur = AST_LIST_NEXT ( cur , frame_list )) {
if (( res = fs -> fmt -> write ( fs , trf ))) {
ast_log ( LOG_WARNING , "Translated frame write failed \n " );
break ;
}
}
2008-01-31 23:14:57 +00:00
ast_frfree ( trf );
2009-06-16 18:54:30 +00:00
} else {
2002-09-03 21:55:10 +00:00
res = 0 ;
2009-06-16 18:54:30 +00:00
}
1999-12-02 20:24:59 +00:00
}
}
2006-04-04 12:59:25 +00:00
return res ;
1999-12-02 20:24:59 +00:00
}
2004-11-13 16:13:07 +00:00
static int copy ( const char * infile , const char * outfile )
2001-06-13 20:36:52 +00:00
{
2006-04-04 12:59:25 +00:00
int ifd , ofd , len ;
char buf [ 4096 ]; /* XXX make it lerger. */
2004-06-23 18:00:50 +00:00
2001-06-13 20:36:52 +00:00
if (( ifd = open ( infile , O_RDONLY )) < 0 ) {
ast_log ( LOG_WARNING , "Unable to open %s in read-only mode \n " , infile );
return - 1 ;
}
2006-12-21 19:44:20 +00:00
if (( ofd = open ( outfile , O_WRONLY | O_TRUNC | O_CREAT , AST_FILE_MODE )) < 0 ) {
2001-06-13 20:36:52 +00:00
ast_log ( LOG_WARNING , "Unable to open %s in write-only mode \n " , outfile );
close ( ifd );
return - 1 ;
}
2006-04-04 12:59:25 +00:00
while ( ( len = read ( ifd , buf , sizeof ( buf )) ) ) {
int res ;
2001-06-13 20:36:52 +00:00
if ( len < 0 ) {
ast_log ( LOG_WARNING , "Read failed on %s: %s \n " , infile , strerror ( errno ));
2006-04-04 12:59:25 +00:00
break ;
2001-06-13 20:36:52 +00:00
}
2006-04-04 12:59:25 +00:00
/* XXX handle partial writes */
res = write ( ofd , buf , len );
if ( res != len ) {
ast_log ( LOG_WARNING , "Write failed on %s (%d of %d): %s \n " , outfile , res , len , strerror ( errno ));
len = - 1 ; /* error marker */
break ;
2001-06-13 20:36:52 +00:00
}
2006-04-04 12:59:25 +00:00
}
2001-06-13 20:36:52 +00:00
close ( ifd );
close ( ofd );
2006-04-04 12:59:25 +00:00
if ( len < 0 ) {
unlink ( outfile );
return - 1 ; /* error */
}
return 0 ; /* success */
2001-06-13 20:36:52 +00:00
}
2006-04-04 12:59:25 +00:00
/*!
* \brief construct a filename. Absolute pathnames are preserved,
* relative names are prefixed by the sounds/ directory.
* The wav49 suffix is replaced by 'WAV'.
* Returns a malloc'ed string to be freed by the caller.
*/
2004-11-14 22:38:34 +00:00
static char * build_filename ( const char * filename , const char * ext )
1999-12-02 20:24:59 +00:00
{
2006-04-04 12:59:25 +00:00
char * fn = NULL ;
2004-07-14 07:44:19 +00:00
2006-04-04 12:59:25 +00:00
if ( ! strcmp ( ext , "wav49" ))
ext = "WAV" ;
2005-01-10 04:18:18 +00:00
2008-11-02 18:52:13 +00:00
if ( filename [ 0 ] == '/' ) {
2012-08-21 20:54:19 +00:00
if ( ast_asprintf ( & fn , "%s.%s" , filename , ext ) < 0 ) {
2008-11-02 18:52:13 +00:00
fn = NULL ;
}
} else {
2012-08-21 20:54:19 +00:00
if ( ast_asprintf ( & fn , "%s/sounds/%s.%s" ,
2008-11-02 18:52:13 +00:00
ast_config_AST_DATA_DIR , filename , ext ) < 0 ) {
fn = NULL ;
}
}
1999-12-02 20:24:59 +00:00
return fn ;
}
2006-04-04 12:59:25 +00:00
/* compare type against the list 'exts' */
/* XXX need a better algorithm */
2004-11-14 22:38:34 +00:00
static int exts_compare ( const char * exts , const char * type )
2004-02-25 22:31:51 +00:00
{
char tmp [ 256 ];
2006-04-04 12:59:25 +00:00
char * stringp = tmp , * ext ;
2004-02-25 22:31:51 +00:00
2005-06-05 16:32:16 +00:00
ast_copy_string ( tmp , exts , sizeof ( tmp ));
2004-02-25 22:31:51 +00:00
while (( ext = strsep ( & stringp , "|" ))) {
2006-04-04 12:59:25 +00:00
if ( ! strcmp ( ext , type ))
2004-02-25 22:31:51 +00:00
return 1 ;
}
return 0 ;
}
2011-11-11 21:57:46 +00:00
/*! \internal \brief Close the file stream by canceling any pending read / write callbacks */
static void filestream_close ( struct ast_filestream * f )
{
enum ast_format_type format_type = AST_FORMAT_GET_TYPE ( f -> fmt -> format . id );
if ( ! f -> owner ) {
return ;
}
/* Stop a running stream if there is one */
switch ( format_type )
{
case AST_FORMAT_TYPE_AUDIO :
2012-02-20 23:43:27 +00:00
ast_channel_stream_set ( f -> owner , NULL );
AST_SCHED_DEL_ACCESSOR ( ast_channel_sched ( f -> owner ), f -> owner , ast_channel_streamid , ast_channel_streamid_set );
2011-11-11 21:57:46 +00:00
ast_settimeout ( f -> owner , 0 , NULL , NULL );
break ;
case AST_FORMAT_TYPE_VIDEO :
2012-02-20 23:43:27 +00:00
ast_channel_vstream_set ( f -> owner , NULL );
AST_SCHED_DEL_ACCESSOR ( ast_channel_sched ( f -> owner ), f -> owner , ast_channel_vstreamid , ast_channel_vstreamid_set );
2011-11-11 21:57:46 +00:00
break ;
default :
ast_log ( AST_LOG_WARNING , "Unable to schedule deletion of filestream with unsupported type %s \n " , f -> fmt -> name );
break ;
}
}
2008-11-20 18:20:00 +00:00
static void filestream_destructor ( void * arg )
{
struct ast_filestream * f = arg ;
2011-07-05 13:38:37 +00:00
int status ;
int pid = - 1 ;
2008-11-20 18:20:00 +00:00
/* Stop a running stream if there is one */
2011-11-11 21:57:46 +00:00
filestream_close ( f );
2008-11-20 18:20:00 +00:00
/* destroy the translator on exit */
if ( f -> trans )
ast_translator_free_path ( f -> trans );
if ( f -> realfilename && f -> filename ) {
2011-07-05 13:38:37 +00:00
pid = ast_safe_fork ( 0 );
if ( ! pid ) {
2011-01-12 00:27:30 +00:00
execl ( "/bin/mv" , "mv" , "-f" , f -> filename , f -> realfilename , SENTINEL );
2011-07-05 13:38:37 +00:00
_exit ( 1 );
}
else if ( pid > 0 ) {
/* Block the parent until the move is complete.*/
waitpid ( pid , & status , 0 );
2011-01-12 00:27:30 +00:00
}
2008-11-20 18:20:00 +00:00
}
if ( f -> filename )
free ( f -> filename );
if ( f -> realfilename )
free ( f -> realfilename );
2009-04-09 04:59:05 +00:00
if ( f -> fmt -> close ) {
void ( * closefn )( struct ast_filestream * ) = f -> fmt -> close ;
closefn ( f );
}
2009-01-07 22:20:31 +00:00
if ( f -> f )
fclose ( f -> f );
2008-11-20 18:20:00 +00:00
if ( f -> vfs )
ast_closestream ( f -> vfs );
2009-05-29 18:39:04 +00:00
if ( f -> write_buffer ) {
ast_free ( f -> write_buffer );
}
2008-11-20 18:20:00 +00:00
if ( f -> orig_chan_name )
free (( void * ) f -> orig_chan_name );
ast_module_unref ( f -> fmt -> module );
}
2011-02-03 16:22:10 +00:00
static struct ast_filestream * get_filestream ( struct ast_format_def * fmt , FILE * bfile )
2006-04-04 12:59:25 +00:00
{
struct ast_filestream * s ;
int l = sizeof ( * s ) + fmt -> buf_size + fmt -> desc_size ; /* total allocation size */
2008-11-20 18:20:00 +00:00
if ( ( s = ao2_alloc ( l , filestream_destructor )) == NULL )
2006-04-04 12:59:25 +00:00
return NULL ;
s -> fmt = fmt ;
s -> f = bfile ;
if ( fmt -> desc_size )
2008-03-04 23:04:29 +00:00
s -> _private = (( char * )( s + 1 )) + fmt -> buf_size ;
2006-04-04 12:59:25 +00:00
if ( fmt -> buf_size )
2008-03-04 23:04:29 +00:00
s -> buf = ( char * )( s + 1 );
2006-04-04 12:59:25 +00:00
s -> fr . src = fmt -> name ;
return s ;
}
/*
* Default implementations of open and rewrite.
* Only use them if you don't have expensive stuff to do.
*/
enum wrap_fn { WRAP_OPEN , WRAP_REWRITE };
static int fn_wrapper ( struct ast_filestream * s , const char * comment , enum wrap_fn mode )
{
2011-02-03 16:22:10 +00:00
struct ast_format_def * f = s -> fmt ;
2006-04-04 12:59:25 +00:00
int ret = - 1 ;
2009-04-09 04:59:05 +00:00
int ( * openfn )( struct ast_filestream * s );
2006-04-04 12:59:25 +00:00
2009-04-09 04:59:05 +00:00
if ( mode == WRAP_OPEN && ( openfn = f -> open ) && openfn ( s ))
2008-03-04 23:04:29 +00:00
ast_log ( LOG_WARNING , "Unable to open format %s \n " , f -> name );
2006-04-04 12:59:25 +00:00
else if ( mode == WRAP_REWRITE && f -> rewrite && f -> rewrite ( s , comment ))
2008-03-04 23:04:29 +00:00
ast_log ( LOG_WARNING , "Unable to rewrite format %s \n " , f -> name );
2006-04-04 12:59:25 +00:00
else {
/* preliminary checks succeed. update usecount */
2006-08-21 02:11:39 +00:00
ast_module_ref ( f -> module );
2006-04-04 12:59:25 +00:00
ret = 0 ;
}
2008-03-04 23:04:29 +00:00
return ret ;
2006-04-04 12:59:25 +00:00
}
static int rewrite_wrapper ( struct ast_filestream * s , const char * comment )
{
return fn_wrapper ( s , comment , WRAP_REWRITE );
}
2008-03-04 23:04:29 +00:00
2006-04-04 12:59:25 +00:00
static int open_wrapper ( struct ast_filestream * s )
{
return fn_wrapper ( s , NULL , WRAP_OPEN );
}
2006-02-09 02:08:04 +00:00
enum file_action {
2006-04-04 12:59:25 +00:00
ACTION_EXISTS = 1 , /* return matching format if file exists, 0 otherwise */
ACTION_DELETE , /* delete file, return 0 on success, -1 on error */
ACTION_RENAME , /* rename file. return 0 on success, -1 on error */
2006-02-09 02:08:04 +00:00
ACTION_OPEN ,
2006-04-04 12:59:25 +00:00
ACTION_COPY /* copy file. return 0 on success, -1 on error */
2006-02-09 02:08:04 +00:00
};
1999-12-02 20:24:59 +00:00
2006-04-04 12:59:25 +00:00
/*!
2011-02-03 16:22:10 +00:00
* \internal
2006-04-04 12:59:25 +00:00
* \brief perform various actions on a file. Second argument
2011-02-03 16:22:10 +00:00
* \note arg2 depends on the command:
* unused for DELETE
* optional ast_format_cap holding all the formats found for a file, for EXISTS.
2006-04-04 12:59:25 +00:00
* destination file name (const char *) for COPY and RENAME
2012-03-22 19:51:16 +00:00
* struct ast_channel * for OPEN
2006-04-06 15:08:31 +00:00
* if fmt is NULL, OPEN will return the first matching entry,
* whereas other functions will run on all matching entries.
2006-04-04 12:59:25 +00:00
*/
2011-02-03 16:22:10 +00:00
static int filehelper ( const char * filename , const void * arg2 , const char * fmt , const enum file_action action )
1999-12-02 20:24:59 +00:00
{
2011-02-03 16:22:10 +00:00
struct ast_format_def * f ;
int res = ( action == ACTION_EXISTS ) ? 0 : - 1 ;
2006-04-04 12:59:25 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats );
2006-04-04 12:59:25 +00:00
/* Check for a specific format */
2007-01-26 22:55:06 +00:00
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2006-04-06 15:08:31 +00:00
char * stringp , * ext = NULL ;
2006-04-04 12:59:25 +00:00
if ( fmt && ! exts_compare ( f -> exts , fmt ))
continue ;
/* Look for a file matching the supported extensions.
* The file must exist, and for OPEN, must match
* one of the formats supported by the channel.
*/
2006-04-06 15:08:31 +00:00
stringp = ast_strdupa ( f -> exts ); /* this is in the stack so does not need to be freed */
2006-04-04 12:59:25 +00:00
while ( ( ext = strsep ( & stringp , "|" )) ) {
struct stat st ;
2006-04-06 15:08:31 +00:00
char * fn = build_filename ( filename , ext );
2006-04-04 12:59:25 +00:00
if ( fn == NULL )
continue ;
if ( stat ( fn , & st ) ) { /* file not existent */
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
continue ;
}
/* for 'OPEN' we need to be sure that the format matches
* what the channel can process
*/
if ( action == ACTION_OPEN ) {
struct ast_channel * chan = ( struct ast_channel * ) arg2 ;
FILE * bfile ;
struct ast_filestream * s ;
2012-02-24 00:32:20 +00:00
if (( ast_format_cmp ( ast_channel_writeformat ( chan ), & f -> format ) == AST_FORMAT_CMP_NOT_EQUAL ) &&
2011-02-03 16:22:10 +00:00
! ((( AST_FORMAT_GET_TYPE ( f -> format . id ) == AST_FORMAT_TYPE_AUDIO ) && fmt ) ||
(( AST_FORMAT_GET_TYPE ( f -> format . id ) == AST_FORMAT_TYPE_VIDEO ) && fmt ))) {
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
continue ; /* not a supported format */
}
if ( ( bfile = fopen ( fn , "r" )) == NULL ) {
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
continue ; /* cannot open file */
}
s = get_filestream ( f , bfile );
if ( ! s ) {
fclose ( bfile );
2007-06-06 21:20:11 +00:00
ast_free ( fn ); /* cannot allocate descriptor */
2006-04-04 12:59:25 +00:00
continue ;
1999-12-02 20:24:59 +00:00
}
2006-04-04 12:59:25 +00:00
if ( open_wrapper ( s )) {
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2009-01-07 22:05:29 +00:00
ast_closestream ( s );
2006-04-04 12:59:25 +00:00
continue ; /* cannot run open on file */
}
2008-11-20 17:37:31 +00:00
if ( st . st_size == 0 ) {
ast_log ( LOG_WARNING , "File %s detected to have zero size. \n " , fn );
}
2006-04-04 12:59:25 +00:00
/* ok this is good for OPEN */
res = 1 ; /* found */
s -> lasttimeout = - 1 ;
s -> fmt = f ;
s -> trans = NULL ;
s -> filename = NULL ;
2011-02-03 16:22:10 +00:00
if ( AST_FORMAT_GET_TYPE ( s -> fmt -> format . id ) == AST_FORMAT_TYPE_AUDIO ) {
2012-02-20 23:43:27 +00:00
if ( ast_channel_stream ( chan ))
ast_closestream ( ast_channel_stream ( chan ));
ast_channel_stream_set ( chan , s );
2007-04-08 01:42:32 +00:00
} else {
2012-02-20 23:43:27 +00:00
if ( ast_channel_vstream ( chan ))
ast_closestream ( ast_channel_vstream ( chan ));
ast_channel_vstream_set ( chan , s );
2007-04-08 01:42:32 +00:00
}
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-06 15:08:31 +00:00
break ;
2006-04-04 12:59:25 +00:00
}
2006-04-06 15:08:31 +00:00
switch ( action ) {
case ACTION_OPEN :
break ; /* will never get here */
case ACTION_EXISTS : /* return the matching format */
2011-02-03 16:22:10 +00:00
/* if arg2 is present, it is a format capabilities structure.
* Add this format to the set of formats this file can be played in */
if ( arg2 ) {
ast_format_cap_add (( struct ast_format_cap * ) arg2 , & f -> format );
}
res = 1 ; /* file does exist and format it exists in is returned in arg2 */
2006-04-06 15:08:31 +00:00
break ;
case ACTION_DELETE :
if ( ( res = unlink ( fn )) )
ast_log ( LOG_WARNING , "unlink(%s) failed: %s \n " , fn , strerror ( errno ));
break ;
case ACTION_RENAME :
case ACTION_COPY : {
char * nfn = build_filename (( const char * ) arg2 , ext );
if ( ! nfn )
ast_log ( LOG_WARNING , "Out of memory \n " );
else {
res = action == ACTION_COPY ? copy ( fn , nfn ) : rename ( fn , nfn );
if ( res )
ast_log ( LOG_WARNING , "%s(%s,%s) failed: %s \n " ,
action == ACTION_COPY ? "copy" : "rename" ,
fn , nfn , strerror ( errno ));
2007-06-06 21:20:11 +00:00
ast_free ( nfn );
2006-04-06 15:08:31 +00:00
}
}
break ;
2006-04-04 12:59:25 +00:00
2006-04-06 15:08:31 +00:00
default :
ast_log ( LOG_WARNING , "Unknown helper %d \n " , action );
2006-04-04 12:59:25 +00:00
}
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
}
1999-12-02 20:24:59 +00:00
}
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats );
1999-12-02 20:24:59 +00:00
return res ;
}
2006-04-04 12:59:25 +00:00
2008-03-06 22:11:30 +00:00
static int is_absolute_path ( const char * filename )
{
return filename [ 0 ] == '/' ;
}
2011-02-03 16:22:10 +00:00
/*!
* \brief test if a file exists for a given format.
* \note result_cap is OPTIONAL
* \retval 1, true and result_cap represents format capabilities file exists in.
* \retval 0, false
*/
static int fileexists_test ( const char * filename , const char * fmt , const char * lang ,
char * buf , int buflen , struct ast_format_cap * result_cap )
2008-03-06 22:11:30 +00:00
{
if ( buf == NULL ) {
2011-02-03 16:22:10 +00:00
return 0 ;
2008-03-06 22:11:30 +00:00
}
2008-04-10 17:27:16 +00:00
if ( ast_language_is_prefix && ! is_absolute_path ( filename )) { /* new layout */
2008-03-06 22:11:30 +00:00
if ( lang ) {
snprintf ( buf , buflen , "%s/%s" , lang , filename );
} else {
snprintf ( buf , buflen , "%s" , filename );
}
} else { /* old layout */
strcpy ( buf , filename ); /* first copy the full string */
if ( lang ) {
/* insert the language and suffix if needed */
const char * c = strrchr ( filename , '/' );
int offset = c ? c - filename + 1 : 0 ; /* points right after the last '/' */
snprintf ( buf + offset , buflen - offset , "%s/%s" , lang , filename + offset );
}
}
2011-02-03 16:22:10 +00:00
return filehelper ( buf , result_cap , fmt , ACTION_EXISTS );
2008-03-06 22:11:30 +00:00
}
2006-04-04 12:59:25 +00:00
/*!
* \brief helper routine to locate a file with a given format
* and language preference.
2012-03-22 19:51:16 +00:00
*
2011-02-03 16:22:10 +00:00
* \note Try preflang, preflang with stripped '_' suffices, or NULL.
2006-04-04 12:59:25 +00:00
*
2011-02-03 16:22:10 +00:00
* \note The last parameter(s) point to a buffer of sufficient size,
2006-04-04 12:59:25 +00:00
* which on success is filled with the matching filename.
2011-02-03 16:22:10 +00:00
*
* \param filename, name of the file.
* \param fmt, format to look for the file in. OPTIONAL
* \param preflang, the perfered language
* \param buf, returns the matching filename
* \param buflen, size of the buf
* \param result_cap, OPTIONAL format capabilities result structure
* returns what formats the file was found in.
*
* \retval 1, true. file exists and result format is set
* \retval 0, false. file does not exist.
2006-04-04 12:59:25 +00:00
*/
2011-02-03 16:22:10 +00:00
static int fileexists_core ( const char * filename , const char * fmt , const char * preflang ,
char * buf , int buflen , struct ast_format_cap * result_cap )
2006-04-04 12:59:25 +00:00
{
2009-09-03 18:42:38 +00:00
char * lang ;
2006-04-04 12:59:25 +00:00
2008-03-06 22:11:30 +00:00
if ( buf == NULL ) {
2011-02-03 16:22:10 +00:00
return 0 ;
2006-04-04 12:59:25 +00:00
}
2008-02-27 16:54:14 +00:00
2008-03-06 22:11:30 +00:00
/* We try languages in the following order:
2009-09-03 18:42:38 +00:00
* preflang (may include dialect and style codes)
2008-03-06 22:11:30 +00:00
* lang (preflang without dialect - if any)
* <none>
* default (unless the same as preflang or lang without dialect)
*/
2009-09-03 18:42:38 +00:00
lang = ast_strdupa ( preflang );
/* Try preferred language, including removing any style or dialect codes */
while ( ! ast_strlen_zero ( lang )) {
char * end ;
2011-02-03 16:22:10 +00:00
if ( fileexists_test ( filename , fmt , lang , buf , buflen , result_cap )) {
return 1 ;
2006-04-04 12:59:25 +00:00
}
2009-09-03 18:42:38 +00:00
if (( end = strrchr ( lang , '_' )) != NULL ) {
* end = '\0' ;
continue ;
}
break ;
2008-03-06 22:11:30 +00:00
}
/* Try without any language */
2011-02-03 16:22:10 +00:00
if ( fileexists_test ( filename , fmt , NULL , buf , buflen , result_cap )) {
return 1 ;
2008-03-06 22:11:30 +00:00
}
/* Finally try the default language unless it was already tried before */
if (( ast_strlen_zero ( preflang ) || strcmp ( preflang , DEFAULT_LANGUAGE )) && ( ast_strlen_zero ( lang ) || strcmp ( lang , DEFAULT_LANGUAGE ))) {
2011-02-03 16:22:10 +00:00
if (( fileexists_test ( filename , fmt , DEFAULT_LANGUAGE , buf , buflen , result_cap )) > 0 ) {
return 1 ;
2008-02-27 16:54:14 +00:00
}
2006-04-04 12:59:25 +00:00
}
2008-02-27 16:54:14 +00:00
2008-03-06 22:11:30 +00:00
return 0 ;
2006-04-04 12:59:25 +00:00
}
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_openstream ( struct ast_channel * chan , const char * filename , const char * preflang )
2004-12-24 01:40:07 +00:00
{
return ast_openstream_full ( chan , filename , preflang , 0 );
}
struct ast_filestream * ast_openstream_full ( struct ast_channel * chan , const char * filename , const char * preflang , int asis )
1999-12-02 20:24:59 +00:00
{
2012-03-22 19:51:16 +00:00
/*
2006-04-04 12:59:25 +00:00
* Use fileexists_core() to find a file in a compatible
* language and format, set up a suitable translator,
* and open the stream.
*/
2011-02-03 16:22:10 +00:00
struct ast_format_cap * file_fmt_cap ;
int res ;
2010-06-18 18:59:05 +00:00
int buflen ;
2006-04-04 12:59:25 +00:00
char * buf ;
2004-12-24 01:40:07 +00:00
if ( ! asis ) {
/* do this first, otherwise we detect the wrong writeformat */
ast_stopstream ( chan );
2012-02-20 23:43:27 +00:00
if ( ast_channel_generator ( chan ))
2004-12-24 01:40:07 +00:00
ast_deactivate_generator ( chan );
}
2006-04-04 12:59:25 +00:00
if ( preflang == NULL )
preflang = "" ;
2008-02-27 17:45:55 +00:00
buflen = strlen ( preflang ) + strlen ( filename ) + 4 ;
2012-07-31 20:21:43 +00:00
buf = ast_alloca ( buflen );
2011-02-03 16:22:10 +00:00
if ( ! ( file_fmt_cap = ast_format_cap_alloc_nolock ())) {
return NULL ;
}
if ( ! fileexists_core ( filename , NULL , preflang , buf , buflen , file_fmt_cap ) ||
! ast_format_cap_has_type ( file_fmt_cap , AST_FORMAT_TYPE_AUDIO )) {
1999-12-02 20:24:59 +00:00
ast_log ( LOG_WARNING , "File %s does not exist in any format \n " , filename );
2011-02-03 16:22:10 +00:00
file_fmt_cap = ast_format_cap_destroy ( file_fmt_cap );
2003-02-06 22:11:43 +00:00
return NULL ;
1999-12-02 20:24:59 +00:00
}
2011-02-03 16:22:10 +00:00
/* Set the channel to a format we can work with and save off the previous format. */
2012-02-24 00:32:20 +00:00
ast_format_copy ( ast_channel_oldwriteformat ( chan ), ast_channel_writeformat ( chan ));
2011-02-03 16:22:10 +00:00
/* Set the channel to the best format that exists for the file. */
res = ast_set_write_format_from_cap ( chan , file_fmt_cap );
/* don't need this anymore now that the channel's write format is set. */
file_fmt_cap = ast_format_cap_destroy ( file_fmt_cap );
2010-09-11 17:12:58 +00:00
if ( res == - 1 ) { /* No format available that works with this channel */
return NULL ;
}
2011-02-03 16:22:10 +00:00
res = filehelper ( buf , chan , NULL , ACTION_OPEN );
2005-10-16 16:12:51 +00:00
if ( res >= 0 )
2012-02-20 23:43:27 +00:00
return ast_channel_stream ( chan );
2003-02-06 22:11:43 +00:00
return NULL ;
}
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_openvstream ( struct ast_channel * chan , const char * filename , const char * preflang )
2003-06-29 03:24:39 +00:00
{
2006-04-04 12:59:25 +00:00
/* As above, but for video. But here we don't have translators
* so we must enforce a format.
*/
2011-02-03 16:22:10 +00:00
struct ast_format tmp_fmt ;
struct ast_format_cap * tmp_cap ;
2006-04-04 12:59:25 +00:00
char * buf ;
int buflen ;
2011-02-03 16:22:10 +00:00
const char * fmt ;
int fd ;
2006-04-04 12:59:25 +00:00
if ( preflang == NULL )
preflang = "" ;
2008-02-27 20:37:32 +00:00
buflen = strlen ( preflang ) + strlen ( filename ) + 4 ;
2012-07-31 20:21:43 +00:00
buf = ast_alloca ( buflen );
2006-04-04 12:59:25 +00:00
2011-02-03 16:22:10 +00:00
/* is the channel capable of video without translation ?*/
2012-02-20 23:43:27 +00:00
if ( ! ast_format_cap_has_type ( ast_channel_nativeformats ( chan ), AST_FORMAT_TYPE_VIDEO )) {
2011-02-03 16:22:10 +00:00
return NULL ;
}
if ( ! ( tmp_cap = ast_format_cap_alloc_nolock ())) {
return NULL ;
}
/* Video is supported, so see what video formats exist for this file */
if ( ! fileexists_core ( filename , NULL , preflang , buf , buflen , tmp_cap )) {
tmp_cap = ast_format_cap_destroy ( tmp_cap );
return NULL ;
}
2006-04-04 12:59:25 +00:00
2011-02-03 16:22:10 +00:00
/* iterate over file formats and pick the first one compatible with the channel's native formats */
ast_format_cap_iter_start ( tmp_cap );
while ( ! ast_format_cap_iter_next ( tmp_cap , & tmp_fmt )) {
fmt = ast_getformatname ( & tmp_fmt );
if (( AST_FORMAT_GET_TYPE ( tmp_fmt . id ) != AST_FORMAT_TYPE_VIDEO ) ||
2012-02-20 23:43:27 +00:00
! ast_format_cap_iscompatible ( ast_channel_nativeformats ( chan ), & tmp_fmt )) {
2006-01-07 17:54:22 +00:00
continue ;
2011-02-03 16:22:10 +00:00
}
fd = filehelper ( buf , chan , fmt , ACTION_OPEN );
if ( fd >= 0 ) {
ast_format_cap_iter_end ( tmp_cap );
tmp_cap = ast_format_cap_destroy ( tmp_cap );
2012-02-20 23:43:27 +00:00
return ast_channel_vstream ( chan );
2011-02-03 16:22:10 +00:00
}
2006-01-07 17:54:22 +00:00
ast_log ( LOG_WARNING , "File %s has video but couldn't be opened \n " , filename );
2003-06-29 03:24:39 +00:00
}
2011-02-03 16:22:10 +00:00
ast_format_cap_iter_end ( tmp_cap );
tmp_cap = ast_format_cap_destroy ( tmp_cap );
2003-06-29 03:24:39 +00:00
return NULL ;
}
2009-10-08 19:52:03 +00:00
static struct ast_frame * read_frame ( struct ast_filestream * s , int * whennext )
2003-12-19 18:06:29 +00:00
{
2009-10-08 19:52:03 +00:00
struct ast_frame * fr , * new_fr ;
if ( ! s || ! s -> fmt ) {
return NULL ;
}
if ( ! ( fr = s -> fmt -> read ( s , whennext ))) {
return NULL ;
}
if ( ! ( new_fr = ast_frisolate ( fr ))) {
ast_frfree ( fr );
return NULL ;
}
if ( new_fr != fr ) {
ast_frfree ( fr );
fr = new_fr ;
2008-11-20 18:20:00 +00:00
}
2009-10-08 19:52:03 +00:00
return fr ;
}
struct ast_frame * ast_readframe ( struct ast_filestream * s )
{
int whennext = 0 ;
return read_frame ( s , & whennext );
2003-12-19 18:06:29 +00:00
}
2007-09-05 20:58:19 +00:00
enum fsread_res {
FSREAD_FAILURE ,
FSREAD_SUCCESS_SCHED ,
FSREAD_SUCCESS_NOSCHED ,
};
2007-09-21 14:40:10 +00:00
static int ast_fsread_audio ( const void * data );
2007-09-05 20:58:19 +00:00
static enum fsread_res ast_readaudio_callback ( struct ast_filestream * s )
2003-02-06 22:11:43 +00:00
{
2003-06-28 22:50:47 +00:00
int whennext = 0 ;
2007-01-23 00:11:32 +00:00
while ( ! whennext ) {
2007-09-05 20:58:19 +00:00
struct ast_frame * fr ;
2009-10-08 19:52:03 +00:00
2012-01-09 22:15:50 +00:00
if ( s -> orig_chan_name && strcasecmp ( ast_channel_name ( s -> owner ), s -> orig_chan_name )) {
2007-09-05 20:58:19 +00:00
goto return_failure ;
2008-12-22 16:07:59 +00:00
}
2009-10-08 19:52:03 +00:00
fr = read_frame ( s , & whennext );
2006-04-04 12:59:25 +00:00
if ( ! fr /* stream complete */ || ast_write ( s -> owner , fr ) /* error writing */ ) {
2009-02-04 15:30:12 +00:00
if ( fr ) {
2003-06-28 22:50:47 +00:00
ast_log ( LOG_WARNING , "Failed to write frame \n " );
2009-02-04 15:30:12 +00:00
ast_frfree ( fr );
}
2007-09-05 20:58:19 +00:00
goto return_failure ;
2012-03-22 19:51:16 +00:00
}
2009-10-08 19:52:03 +00:00
2009-02-04 15:30:12 +00:00
if ( fr ) {
ast_frfree ( fr );
2003-06-28 22:50:47 +00:00
}
}
2009-10-08 19:52:03 +00:00
2003-06-28 22:50:47 +00:00
if ( whennext != s -> lasttimeout ) {
2012-02-20 23:43:27 +00:00
if ( ast_channel_timingfd ( s -> owner ) > - 1 ) {
2011-02-03 16:22:10 +00:00
float samp_rate = ( float ) ast_format_rate ( & s -> fmt -> format );
2008-06-13 12:45:50 +00:00
unsigned int rate ;
rate = ( unsigned int ) roundf ( samp_rate / (( float ) whennext ));
ast_settimeout ( s -> owner , rate , ast_fsread_audio , s );
} else {
2012-02-20 23:43:27 +00:00
ast_channel_streamid_set ( s -> owner , ast_sched_add ( ast_channel_sched ( s -> owner ), whennext / ( ast_format_rate ( & s -> fmt -> format ) / 1000 ), ast_fsread_audio , s ));
2008-06-13 12:45:50 +00:00
}
2003-06-28 22:50:47 +00:00
s -> lasttimeout = whennext ;
2007-09-05 20:58:19 +00:00
return FSREAD_SUCCESS_NOSCHED ;
2003-02-06 22:11:43 +00:00
}
2007-09-05 20:58:19 +00:00
return FSREAD_SUCCESS_SCHED ;
return_failure :
2012-02-20 23:43:27 +00:00
ast_channel_streamid_set ( s -> owner , - 1 );
2007-09-05 20:58:19 +00:00
ast_settimeout ( s -> owner , 0 , NULL , NULL );
return FSREAD_FAILURE ;
2003-06-28 22:50:47 +00:00
}
2007-09-21 14:40:10 +00:00
static int ast_fsread_audio ( const void * data )
2007-09-05 20:58:19 +00:00
{
2007-09-21 14:40:10 +00:00
struct ast_filestream * fs = ( struct ast_filestream * ) data ;
2007-09-05 20:58:19 +00:00
enum fsread_res res ;
res = ast_readaudio_callback ( fs );
if ( res == FSREAD_SUCCESS_SCHED )
return 1 ;
2012-03-22 19:51:16 +00:00
2007-09-05 20:58:19 +00:00
return 0 ;
}
2007-09-21 14:40:10 +00:00
static int ast_fsread_video ( const void * data );
2007-09-05 20:58:19 +00:00
static enum fsread_res ast_readvideo_callback ( struct ast_filestream * s )
2003-06-29 03:24:39 +00:00
{
int whennext = 0 ;
2006-04-04 12:59:25 +00:00
while ( ! whennext ) {
2009-10-08 19:52:03 +00:00
struct ast_frame * fr = read_frame ( s , & whennext );
2009-04-15 20:17:33 +00:00
if ( ! fr /* stream complete */ || ast_write ( s -> owner , fr ) /* error writing */ ) {
if ( fr ) {
2003-06-29 03:24:39 +00:00
ast_log ( LOG_WARNING , "Failed to write frame \n " );
2009-04-15 20:17:33 +00:00
ast_frfree ( fr );
}
2012-02-20 23:43:27 +00:00
ast_channel_vstreamid_set ( s -> owner , - 1 );
2007-09-05 20:58:19 +00:00
return FSREAD_FAILURE ;
2003-06-29 03:24:39 +00:00
}
2009-10-08 19:52:03 +00:00
2009-04-15 20:17:33 +00:00
if ( fr ) {
ast_frfree ( fr );
}
2003-06-29 03:24:39 +00:00
}
2007-09-05 20:58:19 +00:00
2003-06-29 03:24:39 +00:00
if ( whennext != s -> lasttimeout ) {
2012-02-20 23:43:27 +00:00
ast_channel_vstreamid_set ( s -> owner , ast_sched_add ( ast_channel_sched ( s -> owner ), whennext / ( ast_format_rate ( & s -> fmt -> format ) / 1000 ), ast_fsread_video , s ));
2003-06-29 03:24:39 +00:00
s -> lasttimeout = whennext ;
2007-09-05 20:58:19 +00:00
return FSREAD_SUCCESS_NOSCHED ;
2003-06-29 03:24:39 +00:00
}
2007-09-05 20:58:19 +00:00
return FSREAD_SUCCESS_SCHED ;
}
2007-09-21 14:40:10 +00:00
static int ast_fsread_video ( const void * data )
2007-09-05 20:58:19 +00:00
{
2007-09-21 14:40:10 +00:00
struct ast_filestream * fs = ( struct ast_filestream * ) data ;
2007-09-05 20:58:19 +00:00
enum fsread_res res ;
res = ast_readvideo_callback ( fs );
if ( res == FSREAD_SUCCESS_SCHED )
return 1 ;
2012-03-22 19:51:16 +00:00
2007-09-05 20:58:19 +00:00
return 0 ;
2003-06-29 03:24:39 +00:00
}
2003-06-28 22:50:47 +00:00
int ast_applystream ( struct ast_channel * chan , struct ast_filestream * s )
{
s -> owner = chan ;
2003-02-06 22:11:43 +00:00
return 0 ;
}
int ast_playstream ( struct ast_filestream * s )
{
2007-09-05 20:58:19 +00:00
enum fsread_res res ;
2011-02-03 16:22:10 +00:00
if ( AST_FORMAT_GET_TYPE ( s -> fmt -> format . id ) == AST_FORMAT_TYPE_AUDIO )
2007-09-05 20:58:19 +00:00
res = ast_readaudio_callback ( s );
2003-06-29 03:24:39 +00:00
else
2007-09-05 20:58:19 +00:00
res = ast_readvideo_callback ( s );
return ( res == FSREAD_FAILURE ) ? - 1 : 0 ;
2003-02-06 22:11:43 +00:00
}
2006-02-20 23:35:12 +00:00
int ast_seekstream ( struct ast_filestream * fs , off_t sample_offset , int whence )
2003-02-06 22:11:43 +00:00
{
return fs -> fmt -> seek ( fs , sample_offset , whence );
}
int ast_truncstream ( struct ast_filestream * fs )
{
return fs -> fmt -> trunc ( fs );
}
2006-02-20 23:35:12 +00:00
off_t ast_tellstream ( struct ast_filestream * fs )
2003-02-06 22:11:43 +00:00
{
return fs -> fmt -> tell ( fs );
}
2006-02-20 23:35:12 +00:00
int ast_stream_fastforward ( struct ast_filestream * fs , off_t ms )
2003-02-06 22:11:43 +00:00
{
2006-04-04 12:59:25 +00:00
return ast_seekstream ( fs , ms * DEFAULT_SAMPLES_PER_MS , SEEK_CUR );
2003-02-06 22:11:43 +00:00
}
2006-02-20 23:35:12 +00:00
int ast_stream_rewind ( struct ast_filestream * fs , off_t ms )
2003-02-06 22:11:43 +00:00
{
2006-04-04 12:59:25 +00:00
return ast_seekstream ( fs , - ms * DEFAULT_SAMPLES_PER_MS , SEEK_CUR );
2003-02-06 22:11:43 +00:00
}
int ast_closestream ( struct ast_filestream * f )
{
2009-09-20 17:55:49 +00:00
/* This used to destroy the filestream, but it now just decrements a refcount.
2011-11-11 21:57:46 +00:00
* We close the stream in order to quit queuing frames now, because we might
2009-09-20 17:55:49 +00:00
* change the writeformat, which could result in a subsequent write error, if
* the format is different. */
2011-11-11 21:57:46 +00:00
filestream_close ( f );
2008-11-20 18:20:00 +00:00
ao2_ref ( f , - 1 );
2003-02-06 22:11:43 +00:00
return 0 ;
}
2006-04-04 12:59:25 +00:00
/*
* Look the various language-specific places where a file could exist.
*/
2004-11-13 16:13:07 +00:00
int ast_fileexists ( const char * filename , const char * fmt , const char * preflang )
2003-02-06 22:11:43 +00:00
{
2006-04-04 12:59:25 +00:00
char * buf ;
int buflen ;
if ( preflang == NULL )
preflang = "" ;
2008-02-27 20:37:32 +00:00
buflen = strlen ( preflang ) + strlen ( filename ) + 4 ; /* room for everything */
2012-07-31 20:21:43 +00:00
buf = ast_alloca ( buflen );
2011-02-03 16:22:10 +00:00
return fileexists_core ( filename , fmt , preflang , buf , buflen , NULL ) ? 1 : 0 ;
2003-02-06 22:11:43 +00:00
}
2004-11-13 16:13:07 +00:00
int ast_filedelete ( const char * filename , const char * fmt )
2003-02-06 22:11:43 +00:00
{
2011-02-03 16:22:10 +00:00
return filehelper ( filename , NULL , fmt , ACTION_DELETE );
2003-02-06 22:11:43 +00:00
}
2004-11-13 16:13:07 +00:00
int ast_filerename ( const char * filename , const char * filename2 , const char * fmt )
2003-02-06 22:11:43 +00:00
{
2011-02-03 16:22:10 +00:00
return filehelper ( filename , filename2 , fmt , ACTION_RENAME );
2003-02-06 22:11:43 +00:00
}
2004-11-13 16:13:07 +00:00
int ast_filecopy ( const char * filename , const char * filename2 , const char * fmt )
2003-02-06 22:11:43 +00:00
{
2011-02-03 16:22:10 +00:00
return filehelper ( filename , filename2 , fmt , ACTION_COPY );
2003-02-06 22:11:43 +00:00
}
2004-11-13 16:13:07 +00:00
int ast_streamfile ( struct ast_channel * chan , const char * filename , const char * preflang )
2003-02-06 22:11:43 +00:00
{
struct ast_filestream * fs ;
2006-01-07 17:54:22 +00:00
struct ast_filestream * vfs = NULL ;
char fmt [ 256 ];
2012-01-05 22:11:41 +00:00
off_t pos ;
2008-11-20 17:37:31 +00:00
int seekattempt ;
2008-11-25 00:19:55 +00:00
int res ;
2003-02-06 22:11:43 +00:00
fs = ast_openstream ( chan , filename , preflang );
2008-11-24 21:27:26 +00:00
if ( ! fs ) {
2012-02-20 23:43:27 +00:00
ast_log ( LOG_WARNING , "Unable to open %s (format %s): %s \n " , filename , ast_getformatname_multiple ( fmt , sizeof ( fmt ), ast_channel_nativeformats ( chan )), strerror ( errno ));
2008-11-24 21:27:26 +00:00
return - 1 ;
}
2008-11-20 17:37:31 +00:00
/* check to see if there is any data present (not a zero length file),
* done this way because there is no where for ast_openstream_full to
* return the file had no data. */
2012-01-05 22:11:41 +00:00
pos = ftello ( fs -> f );
seekattempt = fseeko ( fs -> f , - 1 , SEEK_END );
if ( seekattempt ) {
if ( errno == EINVAL ) {
/* Zero-length file, as opposed to a pipe */
return 0 ;
} else {
ast_seekstream ( fs , 0 , SEEK_SET );
}
2010-10-06 13:50:33 +00:00
} else {
2012-01-05 22:11:41 +00:00
fseeko ( fs -> f , pos , SEEK_SET );
2010-10-06 13:50:33 +00:00
}
2008-11-20 17:37:31 +00:00
2008-11-24 21:27:26 +00:00
vfs = ast_openvstream ( chan , filename , preflang );
2006-10-03 15:53:07 +00:00
if ( vfs ) {
2011-02-03 16:22:10 +00:00
ast_debug ( 1 , "Ooh, found a video stream, too, format %s \n " , ast_getformatname ( & vfs -> fmt -> format ));
2006-10-03 15:53:07 +00:00
}
2007-01-16 17:39:39 +00:00
2012-03-13 18:20:34 +00:00
if ( ast_test_flag ( ast_channel_flags ( chan ), AST_FLAG_MASQ_NOSTREAM ))
2012-01-09 22:15:50 +00:00
fs -> orig_chan_name = ast_strdup ( ast_channel_name ( chan ));
2008-11-24 21:27:26 +00:00
if ( ast_applystream ( chan , fs ))
return - 1 ;
if ( vfs && ast_applystream ( chan , vfs ))
return - 1 ;
res = ast_playstream ( fs );
if ( ! res && vfs )
res = ast_playstream ( vfs );
2012-02-24 00:32:20 +00:00
ast_verb ( 3 , "<%s> Playing '%s.%s' (language '%s') \n " , ast_channel_name ( chan ), filename , ast_getformatname ( ast_channel_writeformat ( chan )), preflang ? preflang : "default" );
2008-11-24 21:27:26 +00:00
return res ;
1999-12-02 20:24:59 +00:00
}
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_readfile ( const char * filename , const char * type , const char * comment , int flags , int check , mode_t mode )
2003-12-19 18:06:29 +00:00
{
2005-10-16 16:12:51 +00:00
FILE * bfile ;
2011-02-03 16:22:10 +00:00
struct ast_format_def * f ;
2005-07-05 14:37:24 +00:00
struct ast_filestream * fs = NULL ;
2003-12-19 18:06:29 +00:00
char * fn ;
2012-03-22 19:51:16 +00:00
int format_found = 0 ;
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats );
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2006-04-04 12:59:25 +00:00
fs = NULL ;
2005-07-05 14:37:24 +00:00
if ( ! exts_compare ( f -> exts , type ))
continue ;
2012-03-22 19:51:16 +00:00
else
2008-02-19 21:38:39 +00:00
format_found = 1 ;
2005-07-05 14:37:24 +00:00
fn = build_filename ( filename , type );
2012-08-21 20:54:19 +00:00
if ( ! fn ) {
continue ;
}
2006-04-04 12:59:25 +00:00
errno = 0 ;
2005-10-16 16:12:51 +00:00
bfile = fopen ( fn , "r" );
2008-02-19 21:38:39 +00:00
if ( ! bfile || ( fs = get_filestream ( f , bfile )) == NULL || open_wrapper ( fs ) ) {
2006-04-04 12:59:25 +00:00
ast_log ( LOG_WARNING , "Unable to open %s \n " , fn );
2009-01-07 22:36:34 +00:00
if ( fs ) {
ast_closestream ( fs );
}
2008-09-12 16:29:01 +00:00
fs = NULL ;
2009-01-07 22:36:34 +00:00
bfile = NULL ;
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2012-03-22 19:51:16 +00:00
break ;
2006-04-04 12:59:25 +00:00
}
/* found it */
fs -> trans = NULL ;
fs -> fmt = f ;
fs -> flags = flags ;
fs -> mode = mode ;
2007-06-14 23:01:01 +00:00
fs -> filename = ast_strdup ( filename );
2006-04-04 12:59:25 +00:00
fs -> vfs = NULL ;
2012-08-21 20:54:19 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
break ;
2003-12-19 18:06:29 +00:00
}
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats );
2008-02-19 21:38:39 +00:00
if ( ! format_found )
2003-12-19 18:06:29 +00:00
ast_log ( LOG_WARNING , "No such format '%s' \n " , type );
2005-07-05 14:37:24 +00:00
2003-12-19 18:06:29 +00:00
return fs ;
}
1999-12-02 20:24:59 +00:00
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_writefile ( const char * filename , const char * type , const char * comment , int flags , int check , mode_t mode )
1999-12-02 20:24:59 +00:00
{
2005-07-05 14:37:24 +00:00
int fd , myflags = 0 ;
2005-10-16 23:26:35 +00:00
/* compiler claims this variable can be used before initialization... */
FILE * bfile = NULL ;
2011-02-03 16:22:10 +00:00
struct ast_format_def * f ;
2005-07-05 14:37:24 +00:00
struct ast_filestream * fs = NULL ;
char * buf = NULL ;
2004-09-07 01:49:08 +00:00
size_t size = 0 ;
2006-06-14 02:21:47 +00:00
int format_found = 0 ;
2004-09-07 01:49:08 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats );
2005-07-05 14:37:24 +00:00
2003-02-06 22:11:43 +00:00
/* set the O_TRUNC flag if and only if there is no O_APPEND specified */
2006-04-04 12:59:25 +00:00
/* We really can't use O_APPEND as it will break WAV header updates */
2012-03-22 19:51:16 +00:00
if ( flags & O_APPEND ) {
2005-02-03 05:35:03 +00:00
flags &= ~ O_APPEND ;
} else {
2003-12-19 18:06:29 +00:00
myflags = O_TRUNC ;
2005-02-03 05:35:03 +00:00
}
2012-03-22 19:51:16 +00:00
2003-12-19 18:06:29 +00:00
myflags |= O_WRONLY | O_CREAT ;
2006-04-04 12:59:25 +00:00
/* XXX need to fix this - we should just do the fopen,
* not open followed by fdopen()
*/
2007-01-26 22:55:06 +00:00
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2006-04-04 12:59:25 +00:00
char * fn , * orig_fn = NULL ;
2006-01-09 21:30:46 +00:00
if ( fs )
break ;
2005-07-05 14:37:24 +00:00
if ( ! exts_compare ( f -> exts , type ))
continue ;
2006-06-14 02:21:47 +00:00
else
format_found = 1 ;
2005-07-05 14:37:24 +00:00
fn = build_filename ( filename , type );
2012-08-21 20:54:19 +00:00
if ( ! fn ) {
continue ;
}
2005-07-05 14:37:24 +00:00
fd = open ( fn , flags | myflags , mode );
2005-10-16 16:12:51 +00:00
if ( fd > - 1 ) {
/* fdopen() the resulting file stream */
bfile = fdopen ( fd , (( flags | myflags ) & O_RDWR ) ? "w+" : "w" );
if ( ! bfile ) {
ast_log ( LOG_WARNING , "Whoa, fdopen failed: %s! \n " , strerror ( errno ));
close ( fd );
fd = - 1 ;
}
}
2012-03-22 19:51:16 +00:00
2005-12-04 20:40:46 +00:00
if ( ast_opt_cache_record_files && ( fd > - 1 )) {
2005-07-05 14:37:24 +00:00
char * c ;
2006-04-04 12:59:25 +00:00
fclose ( bfile ); /* this also closes fd */
2005-07-05 14:37:24 +00:00
/*
We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
*/
orig_fn = ast_strdupa ( fn );
for ( c = fn ; * c ; c ++ )
if ( * c == '/' )
* c = '_' ;
size = strlen ( fn ) + strlen ( record_cache_dir ) + 2 ;
2012-07-31 20:21:43 +00:00
buf = ast_alloca ( size );
2005-10-16 23:26:35 +00:00
strcpy ( buf , record_cache_dir );
strcat ( buf , "/" );
strcat ( buf , fn );
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2005-07-05 14:37:24 +00:00
fn = buf ;
2003-12-19 18:06:29 +00:00
fd = open ( fn , flags | myflags , mode );
2005-10-16 16:12:51 +00:00
if ( fd > - 1 ) {
/* fdopen() the resulting file stream */
bfile = fdopen ( fd , (( flags | myflags ) & O_RDWR ) ? "w+" : "w" );
if ( ! bfile ) {
ast_log ( LOG_WARNING , "Whoa, fdopen failed: %s! \n " , strerror ( errno ));
close ( fd );
fd = - 1 ;
}
}
2005-07-05 14:37:24 +00:00
}
2005-10-16 23:26:35 +00:00
if ( fd > - 1 ) {
2005-07-05 14:37:24 +00:00
errno = 0 ;
2006-04-04 12:59:25 +00:00
fs = get_filestream ( f , bfile );
2011-01-26 01:27:39 +00:00
if ( fs ) {
if (( fs -> write_buffer = ast_malloc ( 32768 ))) {
setvbuf ( fs -> f , fs -> write_buffer , _IOFBF , 32768 );
}
}
2006-04-04 12:59:25 +00:00
if ( ! fs || rewrite_wrapper ( fs , comment )) {
2005-07-05 14:37:24 +00:00
ast_log ( LOG_WARNING , "Unable to rewrite %s \n " , fn );
close ( fd );
if ( orig_fn ) {
unlink ( fn );
2004-09-07 01:49:08 +00:00
unlink ( orig_fn );
2005-07-05 14:37:24 +00:00
}
2009-01-07 22:36:34 +00:00
if ( fs ) {
ast_closestream ( fs );
fs = NULL ;
}
2012-08-21 20:54:19 +00:00
if ( ! buf ) {
ast_free ( fn );
}
2007-06-06 23:41:37 +00:00
continue ;
2006-04-04 12:59:25 +00:00
}
fs -> trans = NULL ;
fs -> fmt = f ;
fs -> flags = flags ;
fs -> mode = mode ;
if ( orig_fn ) {
2007-06-14 23:01:01 +00:00
fs -> realfilename = ast_strdup ( orig_fn );
fs -> filename = ast_strdup ( fn );
2006-04-04 12:59:25 +00:00
} else {
fs -> realfilename = NULL ;
2007-06-14 23:01:01 +00:00
fs -> filename = ast_strdup ( filename );
2004-09-07 01:49:08 +00:00
}
2006-04-04 12:59:25 +00:00
fs -> vfs = NULL ;
/* If truncated, we'll be at the beginning; if not truncated, then append */
f -> seek ( fs , 0 , SEEK_END );
2005-07-05 14:37:24 +00:00
} else if ( errno != EEXIST ) {
ast_log ( LOG_WARNING , "Unable to open file %s: %s \n " , fn , strerror ( errno ));
if ( orig_fn )
unlink ( orig_fn );
1999-12-02 20:24:59 +00:00
}
2005-07-05 14:37:24 +00:00
/* if buf != NULL then fn is already free and pointing to it */
if ( ! buf )
2007-06-06 21:20:11 +00:00
ast_free ( fn );
1999-12-02 20:24:59 +00:00
}
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats );
2006-06-14 02:21:47 +00:00
if ( ! format_found )
1999-12-02 20:24:59 +00:00
ast_log ( LOG_WARNING , "No such format '%s' \n " , type );
2005-07-05 14:37:24 +00:00
1999-12-02 20:24:59 +00:00
return fs ;
}
2006-04-04 12:59:25 +00:00
/*!
* \brief the core of all waitstream() functions
*/
2012-06-04 20:26:12 +00:00
static int waitstream_core ( struct ast_channel * c ,
const char * breakon ,
const char * forward ,
const char * reverse ,
int skip_ms ,
int audiofd ,
int cmdfd ,
const char * context ,
ast_waitstream_fr_cb cb )
2003-04-03 07:37:30 +00:00
{
2007-09-05 20:58:19 +00:00
const char * orig_chan_name = NULL ;
2012-06-04 20:26:12 +00:00
2007-09-05 20:58:19 +00:00
int err = 0 ;
2005-02-06 06:43:32 +00:00
if ( ! breakon )
2006-04-04 12:59:25 +00:00
breakon = "" ;
2005-02-06 06:43:32 +00:00
if ( ! forward )
2006-04-04 12:59:25 +00:00
forward = "" ;
2008-08-10 19:35:50 +00:00
if ( ! reverse )
reverse = "" ;
2007-08-10 14:17:42 +00:00
/* Switch the channel to end DTMF frame only. waitstream_core doesn't care about the start of DTMF. */
2012-03-13 18:20:34 +00:00
ast_set_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2007-09-05 20:58:19 +00:00
2012-03-13 18:20:34 +00:00
if ( ast_test_flag ( ast_channel_flags ( c ), AST_FLAG_MASQ_NOSTREAM ))
2012-01-09 22:15:50 +00:00
orig_chan_name = ast_strdupa ( ast_channel_name ( c ));
2007-09-05 20:58:19 +00:00
2012-06-04 20:26:12 +00:00
if ( ast_channel_stream ( c ) && cb ) {
long ms_len = ast_tellstream ( ast_channel_stream ( c )) / ( ast_format_rate ( & ast_channel_stream ( c ) -> fmt -> format ) / 1000 );
cb ( c , ms_len , AST_WAITSTREAM_CB_START );
}
2012-02-20 23:43:27 +00:00
while ( ast_channel_stream ( c )) {
2006-04-04 12:59:25 +00:00
int res ;
2007-09-05 20:58:19 +00:00
int ms ;
2012-01-09 22:15:50 +00:00
if ( orig_chan_name && strcasecmp ( orig_chan_name , ast_channel_name ( c ))) {
2007-09-05 20:58:19 +00:00
ast_stopstream ( c );
err = 1 ;
break ;
}
2012-02-20 23:43:27 +00:00
ms = ast_sched_wait ( ast_channel_sched ( c ));
2007-09-05 20:58:19 +00:00
2012-03-13 18:20:34 +00:00
if ( ms < 0 && ! ast_channel_timingfunc ( c )) {
2003-07-01 16:16:28 +00:00
ast_stopstream ( c );
2003-02-23 06:00:11 +00:00
break ;
}
2003-06-29 21:54:58 +00:00
if ( ms < 0 )
ms = 1000 ;
2006-09-01 16:50:02 +00:00
if ( cmdfd < 0 ) {
2006-04-04 12:59:25 +00:00
res = ast_waitfor ( c , ms );
if ( res < 0 ) {
ast_log ( LOG_WARNING , "Select failed (%s) \n " , strerror ( errno ));
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2006-04-04 12:59:25 +00:00
return res ;
}
} else {
int outfd ;
struct ast_channel * rchan = ast_waitfor_nandfds ( & c , 1 , & cmdfd , ( cmdfd > - 1 ) ? 1 : 0 , NULL , & outfd , & ms );
if ( ! rchan && ( outfd < 0 ) && ( ms )) {
/* Continue */
if ( errno == EINTR )
continue ;
ast_log ( LOG_WARNING , "Wait failed (%s) \n " , strerror ( errno ));
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2003-02-23 06:00:11 +00:00
return - 1 ;
2006-04-04 12:59:25 +00:00
} else if ( outfd > - 1 ) { /* this requires cmdfd set */
/* The FD we were watching has something waiting */
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2006-04-04 12:59:25 +00:00
return 1 ;
2003-02-23 06:00:11 +00:00
}
2006-04-04 12:59:25 +00:00
/* if rchan is set, it is 'c' */
res = rchan ? 1 : 0 ; /* map into 'res' values */
}
if ( res > 0 ) {
struct ast_frame * fr = ast_read ( c );
2007-08-10 14:17:42 +00:00
if ( ! fr ) {
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2006-04-04 12:59:25 +00:00
return - 1 ;
2007-08-10 14:17:42 +00:00
}
2007-01-23 00:11:32 +00:00
switch ( fr -> frametype ) {
2006-08-31 01:59:02 +00:00
case AST_FRAME_DTMF_END :
2006-04-04 12:59:25 +00:00
if ( context ) {
2009-11-04 14:05:12 +00:00
const char exten [ 2 ] = { fr -> subclass . integer , '\0' };
2010-07-14 15:48:36 +00:00
if ( ast_exists_extension ( c , context , exten , 1 ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( c ) -> id . number . valid , ast_channel_caller ( c ) -> id . number . str , NULL ))) {
2009-11-04 14:05:12 +00:00
res = fr -> subclass . integer ;
2006-04-04 12:59:25 +00:00
ast_frfree ( fr );
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2006-04-04 12:59:25 +00:00
return res ;
}
} else {
2012-06-04 20:26:12 +00:00
enum ast_waitstream_fr_cb_values cb_val = 0 ;
2009-11-04 14:05:12 +00:00
res = fr -> subclass . integer ;
2008-03-04 23:04:29 +00:00
if ( strchr ( forward , res )) {
2009-06-26 21:21:48 +00:00
int eoftest ;
2012-02-20 23:43:27 +00:00
ast_stream_fastforward ( ast_channel_stream ( c ), skip_ms );
eoftest = fgetc ( ast_channel_stream ( c ) -> f );
if ( feof ( ast_channel_stream ( c ) -> f )) {
ast_stream_rewind ( ast_channel_stream ( c ), skip_ms );
2009-06-26 21:21:48 +00:00
} else {
2012-02-20 23:43:27 +00:00
ungetc ( eoftest , ast_channel_stream ( c ) -> f );
2009-06-26 21:21:48 +00:00
}
2012-06-04 20:26:12 +00:00
cb_val = AST_WAITSTREAM_CB_FASTFORWARD ;
2008-08-10 19:35:50 +00:00
} else if ( strchr ( reverse , res )) {
2012-02-20 23:43:27 +00:00
ast_stream_rewind ( ast_channel_stream ( c ), skip_ms );
2012-06-04 20:26:12 +00:00
cb_val = AST_WAITSTREAM_CB_REWIND ;
2006-04-04 12:59:25 +00:00
} else if ( strchr ( breakon , res )) {
ast_frfree ( fr );
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2006-04-04 12:59:25 +00:00
return res ;
2012-03-22 19:51:16 +00:00
}
2012-06-04 20:26:12 +00:00
if ( cb_val && cb ) {
long ms_len = ast_tellstream ( ast_channel_stream ( c )) / ( ast_format_rate ( & ast_channel_stream ( c ) -> fmt -> format ) / 1000 );
cb ( c , ms_len , cb_val );
}
2003-02-23 06:00:11 +00:00
}
break ;
case AST_FRAME_CONTROL :
2009-11-04 14:05:12 +00:00
switch ( fr -> subclass . integer ) {
2003-02-23 06:00:11 +00:00
case AST_CONTROL_HANGUP :
2006-10-05 19:58:50 +00:00
case AST_CONTROL_BUSY :
2006-10-06 16:56:05 +00:00
case AST_CONTROL_CONGESTION :
2003-02-23 06:00:11 +00:00
ast_frfree ( fr );
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2003-02-23 06:00:11 +00:00
return - 1 ;
case AST_CONTROL_RINGING :
case AST_CONTROL_ANSWER :
2006-05-08 07:56:42 +00:00
case AST_CONTROL_VIDUPDATE :
2008-03-05 22:43:22 +00:00
case AST_CONTROL_SRCUPDATE :
2010-03-25 16:03:51 +00:00
case AST_CONTROL_SRCCHANGE :
2006-09-28 11:12:58 +00:00
case AST_CONTROL_HOLD :
case AST_CONTROL_UNHOLD :
2010-06-07 15:51:39 +00:00
case AST_CONTROL_CONNECTED_LINE :
case AST_CONTROL_REDIRECTING :
case AST_CONTROL_AOC :
2011-10-03 18:58:33 +00:00
case AST_CONTROL_UPDATE_RTP_PEER :
2012-05-14 19:44:27 +00:00
case AST_CONTROL_PVT_CAUSE_CODE :
2009-03-27 01:40:28 +00:00
case - 1 :
2003-02-23 06:00:11 +00:00
/* Unimportant */
break ;
default :
2009-11-04 14:05:12 +00:00
ast_log ( LOG_WARNING , "Unexpected control subclass '%d' \n " , fr -> subclass . integer );
2003-02-23 06:00:11 +00:00
}
2006-09-28 11:12:58 +00:00
break ;
2003-02-23 06:00:11 +00:00
case AST_FRAME_VOICE :
/* Write audio if appropriate */
2008-11-02 18:52:13 +00:00
if ( audiofd > - 1 ) {
if ( write ( audiofd , fr -> data . ptr , fr -> datalen ) < 0 ) {
ast_log ( LOG_WARNING , "write() failed: %s \n " , strerror ( errno ));
}
}
2006-08-31 01:59:02 +00:00
default :
/* Ignore all others */
break ;
2003-02-23 06:00:11 +00:00
}
ast_frfree ( fr );
2003-05-12 04:23:55 +00:00
}
2012-02-20 23:43:27 +00:00
ast_sched_runq ( ast_channel_sched ( c ));
2003-02-23 06:00:11 +00:00
}
2007-08-10 14:17:42 +00:00
2012-03-13 18:20:34 +00:00
ast_clear_flag ( ast_channel_flags ( c ), AST_FLAG_END_DTMF_ONLY );
2007-08-10 14:17:42 +00:00
2012-03-01 22:09:18 +00:00
return ( err || ast_channel_softhangup_internal_flag ( c )) ? - 1 : 0 ;
2003-02-23 06:00:11 +00:00
}
2004-06-30 03:22:29 +00:00
2012-06-04 20:26:12 +00:00
int ast_waitstream_fr_w_cb ( struct ast_channel * c ,
const char * breakon ,
const char * forward ,
const char * reverse ,
int ms ,
ast_waitstream_fr_cb cb )
{
return waitstream_core ( c , breakon , forward , reverse , ms ,
- 1 /* no audiofd */ , - 1 /* no cmdfd */ , NULL /* no context */ , cb );
}
2008-08-10 19:35:50 +00:00
int ast_waitstream_fr ( struct ast_channel * c , const char * breakon , const char * forward , const char * reverse , int ms )
2006-04-04 12:59:25 +00:00
{
2008-08-10 19:35:50 +00:00
return waitstream_core ( c , breakon , forward , reverse , ms ,
2012-06-04 20:26:12 +00:00
- 1 /* no audiofd */ , - 1 /* no cmdfd */ , NULL /* no context */ , NULL /* no callback */ );
2006-04-04 12:59:25 +00:00
}
int ast_waitstream ( struct ast_channel * c , const char * breakon )
{
2012-06-04 20:26:12 +00:00
return waitstream_core ( c , breakon , NULL , NULL , 0 , - 1 , - 1 , NULL , NULL /* no callback */ );
2006-04-04 12:59:25 +00:00
}
int ast_waitstream_full ( struct ast_channel * c , const char * breakon , int audiofd , int cmdfd )
{
return waitstream_core ( c , breakon , NULL , NULL , 0 ,
2012-06-04 20:26:12 +00:00
audiofd , cmdfd , NULL /* no context */ , NULL /* no callback */ );
2006-04-04 12:59:25 +00:00
}
2005-07-12 01:34:06 +00:00
int ast_waitstream_exten ( struct ast_channel * c , const char * context )
{
/* Waitstream, with return in the case of a valid 1 digit extension */
/* in the current or specified context being pressed */
2006-04-04 12:59:25 +00:00
if ( ! context )
2012-02-13 17:27:06 +00:00
context = ast_channel_context ( c );
2006-04-04 12:59:25 +00:00
return waitstream_core ( c , NULL , NULL , NULL , 0 ,
2012-06-04 20:26:12 +00:00
- 1 , - 1 , context , NULL /* no callback */ );
2005-07-12 01:34:06 +00:00
}
2006-04-21 20:28:32 +00:00
/*
* if the file name is non-empty, try to play it.
* Return 0 if success, -1 if error, digit if interrupted by a digit.
* If digits == "" then we can simply check for non-zero.
*/
2006-11-17 23:18:51 +00:00
int ast_stream_and_wait ( struct ast_channel * chan , const char * file , const char * digits )
2006-04-21 20:28:32 +00:00
{
2008-03-04 23:04:29 +00:00
int res = 0 ;
if ( ! ast_strlen_zero ( file )) {
2012-01-09 22:15:50 +00:00
ast_test_suite_event_notify ( "PLAYBACK" , "Message: %s \r\n Channel: %s" , file , ast_channel_name ( chan ));
2012-01-24 20:12:09 +00:00
res = ast_streamfile ( chan , file , ast_channel_language ( chan ));
2008-03-04 23:04:29 +00:00
if ( ! res ) {
res = ast_waitstream ( chan , digits );
}
}
return res ;
2012-03-22 19:51:16 +00:00
}
2006-04-21 20:28:32 +00:00
2009-11-30 21:31:55 +00:00
char * ast_format_str_reduce ( char * fmts )
{
2011-02-03 16:22:10 +00:00
struct ast_format_def * f ;
struct ast_format_def * fmts_ptr [ AST_MAX_FORMATS ];
2009-11-30 21:31:55 +00:00
char * fmts_str [ AST_MAX_FORMATS ];
char * stringp , * type ;
char * orig = fmts ;
2009-12-01 23:27:53 +00:00
int i , j , x , first , found = 0 ;
2009-11-30 21:31:55 +00:00
int len = strlen ( fmts ) + 1 ;
2010-01-08 19:32:11 +00:00
int res ;
2009-11-30 21:31:55 +00:00
if ( AST_RWLIST_RDLOCK ( & formats )) {
ast_log ( LOG_WARNING , "Unable to lock format list \n " );
return NULL ;
}
stringp = ast_strdupa ( fmts );
for ( x = 0 ; ( type = strsep ( & stringp , "|" )) && x < AST_MAX_FORMATS ; x ++ ) {
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
if ( exts_compare ( f -> exts , type )) {
found = 1 ;
break ;
}
}
fmts_str [ x ] = type ;
if ( found ) {
fmts_ptr [ x ] = f ;
} else {
fmts_ptr [ x ] = NULL ;
}
}
AST_RWLIST_UNLOCK ( & formats );
2009-12-01 15:47:36 +00:00
first = 1 ;
2009-11-30 21:31:55 +00:00
for ( i = 0 ; i < x ; i ++ ) {
2009-12-01 15:47:36 +00:00
/* ignore invalid entries */
if ( ! fmts_ptr [ i ]) {
ast_log ( LOG_WARNING , "ignoring unknown format '%s' \n " , fmts_str [ i ]);
continue ;
}
2009-11-30 21:31:55 +00:00
/* special handling for the first entry */
2009-12-01 15:47:36 +00:00
if ( first ) {
2010-01-08 19:32:11 +00:00
res = snprintf ( fmts , len , "%s" , fmts_str [ i ]);
fmts += res ;
len -= res ;
2009-12-01 15:47:36 +00:00
first = 0 ;
2009-11-30 21:31:55 +00:00
continue ;
}
found = 0 ;
for ( j = 0 ; j < i ; j ++ ) {
/* this is a duplicate */
if ( fmts_ptr [ j ] == fmts_ptr [ i ]) {
found = 1 ;
break ;
}
}
if ( ! found ) {
2010-01-08 19:32:11 +00:00
res = snprintf ( fmts , len , "|%s" , fmts_str [ i ]);
fmts += res ;
len -= res ;
2009-11-30 21:31:55 +00:00
}
}
2009-12-01 15:47:36 +00:00
if ( first ) {
ast_log ( LOG_WARNING , "no known formats found in format list (%s) \n " , orig );
return NULL ;
}
2009-11-30 21:31:55 +00:00
return orig ;
}
2007-10-11 19:03:06 +00:00
static char * handle_cli_core_show_file_formats ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2004-06-30 03:22:29 +00:00
{
#define FORMAT "%-10s %-10s %-20s\n"
#define FORMAT2 "%-10s %-10s %-20s\n"
2011-02-03 16:22:10 +00:00
struct ast_format_def * f ;
2005-08-23 15:43:47 +00:00
int count_fmt = 0 ;
2007-10-11 19:03:06 +00:00
switch ( cmd ) {
case CLI_INIT :
e -> command = "core show file formats" ;
e -> usage =
"Usage: core show file formats \n "
" Displays currently registered file formats (if any). \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
if ( a -> argc != 4 )
return CLI_SHOWUSAGE ;
ast_cli ( a -> fd , FORMAT , "Format" , "Name" , "Extensions" );
ast_cli ( a -> fd , FORMAT , "------" , "----" , "----------" );
2004-06-30 03:22:29 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats );
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2011-02-03 16:22:10 +00:00
ast_cli ( a -> fd , FORMAT2 , ast_getformatname ( & f -> format ), f -> name , f -> exts );
2005-08-23 15:43:47 +00:00
count_fmt ++ ;
2006-01-09 21:30:46 +00:00
}
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats );
2007-10-11 19:03:06 +00:00
ast_cli ( a -> fd , "%d file formats registered. \n " , count_fmt );
return CLI_SUCCESS ;
2005-07-15 16:13:26 +00:00
#undef FORMAT
#undef FORMAT2
2004-06-30 03:22:29 +00:00
}
2009-06-15 17:34:30 +00:00
static struct ast_cli_entry cli_file [] = {
2007-10-22 20:05:18 +00:00
AST_CLI_DEFINE ( handle_cli_core_show_file_formats , "Displays file formats" )
2004-06-30 03:22:29 +00:00
};
int ast_file_init ( void )
{
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_file , ARRAY_LEN ( cli_file ));
2004-06-30 03:22:29 +00:00
return 0 ;
}