音频与视频处理
Web 的迷人之处在于你可以结合各种技术创造出新的形式。拥有浏览器中的原生音频和视频意味着我们可在像 <canvas>
、WebGL 或 Web Audio API 等技术的辅助下使用这类数据流,例如:为音频添加混响和压缩效果,或为视频添加灰度/暗色滤镜。本文将为你提供相关的参考。
视频处理
读取视频中每帧的像素值的能力是十分实用的。
视频与 canvas
<canvas>
元素能提供一个平面,让你能在网页上画图,该功能十分强大并且可以与视频紧密结合。
总体技术过程为:
- 从
<video>
元素中读取一帧写到<canvas>
元素中。 - 读取
<canvas>
元素中的数据并处理。 - 将处理完成的数据写入“显示”的
<canvas>
中(实际上可以是相同的元素)。 - 暂停并重复。
例如,我们要处理一个视频,将其灰度显示。在本例中,我们需展示出源视频和输出的灰度帧。正常情况下,如果你要实现“添加灰度滤镜”功能,那也许可以在 <video>
元素的样式里添加 dispaly: none
,目的是防止源视频被绘制到屏幕上,同时只展示显示已被更改了帧的画布。
HTML
我们可将视频播放器和 <canvas>
元素如此设置:
<video
id="my-video"
controls="true"
width="480"
height="270"
crossorigin="anonymous">
<source
src="https://jplayer.org/video/webm/Big_Buck_Bunny_Trailer.webm"
type="video/webm" />
<source
src="https://jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v"
type="video/mp4" />
</video>
<canvas id="my-canvas" width="480" height="270"></canvas>
JavaScript
该代码负责更变帧数。
const processor = {
timerCallback() {
if (this.video.paused || this.video.ended) {
return;
}
this.computeFrame();
setTimeout(() => {
this.timerCallback();
}, 16); // roughly 60 frames per second
},
doLoad() {
this.video = document.getElementById("my-video");
this.c1 = document.getElementById("my-canvas");
this.ctx1 = this.c1.getContext("2d");
this.video.addEventListener(
"play",
() => {
this.width = this.video.width;
this.height = this.video.height;
this.timerCallback();
},
false,
);
},
computeFrame() {
this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
const frame = this.ctx1.getImageData(0, 0, this.width, this.height);
const l = frame.data.length / 4;
for (let i = 0; i < l; i++) {
const grey =
(frame.data[i * 4 + 0] +
frame.data[i * 4 + 1] +
frame.data[i * 4 + 2]) /
3;
frame.data[i * 4 + 0] = grey;
frame.data[i * 4 + 1] = grey;
frame.data[i * 4 + 2] = grey;
}
this.ctx1.putImageData(frame, 0, 0);
return;
},
};
一旦网页加载完成,可以调用
processor.doLoad();
结果
这是个十分简单的例子,展示出了如何用画布来处理视频帧。为提升效率,你应该在支持 requestAnimationFrame
的浏览器上运行代码时,选择使用 requestAnimationFrame()
,而不是 setTimeout()
。
你还可通过在源 <video>
元素中采用 grayscale()
CSS 功能以达到同样的效果。
备注:由于存在潜在安全问题,如果你的视频和代码处在不同域,你需要在视频服务器上启用 CORS(跨源资源共享)。
视频与 WebGL
WebGL 是个很强大的 API,它使用画布来绘制出硬件加速的 3D 或 2D 场景。你可结合 WebGL 与 <video>
元素来创造视频纹理,这意味着你可将视频放置到 3D 场景内。
备注:你可在 GitHub 上找到演示的源代码(查看在线演示)。
播放速度
我们也可使用一个 <audio>
与 <video>
元素的属性来调整视频播放的速度,这个属性叫做 playbackRate
。playbackRate
是一个数值,表示回放速率的倍数,例如 0.5 表示半速,而 2 表示倍速。
请注意,playbackRate
属性适用于 <audio>
和 <video>
,但在这两种情况下,它改变的是播放速度,而不是音高。要调整音频的音调则需使用 Web Audio API。参见 AudioBufferSourceNode.playbackRate
属性。
HTML
<video
id="my-video"
controls
src="https://jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v"></video>
JavaScript
const myVideo = document.getElementById("my-video");
myVideo.playbackRate = 2;
可编辑示例
备注:尝试在线的 playbackRate 示例。
音频处理
除 playbackRate
以外,处理音频通常要使用 Web Audio API。
选择音频源
Web Audio API 可接收各种音源的音频,处理好音频后将其发送回 AudioDestinationNode
,代表的是声音处理后发送到的那个输出设备。
如果音频源是... | 使用这种网页音频节点类型 |
---|---|
来自 HTML <audio> 或 <video> 元素的音频轨道 |
MediaElementAudioSourceNode |
内存中的简单原始音频数据缓冲区 | AudioBufferSourceNode |
产生正弦波或其他计算波形的振荡器 | OscillatorNode |
来自 WebRTC 的音频轨道(比如麦克风输入,可使用 getUserMedia() |
MediaStreamAudioSourceNode |
音频滤镜
Web Audio API 拥有多种不同的滤镜和效果,可以使用 BiquadFilterNode
将其应用于音频中,比如
HTML
<video id="my-video" controls src="myvideo.mp4" type="video/mp4"></video>
JavaScript
const context = new AudioContext();
const audioSource = context.createMediaElementSource(
document.getElementById("my-video"),
);
const filter = context.createBiquadFilter();
audioSource.connect(filter);
filter.connect(context.destination);
// Configure filter
filter.type = "lowshelf";
filter.frequency.value = 1000;
filter.gain.value = 25;
可编辑示例
备注:除非你启用了 CORS,否则,为避免安全问题,你的视频应与你的代码处于同一个域中。
常用音频滤镜
以下为一些可以使用的常用音频滤镜类型:
- 低通(Low Pass):允许低于截止频率的信号通过,并减弱高于截止频率的信号。
- 高通(High Pass):允许高于截止频率的信号通过,并减弱低于截止频率的信号。
- 带通(Band Pass):允许一定范围内的频率通过,并减弱低于或高于该频率范围的信号。
- 低频搁架(Low Shelf):允许所有频率通过,但增强(或减弱)较低频率的信号。
- 高频搁架(High Shelf):允许所有频率通过,但增强(或减弱)较高频率的信号。
- 峰值(Peaking):允许所有频率的信号通过,但增强(或减弱)一定频率范围的信号。
- 陷波(Notch):允许所有频率的信号通过,但一组频率除外。
- 全通(All Pass):允许所有频率的信号通过,但改变个频率之间的相位关系。
备注:参见 BiquadFilterNode
以了解详情。
卷积和脉冲
也可使用 ConvolverNode
将脉冲响应运用到音频中。脉冲响应指短暂声音脉冲后产生的声音(比如拍掌)。脉冲响应表示产生脉冲的环境(比如在隧道中鼓掌产生的回声)。
示例
const convolver = context.createConvolver();
convolver.buffer = this.impulseResponseBuffer;
// Connect the graph.
source.connect(convolver);
convolver.connect(context.destination);
参见 Codepen 以获取应用示例(示例较为尴尬)。
空间音频
我们也可使用 panner 节点来对音频定位。panner 节点——PannerNode
——可以让我们界定出一个源锥(位置和方向)元素,所有这些都是使用 3D 笛卡尔坐标在 3D 空间中界定的。
示例
const panner = context.createPanner();
panner.coneOuterGain = 0.2;
panner.coneOuterAngle = 120;
panner.coneInnerAngle = 0;
panner.connect(context.destination);
source.connect(panner);
source.start(0);
// Position the listener at the origin.
context.listener.setPosition(0, 0, 0);
备注:你可以在 GitHub 仓库中找到示例(也可以查看在线演示)。
JavaScript 编解码器
示例
参见
教程
参考
<audio>
和<video>
元素HTMLMediaElement
API<canvas>
元素- Web Audio API
- AudioContext
- 空间音效的更多相关内容
- Web 媒体技术