Add silence detection to app_record
[asterisk/asterisk.git] / apps / app_record.c
index 7b02be4..30a1b97 100755 (executable)
@@ -18,6 +18,7 @@
 #include <asterisk/pbx.h>
 #include <asterisk/module.h>
 #include <asterisk/translate.h>
 #include <asterisk/pbx.h>
 #include <asterisk/module.h>
 #include <asterisk/translate.h>
+#include <asterisk/dsp.h>
 #include <string.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <string.h>
 #include <stdlib.h>
 #include <pthread.h>
@@ -29,9 +30,10 @@ static char *app = "Record";
 static char *synopsis = "Record to a file";
 
 static char *descrip = 
 static char *synopsis = "Record to a file";
 
 static char *descrip = 
-"  Record(filename:extension): Records from the  channel into a given\n"
+"  Record(filename:extension|silence): Records from the  channel into a given\n"
 "filename. If the file exists it will be overwritten. The 'extension'\n"
 "is the extension of the file type  to  be  recorded (wav, gsm, etc).\n"
 "filename. If the file exists it will be overwritten. The 'extension'\n"
 "is the extension of the file type  to  be  recorded (wav, gsm, etc).\n"
+"'silence' is the number of seconds of silence to allow before returning.\n"
 "Returns -1 when the user hangs up.\n";
 
 STANDARD_LOCAL_USER;
 "Returns -1 when the user hangs up.\n";
 
 STANDARD_LOCAL_USER;
@@ -49,13 +51,21 @@ static int record_exec(struct ast_channel *chan, void *data)
        char * vdata;  /* Used so I don't have to typecast every use of *data */
        int i = 0;
        int j = 0;
        char * vdata;  /* Used so I don't have to typecast every use of *data */
        int i = 0;
        int j = 0;
-       
+
        struct ast_filestream *s = '\0';
        struct localuser *u;
        struct ast_frame *f = NULL;
        
        struct ast_filestream *s = '\0';
        struct localuser *u;
        struct ast_frame *f = NULL;
        
+       struct ast_dsp *sildet;         /* silence detector dsp */
+       int totalsilence = 0;
+       int dspsilence = 0;
+       int silence = 0;                /* amount of silence to allow */
+       int gotsilence = 0;             /* did we timeout for silence? */
+       char silencestr[5];
+       int k = 0;
+       int rfmt;
+
        vdata = data; /* explained above */
        vdata = data; /* explained above */
-       
 
        /* The next few lines of code parse out the filename and header from the input string */
        if (!vdata) { /* no data implies no filename or anything is present */
 
        /* The next few lines of code parse out the filename and header from the input string */
        if (!vdata) { /* no data implies no filename or anything is present */
@@ -76,9 +86,21 @@ static int record_exec(struct ast_channel *chan, void *data)
        }
        fil[i++] = '\0';
 
        }
        fil[i++] = '\0';
 
-       for (; j < 10 && i < strlen(data); i++, j++)
+       for (; j < 10 && i < strlen(data) && (vdata[i] != '|'); i++, j++)
                ext[j] = vdata[i];
        ext[j] = '\0';
                ext[j] = vdata[i];
        ext[j] = '\0';
+
+       if (vdata[i] && (vdata[i] == '|')) i++;
+       for (; vdata[i] && (vdata[i] != '|') && (k < 3) && i < strlen(data); i++, k++)
+               silencestr[k] = vdata[i];
+       silencestr[k] = '\0';
+
+       if (silencestr) {
+               silence = atoi(silencestr);
+               if (silence > 0)
+                       silence *= 1000;
+       }
+
        /* done parsing */
        
        
        /* done parsing */
        
        
@@ -109,7 +131,25 @@ static int record_exec(struct ast_channel *chan, void *data)
                        ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
                }
                ast_stopstream(chan);
                        ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
                }
                ast_stopstream(chan);
+
                /* The end of beep code.  Now the recording starts */
                /* The end of beep code.  Now the recording starts */
+
+
+               if (silence > 0) {
+                       rfmt = chan->readformat;
+                       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+                       if (res < 0) {
+                               ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+                               return -1;
+                       }
+                       sildet = ast_dsp_new();
+                       if (!sildet) {
+                               ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+                               return -1;
+                       }
+                       ast_dsp_set_threshold(sildet, 256);
+               }
+
                s = ast_writefile( tmp, ext, NULL, O_CREAT|O_TRUNC|O_WRONLY , 0, 0644);
        
                if (s) {
                s = ast_writefile( tmp, ext, NULL, O_CREAT|O_TRUNC|O_WRONLY , 0, 0644);
        
                if (s) {
@@ -126,6 +166,22 @@ static int record_exec(struct ast_channel *chan, void *data)
                                                ast_log(LOG_WARNING, "Problem writing frame\n");
                                                break;
                                        }
                                                ast_log(LOG_WARNING, "Problem writing frame\n");
                                                break;
                                        }
+
+                                       if (silence > 0) {
+                                               dspsilence = 0;
+                                               ast_dsp_silence(sildet, f, &dspsilence);
+                                               if (dspsilence) {
+                                                       totalsilence = dspsilence;
+                                               } else {
+                                                       totalsilence = 0;
+                                               }
+                                               if (totalsilence > silence) {
+                                                       /* Ended happily with silence */
+                                                       ast_frfree(f);
+                                                       gotsilence = 1;
+                                                       break;
+                                               }
+                                       }
                                }
                                if ((f->frametype == AST_FRAME_DTMF) &&
                                        (f->subclass == '#')) {
                                }
                                if ((f->frametype == AST_FRAME_DTMF) &&
                                        (f->subclass == '#')) {
@@ -138,9 +194,15 @@ static int record_exec(struct ast_channel *chan, void *data)
                                        ast_log(LOG_DEBUG, "Got hangup\n");
                                        res = -1;
                        }
                                        ast_log(LOG_DEBUG, "Got hangup\n");
                                        res = -1;
                        }
-                       /* Strip off the last 1/4 second of it */
-                       ast_stream_rewind(s, 250);
-                       ast_truncstream(s);
+
+                       if (gotsilence) {
+                               ast_stream_rewind(s, silence-1000);
+                               ast_truncstream(s);
+                       } else {
+                               /* Strip off the last 1/4 second of it */
+                               ast_stream_rewind(s, 250);
+                               ast_truncstream(s);
+                       }
                        ast_closestream(s);
                } else                  
                        ast_log(LOG_WARNING, "Could not create file %s\n", fil);
                        ast_closestream(s);
                } else                  
                        ast_log(LOG_WARNING, "Could not create file %s\n", fil);
@@ -148,6 +210,12 @@ static int record_exec(struct ast_channel *chan, void *data)
                ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
 
        LOCAL_USER_REMOVE(u);
                ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
 
        LOCAL_USER_REMOVE(u);
+       if (silence > 0) {
+               res = ast_set_read_format(chan, rfmt);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
+               ast_dsp_free(sildet);
+       }
        return res;
 }
 
        return res;
 }