Skip to content

Commit 36a5b42

Browse files
committed
chore(ci): blog sync
1 parent 4ac743c commit 36a5b42

4 files changed

+504
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
title: JS二进制数据的处理:浅析Blob、ArrayBuffer、File、Buffer
3+
date: 2023-10-24T06:27:57Z
4+
summary:
5+
tags: ["JavaScript"]
6+
---
7+
8+
### 前言
9+
最近一直在对接媒体相关的需求,流媒体播放、语音输入之类的,踩了很多坑,总结下来是自己对于二进制数据的处理并不是很熟悉,今天抽空整理了一下相关二进制处理对象的使用和他们互相之间的转化。
10+
11+
### Blob
12+
[blob](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob/Blob)全称是 _Binary Large Object_ 表示一个不可变,原始类型的类文件对象,它可以是图片、音频、视频等,它的数据可以按照文本或二进制的格式进行读取。
13+
14+
blob()包含两个参数
15+
+ array 是一个由[ArrayBuffer](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), [ArrayBufferView](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), [Blob](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob), [DOMString](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String) 等对象构成的 [Array](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array) ,或者其他类似对象的混合体,它将会被放进 [Blob](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob)。DOMStrings 会被编码为 UTF-8。
16+
+ options 是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:
17+
18+
- type,默认值为 "",它代表了将会被放入到 blob 中的数组内容的 MIME 类型。
19+
- endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持 blob 中保存的结束符不变 非标准
20+
21+
我们可以尝试构造一个`domString``Blob`
22+
```
23+
let array = ['<a id="a"><b id="b">hey!</b></a>']
24+
const blob = new Blob([array],"text/html")
25+
```
26+
当然也可以是其他的[MIME类型](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types)
27+
28+
29+
### Flie
30+
`File`继承了`Blob`的所有属性,可以看成一个特殊的`Blob`,且可以用在任意的 Blob 类型的 context 中。比如说, [FileReader](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader), [URL.createObjectURL()](https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL_static), [createImageBitmap()](https://developer.mozilla.org/en-US/docs/Web/API/createImageBitmap), 及 [XMLHttpRequest.send()](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest#send()) 都能处理 Blob 和 File。
31+
32+
从常见的来看,我们通过`input`标签上传一个文件,得到的就是一个`File`
33+
34+
35+
### ArrayBuffer
36+
`ArrayBuffer` 对象用来表示通用的、固定长度的原始二进制数据缓冲区。
37+
它是一个字节数组,通常在其他语言中称为“byte array”。你不能直接操作 ArrayBuffer 中的内容;而是要通过[类型化数组对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)[DataView](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/DataView) 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
38+
39+
[ArrayBuffer()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/ArrayBuffer) 构造函数创建一个以字节为单位的给定长度的新 ArrayBuffer。你也可以从现有的数据(例如,从 [Base64](https://developer.mozilla.org/zh-CN/docs/Glossary/Base64) 字符串或者[从本地文件](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/readAsArrayBuffer))获取数组缓冲区。
40+
41+
#### 使用DataView操作
42+
```
43+
// 新建一个buffer
44+
const buffer = new ArrayBuffer(16)
45+
const dataView = new DataView(buffer)
46+
// 向buffer中放入一个4字节的数据 1
47+
dataView.setInt32(0, 1)
48+
49+
console.log(dataView);
50+
```
51+
可以打印出以下结果
52+
![image](https://github.com/coderInwind/inwindBlog/assets/91716457/62838808-ed53-4f3d-97fd-154e8d89f210)
53+
这就是基本的使用,我们可以设置8-bit、16-bit、32-bit、64-bit的写入和读取,设置写入的偏移量,需要注意的是64-bit数据的类型得是大数(bigint)。
54+
55+
#### 使用TypedArray操作
56+
```
57+
const buffer = new ArrayBuffer(16)
58+
59+
const typedArray = new Int16Array(buffer)
60+
61+
typedArray[0] = 16
62+
63+
console.log(typedArray);
64+
```
65+
可以打印出以下结果
66+
![image](https://github.com/coderInwind/inwindBlog/assets/91716457/f7ab52c4-2eb7-4397-91a4-87cfd28c9b84)
67+
68+
69+
70+
71+
72+
73+
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: 浏览器调用麦克风权限实现语音输入功能
3+
date: 2023-10-19T10:12:35Z
4+
summary:
5+
tags: ["WebAPI"]
6+
---
7+
8+
### 前言
9+
今天收到一个需求:在一个聊天室场景中,除了用户除了打字输入,还要求能语音输入,实现这个需求我使用到了浏览器的相关的API、阿里云的语音转换API。
10+
![image](https://github.com/coderInwind/inwindBlog/assets/91716457/a1fccca7-bbdb-4656-903a-0e876312e8ee)
11+
12+
### 采集音频
13+
首先我们得采集到用户说的话,实现这个得使用到两个浏览器的API,第一个是<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia" target="_blank">MediaDevices.getUserMedia()</a> `MediaDevices.getUserMedia()`会提示用户给予使用媒体输入的许可,媒体输入会产生一个[MediaStream](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaStream),里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D 转换器等等),也可能是其他轨道类型。
14+
15+
通俗的来讲,就是可以拉取获取用户媒体权限的弹框,在用户通用后获取一个流。值得注意的是:在实现的过程中这个弹框的触发在各个浏览器中是不同的,在苹果的safari浏览器中获取权限框被用户拒绝后依旧能再次触发,但在某些安卓浏览器中权限框被拒绝后就会维持拒绝状态不会被触发;第二,获取权限和用户语音输入的时机最好错开,作者要实现的交互是按住进行语音输入,松开发送,但某些安卓浏览器在触发获取权限弹窗时会将`touchend`事件的图层覆盖掉,使之不能触发。需要注意的是这个API想要使用有诸多限制,首先只能在https协议下使用,其次使用的设备要有相关的媒体硬件,最后就是需要授权了。
16+
```
17+
navigator.mediaDevices
18+
.getUserMedia({
19+
audio: true,
20+
})
21+
.then((res) => {
22+
speechState.stream = res;
23+
})
24+
.catch(() => {
25+
Toast("抱歉,我们无法获取您的语音权限,无法使用语音输入功能");
26+
})
27+
```
28+
29+
第二步,我们可以通过[MediaRecorder](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder)进行录制:
30+
```
31+
//创建实例
32+
speechState.recordInstance = new MediaRecorder(speechState.stream);
33+
// 录制事件
34+
speechState.recordInstance.ondataavailable = (event) => {
35+
if (event.data.size > 0) {
36+
// 将每次返回的二进制数据存入数组
37+
chunks.push(event.data);
38+
}
39+
};
40+
// 开始事件
41+
speechState.recordInstance.start();
42+
43+
// 停止事件
44+
// 将获取到的二进制数据进行合并,然后转换为文字
45+
speechState.recordInstance.onstop = () => {
46+
const audioBlob = new Blob(chunks, { type: "audio/wav" });
47+
const formData = new FormData();
48+
formData.append("file", audioBlob);
49+
voice2Text(formData);
50+
};
51+
```
52+
53+
54+
### 转化音频
55+
这一步我们选用了阿里云的语音转化接口,但作者在写的时候没有弄清楚阿里云接口所接收的格式,所以卡了很久:
56+
```
57+
// 上传语音
58+
const voice2Text = (payload) => {
59+
Toast.loading({
60+
message: "加载中...",
61+
forbidClick: true,
62+
});
63+
64+
arsVoiceSynthesis(payload).then((res) => {
65+
if (res.data.code == 500) {
66+
Toast.fail("语音处理失败,请重试");
67+
} else {
68+
speechState.result = res.result;
69+
chatState.messageList.push({
70+
message: res.data.message,
71+
isSelf: true,
72+
});
73+
scrollToBottom();
74+
sendMessage(res.data.message);
75+
}
76+
77+
Toast.clear();
78+
});
79+
};
80+
```
81+
值得注意的是,阿里云的接口接收的数据类型有不同的要求,分别是`pcm``wav`格式,如何不注意类型可能会解析不出来,他们的区别我整理如下:
82+
83+
* PCM
84+
PCM(Pulse Code Modulation----脉码调制录音)。所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录。PCM信号是由[1][0]等符号构成的数字信号,而未经过任何编码和压缩处理。与模拟信号比,它不易受传送系统的杂波及失真的影响。动态范围宽,可得到音质相当好的影响效果。
85+
86+
* WAV
87+
wav是一种无损的音频文件格式,WAV符合 PIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。
88+
89+
简单的来说,PCM 就是比 WAV 少一个文件头,因此你可以将他们互相转化。
90+
91+
### 发送音频
92+
其实到这里我们的功能差不多已经实现,接下来就是通过`WebSocket`发送,我就不多赘述了。
93+
94+
95+
### 总结
96+
这个需求完成的过程中的三个重点分别是:不同浏览器获取权限的适配、语音流的获取处理、不同语音格式的区分和转化。
97+
98+
99+
100+
101+

0 commit comments

Comments
 (0)