初めてのShaka Player

## はじめに
最近、プロジェクトでShaka Playerを導入し振り返る機会がありました。
せっかくなので、初めて導入する方向けに、記事を書いてみようと思いました。
需要は少ないかもですが、参考になれば幸いです。

## Shaka Playerの紹介
### 特徴
Googleが開発したオープンソースのJavaScriptライブラリで、主にアダプティブストリーミング（DASHやHLS）をサポートするHTML5ビデオプレイヤーです。

### 利用目的
動画配信プラットフォームやウェブアプリケーションで、効率的かつ柔軟にストリーミングコンテンツを再生できるようにすることです。

### 選定ポイント
プロジェクトでWeb Playerを利用する際の選定する際に以下の項目を評価しました
* 高度な機能や拡張性が高い
* DASHとHLSの両方をサポート
* OSSでドキュメントが充実しており、コミュニティも活発（無料！）
* DRM（Widevine、PlayReady、FairPlay）を使用可能

:::note info
LiveStreaming配信に対応しています。
再生中にad広告を流すことができます。
UI Libraryが用意されていてカスタムが簡単で、OriginalなUIの追加も簡単です。

百聞は一見にしかず、ぜひ[Demo](https://shaka-player-demo.appspot.com/demo/)を見てみてみよう
:::

## 導入してみよう
Shaka Playerの導入はシンプルで簡単です。
導入のためのチュートリアルや解説が充実しているからです。
基本的にチュートリアルを参考にすればうまくいきますので
ここでは補足的な感じで手順をまとめました。

### 必要な準備

Shaka Playerを導入する前に、以下の環境が整っていることを確認してください。

* HTML5対応のブラウザ
最新のChrome、Firefox、ChromiumEdge、safariなど
* Webサーバー
ローカル環境であれば、簡易的なHTTPサーバー（例: http-server）を使用
* 動画ファイル
MPEG-DASH形式（.mpd）またはHLS形式（.m3u8）の動画ファイル
ここではGoogleからMPEG-DASH形式の[sample動画](https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd)を使用

### Shaka Playerのインストール
Shaka PlayerはCDN経由で簡単に利用できます。以下のコードをHTMLに追加します。

```javascript
<script src="https://cdnjs.cloudflare.com/ajax/libs/shaka-player/4.12.6/shaka-player.compiled.min.js"></script>
```
また、npmやyarnを使ってインストールも可能です。
```
npm install shaka-player
```

shaka-playerの特定Versionでカスタマイズして使用しても良いです。
```
## Get the source
git clone https://github.com/shaka-project/shaka-player.git
cd shaka-player

## Use git tag to get a specific ShakaVersion

## Compile the library and generate the docs
docker build -t shaka-player-build build/docker
docker run -v $(pwd):/usr/src --user $(id -u):$(id -g) shaka-player-build

## Place the built generated files on the server
```
:::note info
私のプロジェクトは動画配信サービスの要件が細かいため、特定Versionに追加コードをどうしても含める必要がありました。（と言ってもほんの少しだけです）
:::

もしReactやVueとのIntergrationを検討されている方は以下のリンクが参考になります。
[framework-integrations](https://github.com/shaka-project/shaka-player?tab=readme-ov-file#framework-integrations)

### 実装例
以下は、Shaka Playerを使って動画を再生するための最小構成のコード例です。
```html
<!DOCTYPE html>
<html>
<head>
  <title>初めてのShaka Player</title>
</head>
<body>
  <video id="video" width="640" controls autoplay></video>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/shaka-player/4.12.6/shaka-player.compiled.min.js"></script>
  <script>
    async function initApp() {
      const video = document.getElementById('video');
      const player = new shaka.Player(video);

      // エラーハンドリング
      player.addEventListener('error', (event) => {
        console.error('エラーが発生しました:', event.detail);
      });

      try {
        // 動画のロード
        await player.load('https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd');
        console.log('動画のロードに成功しました！');
      } catch (error) {
        console.error('動画のロードに失敗しました:', error);
      }
    }

    document.addEventListener('DOMContentLoaded', initApp);
  </script>
</body>
</html>
```

online code editorなどにコードをコピペしてみてください。
ブラウザで動画が再生されるはずです。
ほら、とっても簡単でしょう？


# ハマりどころと解決方法
ここからはより実践的な内容です。
プロジェクトで直面した一部の課題とその解決方法を共有します。

### CORS（クロスオリジンリソース共有）エラー

Shaka Playerで動画を再生する際、CORSエラーが発生することがあります。
これは、動画ファイルがホストされているサーバーや使用するAPIが適切なCORSヘッダーを設定していない場合に起こります。

解決方法：サーバー側でCORSヘッダーを設定します。
```
Access-Control-Allow-Origin: https://example.com
```

### DRMライセンスサーバの設定
DRMを使用する場合、ライセンスサーバーのURLを設定する必要があります。

```javascript
player.configure({ 
    drm: { 
        servers: { 
            'com.widevine.alpha': 'https://your-license-server.com/widevine', 
        }, 
    }, 
});
```

使用するDRMサービスと使用Browserによって認証方法が様々ですので、要件にあった必要な処理を追加実装をします
```javascript
 // sample
  player.getNetworkingEngine().registerRequestFilter(function(type, request, context) {
    // Only add headers to license requests:
    if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
      // This is the specific header name and value the server wants:
      request.headers['CWIP-Auth-Header'] = 'VGhpc0lzQVRlc3QK';
    }
  });
```

fairplayサポートなら[こちら](https://shaka-player-demo.appspot.com/docs/api/tutorial-fairplay.html)も必要

### 動画形式とDRMの互換性
Shaka PlayerはMPEG-DASHやHLSに対応していますが、OSやブラウザに依存して再生できない場合があります。

例えば、macOSではDashを再生できるが一部制限があったり、iOSではDash非対応だったりします。（やってみて気付かされることが多かったです）

また、動画形式とDRMとデバイスの組み合わせも大事です。
プロジェクトでは以下の組み合わせで実装しました。
| 動画形式 | DRM | Device |
|:-:|:-:|:-|
| DASH | Widevine | Chrome、Firefox、ChromiumEdge |
| HLS | FairPlay | Safari（macOS、iOS）|
| AMS | PlayReady | LegacyEdge|

### Google Cast連携
Chromecastのcustom receiverチュートリアルをベースにcafPlayerを実装していたのですが、同期処理に互換性がないという問題がありました。
Shaka PlayerはChrome Cast用のUIやLibraryが用意されていますが、これを使用する場合はCastReceiverのWebアプリがShaka Playerで実装されていることを前提としているようです。

解決方法
同期処理に必要な機能だけscratchでCastReceiver側に追加実装していきました。

:::note info
receiver側で追加実装した理由は以下です。
- CastReceiverをShaka Playerベースで実装し直す選択肢がなかった
- ShakaのCast Libraryをフルに活かしたかっため
- 実装を通してshakaのCast Libraryへの理解を深めたかった

しかし、通常はShakaのCast Libraryを捨て、Sender側で実装する方が賢明だと思います。
理由は以下です。
- iOS/AndroidアプリからCast時とwebからCast時で同期処理部分の実装を分ける必要がある
- Receiverの実装にはReceiverApp自体とcafReceiverSDKに対する知見が必要

今回私がShaka playerの開発とCastReceiverの開発を担当しているからできた選択だったと思います。
:::

### UIカスタマイズ
チュートリアルの[Custom UI作成](https://github.com/shaka-project/shaka-player/blob/main/docs/tutorials/ui-customization.md#creating-custom-elements-and-adding-them-to-the-ui)では、今までの流れで進めていると解決できない問題に直面しました。
Sampleのコードをそのまま使用しても期待通りに動作しないのです。

この問題について、Shaka PlayerのコミュニティやGitHubのIssueで相談している方が数人おり、同様の問題に直面していることが確認できました。

解決方法
以下のように、チュートリアルのコードを修正することで問題を解決しました。

修正前（チュートリアルのコード）
```javascript
myapp.SkipButton = class extends shaka.ui.Element {
  // カスタムUI要素の実装
};
```

修正後（修正したコード）
```javascript
class SkipButton extends shaka.ui.Element {
  // カスタムUI要素の実装
}
```

## 感想とまとめ
### 良かった点

* ドキュメントが充実しており、導入が比較的簡単
* DRMやストリーミングの拡張性が高い（API充実してる）
* パフォーマンスも良い（AzureMediaPlayerと比較したレポートは別途記事書くかも）
* Playerを導入する経験とOSS内のコードを触った経験でなんか自信がついた

### 課題点
* OSSを利用するだけで貢献していない。（Contributingにも挑戦していきたい）

## 最後に
Shaka Playerは、初めて導入する際には、公式ドキュメントを参考に進めるとスムーズに導入できると思います。
再生品質は比較的安定していますし、DRMやストリーミングに対応した動画プレイヤーを探している場合に非常に有用だと思います。


# 参考リンク
[Shaka Player公式ドキュメント](https://shaka-player-demo.appspot.com/docs/api/index.html)
[MPEG-DASHについて](https://en.wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP)
[HLSについて](https://en.wikipedia.org/wiki/HTTP_Live_Streaming)
[FFmpeg公式サイト](https://ffmpeg.org/)