html5采集pcm音频通过websocket发送到服务端

1.html中引用该js文件
<script language=javascript src='/js/recorder.js'></script>

该文件下载地址:https://download.csdn.net/download/linyibin_123/86052295

2.js核心代码
var TalkRec = null;
var TalkChunck = null;
var TalkChuncks = null;
var TalkWS = null;

//开始对讲
function StartTalk (nSamplerate, url) { //nSamplerate:采样率 url:websocket要推送的url
	var bsuc = true;
	SpeekStop ();
	TalkRec = Recorder({
		type:"pcm",
		bitRate:16,
		onProcess:function(buffers, powerLevel, bufferDuration, bufferSampleRate){
			TalkSend(buffers, bufferSampleRate, false);
		}
	});
	TalkRec.open(function(){
		TalkChuncks = null; // 重置缓冲区
		// init webscoket
		TalkWS = new WebSocket(url);
		TalkWS.onopen = function () {
			console.log("ws handshake success.");
			if (TalkWS.readyState == 1) {
				TalkRec.start(); // 开始录制数据
			}
		}
		TalkWS.onerror = function (err) {
		    console.log(err);
		}
		TalkWS.onclose=function(e){
		    console.log("ws close success");
		};
	}, function(e, isUserNotAllow){
		bsuc = false;
	});
	return bsuc;
}

//停止对讲
function StopTalk () {
	// close speek record
	if(TalkRec)  {
		TalkRec.close();
		TalkRec = null;
	}
	// close websocket after send.
	TalkSend([], 0, true);
}

//数据发送
function TalkSend(buffers, bufferSampleRate, isClose) {
	if(TalkChuncks == null){
		TalkChunck = null;
		console.logTalkChuncks = [];
	};
	var pcm = [];
	var pcmSampleRate = 0;
	if(buffers.length > 0) {
		//借用SampleData函数进行数据的连续处理,采样率转换是顺带的,得到新的pcm数据
		var chunk = Recorder.SampleData(buffers, bufferSampleRate, 8000, TalkChunck);
		//清理已处理完的缓冲数据,释放内存以支持长时间录音,最后完成录音时不能调用stop,因为数据已经被清掉了
		for(var i = TalkChunck?TalkChunck.index:0; i<chunk.index; i++){
			buffers[i] = null;
		};
		//此时的chunk.data就是原始的音频16位pcm数据(小端LE),直接保存即为16位pcm文件
		TalkChunck = chunk;
		pcm = chunk.data;
		pcmSampleRate = chunk.sampleRate;
		if(pcmSampleRate != 8000) {
			throw new Error("error pcmSampleRate:"+pcmSampleRate+"!=8000");
		}
		console.log("pcm.length="+pcm.length);
	}
	if(pcm && pcm.length > 0){
		//将pcm数据丢进缓冲,凑够一帧发送,缓冲内的数据可能有多帧,循环切分发送
		TalkChuncks.push({pcm:pcm,pcmSampleRate:pcmSampleRate});
	}

	//从缓冲中切出一帧数据
	var chunkSize = speek_fsize/2;//8位时需要的采样数和帧大小一致,16位时采样数为帧大小的一半
	var pcm = new Int16Array(chunkSize), pcmSampleRate=0;
	var pcmOK = false, pcmLen=0;
	for1:for(var i1=0; i1<TalkChuncks.length; i1++){
		var chunk = TalkChuncks[i1];
		pcmSampleRate = chunk.pcmSampleRate;
		
		for(var i2=chunk.offset||0;i2<chunk.pcm.length;i2++){
			pcm[pcmLen] = chunk.pcm[i2];
			pcmLen++;
			//满一帧了,清除已消费掉的缓冲
			if(pcmLen==chunkSize){
				pcmOK = true;
				chunk.offset = i2+1;
				for(var i3=0; i3<i1; i3++){
					TalkChuncks.splice(0, 1);
				};
				break for1;
			}
		}
	};

	//缓冲的数据不够一帧时,不发送 或者 是结束了
	if(!pcmOK) {
		//结束发送,销毁websocket.
		if(isClose && TalkWS) {
			// close websocket
			TalkWS.close();
			TalkWS = null;
		}
		return;
	}
	// 发送一帧数据
	var blob = new Blob([pcm], {type:"audio/pcm"});
	if(TalkWS) { // send to server
		TalkWS.send(blob);
	}
	//循环调用,继续切分缓冲中的数据帧,直到不够一帧
	TalkSend([], 0, isClose);
}
3.websocket服务器端部分代码参考
//websocket接收
static int on_ws_talk(void* param, int opcode, const void* data, size_t bytes, int flags)
{
	http_websocket_t *ws = (http_websocket_t*)param;
	http_session_t *ss = (http_session_t *)ws->session;

	// keepalive for websocket. must keepalive by client.
	ss->activity = ffss_time_get_sec_bootup() + (CONN_TIMEOUT); // to second.
	
	switch (opcode)
	{   
    case WEBSOCKET_OPCODE_BINARY:
		talk_play_audio(data, bytes);  
        break;

	default:
		websocket_send(ws, opcode, data, bytes);
		break;
	}
	
	return 0;
}

//音频数据拷贝和发送到播放端
static void talk_play_audio(const void* data, size_t bytes)
{
	//data copy
    audio_msg_t msgbuf = {0};
    msgbuf.lenth = bytes/2;
    memcpy(msgbuf.buf, (short*)data, msgbuf.lenth);
    msgbuf.mtype = CGI_AUDIO_MSGTYPE;

	//send message to ffavs for play  
    int msgid = ffstream_audio_get_msgid();
	ffss_msg_snd_nowait(msgid, &msgbuf, sizeof(audio_msg_t));
	if(ffss_msg_rcv_wait_timeout(msgid, &msgbuf, sizeof(audio_msg_t), 
		CGI_AUDIO_MSGACK, 500) == -1)
	{
		printf("Websocket Session Push audio timeout");
	}
	else
	{
		printf("Websocket Session Push audio success3 bytes:%d", bytes);	
	}
}

本文参考

https://xiangyuecn.gitee.io/recorder/assets/%E5%B7%A5%E5%85%B7-%E4%BB%A3%E7%A0%81%E8%BF%90%E8%A1%8C%E5%92%8C%E9%9D%99%E6%80%81%E5%88%86%E5%8F%91Runtime.html?jsname=teach.realtime.encode_transfer_frame_pcm

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浅笑一斤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值