简介
TypedArray 是 JavaScript 中的一种数据类型,用于表示一个固定长度的二进制数据缓冲区。它提供了一种有效的方式来操作和处理二进制数据,特别适用于前端音视频业务。本文将介绍 TypedArray 的基本用法以及在前端音视频业务中的几个应用场景。
基本用法
TypedArray 可以表示不同类型的二进制数据,如整数、浮点数等。它包括以下几种类型:
- Int8Array: 8 位带符号整数
- Uint8Array: 8 位无符号整数
- Int16Array: 16 位带符号整数
- Uint16Array: 16 位无符号整数
- Int32Array: 32 位带符号整数
- Uint32Array: 32 位无符号整数
- Float32Array: 32 位浮点数
- Float64Array: 64 位浮点数
以下是创建 TypedArray 的示例代码:
// 创建一个长度为 4 的 Int32Array
const intArray = new Int32Array(4);
// 创建一个包含指定数组的 Uint8Array
const uintArray = new Uint8Array([10, 20, 30, 40]);
// 创建一个共享指定 ArrayBuffer 的 Float32Array
const floatArray = new Float32Array(buffer);
通过访问 TypedArray 实例的属性和方法,我们可以对其中的数据进行读取和修改。例如:
// 修改 Int32Array 中的值
intArray[0] = 42;
// 读取 Uint8Array 中的值
const value = uintArray[2];
前端音视频业务中的应用场景
在前端音频解码过程中,通常需要对原始音频数据进行解析和处理。TypedArray 提供了高效的方式来处理音频样本数据。
例如,有一段音频存储在一个ArrayBuffer中,采样率是44100HZ,位深16bit,单声道。用修改字节的方式把这段音频音量增大一倍,如何做?
- 从ArrayBuffer中获取音频数据。
- 将音频数据解码为PCM格式。
- 遍历PCM数据,并将每个样本乘以2(增大一倍的音量)。
- 如果样本值超出了16位有符号整数的范围(-32768 到 32767),则进行截断或饱和处理。
- 将修改后的PCM数据重新编码为ArrayBuffer。
// 假设您已经有一个名为audioData 的ArrayBuffer
// 创建一个Int16Array来读取和修改音频数据
const dataView = new Int16Array(audioData);
// 计算样本数
const sampleCount = dataView.length;
// 创建一个新的Int16Array来存储修改后的音频数据
const modifiedDataView = new Int16Array(sampleCount);
// 遍历每个样本并增大音量
for (let i = 0; i < sampleCount; i++) {
// 读取样本值
const sample = dataView[i];
// 增大音量一倍
const modifiedSample = sample * 2;
// 截断或饱和处理样本值
const finalSample = Math.max(-32768, Math.min(modifiedSample, 32767));
// 写入修改后的样本值到新的Int16Array
modifiedDataView[i] = finalSample;
}
// modifiedDataView.buffer 现在包含音量增大一倍的音频数据的ArrayBuffer
当然你也可以用DataView来实现上面的逻辑
// 假设您已经有一个名为audioData 的ArrayBuffer
// 创建一个Int16Array来读取和修改音频数据
const dataView = new Int16Array(audioData);
// 计算样本数
const sampleCount = dataView.length;
// 创建一个新的Int16Array来存储修改后的音频数据
const modifiedDataView = new Int16Array(sampleCount);
// 遍历每个样本并增大音量
for (let i = 0; i < sampleCount; i++) {
// 读取样本值
const sample = dataView[i];
// 增大音量一倍
const modifiedSample = sample * 2;
// 截断或饱和处理样本值
const finalSample = Math.max(-32768, Math.min(modifiedSample, 32767));
// 写入修改后的样本值到新的Int16Array
modifiedDataView[i] = finalSample;
}
// modifiedDataView.buffer 现在包含音量增大一倍的音频数据的ArrayBuffer
用TypedArray和DataView各有什么优势呢?
TypedArray 的优点:
- 直观性:TypedArray 提供了更直观的数据访问方式,通过索引来访问和修改数据,不需要手动计算偏移量。
- 更高的性能:TypedArray 在访问和修改数据时更加高效,因为它们使用底层的二进制数据表示,并且直接操作原始内存。
- 内存优化:TypedArray 使用固定的数据类型,它们的内存占用更加紧凑,不会存在字节对齐等额外开销。
DataView 的优点:
- 灵活性:DataView 提供了更灵活的数据解析和访问方式,可以处理不同的数据类型和字节顺序,适用于更复杂的数据操作。
- 兼容性:DataView 支持更广泛的平台和浏览器,包括旧版浏览器和一些特定环境。
- 跨平台和跨语言:DataView 适用于跨平台和跨语言的场景,可以与其他语言和平台进行数据交换。
图片渲染
在前端视频渲染中,通常需要对视频帧进行解码和处理。TypedArray 可以用来存储视频帧的像素数据,然后通过 Canvas API 或 WebGL 进行渲染。例如,创建一个 Uint8Array 来表示图片中的像素数据,就可以对图像中的每一个像素精准控制。
// 创建一个Uint8Array,表示一个10x10的图像,每个像素使用RGB三个通道表示
const imageWidth = 10;
const imageHeight = 10;
const numChannels = 3; // RGB三个通道
const imageDataSize = imageWidth * imageHeight * numChannels;
const imageArray = new Uint8Array(imageDataSize);
// 设置图像像素数据
for (let y = 0; y < imageHeight; y++) {
for (let x = 0; x < imageWidth; x++) {
const pixelIndex = (y * imageWidth + x) * numChannels;
// 设置红色通道的值
imageArray[pixelIndex] = 255;
// 设置绿色通道的值
imageArray[pixelIndex + 1] = 0;
// 设置蓝色通道的值
imageArray[pixelIndex + 2] = 0;
}
}
// 读取图像像素数据
for (let y = 0; y < imageHeight; y++) {
for (let x = 0; x < imageWidth; x++) {
const pixelIndex = (y * imageWidth + x) * numChannels;
// 获取红色通道的值
const red = imageArray[pixelIndex];
// 获取绿色通道的值
const green = imageArray[pixelIndex + 1];
// 获取蓝色通道的值
const blue = imageArray[pixelIndex + 2];
console.log(`Pixel at (${x}, ${y}): RGB(${red}, ${green}, ${blue})`);
}
}
数据计算
Uint32Array (32位无符号整数) 是一种TypedArray,它以无符号的32位整数形式存储数据。Uint32Array常用于处理大范围的数值计算、图像处理和网络通信等领域。
下面展示一下如何使用Uint32Array进行简单的数值计算。
// 创建一个Uint32Array,存储一组数值
const data = new Uint32Array(10);
data[0] = 100;
data[1] = 200;
data[2] = 300;
data[3] = 400;
data[4] = 500;
data[5] = 600;
data[6] = 700;
data[7] = 800;
data[8] = 900;
data[9] = 1000;
// 计算数据的总和
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += data[i];
}
console.log('Sum:', sum);
// 查找最大值和最小值
let max = data[0];
let min = data[0];
for (let i = 1; i < data.length; i++) {
if (data[i] > max) {
max = data[i];
}
if (data[i] < min) {
min = data[i];
}
}
console.log('Max:', max);
console.log('Min:', min);
可能有人说不需要用Uint32Array,普通数组一样可以实现这样的功能啊,那Uint32Array有什么优势呢?
- 内存占用:Uint32Array使用无符号32位整数来存储数据,每个元素占用4个字节。相比之下,普通数组在JavaScript中默认使用64位浮点数表示数字,每个元素占用8个字节。因此,使用Uint32Array可以显著减少内存占用,特别是在处理大量数据时,可以节省大量内存空间。
- 性能优化:Uint32Array是一种类型化数组,底层以原生二进制数据存储,提供了更高效的访问和操作。相比之下,普通数组是动态的,可以包含任意类型的数据,因此在访问和操作时需要进行类型检查和转换,会带来一定的性能损耗。Uint32Array的操作通常比普通数组更快。
- 数值范围:Uint32Array存储的是无符号32位整数,可以表示的数值范围是0到2^32-1,更适合处理大范围的数值计算。而普通数组中的数值在JavaScript中使用64位浮点数表示,受到浮点数精度和数值范围的限制。
- 类型约束:Uint32Array是一种类型化数组,它的元素类型是固定的,只能存储无符号32位整数。这种类型约束可以提供更强的数据一致性和类型安全,避免了因为类型混乱导致的错误。
总结
通过使用 TypedArray,可以高效地处理和操作二进制数据,想想你工作中有哪些场景可以用上一用呢?