pkjy/blog

websocket pcm webaudio 记录

Opened this issue · 6 comments

pkjy commented

经验

PCM介绍

将音频数字化,其实就是将声音数字化。最常见的方式是透过脉冲编码调制PCM(Pulse Code Modulation) 。运作原理如下。首先我们考虑声音经过麦克风,转换成一连串电压变化的信号。要将这样的信号转为 PCM 格式的方法,是使用三个参数来表示声音,它们是:声道数、采样位数和采样频率。

  • 采样频率:即取样频率,指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。由于人耳的分辨率很有限,太高的频率并不能分辨出来。在16位声卡中有22KHz、44KHz等几级,其中,22KHz相当于普通FM广播的音质,44KHz已相当于CD音质了,目前的常用采样频率都不超过48KHz。

  • 采样位数:即采样值或取样值(就是将采样样本幅度量化)。它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。

  • 声道数:很好理解,有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的也处理成两个喇叭输出同一个声道的声音),立体声的pcm可以使两个喇叭都发声(一般左右声道有分工) ,更能感受到空间效果。
    image
    image
    image
    图中的黑色曲线表示的是pcm文件录制的自然界的声波,红色曲线表示的是pcm文件输出的声波,横坐标便是采样频率;纵坐标便是采样位数。这几幅图中的格子从左到右,逐渐加密,先是加大横坐标的密度,然后加大纵坐标的密度。显然,当横坐标的单位越小即两个采样时刻的间隔越小,则越有利于保持原始声音的真实情况,换句话说,采样的频率越大则音质越有保证;同理,当纵坐标的单位越小则越有利于音质的提高,即采样的位数越大越好。
    计算机中采样位数一般有8位和16位之分,但有一点请大家注意,8位不是说把纵坐标分成8份,而是分成2的8次方即256份; 同理16位是把纵坐标分成2的16次方65536份;如下图:
    image

存储量=(采样频率X采样位数X声道)X时间/8(单位:字节数)

safari 的特殊情况

  1. safari 播放支持的采样率最低为 22050 Hz 。简直坑爹,相关链接 safari-webkitaudiocontext-createbuffer-api-raises-notsupportederror-exception
  2. safari 录音的输入采样率实测为48000, iphone11, 虽然设置了其他值,但是实际出来的还是48000.
  3. 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地址,包括audiovideo,他们除了src属性之外,还有srcObject属性,可以直接将Blob地址传给srcObject属性,同样也能正常播放音频与视频。
  • ArrayBuffer是一个提供给js处理二进制数据的数据类型,ArrayBuffer本身是只读的,里面包含了各种类型的TypedArray,是支持直接修改数据的,或者通过DataView视图来修改ArrayBuffer完整的TypedArray参考MDN文档
    如果websocket选择blob类型的话,则需要用File api FileReader.readAsArrayBuffer()把数据读为ArrayBuffer

相关链接

1 ArrayBuffer DataView 数据存储方式 字节序处理

ankye commented

默认demo在ios上没有声音 https://pkjy.github.io/pcm-player/

pkjy commented

默认demo在ios上没有声音 https://pkjy.github.io/pcm-player/

ios15.7 刚才测试播放正常哦

ankye commented

我是ios13.7

pkjy commented

我是ios13.7

ios 14.2 也测试了,没问题。 手头上没有13.7,没法测试... 我开了eruda方便调试,你可以看下播放时,控制台是否有什么报错。

ankye commented

121672971272_ pic

ankye commented

我试了,要加上 navigator.mediaDevices.getUserMedia({ audio: true, video: false }) 这个麦克风的权限才能有声音,很神奇,也有点崩溃