websocket pcm webaudio 记录
Opened this issue · 6 comments
经验
PCM介绍
将音频数字化,其实就是将声音数字化。最常见的方式是透过脉冲编码调制PCM(Pulse Code Modulation) 。运作原理如下。首先我们考虑声音经过麦克风,转换成一连串电压变化的信号。要将这样的信号转为 PCM 格式的方法,是使用三个参数来表示声音,它们是:声道数、采样位数和采样频率。
-
采样频率:即取样频率,指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。由于人耳的分辨率很有限,太高的频率并不能分辨出来。在16位声卡中有22KHz、44KHz等几级,其中,22KHz相当于普通FM广播的音质,44KHz已相当于CD音质了,目前的常用采样频率都不超过48KHz。
-
采样位数:即采样值或取样值(就是将采样样本幅度量化)。它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。
-
声道数:很好理解,有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的也处理成两个喇叭输出同一个声道的声音),立体声的pcm可以使两个喇叭都发声(一般左右声道有分工) ,更能感受到空间效果。
图中的黑色曲线表示的是pcm文件录制的自然界的声波,红色曲线表示的是pcm文件输出的声波,横坐标便是采样频率;纵坐标便是采样位数。这几幅图中的格子从左到右,逐渐加密,先是加大横坐标的密度,然后加大纵坐标的密度。显然,当横坐标的单位越小即两个采样时刻的间隔越小,则越有利于保持原始声音的真实情况,换句话说,采样的频率越大则音质越有保证;同理,当纵坐标的单位越小则越有利于音质的提高,即采样的位数越大越好。
计算机中采样位数一般有8位和16位之分,但有一点请大家注意,8位不是说把纵坐标分成8份,而是分成2的8次方即256份; 同理16位是把纵坐标分成2的16次方65536份;如下图:
存储量=(采样频率X采样位数X声道)X时间/8(单位:字节数)
safari 的特殊情况
- safari 播放支持的采样率最低为 22050 Hz 。简直坑爹,相关链接 safari-webkitaudiocontext-createbuffer-api-raises-notsupportederror-exception
- safari 录音的输入采样率实测为48000, iphone11, 虽然设置了其他值,但是实际出来的还是48000.
- safari 同时只允许4个进行的AudioContext实例,第五个会报错。
更多坑在standardized-audio-context这里面可以看到
AudioContext 限制
Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first
在没有用户交互的情况下,直接进行音频的播放是被浏览器禁止的。可以在有交互的时候就开始音频的播放,然后采取禁音的方法来处理。
g711编码
G711的打包周期分为10ms,20ms,30ms,sample rate是8000,速率是64kbit/s
64kbits,意味着每秒发送64000比特
那么10ms发送= 64000 * (10/1000) = 640 比特 = 80 字节
那么10ms的包 = 80字节
20ms = 160 字节
30ms = 240 字节
那么64kbits 如何来的?
因为采样周期为8000,那么意味着1秒8000个采样,每次采样站8个比特
那么 8000*8 = 64000bit/s = 64kbit/s
接下来计算timestamp的增长量,公式如下:
两帧之间RTP timestamp的增量 = 时钟频率 / 帧率
同样以10ms的G711举例:
10ms的G711帧率 = 100,意味着每秒发送100帧
那么10ms的timestamp增量 = 8000/100 = 80
20ms的timestamp增量 = 160
30ms的timestamp增量 = 240
sdp
m=audio 1234 RTP/AVP 0
a=rtpmap:0 pcma/8000/1
a=framerate:25
c=IN IP4 172.18.168.45
1.m=是媒体级会话的开始处,audio:媒体类型 ; 1234:端口号 ;RTP/AVP:传输协议 ;0:rtp头中的payload格式
2.a=rtpmap:证明是动态绑定的进一步说明 ;0:rtp头中的payload格式;pcma:编码名 ;8000:采样频率;1:单声道
注意:g711有两种编码类型,另一种是pcmu
3.a=framerate:25 指1s播放几个rtp包,单位帧每秒,倒数为一个rtp包承载的数据播放的时间,单位s
8000/25=320 表示每个时间戳增量值 每个rtp包的g711数据大小
4.c=:媒体链接信息;IN:网络类型一般为IN;IP4:地址类型一般为IP4;后面是IP地址(注意是VLC所在的IP地址,不是发送方的IP)
更多sdp介绍参考
websocket 传输二进制数据
websocket可以通过binaryType
属性来选择数据传输的二进制格式,blob
会返回Blob
类型的原始二进制数据,arraybuffer
会返回ArrayBuffer
类型的二进制数据。binaryType参考文档
- 简单来说,
Blob
是一个类文件(file-like)对象,与File
api结合使用。例如文件地址可以通过URL.createObjectURL()
来转换成一个本地的url地址,包括audio
和video
,他们除了src属性之外,还有srcObject
属性,可以直接将Blob
地址传给srcObject
属性,同样也能正常播放音频与视频。 ArrayBuffer
是一个提供给js处理二进制数据的数据类型,ArrayBuffer
本身是只读的,里面包含了各种类型的TypedArray
,是支持直接修改数据的,或者通过DataView
视图来修改ArrayBuffer
。完整的TypedArray参考MDN文档。
如果websocket选择blob类型的话,则需要用File apiFileReader.readAsArrayBuffer()
把数据读为ArrayBuffer
。
相关链接
默认demo在ios上没有声音 https://pkjy.github.io/pcm-player/
默认demo在ios上没有声音 https://pkjy.github.io/pcm-player/
ios15.7 刚才测试播放正常哦
我是ios13.7
我是ios13.7
ios 14.2 也测试了,没问题。 手头上没有13.7,没法测试... 我开了eruda方便调试,你可以看下播放时,控制台是否有什么报错。
我试了,要加上 navigator.mediaDevices.getUserMedia({ audio: true, video: false }) 这个麦克风的权限才能有声音,很神奇,也有点崩溃