提交 bdd22235 authored 作者: Anthony Minessale's avatar Anthony Minessale

more ivr stuff (not done)

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@693 d0543943-73ff-0310-b7d9-9358b9ac24b2
上级 5938cee5
...@@ -104,6 +104,11 @@ SWITCH_DECLARE(int) switch_buffer_write(switch_buffer *buffer, void *data, size_ ...@@ -104,6 +104,11 @@ SWITCH_DECLARE(int) switch_buffer_write(switch_buffer *buffer, void *data, size_
* \return int size of buffer, or 0 if unable to toss that much data * \return int size of buffer, or 0 if unable to toss that much data
*/ */
SWITCH_DECLARE(int) switch_buffer_toss(switch_buffer *buffer, size_t datalen); SWITCH_DECLARE(int) switch_buffer_toss(switch_buffer *buffer, size_t datalen);
/*! \brief Remove all data from the buffer
* \param buffer any buffer of type switch_buffer
*/
SWITCH_DECLARE(void) switch_buffer_zero(switch_buffer *buffer);
/** @} */ /** @} */
......
...@@ -743,7 +743,7 @@ SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, voi ...@@ -743,7 +743,7 @@ SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, voi
\param whence the indicator (see traditional seek) \param whence the indicator (see traditional seek)
\return SWITCH_STATUS_SUCCESS with cur_pos adjusted to new position \return SWITCH_STATUS_SUCCESS with cur_pos adjusted to new position
*/ */
SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, unsigned int samples, int whence); SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, int64_t samples, int whence);
/*! /*!
\brief Close an open file handle \brief Close an open file handle
......
...@@ -85,6 +85,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio ...@@ -85,6 +85,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio
/*! /*!
\brief play a file from the disk to the session \brief play a file from the disk to the session
\param session the session to play the file too \param session the session to play the file too
\param pointer to file handle to use (NULL for builtin one)
\param file the path to the file \param file the path to the file
\param timer_name the name of a timer to use input will be absorbed (NULL to time off the session input). \param timer_name the name of a timer to use input will be absorbed (NULL to time off the session input).
\param dtmf_callback code to execute if any dtmf is dialed during the playback \param dtmf_callback code to execute if any dtmf is dialed during the playback
...@@ -94,6 +95,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio ...@@ -94,6 +95,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio
\note passing a NULL dtmf_callback nad a not NULL buf indicates to copy any dtmf to buf and stop playback. \note passing a NULL dtmf_callback nad a not NULL buf indicates to copy any dtmf to buf and stop playback.
*/ */
SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
switch_file_handle *fh,
char *file, char *file,
char *timer_name, char *timer_name,
switch_dtmf_callback_function dtmf_callback, switch_dtmf_callback_function dtmf_callback,
......
...@@ -245,7 +245,7 @@ struct switch_file_interface { ...@@ -245,7 +245,7 @@ struct switch_file_interface {
/*! function to write from the file */ /*! function to write from the file */
switch_status (*file_write)(switch_file_handle *, void *data, size_t *len); switch_status (*file_write)(switch_file_handle *, void *data, size_t *len);
/*! function to seek to a certian position in the file */ /*! function to seek to a certian position in the file */
switch_status (*file_seek)(switch_file_handle *, unsigned int *cur_pos, unsigned int samples, int whence); switch_status (*file_seek)(switch_file_handle *, unsigned int *cur_pos, int64_t samples, int whence);
/*! list of supported file extensions */ /*! list of supported file extensions */
char **extens; char **extens;
const struct switch_file_interface *next; const struct switch_file_interface *next;
...@@ -273,10 +273,14 @@ struct switch_file_handle { ...@@ -273,10 +273,14 @@ struct switch_file_handle {
int seekable; int seekable;
/*! the sample count of the file */ /*! the sample count of the file */
unsigned int sample_count; unsigned int sample_count;
/*! the speed of the file playback*/
int speed;
/*! the handle's memory pool */ /*! the handle's memory pool */
switch_memory_pool *memory_pool; switch_memory_pool *memory_pool;
/*! private data for the format module to store handle specific info */ /*! private data for the format module to store handle specific info */
void *private_info; void *private_info;
int64_t pos;
switch_buffer *audio_buffer;
}; };
......
...@@ -319,6 +319,7 @@ SWITCH_FILE_DATA_INT = (1 << 4) - Read data in ints ...@@ -319,6 +319,7 @@ SWITCH_FILE_DATA_INT = (1 << 4) - Read data in ints
SWITCH_FILE_DATA_FLOAT = (1 << 5) - Read data in floats SWITCH_FILE_DATA_FLOAT = (1 << 5) - Read data in floats
SWITCH_FILE_DATA_DOUBLE = (1 << 6) - Read data in doubles SWITCH_FILE_DATA_DOUBLE = (1 << 6) - Read data in doubles
SWITCH_FILE_DATA_RAW = (1 << 7) - Read data as is SWITCH_FILE_DATA_RAW = (1 << 7) - Read data as is
SWITCH_FILE_PAUSE = (1 << 8) - Pause
</pre> </pre>
*/ */
typedef enum { typedef enum {
...@@ -330,6 +331,7 @@ typedef enum { ...@@ -330,6 +331,7 @@ typedef enum {
SWITCH_FILE_DATA_FLOAT = (1 << 5), SWITCH_FILE_DATA_FLOAT = (1 << 5),
SWITCH_FILE_DATA_DOUBLE = (1 << 6), SWITCH_FILE_DATA_DOUBLE = (1 << 6),
SWITCH_FILE_DATA_RAW = (1 << 7), SWITCH_FILE_DATA_RAW = (1 << 7),
SWITCH_FILE_PAUSE = (1 << 8)
} switch_file_flag; } switch_file_flag;
typedef enum { typedef enum {
......
...@@ -149,7 +149,7 @@ static void ivrtest_function(switch_core_session *session, char *data) ...@@ -149,7 +149,7 @@ static void ivrtest_function(switch_core_session *session, char *data)
/* you could have passed NULL instead of on_dtmf to get this exact behaviour (copy the digits to buf and stop playing) /* you could have passed NULL instead of on_dtmf to get this exact behaviour (copy the digits to buf and stop playing)
but you may want to pass the function if you have something cooler to do... but you may want to pass the function if you have something cooler to do...
*/ */
status = switch_ivr_play_file(session, data, NULL, on_dtmf, buf, sizeof(buf)); status = switch_ivr_play_file(session, NULL, data, NULL, on_dtmf, buf, sizeof(buf));
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
switch_channel_hangup(channel); switch_channel_hangup(channel);
......
...@@ -64,7 +64,7 @@ static void playback_function(switch_core_session *session, char *data) ...@@ -64,7 +64,7 @@ static void playback_function(switch_core_session *session, char *data)
channel = switch_core_session_get_channel(session); channel = switch_core_session_get_channel(session);
assert(channel != NULL); assert(channel != NULL);
if (switch_ivr_play_file(session, file_name, timer_name, on_dtmf, NULL, 0) != SWITCH_STATUS_SUCCESS) { if (switch_ivr_play_file(session, NULL, file_name, timer_name, on_dtmf, NULL, 0) != SWITCH_STATUS_SUCCESS) {
switch_channel_hangup(channel); switch_channel_hangup(channel);
} }
......
...@@ -117,6 +117,8 @@ struct private_object { ...@@ -117,6 +117,8 @@ struct private_object {
char call_id[50]; char call_id[50];
int ssrc; int ssrc;
char last_digit; char last_digit;
unsigned int dc;
time_t last_digit_time;
switch_mutex_t *rtp_lock; switch_mutex_t *rtp_lock;
switch_queue_t *dtmf_queue; switch_queue_t *dtmf_queue;
char out_digit; char out_digit;
...@@ -480,6 +482,8 @@ static switch_status exosip_answer_channel(switch_core_session *session) ...@@ -480,6 +482,8 @@ static switch_status exosip_answer_channel(switch_core_session *session)
struct private_object *tech_pvt; struct private_object *tech_pvt;
switch_channel *channel = NULL; switch_channel *channel = NULL;
assert(session != NULL);
channel = switch_core_session_get_channel(session); channel = switch_core_session_get_channel(session);
assert(channel != NULL); assert(channel != NULL);
...@@ -554,11 +558,24 @@ static switch_status exosip_read_frame(switch_core_session *session, switch_fram ...@@ -554,11 +558,24 @@ static switch_status exosip_read_frame(switch_core_session *session, switch_fram
int duration = (packet[2]<<8) + packet[3]; int duration = (packet[2]<<8) + packet[3];
char key = switch_rfc2833_to_char(packet[0]); char key = switch_rfc2833_to_char(packet[0]);
if (duration && end && key != tech_pvt->last_digit) { /* SHEESH.... Curse you RFC2833 inventors!!!!*/
char digit_str[] = {key, 0}; if ((time(NULL) - tech_pvt->last_digit_time) > 2) {
switch_channel_queue_dtmf(channel, digit_str); tech_pvt->last_digit = 0;
tech_pvt->last_digit = key; tech_pvt->dc = 0;
} }
if (duration && end) {
if (key != tech_pvt->last_digit) {
char digit_str[] = {key, 0};
time(&tech_pvt->last_digit_time);
switch_channel_queue_dtmf(channel, digit_str);
}
if (++tech_pvt->dc >= 3) {
tech_pvt->last_digit = 0;
tech_pvt->dc = 0;
} else {
tech_pvt->last_digit = key;
}
}
} }
if (globals.supress_telephony_events && payload != tech_pvt->payload_num) { if (globals.supress_telephony_events && payload != tech_pvt->payload_num) {
......
...@@ -138,7 +138,7 @@ switch_status sndfile_file_open(switch_file_handle *handle, char *path) ...@@ -138,7 +138,7 @@ switch_status sndfile_file_open(switch_file_handle *handle, char *path)
handle->format = context->sfinfo.format; handle->format = context->sfinfo.format;
handle->sections = context->sfinfo.sections; handle->sections = context->sfinfo.sections;
handle->seekable = context->sfinfo.seekable; handle->seekable = context->sfinfo.seekable;
handle->speed = 0;
handle->private_info = context; handle->private_info = context;
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
...@@ -153,15 +153,17 @@ switch_status sndfile_file_close(switch_file_handle *handle) ...@@ -153,15 +153,17 @@ switch_status sndfile_file_close(switch_file_handle *handle)
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
switch_status sndfile_file_seek(switch_file_handle *handle, unsigned int *cur_sample, unsigned int samples, int whence) switch_status sndfile_file_seek(switch_file_handle *handle, unsigned int *cur_sample, int64_t samples, int whence)
{ {
sndfile_context *context = handle->private_info; sndfile_context *context = handle->private_info;
if (!handle->seekable) { if (!handle->seekable) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "File is not seekable\n");
return SWITCH_STATUS_NOTIMPL; return SWITCH_STATUS_NOTIMPL;
} }
*cur_sample = (unsigned int) sf_seek(context->handle, samples, whence); *cur_sample = (unsigned int) sf_seek(context->handle, samples, whence);
handle->pos = *cur_sample;
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
......
...@@ -138,3 +138,12 @@ SWITCH_DECLARE(int) switch_buffer_write(switch_buffer *buffer, void *data, size_ ...@@ -138,3 +138,12 @@ SWITCH_DECLARE(int) switch_buffer_write(switch_buffer *buffer, void *data, size_
//printf("i %d = %d\n", datalen, buffer->used); //printf("i %d = %d\n", datalen, buffer->used);
return (int) buffer->used; return (int) buffer->used;
} }
SWITCH_DECLARE(void) switch_buffer_zero(switch_buffer *buffer)
{
assert(buffer != NULL);
assert(buffer->data != NULL);
buffer->used = 0;
}
...@@ -416,7 +416,7 @@ SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, voi ...@@ -416,7 +416,7 @@ SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, voi
return fh->file_interface->file_write(fh, data, len); return fh->file_interface->file_write(fh, data, len);
} }
SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, unsigned int samples, SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, int64_t samples,
int whence) int whence)
{ {
return fh->file_interface->file_seek(fh, cur_pos, samples, whence); return fh->file_interface->file_seek(fh, cur_pos, samples, whence);
......
...@@ -220,6 +220,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio ...@@ -220,6 +220,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio
} }
SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
switch_file_handle *fh,
char *file, char *file,
char *timer_name, char *timer_name,
switch_dtmf_callback_function dtmf_callback, switch_dtmf_callback_function dtmf_callback,
...@@ -230,24 +231,27 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, ...@@ -230,24 +231,27 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
short abuf[960]; short abuf[960];
char dtmf[128]; char dtmf[128];
int interval = 0, samples = 0; int interval = 0, samples = 0;
size_t len = 0, ilen = 0; size_t len = 0, ilen = 0, olen = 0;
switch_frame write_frame; switch_frame write_frame;
switch_timer timer; switch_timer timer;
switch_core_thread_session thread_session; switch_core_thread_session thread_session;
switch_codec codec; switch_codec codec;
switch_memory_pool *pool = switch_core_session_get_pool(session); switch_memory_pool *pool = switch_core_session_get_pool(session);
switch_file_handle fh;
char *codec_name; char *codec_name;
int x; int x;
int stream_id; int stream_id;
switch_status status = SWITCH_STATUS_SUCCESS; switch_status status = SWITCH_STATUS_SUCCESS;
switch_file_handle lfh;
memset(&fh, 0, sizeof(fh));
if (!fh) {
fh = &lfh;
memset(fh, 0, sizeof(lfh));
}
channel = switch_core_session_get_channel(session); channel = switch_core_session_get_channel(session);
assert(channel != NULL); assert(channel != NULL);
if (switch_core_file_open(&fh, if (switch_core_file_open(fh,
file, file,
SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT,
switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
...@@ -261,27 +265,27 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, ...@@ -261,27 +265,27 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
write_frame.buflen = sizeof(abuf); write_frame.buflen = sizeof(abuf);
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s %dhz %d channels\n", file, fh.samplerate, fh.channels); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s %dhz %d channels\n", file, fh->samplerate, fh->channels);
interval = 20; interval = 20;
samples = (fh.samplerate / 50) * fh.channels; samples = (fh->samplerate / 50) * fh->channels;
len = samples * 2; len = samples * 2;
codec_name = "L16"; codec_name = "L16";
if (switch_core_codec_init(&codec, if (switch_core_codec_init(&codec,
codec_name, codec_name,
fh.samplerate, fh->samplerate,
interval, interval,
fh.channels, fh->channels,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL, pool) == SWITCH_STATUS_SUCCESS) { NULL, pool) == SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activated\n"); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activated\n");
write_frame.codec = &codec; write_frame.codec = &codec;
} else { } else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activation Failed %s@%dhz %d channels %dms\n", switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activation Failed %s@%dhz %d channels %dms\n",
codec_name, fh.samplerate, fh.channels, interval); codec_name, fh->samplerate, fh->channels, interval);
switch_core_file_close(&fh); switch_core_file_close(fh);
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
...@@ -289,12 +293,12 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, ...@@ -289,12 +293,12 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
if (switch_core_timer_init(&timer, timer_name, interval, samples, pool) != SWITCH_STATUS_SUCCESS) { if (switch_core_timer_init(&timer, timer_name, interval, samples, pool) != SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer failed!\n"); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer failed!\n");
switch_core_codec_destroy(&codec); switch_core_codec_destroy(&codec);
switch_core_file_close(&fh); switch_core_file_close(fh);
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer success %d bytes per %d ms!\n", len, interval); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer success %d bytes per %d ms!\n", len, interval);
} }
write_frame.rate = fh.samplerate; write_frame.rate = fh->samplerate;
if (timer_name) { if (timer_name) {
/* start a thread to absorb incoming audio */ /* start a thread to absorb incoming audio */
...@@ -306,10 +310,11 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, ...@@ -306,10 +310,11 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
ilen = samples; ilen = samples;
while (switch_channel_get_state(channel) == CS_EXECUTE) { while (switch_channel_get_state(channel) == CS_EXECUTE) {
int done = 0; int done = 0;
int do_speed = 1;
if (dtmf_callback || buf) { int last_speed = -1;
if (dtmf_callback || buf) {
/* /*
dtmf handler function you can hook up to be executed when a digit is dialed during playback dtmf handler function you can hook up to be executed when a digit is dialed during playback
if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. if you return anything but SWITCH_STATUS_SUCCESS the playback will stop.
...@@ -330,18 +335,77 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, ...@@ -330,18 +335,77 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
} }
} }
switch_core_file_read(&fh, abuf, &ilen); if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
memset(abuf, 0, ilen * 2);
} else if (fh->audio_buffer && (switch_buffer_inuse(fh->audio_buffer) > (ilen * 2))) {
switch_buffer_read(fh->audio_buffer, abuf, ilen * 2);
olen = ilen;
do_speed = 0;
} else {
olen = ilen;
switch_core_file_read(fh, abuf, &olen);
}
if (done || ilen <= 0) { if (done || olen <= 0) {
break; break;
} }
write_frame.datalen = ilen * 2; if (fh->speed > 2) {
write_frame.samples = (int) ilen; fh->speed = 2;
#ifdef SWAP_LINEAR } else if(fh->speed < -2) {
fh->speed = -2;
}
if (fh->audio_buffer && last_speed > -1 && last_speed != fh->speed) {
switch_buffer_zero(fh->audio_buffer);
}
if (fh->speed && do_speed) {
float factor = 0.25 * abs(fh->speed);
unsigned int newlen, supplement, step;
short *bp = write_frame.data;
int wrote = 0;
if (!fh->audio_buffer) {
switch_buffer_create(fh->memory_pool, &fh->audio_buffer, SWITCH_RECCOMMENDED_BUFFER_SIZE);
}
supplement = (int) (factor * olen);
newlen = (fh->speed > 0) ? olen - supplement : olen + supplement;
step = (fh->speed > 0) ? (newlen / supplement) : (olen / supplement);
while ((wrote + step) < newlen) {
switch_buffer_write(fh->audio_buffer, bp, step * 2);
wrote += step;
bp += step;
if (fh->speed > 0) {
bp++;
} else {
float f;
short s;
f = *bp + *(bp+1) + *(bp-1);
f /= 3;
s = (short) f;
switch_buffer_write(fh->audio_buffer, &s, 2);
wrote++;
}
}
if (wrote < newlen) {
unsigned int r = newlen - wrote;
switch_buffer_write(fh->audio_buffer, bp, r*2);
wrote += r;
}
last_speed = fh->speed;
continue;
}
write_frame.datalen = olen * 2;
write_frame.samples = (int) olen;
#if __BYTE_ORDER == __BIG_ENDIAN
switch_swap_linear(write_frame.data, (int) write_frame.datalen / 2); switch_swap_linear(write_frame.data, (int) write_frame.datalen / 2);
#endif #endif
for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) { for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n"); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n");
...@@ -366,7 +430,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, ...@@ -366,7 +430,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
} }
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n"); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n");
switch_core_file_close(&fh); switch_core_file_close(fh);
switch_core_codec_destroy(&codec); switch_core_codec_destroy(&codec);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论