freetdm: add channel commands to record and dump media
This commit is contained in:
parent
71e8ed8180
commit
8dd33bc8cc
|
@ -86,6 +86,110 @@ FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void)
|
||||||
#endif
|
#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 {
|
typedef struct {
|
||||||
uint8_t enabled;
|
uint8_t enabled;
|
||||||
uint8_t running;
|
uint8_t running;
|
||||||
|
@ -448,10 +552,6 @@ static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan)
|
||||||
ftdm_sleep(500);
|
ftdm_sleep(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
ftdm_mutex_destroy(&ftdmchan->dtmfdbg.mutex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
|
ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
|
||||||
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
|
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
|
||||||
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
|
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->mutex);
|
||||||
ftdm_mutex_create(&new_chan->pre_buffer_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->digit_buffer, 128, 128, 0);
|
||||||
ftdm_buffer_create(&new_chan->gen_dtmf_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);
|
static ftdm_status_t ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan);
|
||||||
FT_DECLARE(ftdm_status_t) ftdm_channel_done(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);
|
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
|
||||||
ftdmchan->pre_buffer_size = 0;
|
ftdmchan->pre_buffer_size = 0;
|
||||||
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
|
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
disable_dtmf_debug(ftdmchan);
|
||||||
close_dtmf_debug(ftdmchan);
|
|
||||||
#endif
|
|
||||||
ftdm_channel_clear_vars(ftdmchan);
|
ftdm_channel_clear_vars(ftdmchan);
|
||||||
if (ftdmchan->hangup_timer) {
|
if (ftdmchan->hangup_timer) {
|
||||||
ftdm_sched_cancel_timer(globals.timingsched, 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->init_state = FTDM_CHANNEL_STATE_DOWN;
|
||||||
ftdmchan->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)) {
|
if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
|
||||||
ftdm_sigmsg_t sigmsg;
|
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_span_send_signal(ftdmchan->span, &sigmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "channel done\n");
|
||||||
|
|
||||||
ftdm_mutex_unlock(ftdmchan->mutex);
|
ftdm_mutex_unlock(ftdmchan->mutex);
|
||||||
|
|
||||||
return FTDM_SUCCESS;
|
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)
|
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);
|
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;
|
ftdm_status_t status = FTDM_FAIL;
|
||||||
|
|
||||||
assert(ftdmchan != NULL);
|
ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
|
||||||
assert(ftdmchan->fio != NULL);
|
ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n");
|
||||||
|
|
||||||
ftdm_mutex_lock(ftdmchan->mutex);
|
ftdm_mutex_lock(ftdmchan->mutex);
|
||||||
|
|
||||||
|
@ -2533,7 +2614,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
|
||||||
break;
|
break;
|
||||||
case FTDM_COMMAND_TRACE_INPUT:
|
case FTDM_COMMAND_TRACE_INPUT:
|
||||||
{
|
{
|
||||||
char *path = (char *) obj;
|
char *path = FTDM_COMMAND_OBJ_CHAR_P;
|
||||||
if (ftdmchan->fds[FTDM_READ_TRACE_INDEX] > 0) {
|
if (ftdmchan->fds[FTDM_READ_TRACE_INDEX] > 0) {
|
||||||
close(ftdmchan->fds[FTDM_READ_TRACE_INDEX]);
|
close(ftdmchan->fds[FTDM_READ_TRACE_INDEX]);
|
||||||
ftdmchan->fds[FTDM_READ_TRACE_INDEX] = -1;
|
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);
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case FTDM_COMMAND_SET_INTERVAL:
|
||||||
{
|
{
|
||||||
if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_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;
|
ftdm_size_t wr = 0;
|
||||||
const char *p;
|
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);
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Queuing DTMF %s\n", dtmf);
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
if (!ftdmchan->dtmfdbg.enabled) {
|
||||||
ftdm_mutex_lock(ftdmchan->dtmfdbg.mutex);
|
goto skipdebug;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ftdmchan->dtmfdbg.file) {
|
if (!ftdmchan->dtmfdbg.file) {
|
||||||
struct tm currtime;
|
struct tm currtime;
|
||||||
time_t currsec;
|
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,
|
ftdmchan->span_id, ftdmchan->chan_id,
|
||||||
currtime.tm_year-100, currtime.tm_mon+1, currtime.tm_mday,
|
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");
|
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) {
|
if (!ftdmchan->dtmfdbg.file) {
|
||||||
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "failed to open debug dtmf file %s\n", dfile);
|
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "failed to open debug dtmf file %s\n", dfile);
|
||||||
} else {
|
} 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;
|
ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
|
||||||
if (ftdmchan->dtmfdbg.wrapped) {
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_INPUT, ftdmchan->dtmfdbg.file);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (ftdmchan->pre_buffer) {
|
||||||
ftdm_buffer_zero(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);
|
return ftdmchan->fio->write(ftdmchan, data, datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static FIO_READ_FUNCTION(ftdm_raw_read)
|
static FIO_READ_FUNCTION(ftdm_raw_read)
|
||||||
{
|
{
|
||||||
ftdm_status_t status = ftdmchan->fio->read(ftdmchan, data, datalen);
|
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);
|
ftdmchan->span->sig_read(ftdmchan, data, *datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
if (status == FTDM_SUCCESS) {
|
if (status == FTDM_SUCCESS) {
|
||||||
int dlen = (int) *datalen;
|
int dlen = (int) *datalen;
|
||||||
int rc = 0;
|
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) {
|
write_chan_io_dump(ftdmchan, &ftdmchan->rxdump, data, dlen);
|
||||||
int diff = dlen - avail;
|
|
||||||
/* write only what we can and the rest at the beginning of the buffer */
|
/* if dtmf debug is enabled and initialized, write there too */
|
||||||
memcpy(&ftdmchan->dtmfdbg.buffer[windex], dataptr, avail);
|
if (ftdmchan->dtmfdbg.file) {
|
||||||
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 {
|
|
||||||
rc = fwrite(data, 1, dlen, ftdmchan->dtmfdbg.file);
|
rc = fwrite(data, 1, dlen, ftdmchan->dtmfdbg.file);
|
||||||
if (rc != dlen) {
|
if (rc != dlen) {
|
||||||
ftdm_log(FTDM_LOG_WARNING, "DTMF debugger wrote only %d out of %d bytes: %s\n", rc, datalen, strerror(errno));
|
ftdm_log(FTDM_LOG_WARNING, "DTMF debugger wrote only %d out of %d bytes: %s\n", rc, datalen, strerror(errno));
|
||||||
}
|
}
|
||||||
ftdmchan->dtmfdbg.closetimeout--;
|
ftdmchan->dtmfdbg.closetimeout--;
|
||||||
if (!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;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -431,9 +431,38 @@ typedef enum {
|
||||||
FTDM_COMMAND_WINK,
|
FTDM_COMMAND_WINK,
|
||||||
FTDM_COMMAND_ENABLE_PROGRESS_DETECT,
|
FTDM_COMMAND_ENABLE_PROGRESS_DETECT,
|
||||||
FTDM_COMMAND_DISABLE_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_INPUT,
|
||||||
FTDM_COMMAND_TRACE_OUTPUT,
|
FTDM_COMMAND_TRACE_OUTPUT,
|
||||||
|
|
||||||
|
/*!< Stop both Input and Output trace, closing the files */
|
||||||
FTDM_COMMAND_TRACE_END_ALL,
|
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_ENABLE_CALLERID_DETECT,
|
||||||
FTDM_COMMAND_DISABLE_CALLERID_DETECT,
|
FTDM_COMMAND_DISABLE_CALLERID_DETECT,
|
||||||
FTDM_COMMAND_ENABLE_ECHOCANCEL,
|
FTDM_COMMAND_ENABLE_ECHOCANCEL,
|
||||||
|
|
|
@ -342,21 +342,23 @@ typedef enum {
|
||||||
FTDM_TYPE_CHANNEL
|
FTDM_TYPE_CHANNEL
|
||||||
} ftdm_data_type_t;
|
} ftdm_data_type_t;
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
/* number of bytes for the IO dump circular buffer (5 seconds worth of audio by default) */
|
||||||
/* number of bytes for the circular buffer (5 seconds worth of audio) */
|
#define FTDM_IO_DUMP_DEFAULT_BUFF_SIZE 8 * 5000
|
||||||
#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
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FILE *file;
|
char *buffer;
|
||||||
char buffer[DTMF_DEBUG_SIZE];
|
ftdm_size_t size;
|
||||||
int windex;
|
int windex;
|
||||||
int wrapped;
|
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_mutex_t *mutex;
|
||||||
} ftdm_dtmf_debug_t;
|
} ftdm_dtmf_debug_t;
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *file;
|
const char *file;
|
||||||
|
@ -471,9 +473,9 @@ struct ftdm_channel {
|
||||||
void *user_private;
|
void *user_private;
|
||||||
ftdm_timer_id_t hangup_timer;
|
ftdm_timer_id_t hangup_timer;
|
||||||
ftdm_channel_iostats_t iostats;
|
ftdm_channel_iostats_t iostats;
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
ftdm_dtmf_debug_t dtmfdbg;
|
ftdm_dtmf_debug_t dtmfdbg;
|
||||||
#endif
|
ftdm_io_dump_t rxdump;
|
||||||
|
ftdm_io_dump_t txdump;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ftdm_span {
|
struct ftdm_span {
|
||||||
|
|
|
@ -58,6 +58,7 @@ typedef int ftdm_filehandle_t;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define FTDM_COMMAND_OBJ_SIZE *((ftdm_size_t *)obj)
|
||||||
#define FTDM_COMMAND_OBJ_INT *((int *)obj)
|
#define FTDM_COMMAND_OBJ_INT *((int *)obj)
|
||||||
#define FTDM_COMMAND_OBJ_CHAR_P (char *)obj
|
#define FTDM_COMMAND_OBJ_CHAR_P (char *)obj
|
||||||
#define FTDM_COMMAND_OBJ_FLOAT *(float *)obj
|
#define FTDM_COMMAND_OBJ_FLOAT *(float *)obj
|
||||||
|
|
Loading…
Reference in New Issue