]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
freetdm: add channel commands to record and dump media
authorMoises Silva <moy@sangoma.com>
Thu, 2 Dec 2010 23:35:48 +0000 (18:35 -0500)
committerMoises Silva <moy@sangoma.com>
Thu, 2 Dec 2010 23:35:48 +0000 (18:35 -0500)
libs/freetdm/src/ftdm_io.c
libs/freetdm/src/include/freetdm.h
libs/freetdm/src/include/private/ftdm_core.h
libs/freetdm/src/include/private/ftdm_types.h

index 3dd1aa9ece0776d6d6d958b6bc1e1053971043b5..92302c5071955198d590472f2db2d0d2c86b6be8 100644 (file)
@@ -86,6 +86,110 @@ FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void)
 #endif
 }
 
+static void write_chan_io_dump(ftdm_channel_t *fchan, ftdm_io_dump_t *dump, char *dataptr, int dlen)
+{
+       int windex = dump->windex;
+       int avail = dump->size - windex;
+
+       if (dlen > avail) {
+               int diff = dlen - avail;
+               
+               ftdm_assert(diff < dump->size, "Very small buffer or very big IO chunk!\n");
+
+               /* write only what we can and the rest at the beginning of the buffer */
+               memcpy(&dump->buffer[windex], dataptr, avail);
+               memcpy(&dump->buffer[0], &dataptr[avail], diff);
+               windex = diff;
+
+               ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "wrapping around dump buffer %p up to index %d\n\n", dump, windex);
+               dump->wrapped = 1;
+       } else {
+               memcpy(&dump->buffer[windex], dataptr, dlen);
+               windex += dlen;
+       }
+
+       if (windex == dump->size) {
+               ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "wrapping around dump buffer %p\n", dump);
+               windex = 0;
+               dump->wrapped = 1;
+       }
+
+       dump->windex = windex;
+}
+
+static void dump_chan_io_to_file(ftdm_channel_t *fchan, ftdm_io_dump_t *dump, FILE *file)
+{
+       /* write the saved audio buffer */
+       int rc = 0;
+       int towrite = dump->size - dump->windex;
+       if (dump->wrapped) {
+               rc = fwrite(&dump->buffer[dump->windex], 1, towrite, file);
+               if (rc != towrite) {
+                       ftdm_log_chan(fchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
+               }
+       }
+       if (dump->windex) {
+               towrite = dump->windex;
+               rc = fwrite(&dump->buffer[0], 1, towrite, file);
+               if (rc != towrite) {
+                       ftdm_log_chan(fchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
+               }
+       }
+       dump->windex = 0;
+       dump->wrapped = 0;
+}
+
+static void stop_chan_io_dump(ftdm_io_dump_t *dump)
+{
+       if (!dump->buffer) {
+               return;
+       }
+       ftdm_safe_free(dump->buffer);
+       memset(dump, 0, sizeof(dump));
+}
+
+static int start_chan_io_dump(ftdm_channel_t *chan, ftdm_io_dump_t *dump, ftdm_size_t size)
+{
+       if (dump->buffer) {
+               ftdm_log_chan_msg(chan, FTDM_LOG_ERROR, "IO dump is already started\n");
+               return -1;
+       }
+       memset(dump, 0, sizeof(*dump));
+       dump->buffer = ftdm_malloc(size);
+       if (!dump->buffer) {
+               return -1;
+       }
+       dump->size = size;
+       return 0;
+}
+
+
+static void close_dtmf_debug_file(ftdm_channel_t *ftdmchan)
+{
+       if (ftdmchan->dtmfdbg.file) {
+               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "closing debug dtmf file\n");
+               fclose(ftdmchan->dtmfdbg.file);
+               ftdmchan->dtmfdbg.file = NULL;
+       }
+}
+
+static int disable_dtmf_debug(ftdm_channel_t *ftdmchan)
+{
+       if (!ftdmchan->dtmfdbg.enabled) {
+               return 0;
+       }
+
+       if (!ftdmchan->rxdump.buffer) {
+               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "DTMF debug enabled but no rx dump?\n");    
+               return -1;
+       }
+
+       close_dtmf_debug_file(ftdmchan);
+       stop_chan_io_dump(&ftdmchan->rxdump);
+       ftdmchan->dtmfdbg.enabled = 0;
+       return 0;
+}
+
 typedef struct {
        uint8_t         enabled;
        uint8_t         running;
@@ -448,10 +552,6 @@ static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan)
                        ftdm_sleep(500);
                }
 
-#ifdef FTDM_DEBUG_DTMF
-               ftdm_mutex_destroy(&ftdmchan->dtmfdbg.mutex);
-#endif
-
                ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
                ftdm_buffer_destroy(&ftdmchan->pre_buffer);
                ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
@@ -870,9 +970,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_add_channel(ftdm_span_t *span, ftdm_socket_t
 
                ftdm_mutex_create(&new_chan->mutex);
                ftdm_mutex_create(&new_chan->pre_buffer_mutex);
-#ifdef FTDM_DEBUG_DTMF
-               ftdm_mutex_create(&new_chan->dtmfdbg.mutex);
-#endif
 
                ftdm_buffer_create(&new_chan->digit_buffer, 128, 128, 0);
                ftdm_buffer_create(&new_chan->gen_dtmf_buffer, 128, 128, 0);
@@ -2355,23 +2452,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_get_sig_status(ftdm_span_t *span, ftdm_signa
        }
 }
 
-#ifdef FTDM_DEBUG_DTMF
-static void close_dtmf_debug(ftdm_channel_t *ftdmchan)
-{
-       ftdm_mutex_lock(ftdmchan->dtmfdbg.mutex);
-
-       if (ftdmchan->dtmfdbg.file) {
-               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "closing debug dtmf file\n");
-               fclose(ftdmchan->dtmfdbg.file);
-               ftdmchan->dtmfdbg.file = NULL;
-       }
-       ftdmchan->dtmfdbg.windex = 0;
-       ftdmchan->dtmfdbg.wrapped = 0;
-
-       ftdm_mutex_unlock(ftdmchan->dtmfdbg.mutex);
-}
-#endif
-
 static ftdm_status_t ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan);
 FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
 {
@@ -2400,9 +2480,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
        ftdm_buffer_destroy(&ftdmchan->pre_buffer);
        ftdmchan->pre_buffer_size = 0;
        ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
-#ifdef FTDM_DEBUG_DTMF
-       close_dtmf_debug(ftdmchan);
-#endif
+       disable_dtmf_debug(ftdmchan);
        ftdm_channel_clear_vars(ftdmchan);
        if (ftdmchan->hangup_timer) {
                ftdm_sched_cancel_timer(globals.timingsched, ftdmchan->hangup_timer);
@@ -2411,7 +2489,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
        ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
        ftdmchan->state = FTDM_CHANNEL_STATE_DOWN;
 
-       ftdm_log(FTDM_LOG_DEBUG, "channel done %u:%u\n", ftdmchan->span_id, ftdmchan->chan_id);
+       stop_chan_io_dump(&ftdmchan->txdump);
+       stop_chan_io_dump(&ftdmchan->rxdump);
 
        if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
                ftdm_sigmsg_t sigmsg;
@@ -2423,6 +2502,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
                ftdm_span_send_signal(ftdmchan->span, &sigmsg);
        }
 
+       ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "channel done\n");
+
        ftdm_mutex_unlock(ftdmchan->mutex);
 
        return FTDM_SUCCESS;
@@ -2431,7 +2512,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
 FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan)
 {
 
-       assert(ftdmchan != NULL);
+       ftdm_assert(ftdmchan != NULL, "Null channel\n");
 
        ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INUSE);
 
@@ -2505,8 +2586,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
 {
        ftdm_status_t status = FTDM_FAIL;
        
-       assert(ftdmchan != NULL);
-       assert(ftdmchan->fio != NULL);
+       ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
+       ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n");
 
        ftdm_mutex_lock(ftdmchan->mutex);
 
@@ -2533,7 +2614,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
                break;
        case FTDM_COMMAND_TRACE_INPUT:
                {
-                       char *path = (char *) obj;
+                       char *path = FTDM_COMMAND_OBJ_CHAR_P;
                        if (ftdmchan->fds[FTDM_READ_TRACE_INDEX] > 0) {
                                close(ftdmchan->fds[FTDM_READ_TRACE_INDEX]);
                                ftdmchan->fds[FTDM_READ_TRACE_INDEX] = -1;
@@ -2576,6 +2657,109 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
                        GOTO_STATUS(done, FTDM_SUCCESS);
                }
                break;
+
+       /*!< Enable DTMF debugging */
+       case FTDM_COMMAND_ENABLE_DEBUG_DTMF:
+               {
+                       if (ftdmchan->dtmfdbg.enabled) {
+                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot enable debug DTMF again\n");        
+                               GOTO_STATUS(done, FTDM_FAIL);
+                       }
+                       if (ftdmchan->rxdump.buffer) {
+                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot debug DTMF if Rx dumping is already enabled\n");    
+                               GOTO_STATUS(done, FTDM_FAIL);
+                       }
+                       if (start_chan_io_dump(ftdmchan, &ftdmchan->rxdump, FTDM_IO_DUMP_DEFAULT_BUFF_SIZE)) {
+                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to enable rx dump for DTMF debugging\n");   
+                       }
+                       ftdmchan->dtmfdbg.enabled = 1;
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
+       /*!< Disable DTMF debugging (if not disabled explicitly, it is disabled automatically when calls hangup) */
+       case FTDM_COMMAND_DISABLE_DEBUG_DTMF:
+               {
+                       if (!ftdmchan->dtmfdbg.enabled) {
+                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "DTMF debug is already disabled\n");        
+                               GOTO_STATUS(done, FTDM_SUCCESS);
+                       }
+                       if (disable_dtmf_debug(ftdmchan)) {
+                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to disable DTMF debug\n");  
+                               GOTO_STATUS(done, FTDM_FAIL);
+                       }
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
+       /*!< Start dumping all input to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
+       case FTDM_COMMAND_ENABLE_INPUT_DUMP:
+               {
+                       ftdm_size_t size = obj ? FTDM_COMMAND_OBJ_SIZE : FTDM_IO_DUMP_DEFAULT_BUFF_SIZE;
+                       if (start_chan_io_dump(ftdmchan, &ftdmchan->rxdump, size)) {
+                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to enable input dump\n");   
+                               GOTO_STATUS(done, FTDM_FAIL);
+                       }
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Enabled input dump with size %zd\n", size);
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
+       /*!< Stop dumping all input to a circular buffer. */
+       case FTDM_COMMAND_DISABLE_INPUT_DUMP:
+               {
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Disabled input dump of size %zd\n", ftdmchan->rxdump.size);
+                       stop_chan_io_dump(&ftdmchan->rxdump);
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
+       /*!< Start dumping all output to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
+       case FTDM_COMMAND_ENABLE_OUTPUT_DUMP:
+               {
+                       ftdm_size_t size = obj ? FTDM_COMMAND_OBJ_SIZE : FTDM_IO_DUMP_DEFAULT_BUFF_SIZE;
+                       if (start_chan_io_dump(ftdmchan, &ftdmchan->txdump, size)) {
+                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to enable output dump\n");  
+                               GOTO_STATUS(done, FTDM_FAIL);
+                       }
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Enabled output dump with size %zd\n", size);
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
+       /*!< Stop dumping all output to a circular buffer. */
+       case FTDM_COMMAND_DISABLE_OUTPUT_DUMP:
+               {
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Disabled output dump of size %zd\n", ftdmchan->rxdump.size);
+                       stop_chan_io_dump(&ftdmchan->txdump);
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
+       /*!< Dump the current input circular buffer to the specified FILE* structure */
+       case FTDM_COMMAND_DUMP_INPUT:
+               {
+                       if (!obj) {
+                               GOTO_STATUS(done, FTDM_FAIL);
+                       }
+                       dump_chan_io_to_file(ftdmchan, &ftdmchan->rxdump, obj);
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Dumped input of size %zd to file %p\n", ftdmchan->rxdump.size, obj);
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
+       /*!< Dump the current output circular buffer to the specified FILE* structure */
+       case FTDM_COMMAND_DUMP_OUTPUT:
+               {
+                       if (!obj) {
+                               GOTO_STATUS(done, FTDM_FAIL);
+                       }
+                       dump_chan_io_to_file(ftdmchan, &ftdmchan->txdump, obj);
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Dumped input of size %zd to file %p\n", ftdmchan->txdump.size, obj);
+                       GOTO_STATUS(done, FTDM_SUCCESS);
+               }
+               break;
+
        case FTDM_COMMAND_SET_INTERVAL:
                {
                        if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL)) {
@@ -3044,12 +3228,14 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
        ftdm_size_t wr = 0;
        const char *p;
        
-       assert(ftdmchan != NULL);
+       ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
 
        ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Queuing DTMF %s\n", dtmf);
 
-#ifdef FTDM_DEBUG_DTMF
-       ftdm_mutex_lock(ftdmchan->dtmfdbg.mutex);
+       if (!ftdmchan->dtmfdbg.enabled) {
+               goto skipdebug;
+       }
+
        if (!ftdmchan->dtmfdbg.file) {
                struct tm currtime;
                time_t currsec;
@@ -3062,37 +3248,18 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
                                ftdmchan->span_id, ftdmchan->chan_id, 
                                currtime.tm_year-100, currtime.tm_mon+1, currtime.tm_mday,
                                currtime.tm_hour, currtime.tm_min, currtime.tm_sec, ftdmchan->native_codec == FTDM_CODEC_ULAW ? "ulaw" : ftdmchan->native_codec == FTDM_CODEC_ALAW ? "alaw" : "sln");
-               ftdmchan->dtmfdbg.file = fopen(dfile, "w");
+               ftdmchan->dtmfdbg.file = fopen(dfile, "w");     
                if (!ftdmchan->dtmfdbg.file) {
                        ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "failed to open debug dtmf file %s\n", dfile);
                } else {
-                       /* write the saved audio buffer */
-                       int rc = 0;
-                       int towrite = sizeof(ftdmchan->dtmfdbg.buffer) - ftdmchan->dtmfdbg.windex;
-               
-                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "created debug DTMF file %s\n", dfile);
                        ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
-                       if (ftdmchan->dtmfdbg.wrapped) {
-                               rc = fwrite(&ftdmchan->dtmfdbg.buffer[ftdmchan->dtmfdbg.windex], 1, towrite, ftdmchan->dtmfdbg.file);
-                               if (rc != towrite) {
-                                       ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
-                               }
-                       }
-                       if (ftdmchan->dtmfdbg.windex) {
-                               towrite = ftdmchan->dtmfdbg.windex;
-                               rc = fwrite(&ftdmchan->dtmfdbg.buffer[0], 1, towrite, ftdmchan->dtmfdbg.file);
-                               if (rc != towrite) {
-                                       ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
-                               }
-                       }
-                       ftdmchan->dtmfdbg.windex = 0;
-                       ftdmchan->dtmfdbg.wrapped = 0;
+                       ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_INPUT, ftdmchan->dtmfdbg.file);
                }
        } else {
-                       ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
+               ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
        }
-       ftdm_mutex_unlock(ftdmchan->dtmfdbg.mutex);
-#endif
+
+skipdebug:
 
        if (ftdmchan->pre_buffer) {
                ftdm_buffer_zero(ftdmchan->pre_buffer);
@@ -3146,7 +3313,6 @@ static FIO_WRITE_FUNCTION(ftdm_raw_write)
        return ftdmchan->fio->write(ftdmchan, data, datalen);
 }
 
-
 static FIO_READ_FUNCTION(ftdm_raw_read)
 {
        ftdm_status_t  status = ftdmchan->fio->read(ftdmchan, data, datalen);
@@ -3161,48 +3327,24 @@ static FIO_READ_FUNCTION(ftdm_raw_read)
                ftdmchan->span->sig_read(ftdmchan, data, *datalen);
        }
 
-#ifdef FTDM_DEBUG_DTMF
        if (status == FTDM_SUCCESS) {
                int dlen = (int) *datalen;
                int rc = 0;
-               ftdm_mutex_lock(ftdmchan->dtmfdbg.mutex);
-               if (!ftdmchan->dtmfdbg.file) {
-                       /* no file yet, write to our circular buffer */
-                       int windex = ftdmchan->dtmfdbg.windex;
-                       int avail = sizeof(ftdmchan->dtmfdbg.buffer) - windex;
-                       char *dataptr = data;
-
-                       if (dlen > avail) {
-                               int diff = dlen - avail;
-                               /* write only what we can and the rest at the beginning of the buffer */
-                               memcpy(&ftdmchan->dtmfdbg.buffer[windex], dataptr, avail);
-                               memcpy(&ftdmchan->dtmfdbg.buffer[0], &dataptr[avail], diff);
-                               windex = diff;
-                               /*ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "wrapping around dtmf read buffer up to index %d\n\n", windex);*/
-                               ftdmchan->dtmfdbg.wrapped = 1;
-                       } else {
-                               memcpy(&ftdmchan->dtmfdbg.buffer[windex], dataptr, dlen);
-                               windex += dlen;
-                       }
-                       if (windex == sizeof(ftdmchan->dtmfdbg.buffer)) {
-                               /*ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "wrapping around dtmf read buffer\n");*/
-                               windex = 0;
-                               ftdmchan->dtmfdbg.wrapped = 1;
-                       }
-                       ftdmchan->dtmfdbg.windex = windex;
-               } else {
+
+               write_chan_io_dump(ftdmchan, &ftdmchan->rxdump, data, dlen);
+
+               /* if dtmf debug is enabled and initialized, write there too */
+               if (ftdmchan->dtmfdbg.file) {
                        rc = fwrite(data, 1, dlen, ftdmchan->dtmfdbg.file);
                        if (rc != dlen) {
                                ftdm_log(FTDM_LOG_WARNING, "DTMF debugger wrote only %d out of %d bytes: %s\n", rc, datalen, strerror(errno));
                        }
                        ftdmchan->dtmfdbg.closetimeout--;
                        if (!ftdmchan->dtmfdbg.closetimeout) {
-                               close_dtmf_debug(ftdmchan);
+                               close_dtmf_debug_file(ftdmchan);
                        }
                }
-               ftdm_mutex_unlock(ftdmchan->dtmfdbg.mutex);
        }
-#endif
        return status;
 }
 
index 45254e817f49708ba8946650345f7a91fb39b5fd..1adbaa819aadc3cc6b18fc4e62682a3dbfc70104 100644 (file)
@@ -431,9 +431,38 @@ typedef enum {
        FTDM_COMMAND_WINK,
        FTDM_COMMAND_ENABLE_PROGRESS_DETECT,
        FTDM_COMMAND_DISABLE_PROGRESS_DETECT,
+
+       /*!< Start tracing input and output from channel to the given file */
        FTDM_COMMAND_TRACE_INPUT,
        FTDM_COMMAND_TRACE_OUTPUT,
+
+       /*!< Stop both Input and Output trace, closing the files */
        FTDM_COMMAND_TRACE_END_ALL,
+
+       /*!< Enable DTMF debugging */
+       FTDM_COMMAND_ENABLE_DEBUG_DTMF,
+
+       /*!< Disable DTMF debugging (if not disabled explicitly, it is disabled automatically when calls hangup) */
+       FTDM_COMMAND_DISABLE_DEBUG_DTMF,
+
+       /*!< Start dumping all input to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
+       FTDM_COMMAND_ENABLE_INPUT_DUMP,
+
+       /*!< Stop dumping all input to a circular buffer. */
+       FTDM_COMMAND_DISABLE_INPUT_DUMP,
+
+       /*!< Start dumping all output to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
+       FTDM_COMMAND_ENABLE_OUTPUT_DUMP,
+
+       /*!< Stop dumping all output to a circular buffer. */
+       FTDM_COMMAND_DISABLE_OUTPUT_DUMP,
+
+       /*!< Dump the current input circular buffer to the specified FILE* structure */
+       FTDM_COMMAND_DUMP_INPUT,
+
+       /*!< Dump the current output circular buffer to the specified FILE* structure */
+       FTDM_COMMAND_DUMP_OUTPUT,
+
        FTDM_COMMAND_ENABLE_CALLERID_DETECT,
        FTDM_COMMAND_DISABLE_CALLERID_DETECT,
        FTDM_COMMAND_ENABLE_ECHOCANCEL,
index 7683ec71453b42dfc9d06b06a114e2ad0cd0db2e..04458adda1e154813a39cadd170bb089e243fcee 100644 (file)
@@ -342,21 +342,23 @@ typedef enum {
        FTDM_TYPE_CHANNEL
 } ftdm_data_type_t;
 
-#ifdef FTDM_DEBUG_DTMF
-/* number of bytes for the circular buffer (5 seconds worth of audio) */
-#define DTMF_DEBUG_SIZE 8 * 5000
-/* number of 20ms cycles before timeout and close the debug dtmf file (5 seconds) */
-#define DTMF_DEBUG_TIMEOUT 250
+/* number of bytes for the IO dump circular buffer (5 seconds worth of audio by default) */
+#define FTDM_IO_DUMP_DEFAULT_BUFF_SIZE 8 * 5000
 typedef struct {
-       FILE *file;
-       char buffer[DTMF_DEBUG_SIZE];
+       char *buffer;
+       ftdm_size_t size;
        int windex;
        int wrapped;
-       int closetimeout;
+} ftdm_io_dump_t;
+
+/* number of interval cycles before timeout and close the debug dtmf file (5 seconds if interval is 20) */
+#define DTMF_DEBUG_TIMEOUT 250
+typedef struct {
+       uint8_t enabled;
+       FILE *file;
+       int32_t closetimeout;
        ftdm_mutex_t *mutex;
 } ftdm_dtmf_debug_t;
-#endif
-
 
 typedef struct {
        const char *file;
@@ -471,9 +473,9 @@ struct ftdm_channel {
        void *user_private;
        ftdm_timer_id_t hangup_timer;
        ftdm_channel_iostats_t iostats;
-#ifdef FTDM_DEBUG_DTMF
        ftdm_dtmf_debug_t dtmfdbg;
-#endif
+       ftdm_io_dump_t rxdump;
+       ftdm_io_dump_t txdump;
 };
 
 struct ftdm_span {
index f265cb1a3dea83f6bbc6ca52431fbf5d038506f8..db1428c962b38d3c3d3d23c02914367e4f3ebf4f 100644 (file)
@@ -58,6 +58,7 @@ typedef int ftdm_filehandle_t;
 extern "C" {
 #endif
 
+#define FTDM_COMMAND_OBJ_SIZE *((ftdm_size_t *)obj)
 #define FTDM_COMMAND_OBJ_INT *((int *)obj)
 #define FTDM_COMMAND_OBJ_CHAR_P (char *)obj
 #define FTDM_COMMAND_OBJ_FLOAT *(float *)obj