需求:freeswitch放音和录音使用远程文件,不可再本地生成文件,不可挂载
1.前期思路 放音:在文件内存处将文件变成语音流并上传至freeswitch内存中
录音:使用远程写入文件的方式,重写写入函数
freeswitch模块mod_shout支持流式录放音
https://freeswitch.org/confluence/display/FREESWITCH/mod_shout
icecast流媒体服务器
修改一下配置 fileroot是自行修改代码后加的配置项,存储录音文件的路径
放音文件放在webroot下
0.0.0.0
/home/umg/icecast/log
/usr/local/share/icecast/web
/home/umg/icecastfile
/usr/local/share/icecast/admin
修改代码 source.c
.
//if (mountinfo && mountinfo->dumpfile) //huangzl
{
char *filename = source->dumpfilename;
ice_config_t *config = config_get_config_unlocked ();
source->dumpfilename = malloc(strlen(config->fileroot_dir) + strlen(source->mount)+1);
sprintf(source->dumpfilename, "%s%s", config->fileroot_dir, source->mount);
//source->dumpfilename = strdup (mountinfo->dumpfile);
free (filename);
}
//else
// source->dumpfilename = NULL;
修改代码 cfgfile.c
.
else if (xmlStrcmp (node->name, XMLSTR("fileroot")) == 0) { //huangzl
if (!(temp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1))) {
ICECAST_LOG_WARN("<fileroot> must not be empty.");
continue;
}
if (configuration->fileroot_dir) xmlFree(configuration->fileroot_dir);
configuration->fileroot_dir = temp;
if(configuration->fileroot_dir[strlen(configuration->fileroot_dir)-1] == '/')
configuration->fileroot_dir[strlen(configuration->fileroot_dir)-1] = 0;
}
测试过程中出现4.4-5.4秒中间出现1秒左右空白音,尾部少3秒左右长度
使用版本是:freeswitch1.6.20 修改mod_shout.c
static void *SWITCH_THREAD_FUNC write_stream_thread(switch_thread_t *thread, void *obj)
{
shout_context_t *context = (shout_context_t *) obj;
switch_size_t audio_read = 0;
switch_thread_rwlock_rdlock(context->rwlock);
if (!context->lame_ready) {
lame_init_params(context->gfp);
lame_print_config(context->gfp);
context->lame_ready = 1;
}
while (!(context->err && audio_read == 0)) {
unsigned char mp3buf[20480] = "";
int16_t audio[8192] = { 0 };
int rlen = 0;
long ret = 0;
audio_read = 0;
switch_mutex_lock(context->audio_mutex);
if (context->audio_buffer) {
audio_read = switch_buffer_read(context->audio_buffer, audio, sizeof(audio));
} else {
context->err++;
}
switch_mutex_unlock(context->audio_mutex);
//error_check();
if (!audio_read) {
//audio_read = sizeof(audio);
//memset(audio, 255, sizeof(audio));
if(!context->err){
switch_yield(100000);
}
continue;
}
if (context->channels == 2) {
int16_t l[4800] = { 0 };
int16_t r[4800] = { 0 };
int j = 0;
switch_size_t i;
for (i = 0; i < audio_read / 4; i++) {
l[i] = audio[j++];
r[i] = audio[j++];
}
if ((rlen = lame_encode_buffer(context->gfp, l, r, (int)(audio_read / 4), mp3buf, sizeof(mp3buf))) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MP3 encode error %d!\n", rlen);
goto error;
}
} else if (context->channels == 1) {
if ((rlen = lame_encode_buffer(context->gfp, (void *) audio, NULL, (int)(audio_read / sizeof(int16_t)), mp3buf, sizeof(mp3buf))) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MP3 encode error %d!\n", rlen);
goto error;
}
}
if (rlen) {
ret = shout_send(context->shout, mp3buf, rlen);
if (ret != SHOUTERR_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Send error: %s\n", shout_get_error(context->shout));
goto error;
}
} else {
memset(mp3buf, 0, 128);
ret = shout_send(context->shout, mp3buf, 128);
}
}
if(!context->err){
shout_sync(context->shout);
switch_yield(100000); }
}
error:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write Thread Done\n");
switch_thread_rwlock_unlock(context->rwlock);
return NULL;
}
出现新问题 挂断事件需要4秒后才能产生
修改释放时的异步等待
static inline void free_context(shout_context_t *context)
{
.....................................
if (context->shout) {
if (context->gfp) {
unsigned char mp3buffer[8192];
int len;
int16_t blank[2048] = { 0 }, *r = NULL;
int framesize;
if (context->channels == 2) {
r = blank;
}
len = lame_encode_buffer(context->gfp, blank, r, sizeof(blank) / 2, mp3buffer, sizeof(mp3buffer));
if (len) {
ret = shout_send(context->shout, mp3buffer, len);
if (ret == SHOUTERR_SUCCESS) {
//shout_sync(context->shout);
switch_yield(10000);
}
}
framesize = lame_get_framesize(context->gfp);
if ( framesize ) {
while ((len = lame_encode_flush(context->gfp, mp3buffer, sizeof(mp3buffer))) > 0) {
ret = shout_send(context->shout, mp3buffer, len);
if (ret != SHOUTERR_SUCCESS) {
break;
} else {
switch_yield(10000);
//shout_sync(context->shout);
}
}
}
}
shout_close(context->shout);
context->shout = NULL;
}
.....................................
}
icecast的source链接并发限制只有个位数,故将icecast的接收和写文件部分单独提出,解开了并发限制,作为流式录音服务器。