Shaka播放媒体播放的播放器 - 实施,用例,优点和缺点
#javascript #网络开发人员 #开源 #视频

如果您正在寻找播放受DRM保护内容的库,传播直播或将内容货币化,则 shaka Player 可以做到所有这些,等等! Shaka Player是一个开源库,是媒体播放的第二大图书馆。该库使用本机浏览器API,例如MediaSourceEncrypted Media Extensions。本文将帮助您实施Shaka Player的基本实施,描述其功能并提出其优势和劣势。

一点介绍

Shaka Player的核心使用引擎盖下的一些本机浏览器API:

  • 加密的媒体扩展 eme )API,当浏览器接收视频并发射“加密”的侦听器时。 This Google article在解释它的“下层”工作以及如何以及何时使用它方面做得很棒。
  • 媒体源扩展 API,可帮助创建和将视频段组合在一起,插入广告并根据设备功能和性能更改视频分辨率。 Another Google article以可访问的方式更详细地解释了这些概念。

  • MediaCapabilities API确定您的设备是否支持您要播放的媒体,并可以告诉您您的媒体配置功能是否允许光滑且有效的播放。

  • IndexedDB API用于缓存下载的视频,然后允许脱机播放。

shaka播放器通常与后端与Shaka Packager结合使用,以添加内容保护,文本流,将媒体文件分解为流段以进行流并创建DASH的元数据或为HLS创建相应的包装。

基本用法

出于本文的目的,我们将使用免费的Dash清单from here

要在您的项目中下载并使用Shaka Player,您可以通过两种方式进行操作:

  1. Download the Shaka repository to your computer and follow this instruction
  2. 通过NPM/纱线:
npm install --save shaka-player


yarn add shaka-player

注意:如果您在项目中使用Typescript,则需要在项目中的d.ts文件中声明类型,例如:

declare module 'shaka-player' {
  export = shaka;
}

declare module 'shaka-player/dist/shaka-player.compiled' {
  export = shaka;
}

Shaka将开始支持V5中的类型,如here

为了启动播放器实例,您需要html中的<video>元素,您可以附加到:

<!DOCTYPE html>
<html>
  <head>
    <title>Basic Shaka implementation</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <video id="video" width="640" height="480" controls></video>

    <script src="index.js"></script>
  </body>
</html>

现在,在index.js中:

import shaka from "shaka-player";

const exampleManifestUri = 
"https://bitmovin-a.akamaihd.net
/content/MI201109210084_1
/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd";

async function initPlayer() {
  // Install polyfills to patch out browser incompatibilities
  shaka.polyfill.installAll();
  // Check if your browser supports Shaka at all
  if (!shaka.Player.isBrowserSupported()) {
    console.error("Browser not supported!");
    return;
  }

  //Search for available video element and attach the player to it
  const video = document.getElementById("video");
  const player = new shaka.Player(video);
  // listen for errors for further error handling
  player.addEventListener("error", console.error);
  try {
    await player.load(exampleManifestUri);
    console.log("player has loaded the video, you can play it now");
  } catch (error) {
    console.log(error);
  }
}

document.addEventListener("DOMContentLoaded", initPlayer);

Working example

使用控制

Shaka Player有自己的控件UI,您可以在播放器启动之前添加。为此,我们需要更新我们的HTML和JavaScript代码。

在我们的html中,我们必须添加样式和一个容器元素:

<!DOCTYPE html>
<html>
  <head>
    <title>Shaka player built-in UI example</title>
    <meta charset="UTF-8" />
    <!-- for the sake of this article we
 use a sandbox path to shaka styles -->
    <link
      rel="stylesheet"
      type="text/css"
      href="node_modules/shaka-player/dist/controls.css"
    />
    <link rel="stylesheet" type="text/css" href="src/styles.css" />
  </head>

  <body>
    <!-- We need a container to build the UI on top of it -->
    <div id="container" style="width: 100%; height: 100%;">
      <video autoplay id="video"></video>
    </div>

    <script src="src/index.js"></script>
  </body>
</html>

在我们的JavaScript代码中,我们更改导入源并添加更多逻辑:

/* we need to import the shaka ui module instead of 
the regular module to gain access to the Overlay constructor */
import shaka from "shaka-player/dist/shaka-player.ui.js";

const manifestUri =
  "https://bitmovin-a.akamaihd.net/content
/MI201109210084_1
/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd";

async function init() {
  const video = document.getElementById("video");
  const container = document.getElementById("container");
  const player = new shaka.Player(video);
  // initialize the UI instance onto the HTML element we created
  const ui = new shaka.ui.Overlay(player, container, video);

  const controls = ui.getControls();
  // Listen for error events for possible error handling.
  player.addEventListener("error", console.error);
  controls.addEventListener("error", console.error);
  try {
    await player.load(manifestUri);
  } catch (e) {
    console.error(e);
  }
}

/* Listen to the custom shaka-ui-loaded event, 
to wait until the UI is loaded. */
document.addEventListener("shaka-ui-loaded", init);

Working example

现在,玩家控件应在您的播放器覆盖中可见。

注意:如果您想脱颖而出,可以使用custom shaka styles

添加配置

shaka播放器使您可以配置播放器实例及其控件。想象一下,您在内容中添加了DRM保护,或者想缓存视频,或者想在控件中添加更多功能。所有这些都是可能的configure方法。
让我们从第二个10开始我们的视频,然后在我们的控件中添加工具提示:

async function init() {
  const video = document.getElementById("video");
  const container = document.getElementById("container");
  const player = new shaka.Player(video);

  // initialize the UI instance onto the HTML element we created
  const ui = new shaka.ui.Overlay(player, container, video);

  player.configure({
    playRangeStart: 10
  });

  ui.configure({
    enableTooltips: true
  });

  const controls = ui.getControls();
  // Listen for error events for possible error handling.
  player.addEventListener("error", console.error);
  controls.addEventListener("error", console.error);

  try {
    await player.load(manifestUri);
  } catch (e) {
    console.error(e);
  }
}

使用player configUI config您可以做更多的事情 - 添加自定义DRM源,切换媒体自动改编,添加更多UI功能等。

离线播放

shaka允许我们下载资产并将其存储在浏览器数据库中(从上方索引了DB API)。下图中的代码可能更复杂,但是其中一些已经看起来很熟悉。

import shaka from "shaka-player/dist/shaka-player.ui.js";

const exampleManifestUri =
  "https://bitmovin-a.akamaihd.net
  /content/MI201109210084_1/mpds
  /f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd";

function initApp() {
  // Install built-in polyfills to patch browser incompatibilities.
  shaka.polyfill.installAll();

  /* Check to see if the browser supports 
the basic APIs Shaka needs.*/
  if (!shaka.Player.isBrowserSupported()) {
    // This browser does not have the minimum set of APIs we need.
    console.error("Browser not supported!");
  }

  addNetworkListeners();
}

// this function initializes the player and adds the event listeners
async function initPlayer() {
  const video = document.getElementById("video");
  const container = document.getElementById("container");
  const player = new shaka.Player(video);

  /* we will need this in other functions 
so we assign it to the window object */
  window.player = player;

  const ui = new shaka.ui.Overlay(player, container, video);
  const controls = ui.getControls();
  controls.addEventListener("error", console.error);

  player.addEventListener("error", console.error);

  initOfflineStorage(player);

  const downloadButton = document.getElementById("download-button");
  downloadButton.addEventListener("click", downloadContent);

  try {
    await player.load(exampleManifestUri);
  } catch (error) {
    console.error(error);
  }
}

/* Update the online status and add listeners so that we can display
  the network state to the user. */
function addNetworkListeners() {
  updateOnlineStatus();
  window.addEventListener("online", updateOnlineStatus);
  window.addEventListener("offline", updateOnlineStatus);
}

/* grabs an HTML element and modifies it 
depending on your network status */
function updateOnlineStatus() {
  const signal = document.getElementById("online-indicator");
  if (navigator.onLine) {
    signal.innerHTML = "You are ONLINE";
    signal.style.background = "green";
  } else {
    signal.innerHTML = "You are OFFLINE";
    signal.style.background = "black";
  }
}

// downloads the content and stores it in the browser
async function downloadContent(event) {
  event.target.disabled = true;

  try {
    const metadata = {
      title: "Test content",
      downloaded: new Date(),
    };

    // use shaka.offline.Storage to download the content
    console.log("Downloading content...");
    await window.storage.store(exampleManifestUri, metadata);
    console.log("Content downloaded!");
  } catch (error) {
    console.error(error);
  }
}

function initOfflineStorage(player) {
  /* assign the storage object to the window so that we can
 access it later */
  window.storage = new shaka.offline.Storage(player);
  window.storage.configure({
    offline: {
      progressCallback: setDownloadProgress,
    },
  });
  refreshDownloads();
}

// updates the progress number in the HTML
function setDownloadProgress(_, progress) {
  const progressElement = document.getElementById("download-progress");
  const progressInPercent = progress.toFixed(2) * 100;
  progressElement.setAttribute("value", progressInPercent);
  progressElement.innerText = `${Math.floor(progressInPercent)}%`;

  if (progress === 1) {
    // refresh the download list when the download is finished
    refreshDownloads();
  }
}

// fetches list of downloaded files in indexedDB
async function refreshDownloads() {
  const downloadList = document.getElementById("downloaded-content");
  const content = await window.storage.list();
  downloadList.innerHTML = "";
  if (content.length === 0) {
    const listItem = document.createElement("li");
    listItem.innerText = "No downloads yet";
    downloadList.appendChild(listItem);
    return;
  }

  /* list content from indexedDB and 
add buttons to play and remove content */

  content.forEach((item) => {
    const listItem = document.createElement("li");

    const playButton = document.createElement("button");
    playButton.className = "play-button";
    playButton.innerText = "Play";
    playButton.addEventListener("click", () => {
      window.player.load(item.offlineUri);
    });

    const removeButton = document.createElement("button");
    removeButton.className = "remove-button";
    removeButton.innerText = "Remove";
    removeButton.addEventListener("click", async () => {
      await window.storage.remove(item.offlineUri);
      refreshDownloads();
    });

    listItem.innerText = item.appMetadata.title;
    listItem.appendChild(playButton);
    listItem.appendChild(removeButton);
    downloadList.appendChild(listItem);

    const downloadButton = document.getElementById("download-button");
    downloadButton.disabled = false;
  });
}

document.addEventListener("DOMContentLoaded", initApp);
document.addEventListener("shaka-ui-loaded", initPlayer);

indexedDB在CodeSandbox中不起作用,因此我创建了一个小的git repository with the working feature供您在本地进行测试。

您可以关闭DevTools中的连接以完全测试此功能,例如:
DevTools screenshot from the Network tab

集成广告

如果您正在阅读本文,那么您会经历最困难的部分 - 将广告整合到Shaka中很轻松!让我们从头开始:

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Shaka player built-in UI example</title>
    <meta charset="UTF-8" />
    <!-- for the sake of this article 
we use a sandbox path to shaka styles -->
    <link
      rel="stylesheet"
      type="text/css"
      href="node_modules/shaka-player/dist/controls.css"
    />
    <link rel="stylesheet" type="text/css" href="style.css" />
  </head>

  <body>
    <!-- We need a container to build the UI on top of it -->
    <div id="container" style="width: 100%; height: 100%">
      <video autoplay id="video"></video>
    </div>

    <!-- add client-side IMA SDK -->
    <script
      type="text/javascript"
      src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"
    ></script>
    <script type="module" src="src/index.js"></script>
  </body>
</html>

在我们的JS文件中,我们将在播放器初始化之后初始化广告管理器:

import shaka from "shaka-player/dist/shaka-player.ui.js";

const exampleManifestUri =
  "https://bitmovin-a.akamaihd.net/content/
MI201109210084_1/mpds
/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd";

function initApp() {
  // Install built-in polyfills to patch browser incompatibilities.
  shaka.polyfill.installAll();

  // Check to see if the browser supports the basic APIs Shaka needs.
  if (!shaka.Player.isBrowserSupported()) {
    // This browser does not have the minimum set of APIs we need.
    console.error("Browser not supported!");
    return;
  }
}

/* This function initializes the player 
and adds the event listeners */
async function initPlayer() {
  const video = document.getElementById("video");
  const container = document.getElementById("container");
  const player = new shaka.Player(video);

  /* we will need this in other functions 
  so we assign it to the window object */
  window.player = player;

  const ui = new shaka.ui.Overlay(player, container, video);
  const controls = ui.getControls();
  controls.addEventListener("error", console.error);

  player.addEventListener("error", console.error);

  /* Initiates the client-side ad manager
   and attaches it to the player */
  const adManager = player.getAdManager();
  const adContainer = video.ui
.getControls()
.getClientSideAdContainer();
  adManager.initClientSide(adContainer, video);
  runSampleAd();
  try {
    await player.load(exampleManifestUri);
  } catch (error) {
    console.error(error);
  }
}

function runSampleAd() {
  /* the script we added to index.html 
adds the google object to the window */
  const google = window.google;
  const adUrl =
    "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923
/external/single_ad_samples&sz=640x480&cust_params=sample_ct%3D
linear&ciu_szs=300x250%2C728x90&gdfp_req=1
&output=vast&unviewed_position_start=1
&env=vp&impl=s&correlator=";
  const adsRequest = new google.ima.AdsRequest();
  adsRequest.adTagUrl = adUrl;

  const adManager = player.getAdManager();
  adManager.requestClientSideAds(adsRequest);
}

document.addEventListener("DOMContentLoaded", initApp);
document.addEventListener("shaka-ui-loaded", initPlayer);

This GitHub repo是此功能的一个工作示例,因为Codesandbox不允许第三方脚本提出请求。

利弊

,由于它是一个多功能的图书馆,因此很难不偏向Shaka Player,但我会尽力保持客观。

为什么shaka是好

  • 令人惊叹的社区支持,尤其是维护者。
  • 您的大部分视频流有关功能都可以从包装盒中
  • 高度配置
  • 非常简单的基本实现

Shaka可以在哪里改善

  • 打字稿支持
  • 图书馆不可摇晃 - 无论您使用多少部分
  • 许多带有可疑支持的设备/引擎
  • Chromecast问题

结论

总的来说,Shaka Player是一个很棒的图书馆,可以立即将视频播放相关功能的生态系统带入您的项目。借助社区支持和相对简单的实施,Shaka生态系统可以使网络上的内容更加顺畅,并且更容易访问。本文刮了Shaka可以做的事情的表面,并提供了一些工作代码示例。如果您有任何内容可以关注此内容,请自由!