音訊和影片操作

Web的美妙之處在於您可以將多種技術結合起來創造新的形式。瀏覽器原生支援音訊和影片,這意味著我們可以使用這些資料流結合諸如 <canvas>WebGLWeb Audio API 等技術來直接修改音訊和影片。例如,為音訊新增混響/壓縮效果,或為影片新增灰度/硒色濾鏡。本文提供了一份參考指南,解釋您需要做什麼。

影片操作

能夠讀取影片每一幀的畫素值非常有用。

影片和 Canvas

<canvas> 元素為在網頁上繪製圖形提供了一個表面;它功能強大,並且可以與影片緊密結合。

通用技術是

  1. <video> 元素的幀寫入 <canvas> 元素。
  2. 讀取 <canvas> 元素的資料並進行操作。
  3. 將操作後的資料寫入您的“顯示” <canvas>(實際上可以是同一個元素)。
  4. 暫停並重復。

例如,我們來處理一個影片以灰度顯示它。在這種情況下,我們將同時顯示源影片和輸出的灰度幀。通常情況下,如果您要實現“以灰度播放影片”的功能,您可能會為 <video> 元素新增 display: none 樣式,以避免在顯示僅顯示經過修改幀的 Canvas 時,源影片也顯示在螢幕上。

HTML

我們可以這樣設定我們的影片播放器和 <canvas> 元素

html
<video id="my-video" controls 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

此程式碼處理幀的修改。

js
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();
    });
  },

  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);
  },
};

頁面載入完成後,您可以呼叫

js
processor.doLoad();

結果

這是一個使用 Canvas 操作影片幀的示例。為了提高效率,您應該考慮在支援 requestAnimationFrame() 的瀏覽器上使用它,而不是 setTimeout()

您可以透過將 <video> 元素的 grayscale() CSS 函式應用於源 <video> 元素來實現相同的結果。

注意: 由於潛在的安全問題(如果您的影片與您的程式碼不在同一個域),您需要在您的影片伺服器上啟用 CORS(跨源資源共享)

影片和 WebGL

WebGL 是一個強大的 API,它使用 Canvas 繪製硬體加速的 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 loop>
  <source src="/shared-assets/videos/flower.mp4" type="video/mp4" />
  <source src="/shared-assets/videos/flower.webm" type="video/webm" />
</video>
<label for="rate">Playback rate <output id="rate-value">1.0</output></label>
<input type="range" id="rate" name="rate" min="0" max="4" value="1" step=".2" />
js
const rateSlider = document.getElementById("rate");
const rateValue = document.getElementById("rate-value");
const myVideo = document.getElementById("my-video");

rateSlider.addEventListener("input", () => {
  myVideo.playbackRate = rateSlider.value;
  rateValue.textContent = parseFloat(rateSlider.value);
});

開始播放影片,然後調整滑塊以更改媒體的播放速率

音訊操作

除了 playbackRate 之外,要操作音訊,您通常會使用 Web Audio API

選擇音訊源

Web Audio API 可以從各種來源接收音訊,然後對其進行處理,並將其傳送回一個 AudioDestinationNode,該節點代表處理後傳送聲音的輸出裝置。

如果音訊源是… 使用此 Web Audio 節點型別
來自 HTML <audio><video> 元素的音訊軌道 MediaElementAudioSourceNode
記憶體中的純原始音訊資料緩衝區 AudioBufferSourceNode
生成正弦波或其他計算波形的振盪器 OscillatorNode
來自 WebRTC 的音訊軌道(例如,您可以使用 getUserMedia() 獲取的麥克風輸入)。 MediaStreamAudioSourceNode

音訊濾波器

Web Audio API 提供了許多不同的濾波器/效果,可以使用 BiquadFilterNode 等進行應用,例如。

html
<video id="my-video" controls loop>
  <source src="/shared-assets/videos/friday.mp4" type="video/mp4" />
</video>
<label for="freq">Filter freq. <output id="freq-value">1.0</output>hz</label>
<input type="range" id="freq" name="freq" max="20000" value="1000" step="100" />
js
const freqSlider = document.getElementById("freq");
const freqValue = document.getElementById("freq-value");

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 = 20;

freqSlider.addEventListener("input", () => {
  filter.frequency.value = freqSlider.value;
  freqValue.textContent = parseFloat(freqSlider.value);
});

注意: 除非您啟用了 CORS,否則為了避免安全問題,您的影片應與您的程式碼在同一域上。

常用音訊濾波器

以下是一些您可以應用的常見音訊濾波器型別

  • 低通:允許低於截止頻率的頻率透過,並衰減高於截止頻率的頻率。
  • 高通:允許高於截止頻率的頻率透過,並衰減低於截止頻率的頻率。
  • 帶通:允許一定範圍的頻率透過,並衰減此頻率範圍之外的頻率。
  • 低架:允許所有頻率透過,但對較低頻率進行增強(或衰減)。
  • 高架:允許所有頻率透過,但對較高頻率進行增強(或衰減)。
  • 峰值:允許所有頻率透過,但對一定範圍的頻率進行增強(或衰減)。
  • 陷波:允許所有頻率透過,除了特定頻率範圍。
  • 全通:允許所有頻率透過,但改變了各個頻率之間的相位關係。

注意: 有關更多資訊,請參閱 BiquadFilterNode

卷積和衝激

還可以使用 ConvolverNode 將衝激響應應用於音訊。衝激響應是指在短暫的聲音脈衝(如拍手聲)之後產生的聲音。衝激響應將指示脈衝產生的環境(例如,在隧道中拍手產生的回聲)。

示例

js
const convolver = context.createConvolver();
convolver.buffer = this.impulseResponseBuffer;
// Connect the graph.
source.connect(convolver);
convolver.connect(context.destination);

請參閱我們的 HolySpaceCow 示例,這是一個應用(但非常非常愚蠢)的示例。

空間音訊

我們還可以使用聲像節點來定位音訊。聲像節點—PannerNode—允許我們定義一個聲源錐體以及位置和方向元素,所有這些都以 3D 笛卡爾座標定義的 3D 空間中的。

示例

js
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 儲存庫中找到一個示例(也可以 線上檢視)。

示例

另見

指南

參考