专注于freeswitch,智能外呼,webrtc,欢迎联系
在学习这两个源码之前,我们自己先想一下如果是我们自己写,逻辑应该如何实现。
对于playback,肯定是先读取对于的file文件,然后写入到对应的channel
record,肯定是实时读取正在进行的session流,然后写入到对应的文件中
为了更好地学习,我们把git版本切换到历史版本
先看看git 信息
#include <switch.h>
#include <switch_ivr.h>
/* TBD (Lots! there are not very many functions in here lol) */
SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *session,
char *file,
switch_dtmf_callback_function dtmf_callback)
{
switch_channel *channel;
char dtmf[128];
switch_file_handle fh;
switch_frame *read_frame;
switch_codec codec, *read_codec;
char *codec_name;
switch_status status = SWITCH_STATUS_SUCCESS;
memset(&fh, 0, sizeof(fh));
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
read_codec = switch_core_session_get_read_codec(session);
assert(read_codec != NULL);
fh.channels = read_codec->implementation->number_of_channels;
fh.samplerate = read_codec->implementation->samples_per_second;
if (switch_core_file_open(&fh,
file,
SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT,
switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
switch_channel_hangup(channel);
return SWITCH_STATUS_GENERR;
}
switch_channel_answer(channel);
codec_name = "L16";
if (switch_core_codec_init(&codec,
codec_name,
read_codec->implementation->samples_per_second,
read_codec->implementation->microseconds_per_frame / 1000,
read_codec->implementation->number_of_channels,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activated\n");
switch_core_session_set_read_codec(session, &codec);
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activation Failed %s@%dhz %d channels %dms\n",
codec_name, fh.samplerate, fh.channels, read_codec->implementation->microseconds_per_frame / 1000);
switch_core_file_close(&fh);
return SWITCH_STATUS_GENERR;
}
while (switch_channel_get_state(channel) == CS_EXECUTE) {
int len;
if (dtmf_callback) {
/*
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 (switch_channel_has_dtmf(channel)) {
switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
status = dtmf_callback(session, dtmf);
}
if (status != SWITCH_STATUS_SUCCESS) {
break;
}
}
if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) {
break;
}
len = read_frame->datalen / 2;
switch_core_file_write(&fh, read_frame->data, &len);
}
switch_core_session_set_read_codec(session, read_codec);
switch_core_file_close(&fh);
return status;
}
SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session,
char *file,
char *timer_name,
switch_dtmf_callback_function dtmf_callback)
{
switch_channel *channel;
short buf[960];
char dtmf[128];
int interval = 0, samples = 0;
size_t len = 0, ilen = 0;
switch_frame write_frame;
switch_timer timer;
switch_core_thread_session thread_session;
switch_codec codec;
switch_memory_pool *pool = switch_core_session_get_pool(session);
switch_file_handle fh;
char *codec_name;
int x;
int stream_id;
switch_status status = SWITCH_STATUS_SUCCESS;
memset(&fh, 0, sizeof(fh));
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
if (switch_core_file_open(&fh,
file,
SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT,
switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
switch_channel_hangup(channel);
return SWITCH_STATUS_GENERR;
}
switch_channel_answer(channel);
write_frame.data = buf;
write_frame.buflen = sizeof(buf);
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s %dhz %d channels\n", file, fh.samplerate, fh.channels);
interval = 20;
samples = (fh.samplerate / 50) * fh.channels;
len = samples * 2;
codec_name = "L16";
if (switch_core_codec_init(&codec,
codec_name,
fh.samplerate,
interval,
fh.channels,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL, pool) == SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activated\n");
write_frame.codec = &codec;
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activation Failed %s@%dhz %d channels %dms\n",
codec_name, fh.samplerate, fh.channels, interval);
switch_core_file_close(&fh);
return SWITCH_STATUS_GENERR;
}
if (timer_name) {
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_core_codec_destroy(&codec);
switch_core_file_close(&fh);
return SWITCH_STATUS_GENERR;
}
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer success %d bytes per %d ms!\n", len, interval);
}
write_frame.rate = fh.samplerate;
if (timer_name) {
/* start a thread to absorb incoming audio */
for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
switch_core_service_session(session, &thread_session, stream_id);
}
}
ilen = samples;
while (switch_channel_get_state(channel) == CS_EXECUTE) {
int done = 0;
if (dtmf_callback) {
/*
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 (switch_channel_has_dtmf(channel)) {
switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
status = dtmf_callback(session, dtmf);
}
if (status != SWITCH_STATUS_SUCCESS) {
done = 1;
break;
}
}
switch_core_file_read(&fh, buf, &ilen);
if (done || ilen <= 0) {
break;
}
write_frame.datalen = ilen * 2;
write_frame.samples = (int) ilen;
#ifdef SWAP_LINEAR
switch_swap_linear(write_frame.data, (int) write_frame.datalen / 2);
#endif
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) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n");
done = 1;
break;
}
if (done) {
break;
}
}
if (timer_name) {
if ((x = switch_core_timer_next(&timer)) < 0) {
break;
}
} else { /* time off the channel (if you must) */
switch_frame *read_frame;
if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) {
break;
}
}
}
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n");
switch_core_file_close(&fh);
switch_core_codec_destroy(&codec);
if (timer_name) {
/* End the audio absorbing thread */
switch_core_thread_session_end(&thread_session);
switch_core_timer_destroy(&timer);
}
return status;
}
这这块代码非常简单,我们通过学习历史初期的freeswitch代码,来理解各类应用是如何实现的
我们先看录音:
先获取对应的channel和读编码
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
read_codec = switch_core_session_get_read_codec(session);
assert(read_codec != NULL);
打开文件句柄switch_core_file_open
然后初始化 编码switch_core_codec_init
while (switch_channel_get_state(channel) == CS_EXECUTE) {
int len;
if (dtmf_callback) {
/*
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 (switch_channel_has_dtmf(channel)) {
switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
status = dtmf_callback(session, dtmf);
}
if (status != SWITCH_STATUS_SUCCESS) {
break;
}
}
if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) {
break;
}
len = read_frame->datalen / 2;
switch_core_file_write(&fh, read_frame->data, &len);
}
dtmf我们先忽略,通过 while循环判断当前channel状态是不是一直是execute状态
while (switch_channel_get_state(channel) == CS_EXECUTE)
然后读取和写入文件
if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) {
break;
}
len = read_frame->datalen / 2;
switch_core_file_write(&fh, read_frame->data, &len);
最后当channel状态变换之后,释放对应的编码和句柄
switch_core_session_set_read_codec(session, read_codec);
switch_core_file_close(&fh);
再对比一下新版本的freeswitch对应代码。
大体的逻辑没有大的改变,增加了更多的channel 变量判断,以及视频处理,event事件处理, while循环改成了,无限for循环等。
最终还是调用switch_core_file_write进行写入。 只是说年代久远之后,系统越来越复杂,但是从历史版本来学习,还是能有很多收获。
playback也同样,先初始化文件句柄,
循环读取文件,switch_core_file_read(&fh, buf, &ilen);
然后循环写入 switch_core_session_write_frame