如何使用NodeJS在服务端拼接来自js前端录制的音频?

开发时遇到一个难题,即我想将在前端录制的音频拼接起来,但总遇到一些不明所以的问题,希望有老哥解答。
下面我简单说明一下我遇到的问题:
初始的时候我使用js-audio-recorder这个包来实现在前端录制语音。
因为临时用了百度的语音转写API,采样值固定在16000.

我尝试了好几种方法来拼接我录制的音频,只有一种方法成功了,那就是在前端使用Crunker这个包来实现,因为crunker中用了Web API,所以无法在服务端使用,这样音频数据多的话,延迟太久。

并且crunker还有一个巨大的问题,那就是拼接以后的音频迷之大,我看了一下10k的音频拼接后变成100多k,放大了10倍。

有老哥说这是采样率的问题,我试着调低了一点采样率,音频质量是降了一点,但结果是音频彻底变质,几乎听不清了。

遂放弃Crunker

回到服务端,我使用过AudioSprite这个包,但是官方的示例太少,只能合成音频(叠加),不懂得如何拼接。

然后用了ffmpeg这个包,因为依旧没有找到示例,只能自己尝试但一直报错不明所以。

后来,看到了一种使用stream流直接拼合blob数据的方法,如下:

    //创建一个可写入的地址,使用stream流的方式,将音频数据写入新的地址中
    let filepath =  path.join(__dirname, '../../../','public','uploads')+'/uploads_'+ Math.round(Math.random()*Math.pow(10,16))+'.mp3';
    let dhh = fs.createWriteStream(filepath);  //创建可写入流
    var files = SpeechSequence; //获取音频序列
    // SpeechSequence = files;
    let currentfile; 
    let stream;
    function main(){
        if (!files.length) {
            dhh.end("Done");
            console.log('结束');
            return;
        }
        // 删除文件数组中的第一个元素,并返回第一个元素
        currentfile =  files.shift();
        console.log("下一个拼接:" + currentfile);
        // 获取第一个文件的流
        stream = fs.createReadStream(currentfile);
        // 写入创建好的流文件中
        stream.pipe(dhh, {end: false});
        stream.on('error',function(error){
            console.log('写入错误'+ error);
        })
        // 当前文件写入完成回调
        stream.on("end", function() {
            // console.log(currentfile + ' appended');
            main();
        });
        // console.log("3");
    }
    main();
    console.log("拼接好的音频:" + filepath);

我通过百度语音合成API合成的音频,是可以通过这个方法进行拼接的。

但这个方法并不能拼接我在前端录制的音频,来自前端录制的语音blob通过steam流拼接以后,播放只能播放第一段语音。

补充:
经尝试,原来ffmpeg这个包要自己下载,然后配置系统路径,之前一直用npm安装。
但结果还是一样,合成的语音可以拼接,录制的语音通过audioconcat这个包实现拼接后只能听到第一段。(audioconcat 是使用 ffmpeg进行拼接的)。
所以问题可能是前端录制的音频文件有问题,但我换了个recorder-js这个包来实现依旧无法解决问题。

希望有老哥帮忙,谢谢!

阅读 4.1k
1 个回答

我想,你的问题其实可以从如下4个方面来考虑:

  1. 前端来的数据格式或者说数据流格式
  2. 前端来的多个数据子部分如何管理的
  3. nodejs所在的服务器端有哪些资源
  4. 期望处理结果
    对于1,从你的介绍中可见 已经是多个 统一格式的 音频文件
    对于2,没有看到具体介绍
    对于3,你只介绍了有ffmpeg
    对于4,只能大致了解需要按某种顺序,将1中文件首尾连在一起。

其实在这里用ffmpeg是比较恰当的,因为ffmpeg其实能实现任何支持解码音频格式 向支持的编码格式输出。所有待拼接的文件写入一个文本文件列表(清单文件,命名其实都无所谓),比如filelist.txt

file '1.m4a'
file '2.m4a'
file '3.m4a'

ffmpeg  -f concat -i filelist.txt  <输出文件路径>

就可以完成拼接,这时ffmpeg会自动根据<输出文件路径>中扩展名来推测输出编码,一般扩展名可以是mp3m4awav之类,也可以在<输出文件路径> 前用参数来详细指定输出文件的类型、编码率、采样率等参数。

不过要速度快就需要所谓统一输入和输出文件格式要求啦,这里的统一是指:

  1. 文件原有编码格式受ffmpeg支持(编码和解码都支持)
  2. 采样率统一
  3. 编码率统一
  4. 编码格式统一
    这时的指令变成:

    ffmpeg -y -f concat -i filelist.txt -c copy <输出文件路径>

    因为不涉及重新编码,其实基本上就是数据包的复制,只有metadata中音频总长度和文件大小会发生变化。

你前面尝试中多段拼接只播放第一段的问题就是文件中metadata中音频总长和文件大小没有正确处理,这样播放时就只能播放第一段。

关于ffmpeg拼接音频视频,可以参考:https://trac.ffmpeg.org/wiki/...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题