使用 Media Capabilities API

Baseline 廣泛可用 *

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2020 年 1 月⁩ 起,所有主流瀏覽器均已支援。

* 此特性的某些部分可能存在不同級別的支援。

媒體能力 API(Media Capabilities API)提供了幾個關鍵功能,可以幫助您更好地決定如何處理媒體,以及即時確定媒體的處理效果。

這些功能包括:

  • 能夠查詢瀏覽器,以確定在給定一組特定的編碼引數(可能包括編解碼器、解析度、位元率、幀率等)的情況下,瀏覽器編碼或解碼媒體的能力。藉助媒體能力 API,您不僅可以確定瀏覽器是否支援某種格式,還可以確定它是否能高效、流暢地支援。
  • 更精細、更詳細的顯示器屬性資訊,以便在為使用者裝置選擇最佳播放格式時做出明智的決策。例如,您可以使用該 API 來確保您不會嘗試在標準動態範圍(SDR)螢幕上播放高動態範圍(HDR)內容。
  • 支援獲取媒體播放的即時反饋,以便您的程式碼可以根據此資訊調整流的質量或其他設定,從而管理使用者感知到的媒體效能和質量。其中一項功能是能夠檢測裝置何時切換 GPU,以便您可以根據新 GPU 的能力進行相應調整。

注意: 上述第三點中提到的顯示器能力功能尚未在任何瀏覽器中實現。一旦可用,它們將成為該 API 的有用功能,但在此功能在瀏覽器中實現之前,很可能會發生很大變化。

MediaCapabilities 介面

可以使用 `navigator` 物件和 `WorkerNavigator` 物件提供的 `mediaCapabilities` 屬性來訪問 MediaCapabilities。換句話說,媒體能力 API 既可以在主執行緒中使用,也可以在 worker 中使用。

如果該物件存在,則媒體能力 API 可用。因此,您可以像這樣測試 API 的存在性:

js
if ("mediaCapabilities" in navigator) {
  // mediaCapabilities is available
} else {
  // mediaCapabilities IS NOT available
}

以影片為例,要獲取有關影片解碼能力的資訊,您需要建立一個影片解碼配置,並將其作為引數傳遞給 MediaCapabilities.decodingInfo() 方法。該方法返回一個 Promise,該 Promise 會解析出關於媒體能力的以下資訊:影片是否可以解碼,以及解碼是否會流暢且節能。您也可以測試音訊解碼以及影片和音訊編碼。

建立影片解碼配置

MediaCapabilities.decodingInfo() 方法接受一個媒體解碼配置作為引數。

在我們的示例中,我們正在測試影片配置的解碼能力。該配置需要被測試媒體的型別——例如,一個普通的 `file` 或 `MediaSource` —以及一個影片配置物件,該物件包含 `contentType`、`width`、`height`、`bitrate` 和 `framerate` 等值。

  • `contentType` 必須是一個指定 有效影片 MIME 型別的字串。
  • `width` 和 `height` 是影片的水平和垂直尺寸;它們也用於確定 縱橫比
  • `bitrate` 是編碼一秒鐘影片所使用的位元數。
  • `framerate` 是播放影片時每秒鐘播放的幀數。
js
const videoConfiguration = {
  type: "file",
  video: {
    contentType: "video/webm;codecs=vp8",
    width: 800,
    height: 600,
    bitrate: 10000,
    framerate: 15,
  },
};

如果我們查詢的是音訊檔案的可解碼性,我們將建立一個包含聲道數和取樣率的音訊配置,並省略僅適用於影片的屬性——即尺寸和幀率。

js
const audioConfiguration = {
  type: "file",
  audio: {
    contentType: "audio/ogg",
    channels: 2,
    bitrate: 132700,
    samplerate: 5200,
  },
};

如果我們測試的是編碼能力,我們將建立一個略有不同的配置。在這種情況下,被測試媒體的型別是 `record`(用於錄製媒體,即 MediaRecorder 物件)或 `transmission`(用於透過電子方式傳輸的媒體,如 RTCPeerConnection)—再加上一個音訊或影片配置,如上所述。

查詢瀏覽器關於解碼能力的資訊

現在我們已經建立了一個影片解碼配置,我們可以將其作為引數傳遞給 decodingInfo() 方法,以確定是否可以解碼符合此配置的影片,以及播放是否流暢且節能。

js
let promise = navigator.mediaCapabilities.decodingInfo(videoConfiguration);

`decodingInfo()` 和 `encodingInfo()` 方法都返回 Promise。一旦 Promise 狀態 fulfilled(已完成),您就可以從返回的物件中訪問 `supported`、`smooth` 和 `powerEfficient` 屬性。

處理響應

我們可以將 Promise 返回的值輸出到控制檯,而不是將 Promise 分配給一個變數:

js
navigator.mediaCapabilities.decodingInfo(videoConfiguration).then((result) => {
  console.log(
    `This configuration is ${result.supported ? "" : "not "}supported,`,
  );
  console.log(`${result.smooth ? "" : "not "}smooth, and`);
  console.log(`${result.powerEfficient ? "" : "not "}power efficient.`);
});

處理錯誤

在我們的影片解碼示例中,如果傳遞給 decodingInfo() 方法的媒體配置無效,將會引發 `TypeError`。發生錯誤可能有幾個原因,包括:

  • 指定的 `type` 不是兩個允許值之一:`file` 或 `media-source`。
  • 提供的 `contentType` 是:

錯誤可能是由於 `type` 不是兩個可能值之一,`contentType` 不是有效的編解碼器 MIME 型別,或者影片配置物件中缺少無效或省略的定義。

js
navigator.mediaCapabilities
  .decodingInfo(videoConfiguration)
  .then(() => console.log("It worked"))
  .catch((error) => console.error(`It failed: ${error}`));

媒體能力即時示例

CSS

css
li {
  margin: 1em;
}

HTML

html
<form>
  <p>
    Select your video configuration and find out if this browser supports the
    codec, and whether decoding will be smooth and power efficient:
  </p>
  <ul>
    <li>
      <label for="codec">Select a codec</label>
      <select id="codec">
        <option>video/webm; codecs=vp8</option>
        <option>video/webm; codecs=vp9</option>
        <option>video/mp4; codecs=avc1</option>
        <option>video/mp4; codecs=avc1.420034</option>
        <option>invalid</option>
      </select>
    </li>
    <li>
      <label for="size">Select a size</label>
      <select id="size">
        <option>7680x4320</option>
        <option>3840x2160</option>
        <option>2560x1440</option>
        <option>1920x1080</option>
        <option>1280x720</option>
        <option selected>800x600</option>
        <option>640x480</option>
        <option>320x240</option>
        <option value=" x ">none</option>
      </select>
    </li>
    <li>
      <label for="framerate">Select a framerate</label>
      <select id="framerate">
        <option>60</option>
        <option>50</option>
        <option>30</option>
        <option>24</option>
        <option selected>15</option>
      </select>
    </li>
    <li>
      <label for="bitrate">Select a bitrate</label>
      <select id="bitrate">
        <option>4000</option>
        <option>2500</option>
        <option>800</option>
      </select>
    </li>
  </ul>
  <p>
    <input type="button" value="Test this Video Configuration" id="try-it" />
  </p>
</form>

<ul id="results"></ul>

JavaScript

js
let mc = {
  videoConfiguration: {},

  tryIt() {
    mc.createConfiguration();
    mc.testIt();
  },

  createConfiguration() {
    const size = document.getElementById("size").value.split("x");
    mc.videoConfiguration = {
      type: "file",
      video: {
        contentType: document.getElementById("codec").value,
        width: size[0],
        height: size[1],
        bitrate: document.getElementById("bitrate").value,
        framerate: document.getElementById("framerate").value,
      },
    };
  },

  testIt() {
    let content = "";
    navigator.mediaCapabilities
      .decodingInfo(mc.videoConfiguration)
      .then((result) => {
        const li = document.createElement("li"),
          mcv = mc.videoConfiguration.video;
        content = `A ${mcv.width}x${mcv.height}, ${mcv.contentType} at ${
          mcv.framerate
        }fps and ${mcv.bitrate} bps video ${
          result.supported ? " IS " : "IS NOT "
        } supported,`;
        content += `${result.smooth ? " IS " : " is NOT "} smooth, and`;
        content += `${
          result.powerEfficient ? " IS " : " IS NOT "
        }power efficient.`;
        const ul = document.getElementById("results");
        li.textContent = content;
        ul.appendChild(li);
      })
      .catch((error) => {
        const li = document.createElement("li"),
          ul = document.getElementById("results");
        li.textContent = `Codec ${mc.videoConfiguration.video.contentType} threw an error: ${error}`;
        ul.appendChild(li);
      });
  },
};

document.getElementById("try-it").addEventListener("click", mc.tryIt);

即時結果

瀏覽器相容性

另見