Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

websocket pcm webaudio 记录 #6

Open
pkjy opened this issue Jun 13, 2020 · 8 comments
Open

websocket pcm webaudio 记录 #6

pkjy opened this issue Jun 13, 2020 · 8 comments

Comments

@pkjy
Copy link
Owner

pkjy commented Jun 13, 2020

经验

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
Copy link

ankye commented Jan 5, 2023

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

@pkjy
Copy link
Owner Author

pkjy commented Jan 5, 2023

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

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

@ankye
Copy link

ankye commented Jan 5, 2023

我是ios13.7

@pkjy
Copy link
Owner Author

pkjy commented Jan 6, 2023

我是ios13.7

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

@ankye
Copy link

ankye commented Jan 6, 2023

121672971272_ pic

@ankye
Copy link

ankye commented Jan 6, 2023

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

@Snow5z
Copy link

Snow5z commented Sep 21, 2024

使用websocket链接形式,onend 事件会错误触发多次,这个怎么知道什么时候是真的播放结束了呀

@pkjy
Copy link
Owner Author

pkjy commented Nov 6, 2024

使用websocket链接形式,onend 事件会错误触发多次,这个怎么知道什么时候是真的播放结束了呀

建议通过websocket连接断开来判断,如果播放结束,服务端断开此次连接,然后客户端监听close来处理。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants