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"
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"
2013-06-24 13:49:20 +00:00
# include "asterisk/stasis.h"
# include "asterisk/json.h"
2013-07-08 14:42:57 +00:00
# include "asterisk/stasis_system.h"
2015-12-26 15:29:04 -06:00
# include "asterisk/media_cache.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
2013-06-24 13:49:20 +00:00
STASIS_MESSAGE_TYPE_DEFN ( ast_format_register_type ) ;
STASIS_MESSAGE_TYPE_DEFN ( ast_format_unregister_type ) ;
static struct ast_json * json_array_from_list ( const char * list , const char * sep )
{
RAII_VAR ( struct ast_json * , array , ast_json_array_create ( ) , ast_json_unref ) ;
2013-08-29 21:37:29 +00:00
char * stringp , * ext ;
2013-06-24 13:49:20 +00:00
2013-08-29 21:37:29 +00:00
stringp = ast_strdupa ( list ) ; /* this is in the stack so does not need to be freed */
2013-06-24 13:49:20 +00:00
if ( ! array | | ! stringp ) {
return NULL ;
}
while ( ( ext = strsep ( & stringp , sep ) ) ) {
if ( ast_json_array_append ( array , ast_json_string_create ( ext ) ) ) {
return NULL ;
}
}
return ast_json_ref ( array ) ;
}
static int publish_format_update ( const struct ast_format_def * f , struct stasis_message_type * type )
{
RAII_VAR ( struct stasis_message * , msg , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_json_payload * , json_payload , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_json * , json_object , NULL , ast_json_unref ) ;
2014-08-06 12:55:28 +00:00
if ( ! type ) {
return - 1 ;
}
2013-06-24 13:49:20 +00:00
json_object = ast_json_pack ( " {s: s, s: o} " ,
" format " , f - > name ,
" extensions " , json_array_from_list ( f - > exts , " | " ) ) ;
if ( ! json_object ) {
return - 1 ;
}
json_payload = ast_json_payload_create ( json_object ) ;
if ( ! json_payload ) {
return - 1 ;
}
msg = stasis_message_create ( type , json_payload ) ;
if ( ! msg ) {
return - 1 ;
}
stasis_publish ( ast_system_topic ( ) , msg ) ;
return 0 ;
}
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 ) ;
2013-06-24 13:49:20 +00:00
publish_format_update ( f , ast_format_register_type ( ) ) ;
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 ) ;
2013-06-24 13:49:20 +00:00
publish_format_update ( tmp , ast_format_unregister_type ( ) ) ;
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 ) ;
2014-07-20 22:06:33 +00:00
if ( ast_channel_oldwriteformat ( tmp ) & & ast_set_write_format ( tmp , ast_channel_oldwriteformat ( tmp ) ) )
ast_log ( LOG_WARNING , " Unable to restore format back to %s \n " , ast_format_get_name ( 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 ) {
2014-07-20 22:06:33 +00:00
if ( ast_format_get_type ( fs - > fmt - > format ) = = AST_MEDIA_TYPE_AUDIO ) {
2003-06-29 03:24:39 +00:00
/* This is the audio portion. Call the video one... */
if ( ! fs - > vfs & & fs - > filename ) {
2014-07-20 22:06:33 +00:00
const char * type = ast_format_get_name ( 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 ;
}
2014-07-20 22:06:33 +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 */
2014-07-20 22:06:33 +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 ;
}
2014-07-20 22:06:33 +00:00
if ( ! fs - > trans ) {
fs - > trans = ast_translator_build_path ( fs - > fmt - > format , f - > subclass . format ) ;
}
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 " ,
2014-07-20 22:06:33 +00:00
fs - > fmt - > name , ast_format_get_name ( f - > subclass . format ) ) ;
} else {
2006-04-04 12:59:25 +00:00
struct ast_frame * trf ;
2014-07-20 22:06:33 +00:00
ao2_replace ( 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
2020-02-17 09:05:16 -05:00
/* The wav49 -> WAV translation is duplicated in apps/app_mixmonitor.c, so
if you change it here you need to change it there as well */
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 21:01:11 +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 21:01:11 +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 */
2018-02-13 10:55:47 -08:00
static int type_in_list ( const char * list , const char * type , int ( * cmp ) ( const char * s1 , const char * s2 ) )
2004-02-25 22:31:51 +00:00
{
2018-02-13 10:55:47 -08:00
char * stringp = ast_strdupa ( list ) , * item ;
2004-02-25 22:31:51 +00:00
2018-02-13 10:55:47 -08:00
while ( ( item = strsep ( & stringp , " | " ) ) ) {
if ( ! cmp ( item , type ) ) {
2004-02-25 22:31:51 +00:00
return 1 ;
2018-02-13 10:55:47 -08:00
}
2004-02-25 22:31:51 +00:00
}
return 0 ;
}
2018-02-13 10:55:47 -08:00
# define exts_compare(list, type) (type_in_list((list), (type), strcmp))
2013-08-16 17:33:21 +00:00
/*!
* \internal
* \brief Close the file stream by canceling any pending read / write callbacks
*/
2011-11-11 21:57:46 +00:00
static void filestream_close ( struct ast_filestream * f )
{
2014-07-20 22:06:33 +00:00
enum ast_media_type format_type = ast_format_get_type ( f - > fmt - > format ) ;
2011-11-11 21:57:46 +00:00
if ( ! f - > owner ) {
return ;
}
/* Stop a running stream if there is one */
switch ( format_type )
{
2014-07-20 22:06:33 +00:00
case AST_MEDIA_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 ;
2014-07-20 22:06:33 +00:00
case AST_MEDIA_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 ) ;
2013-01-28 01:58:41 +00:00
if ( f - > fmt - > close ) {
void ( * closefn ) ( struct ast_filestream * ) = f - > fmt - > close ;
closefn ( f ) ;
}
if ( f - > f ) {
fclose ( f - > f ) ;
}
2008-11-20 18:20:00 +00:00
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
}
2014-09-26 14:41:38 +00:00
ast_free ( f - > filename ) ;
ast_free ( f - > realfilename ) ;
2008-11-20 18:20:00 +00:00
if ( f - > vfs )
ast_closestream ( f - > vfs ) ;
2014-09-26 14:41:38 +00:00
ast_free ( f - > write_buffer ) ;
ast_free ( ( void * ) f - > orig_chan_name ) ;
2014-07-20 22:06:33 +00:00
ao2_cleanup ( f - > lastwriteformat ) ;
ao2_cleanup ( f - > fr . subclass . format ) ;
2008-11-20 18:20:00 +00:00
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 */
2017-12-29 19:24:02 -05:00
if ( ! ast_module_running_ref ( fmt - > module ) ) {
return NULL ;
}
s = ao2_alloc ( l , filestream_destructor ) ;
if ( ! s ) {
ast_module_unref ( fmt - > module ) ;
2006-04-04 12:59:25 +00:00
return NULL ;
2017-12-29 19:24:02 -05:00
}
2006-04-04 12:59:25 +00:00
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 ;
2014-07-20 22:06:33 +00:00
if ( ast_format_get_type ( fmt - > format ) = = AST_MEDIA_TYPE_AUDIO ) {
s - > fr . frametype = AST_FRAME_VOICE ;
} else if ( ast_format_get_type ( fmt - > format ) = = AST_MEDIA_TYPE_VIDEO ) {
s - > fr . frametype = AST_FRAME_VIDEO ;
}
s - > fr . mallocd = 0 ;
s - > fr . subclass . format = ao2_bump ( fmt - > format ) ;
2006-04-04 12:59:25 +00:00
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 {
2014-11-06 12:15:54 +00:00
/* preliminary checks succeed. */
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 ) {
2019-10-23 12:36:17 -05:00
char * ext = NULL ;
char storage [ strlen ( f - > exts ) + 1 ] ;
char * stringp ;
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.
*/
2019-10-23 12:36:17 -05:00
strcpy ( storage , f - > exts ) ; /* safe - this is in the stack so does not need to be freed */
stringp = storage ;
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 ;
2014-07-20 22:06:33 +00:00
if ( ( ast_format_cmp ( ast_channel_writeformat ( chan ) , f - > format ) = = AST_FORMAT_CMP_NOT_EQUAL ) & &
! ( ( ( ast_format_get_type ( f - > format ) = = AST_MEDIA_TYPE_AUDIO ) & & fmt ) | |
( ( ast_format_get_type ( f - > format ) = = AST_MEDIA_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 ;
2014-07-20 22:06:33 +00:00
if ( ast_format_get_type ( s - > fmt - > format ) = = AST_MEDIA_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 ) {
2014-07-20 22:06:33 +00:00
ast_format_cap_append ( ( struct ast_format_cap * ) arg2 , f - > format , 0 ) ;
2011-02-03 16:22:10 +00:00
}
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 :
2014-05-09 22:49:26 +00:00
ast_log ( LOG_WARNING , " Unknown helper %u \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 ] = = ' / ' ;
}
2019-11-21 12:48:42 -06:00
static int is_remote_path ( const char * filename )
{
return strstr ( filename , " :// " ) ? 1 : 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
}
2019-11-21 12:48:42 -06:00
if ( is_remote_path ( filename ) & & ! ast_media_cache_retrieve ( filename , NULL , buf , buflen ) ) {
2015-12-26 15:29:04 -06:00
return filehelper ( buf , result_cap , NULL , ACTION_EXISTS ) ;
}
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
*
2012-08-30 14:23:28 +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
2011-02-03 16:22:10 +00:00
* 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
2014-07-20 22:06:33 +00:00
if ( ! ( file_fmt_cap = ast_format_cap_alloc ( AST_FORMAT_CAP_FLAG_DEFAULT ) ) ) {
2011-02-03 16:22:10 +00:00
return NULL ;
}
if ( ! fileexists_core ( filename , NULL , preflang , buf , buflen , file_fmt_cap ) | |
2014-07-20 22:06:33 +00:00
! ast_format_cap_has_type ( file_fmt_cap , AST_MEDIA_TYPE_AUDIO ) ) {
2011-02-03 16:22:10 +00:00
1999-12-02 20:24:59 +00:00
ast_log ( LOG_WARNING , " File %s does not exist in any format \n " , filename ) ;
2014-07-20 22:06:33 +00:00
ao2_ref ( file_fmt_cap , - 1 ) ;
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. */
2014-08-20 22:52:44 +00:00
ast_channel_lock ( chan ) ;
2014-07-20 22:06:33 +00:00
ast_channel_set_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 ) ;
2014-08-20 22:52:44 +00:00
ast_channel_unlock ( chan ) ;
2011-02-03 16:22:10 +00:00
/* don't need this anymore now that the channel's write format is set. */
2014-07-20 22:06:33 +00:00
ao2_ref ( file_fmt_cap , - 1 ) ;
2011-02-03 16:22:10 +00:00
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.
*/
2016-05-05 07:07:50 -03:00
struct ast_format_cap * nativeformats , * tmp_cap ;
2006-04-04 12:59:25 +00:00
char * buf ;
int buflen ;
2014-07-20 22:06:33 +00:00
int i , fd ;
2006-04-04 12:59:25 +00:00
2014-07-20 22:06:33 +00:00
if ( preflang = = NULL ) {
2006-04-04 12:59:25 +00:00
preflang = " " ;
2014-07-20 22:06:33 +00:00
}
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
2016-05-05 07:07:50 -03:00
ast_channel_lock ( chan ) ;
nativeformats = ao2_bump ( ast_channel_nativeformats ( chan ) ) ;
ast_channel_unlock ( chan ) ;
2011-02-03 16:22:10 +00:00
/* is the channel capable of video without translation ?*/
2016-05-05 07:07:50 -03:00
if ( ! ast_format_cap_has_type ( nativeformats , AST_MEDIA_TYPE_VIDEO ) ) {
ao2_cleanup ( nativeformats ) ;
2011-02-03 16:22:10 +00:00
return NULL ;
}
2014-07-20 22:06:33 +00:00
if ( ! ( tmp_cap = ast_format_cap_alloc ( AST_FORMAT_CAP_FLAG_DEFAULT ) ) ) {
2016-05-05 07:07:50 -03:00
ao2_cleanup ( nativeformats ) ;
2011-02-03 16:22:10 +00:00
return NULL ;
}
/* Video is supported, so see what video formats exist for this file */
if ( ! fileexists_core ( filename , NULL , preflang , buf , buflen , tmp_cap ) ) {
2014-07-20 22:06:33 +00:00
ao2_ref ( tmp_cap , - 1 ) ;
2016-05-05 07:07:50 -03:00
ao2_cleanup ( nativeformats ) ;
2011-02-03 16:22:10 +00:00
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 */
2014-07-20 22:06:33 +00:00
for ( i = 0 ; i < ast_format_cap_count ( tmp_cap ) ; + + i ) {
struct ast_format * format = ast_format_cap_get_format ( tmp_cap , i ) ;
if ( ( ast_format_get_type ( format ) ! = AST_MEDIA_TYPE_VIDEO ) | |
2016-05-05 07:07:50 -03:00
! ast_format_cap_iscompatible ( nativeformats , tmp_cap ) ) {
2014-07-20 22:06:33 +00:00
ao2_ref ( format , - 1 ) ;
2006-01-07 17:54:22 +00:00
continue ;
2011-02-03 16:22:10 +00:00
}
2014-07-20 22:06:33 +00:00
fd = filehelper ( buf , chan , ast_format_get_name ( format ) , ACTION_OPEN ) ;
2011-02-03 16:22:10 +00:00
if ( fd > = 0 ) {
2014-07-20 22:06:33 +00:00
ao2_ref ( format , - 1 ) ;
ao2_ref ( tmp_cap , - 1 ) ;
2016-05-05 07:07:50 -03:00
ao2_cleanup ( nativeformats ) ;
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 ) ;
2014-07-20 22:06:33 +00:00
ao2_ref ( format , - 1 ) ;
2003-06-29 03:24:39 +00:00
}
2014-07-20 22:06:33 +00:00
ao2_ref ( tmp_cap , - 1 ) ;
2016-05-05 07:07:50 -03:00
ao2_cleanup ( nativeformats ) ;
2011-02-03 16:22:10 +00:00
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 ) {
2015-05-20 18:05:20 -06:00
ast_debug ( 2 , " 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 ) {
2014-07-20 22:06:33 +00:00
float samp_rate = ( float ) ast_format_get_sample_rate ( s - > fmt - > format ) ;
2008-06-13 12:45:50 +00:00
unsigned int rate ;
rate = ( unsigned int ) roundf ( samp_rate / ( ( float ) whennext ) ) ;
2014-01-27 01:25:23 +00:00
ast_settimeout_full ( s - > owner , rate , ast_fsread_audio , s , 1 ) ;
2008-06-13 12:45:50 +00:00
} else {
2014-07-20 22:06:33 +00:00
ast_channel_streamid_set ( s - > owner , ast_sched_add ( ast_channel_sched ( s - > owner ) , whennext / ( ast_format_get_sample_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 ) {
2015-05-20 18:05:20 -06:00
ast_debug ( 2 , " 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 ) {
2014-07-20 22:06:33 +00:00
ast_channel_vstreamid_set ( s - > owner , ast_sched_add ( ast_channel_sched ( s - > owner ) , whennext / ( ast_format_get_sample_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 ;
2014-07-20 22:06:33 +00:00
if ( ast_format_get_type ( s - > fmt - > format ) = = AST_MEDIA_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 ) ;
}
2014-09-16 16:33:53 +00:00
int ast_ratestream ( struct ast_filestream * fs )
{
return ast_format_get_sample_rate ( fs - > fmt - > format ) ;
}
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. */
2013-07-03 17:58:45 +00:00
if ( f = = NULL ) {
return 0 ;
}
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
}
2016-11-15 11:01:04 -07:00
static int __ast_file_read_dirs ( const char * path , ast_file_on_file on_file ,
2016-10-28 15:11:35 -05:00
void * obj , int max_depth )
{
DIR * dir ;
struct dirent * entry ;
int res ;
2016-11-15 11:01:04 -07:00
if ( ! ( dir = opendir ( path ) ) ) {
2016-10-28 15:11:35 -05:00
ast_log ( LOG_ERROR , " Error opening directory - %s: %s \n " ,
2016-11-15 11:01:04 -07:00
path , strerror ( errno ) ) ;
2016-10-28 15:11:35 -05:00
return - 1 ;
}
- - max_depth ;
res = 0 ;
while ( ( entry = readdir ( dir ) ) ! = NULL & & ! errno ) {
2016-11-15 11:01:04 -07:00
int is_file = 0 ;
int is_dir = 0 ;
RAII_VAR ( char * , full_path , NULL , ast_free ) ;
2016-10-28 15:11:35 -05:00
if ( ! strcmp ( entry - > d_name , " . " ) | | ! strcmp ( entry - > d_name , " .. " ) ) {
continue ;
}
/*
* If the dirent structure has a d_type use it to determine if we are dealing with
* a file or directory. Unfortunately if it doesn't have it, or if the type is
* unknown, or a link then we'll need to use the stat function instead.
*/
# ifdef _DIRENT_HAVE_D_TYPE
if ( entry - > d_type ! = DT_UNKNOWN & & entry - > d_type ! = DT_LNK ) {
is_file = entry - > d_type = = DT_REG ;
is_dir = entry - > d_type = = DT_DIR ;
} else
# endif
{
struct stat statbuf ;
/*
2016-11-15 11:01:04 -07:00
* Don't use alloca or we risk blowing out the stack if recursing
* into subdirectories.
2016-10-28 15:11:35 -05:00
*/
2016-11-15 11:01:04 -07:00
full_path = ast_malloc ( strlen ( path ) + strlen ( entry - > d_name ) + 2 ) ;
if ( ! full_path ) {
return - 1 ;
}
sprintf ( full_path , " %s/%s " , path , entry - > d_name ) ;
2016-10-28 15:11:35 -05:00
2016-11-15 11:01:04 -07:00
if ( stat ( full_path , & statbuf ) ) {
2016-10-28 15:11:35 -05:00
ast_log ( LOG_ERROR , " Error reading path stats - %s: %s \n " ,
2016-11-15 11:01:04 -07:00
full_path , strerror ( errno ) ) ;
2016-10-28 15:11:35 -05:00
/*
* Output an error, but keep going. It could just be
* a broken link and other files could be fine.
*/
continue ;
}
is_file = S_ISREG ( statbuf . st_mode ) ;
is_dir = S_ISDIR ( statbuf . st_mode ) ;
}
if ( is_file ) {
/* If the handler returns non-zero then stop */
2016-11-15 11:01:04 -07:00
if ( ( res = on_file ( path , entry - > d_name , obj ) ) ) {
2016-10-28 15:11:35 -05:00
break ;
}
/* Otherwise move on to next item in directory */
continue ;
}
if ( ! is_dir ) {
2016-11-15 11:01:04 -07:00
ast_debug ( 5 , " Skipping %s: not a regular file or directory \n " , full_path ) ;
2016-10-28 15:11:35 -05:00
continue ;
}
/* Only re-curse into sub-directories if not at the max depth */
if ( max_depth ! = 0 ) {
2016-11-15 11:01:04 -07:00
if ( ! full_path ) {
/* Don't use alloca. See note above. */
full_path = ast_malloc ( strlen ( path ) + strlen ( entry - > d_name ) + 2 ) ;
if ( ! full_path ) {
return - 1 ;
}
sprintf ( full_path , " %s/%s " , path , entry - > d_name ) ;
2016-10-28 15:11:35 -05:00
}
2016-11-15 11:01:04 -07:00
if ( ( res = __ast_file_read_dirs ( full_path , on_file , obj , max_depth ) ) ) {
2016-10-28 15:11:35 -05:00
break ;
}
}
}
closedir ( dir ) ;
if ( ! res & & errno ) {
ast_log ( LOG_ERROR , " Error while reading directories - %s: %s \n " ,
2016-11-15 11:01:04 -07:00
path , strerror ( errno ) ) ;
2016-10-28 15:11:35 -05:00
res = - 1 ;
}
return res ;
}
# if !defined(__GLIBC__)
/*!
* \brief Lock to hold when iterating over directories.
*
* Currently, 'readdir' is not required to be thread-safe. In most modern implementations
* it should be safe to make concurrent calls into 'readdir' that specify different directory
* streams (glibc would be one of these). However, since it is potentially unsafe for some
* implementations we'll use our own locking in order to achieve synchronization for those.
*/
AST_MUTEX_DEFINE_STATIC ( read_dirs_lock ) ;
# endif
int ast_file_read_dirs ( const char * dir_name , ast_file_on_file on_file , void * obj , int max_depth )
{
int res ;
errno = 0 ;
# if !defined(__GLIBC__)
ast_mutex_lock ( & read_dirs_lock ) ;
# endif
2016-11-15 11:01:04 -07:00
res = __ast_file_read_dirs ( dir_name , on_file , obj , max_depth ) ;
2016-10-28 15:11:35 -05:00
# if !defined(__GLIBC__)
ast_mutex_unlock ( & read_dirs_lock ) ;
# endif
return res ;
}
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 ;
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 ) {
2015-11-09 10:01:41 +01:00
struct ast_str * codec_buf = ast_str_alloca ( AST_FORMAT_CAP_NAMES_LEN ) ;
2016-05-05 07:07:50 -03:00
ast_channel_lock ( chan ) ;
2014-07-20 22:06:33 +00:00
ast_log ( LOG_WARNING , " Unable to open %s (format %s): %s \n " ,
filename , ast_format_cap_get_names ( ast_channel_nativeformats ( chan ) , & codec_buf ) , strerror ( errno ) ) ;
2016-05-05 07:07:50 -03:00
ast_channel_unlock ( chan ) ;
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 ) {
2014-07-20 22:06:33 +00:00
ast_debug ( 1 , " Ooh, found a video stream, too, format %s \n " , ast_format_get_name ( 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 ;
2014-08-14 20:59:15 +00:00
ast_test_suite_event_notify ( " PLAYBACK " , " Message: %s \r \n Channel: %s " , filename , ast_channel_name ( chan ) ) ;
2008-11-24 21:27:26 +00:00
res = ast_playstream ( fs ) ;
if ( ! res & & vfs )
res = ast_playstream ( vfs ) ;
2016-05-05 07:07:50 -03:00
if ( VERBOSITY_ATLEAST ( 3 ) ) {
ast_channel_lock ( chan ) ;
ast_verb ( 3 , " <%s> Playing '%s.%s' (language '%s') \n " , ast_channel_name ( chan ) , filename , ast_format_get_name ( ast_channel_writeformat ( chan ) ) , preflang ? preflang : " default " ) ;
ast_channel_unlock ( chan ) ;
}
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 21:01:11 +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 21:01:11 +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 21:01:11 +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.
*/
2019-10-23 12:36:17 -05:00
orig_fn = ast_strdup ( fn ) ;
2005-07-05 14:37:24 +00:00
for ( c = fn ; * c ; c + + )
if ( * c = = ' / ' )
* c = ' _ ' ;
size = strlen ( fn ) + strlen ( record_cache_dir ) + 2 ;
2019-10-23 12:36:17 -05:00
buf = ast_malloc ( 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 ) ;
2019-10-23 12:36:17 -05:00
ast_free ( 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 ;
}
2019-10-23 12:36:17 -05:00
/*
* 'fn' was has either been allocated from build_filename, or that was freed
* and now 'fn' points to memory allocated for 'buf'. Either way the memory
* now needs to be released.
*/
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 ) {
2019-10-23 12:36:17 -05:00
fs - > realfilename = orig_fn ;
fs - > filename = fn ;
/*
* The above now manages the memory allocated for 'orig_fn' and 'fn', so
* set them to NULL, so they don't get released at the end of the loop.
*/
orig_fn = NULL ;
fn = NULL ;
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
}
2019-10-23 12:36:17 -05:00
/* Free 'fn', or if 'fn' points to 'buf' then free 'buf' */
ast_free ( fn ) ;
ast_free ( orig_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 ;
}
2013-01-22 15:16:20 +00:00
static void waitstream_control ( struct ast_channel * c ,
enum ast_waitstream_fr_cb_values type ,
ast_waitstream_fr_cb cb ,
int skip_ms )
{
switch ( type )
{
case AST_WAITSTREAM_CB_FASTFORWARD :
{
int eoftest ;
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 ) ;
} else {
ungetc ( eoftest , ast_channel_stream ( c ) - > f ) ;
}
}
break ;
case AST_WAITSTREAM_CB_REWIND :
ast_stream_rewind ( ast_channel_stream ( c ) , skip_ms ) ;
break ;
default :
break ;
}
if ( cb ) {
2014-07-20 22:06:33 +00:00
long ms_len = ast_tellstream ( ast_channel_stream ( c ) ) / ( ast_format_get_sample_rate ( ast_channel_stream ( c ) - > fmt - > format ) / 1000 ) ;
2013-01-22 15:16:20 +00:00
cb ( c , ms_len , type ) ;
}
ast_test_suite_event_notify ( " PLAYBACK " , " Channel: %s \r \n "
" Control: %s \r \n "
" SkipMs: %d \r \n " ,
ast_channel_name ( c ) ,
( type = = AST_WAITSTREAM_CB_FASTFORWARD ) ? " FastForward " : " Rewind " ,
skip_ms ) ;
}
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. */
2017-05-13 16:40:00 +00:00
ast_channel_set_flag ( 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 ) {
2014-07-20 22:06:33 +00:00
long ms_len = ast_tellstream ( ast_channel_stream ( c ) ) / ( ast_format_get_sample_rate ( ast_channel_stream ( c ) - > fmt - > format ) / 1000 ) ;
2012-06-04 20:26:12 +00:00
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 ) ) ;
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( 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 ) ) ;
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( 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 */
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( 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 ) {
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( 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 ) ;
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( c , AST_FLAG_END_DTMF_ONLY ) ;
2006-04-04 12:59:25 +00:00
return res ;
}
} else {
2009-11-04 14:05:12 +00:00
res = fr - > subclass . integer ;
2008-03-04 23:04:29 +00:00
if ( strchr ( forward , res ) ) {
2013-01-22 15:16:20 +00:00
waitstream_control ( c , AST_WAITSTREAM_CB_FASTFORWARD , cb , skip_ms ) ;
2008-08-10 19:35:50 +00:00
} else if ( strchr ( reverse , res ) ) {
2013-01-22 15:16:20 +00:00
waitstream_control ( c , AST_WAITSTREAM_CB_REWIND , cb , skip_ms ) ;
2006-04-04 12:59:25 +00:00
} else if ( strchr ( breakon , res ) ) {
2013-01-22 15:16:20 +00:00
ast_test_suite_event_notify ( " PLAYBACK " , " Channel: %s \r \n "
" Control: %s \r \n " ,
ast_channel_name ( c ) ,
" Break " ) ;
2006-04-04 12:59:25 +00:00
ast_frfree ( fr ) ;
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( c , AST_FLAG_END_DTMF_ONLY ) ;
2006-04-04 12:59:25 +00:00
return res ;
2012-03-22 19:51:16 +00:00
}
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 ) {
2013-01-22 15:16:20 +00:00
case AST_CONTROL_STREAM_STOP :
case AST_CONTROL_STREAM_SUSPEND :
case AST_CONTROL_STREAM_RESTART :
/* Fall-through and break out */
ast_test_suite_event_notify ( " PLAYBACK " , " Channel: %s \r \n "
" Control: %s \r \n " ,
ast_channel_name ( c ) ,
" Break " ) ;
res = fr - > subclass . integer ;
ast_frfree ( fr ) ;
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( c , AST_FLAG_END_DTMF_ONLY ) ;
2013-01-22 15:16:20 +00:00
return res ;
case AST_CONTROL_STREAM_REVERSE :
if ( ! skip_ms ) {
skip_ms = 3000 ;
}
waitstream_control ( c , AST_WAITSTREAM_CB_REWIND , cb , skip_ms ) ;
break ;
case AST_CONTROL_STREAM_FORWARD :
if ( ! skip_ms ) {
skip_ms = 3000 ;
}
waitstream_control ( c , AST_WAITSTREAM_CB_FASTFORWARD , cb , skip_ms ) ;
break ;
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 ) ;
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( 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 :
2021-05-13 11:13:55 -04:00
case AST_CONTROL_FLASH :
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
2017-05-13 16:40:00 +00:00
ast_channel_clear_flag ( 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
}
2013-01-22 15:16:20 +00:00
/*! \internal
* \brief Clean up the return value of a waitstream call
*
* It's possible for a control frame to come in from an external source and break the
* playback. From a consumer of most ast_waitstream_* function callers, this should
* appear like normal playback termination, i.e., return 0 and not the value of the
* control frame.
*/
static int sanitize_waitstream_return ( int return_value )
{
switch ( return_value ) {
case AST_CONTROL_STREAM_STOP :
case AST_CONTROL_STREAM_SUSPEND :
case AST_CONTROL_STREAM_RESTART :
/* Fall through and set return_value to 0 */
return_value = 0 ;
break ;
default :
/* Do nothing */
break ;
}
return return_value ;
}
2006-04-04 12:59:25 +00:00
int ast_waitstream ( struct ast_channel * c , const char * breakon )
{
2013-01-22 15:16:20 +00:00
int res ;
res = waitstream_core ( c , breakon , NULL , NULL , 0 , - 1 , - 1 , NULL , NULL /* no callback */ ) ;
return sanitize_waitstream_return ( res ) ;
2006-04-04 12:59:25 +00:00
}
int ast_waitstream_full ( struct ast_channel * c , const char * breakon , int audiofd , int cmdfd )
{
2013-01-22 15:16:20 +00:00
int res ;
res = waitstream_core ( c , breakon , NULL , NULL , 0 ,
2012-06-04 20:26:12 +00:00
audiofd , cmdfd , NULL /* no context */ , NULL /* no callback */ ) ;
2013-01-22 15:16:20 +00:00
return sanitize_waitstream_return ( res ) ;
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 )
{
2013-01-22 15:16:20 +00:00
int res ;
2005-07-12 01:34:06 +00:00
/* 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 ) ;
2013-01-22 15:16:20 +00:00
res = waitstream_core ( c , NULL , NULL , NULL , 0 ,
2012-06-04 20:26:12 +00:00
- 1 , - 1 , context , NULL /* no callback */ ) ;
2013-01-22 15:16:20 +00:00
return sanitize_waitstream_return ( res ) ;
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-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 ) {
2014-07-20 22:06:33 +00:00
ast_cli ( a - > fd , FORMAT2 , ast_format_get_name ( 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
}
2014-07-20 22:06:33 +00:00
struct ast_format * ast_get_format_for_file_ext ( const char * file_ext )
2013-06-24 13:49:20 +00:00
{
struct ast_format_def * f ;
SCOPED_RDLOCK ( lock , & formats . lock ) ;
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
if ( exts_compare ( f - > exts , file_ext ) ) {
2014-07-20 22:06:33 +00:00
return f - > format ;
2013-06-24 13:49:20 +00:00
}
}
return NULL ;
}
2018-02-13 10:55:47 -08:00
int ast_get_extension_for_mime_type ( const char * mime_type , char * buffer , size_t capacity )
{
struct ast_format_def * f ;
SCOPED_RDLOCK ( lock , & formats . lock ) ;
ast_assert ( buffer & & capacity ) ;
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
if ( type_in_list ( f - > mime_types , mime_type , strcasecmp ) ) {
size_t item_len = strcspn ( f - > exts , " | " ) ;
size_t bytes_written = snprintf ( buffer , capacity , " .%.*s " , ( int ) item_len , f - > exts ) ;
if ( bytes_written < capacity ) {
/* Only return success if we didn't truncate */
return 1 ;
}
}
}
return 0 ;
}
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
} ;
2012-12-11 22:03:23 +00:00
static void file_shutdown ( void )
{
ast_cli_unregister_multiple ( cli_file , ARRAY_LEN ( cli_file ) ) ;
2013-06-24 13:49:20 +00:00
STASIS_MESSAGE_TYPE_CLEANUP ( ast_format_register_type ) ;
STASIS_MESSAGE_TYPE_CLEANUP ( ast_format_unregister_type ) ;
2012-12-11 22:03:23 +00:00
}
2004-06-30 03:22:29 +00:00
int ast_file_init ( void )
{
2013-06-24 13:49:20 +00:00
STASIS_MESSAGE_TYPE_INIT ( ast_format_register_type ) ;
STASIS_MESSAGE_TYPE_INIT ( ast_format_unregister_type ) ;
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_file , ARRAY_LEN ( cli_file ) ) ;
2015-03-26 22:24:26 +00:00
ast_register_cleanup ( file_shutdown ) ;
2004-06-30 03:22:29 +00:00
return 0 ;
}