Index: apps/app_meetme.c =================================================================== RCS file: /usr/local/cvsroot/asterisk/apps/app_meetme.c,v retrieving revision 1.60.2.2 diff -u -r1.60.2.2 app_meetme.c --- apps/app_meetme.c 25 Nov 2004 18:27:22 -0000 1.60.2.2 +++ apps/app_meetme.c 21 Jan 2005 16:51:49 -0000 @@ -67,6 +67,9 @@ " 'e' -- select an empty conference\n" " 'E' -- select an empty pinless conference\n" " 'v' -- video mode\n" +" 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n" +" using format ${MEETME_RECORDINGFORMAT}). Default filename is\n" +" meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n" " 'q' -- quiet mode (don't play enter/leave sounds)\n" " 'M' -- enable music on hold when the conference has a single caller\n" " 'x' -- close the conference when last marked user exits\n" @@ -92,6 +95,7 @@ " 'l' -- Unlock conference\n" " 'M' -- Mute conference\n" " 'm' -- Unmute conference\n" +" 'r' -- Record conference\n" ""; STANDARD_LOCAL_USER; @@ -108,8 +112,14 @@ struct ast_conf_user *firstuser; /* Pointer to the first user struct */ struct ast_conf_user *lastuser; /* Pointer to the last user struct */ time_t start; /* Start time (s) */ + int recording; /* recording status */ int isdynamic; /* Created on the fly? */ + int isrecording; /* Is the conference being recorded? */ int locked; /* Is the conference locked? */ + pthread_t recordthread; /* thread for recording */ + pthread_attr_t attr; /* thread attribute */ + char *recordingfilename; /* Filename to record the Conference into */ + char *recordingformat; /* Format to record the Conference in */ char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */ struct ast_conference *next; } *confs; @@ -133,12 +143,18 @@ static int admin_exec(struct ast_channel *chan, void *data); +void *recordthread(void *args); + #include "enter.h" #include "leave.h" #define ENTER 0 #define LEAVE 1 +#define MEETME_RECORD_OFF 0 +#define MEETME_RECORD_ACTIVE 1 +#define MEETME_RECORD_TERMINATE 2 + #define CONF_SIZE 320 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */ @@ -154,7 +170,7 @@ #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */ #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */ #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */ - +#define CONFFLAG_RECORDCONF (1 << 14) /* If set, the MeetMe will be recorded */ static int careful_write(int fd, unsigned char *data, int len) { @@ -237,7 +253,7 @@ /* Setup a new zap conference */ ztc.chan = 0; ztc.confno = -1; - ztc.confmode = ZT_CONF_CONFANN; + ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON; if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); if (cnf->chan) @@ -505,12 +521,13 @@ int x; int menu_active = 0; int using_pseudo = 0; - + struct ast_app *app; char *agifile; char *agifiledefault = "conf-background.agi"; char meetmesecs[30] = ""; char exitcontext[AST_MAX_EXTENSION] = ""; + char recordingtmp[AST_MAX_EXTENSION] = ""; int dtmf; ZT_BUFFERINFO bi; @@ -523,6 +540,23 @@ } memset(user, 0, sizeof(struct ast_conf_user)); + if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) { + conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE"); + if (!conf->recordingfilename) { + snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid); + conf->recordingfilename = ast_strdupa(recordingtmp); + } + conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"); + if (!conf->recordingformat) { + snprintf(recordingtmp,sizeof(recordingtmp), "wav"); + conf->recordingformat = ast_strdupa(recordingtmp); + } + pthread_attr_init(&conf->attr); + pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED); + ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat); + ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf); + } + user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */ time(&user->jointime); @@ -1011,10 +1045,24 @@ } if (!cur) ast_log(LOG_WARNING, "Conference not found\n"); + if (conf->recording == MEETME_RECORD_ACTIVE) { + conf->recording = MEETME_RECORD_TERMINATE; + ast_mutex_unlock(&conflock); + while (1) { + ast_mutex_lock(&conflock); + if (conf->recording == MEETME_RECORD_OFF) + break; + ast_mutex_unlock(&conflock); + } + } if (conf->chan) ast_hangup(conf->chan); else close(conf->fd); + if (conf->isrecording) { + conf->isrecording=0; + ast_monitor_stop(conf->chan, 1); + } free(conf); } else { /* Remove the user struct */ @@ -1226,6 +1274,8 @@ confflags |= CONFFLAG_AGI; if (strchr(inflags, 'w')) confflags |= CONFFLAG_WAITMARKED; + if (strchr(inflags, 'r')) + confflags |= CONFFLAG_RECORDCONF; if (strchr(inflags, 'd')) dynamic = 1; if (strchr(inflags, 'D')) { @@ -1519,6 +1569,45 @@ return 0; } +void *recordthread(void *args) +{ + struct ast_conference *cnf; + struct ast_frame *f=NULL; + int flags; + struct ast_filestream *s; + int res=0; + + cnf = (struct ast_conference *)args; + ast_stopstream(cnf->chan); + flags = O_CREAT|O_TRUNC|O_WRONLY; + s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644); + + if (s) { + cnf->recording = MEETME_RECORD_ACTIVE; + while (ast_waitfor(cnf->chan, -1) > -1) { + f = ast_read(cnf->chan); + if (!f) { + res = -1; + break; + } + if (f->frametype == AST_FRAME_VOICE) { + res = ast_writestream(s, f); + if (res) + break; + } + ast_frfree(f); + if (cnf->recording == MEETME_RECORD_TERMINATE) { + ast_mutex_lock(&conflock); + ast_mutex_unlock(&conflock); + break; + } + } + cnf->recording = MEETME_RECORD_OFF; + ast_closestream(s); + } + pthread_exit(0); +} + int unload_module(void) { STANDARD_HANGUP_LOCALUSERS;