DMM.comの、一番深くておもしろいトコロ。

Electron製動画再生プレイヤー「DMM Player v2」

Electron製動画再生プレイヤー「DMM Player v2」

  • このエントリーをはてなブックマークに追加

はじめに

こんにちは。
動画配信事業部・配信基盤チーム所属の保月と申します。

「進化する動画配信基盤」についての連載第1回目となるこの記事では、Electron製動画再生プレイヤー「DMM Player v2」 について記載します。

目次記事はこちらです。 inside.dmm.com

ダウンロード再生とは

2019年9月にDMM Player v2というPC向け動画ダウンロード再生プレイヤーをリリースしましたが、まずダウンロード再生とはどのようなものなのかを説明したいと思います。

DMM動画配信サービスの場合、動画を再生する形式は大きく分けて「ストリーミング再生」と「ダウンロード再生」の2種類あります。

ストリーミング再生は、動画データを少しづつダウンロードしながら動画プレイヤーで再生する方法です。
動画データを全てダウンロードしていなくても再生を開始できるため、見たいと思った時にすぐに再生できますが、ダウンロードしながらの再生になるため、ダウンロードしようとしている動画データサイズに対して再生環境の回線速度が間に合っていない場合はスムーズに再生されない(いわゆるバッファ処理が頻繁に発生してしまう)状態になります。
そのため、高画質な動画を再生するためには、それだけ回線速度が速くないといけません。 また、ダウンロードした動画データは基本的にディスクには保存されないため、次に再生する時に再度動画データをダウンロードする必要があります。

もう一方のダウンロード再生は、最初に動画データ全体をダウンロードしてPCのディスクに保存し、その保存したデータを動画プレイヤーに読み込ませて再生する方法です。
全データのダウンロードが完了しないと再生が開始できないものの、回線速度が速くない環境でもダウンロードさえしてしまえば、高画質な動画をスムーズに再生できます。
また、一度ダウンロードしてしまえば次の再生時にはダウンロードの必要がなく、すぐに再生できます。

f:id:yanoshi:20200127120118p:plain
ストリーミング再生とダウンロード再生の違い

近年はインターネットの環境(回線速度など)も整っているので、気軽にすぐ再生できるストリーミング再生ばかり利用されていて、ダウンロード再生はまったく利用されていないのではないかと思う人もいるかもしれません。
そこで、実際にそれぞれの利用状況を調査してみたところ、2019年12月の実績でストリーミングが約8割、ダウンロードが約2割という数値が出ており、ダウンロード再生もまだまだそれなりに利用されていることが分かります。

新しくプレイヤーを作ることになった経緯(脱Silverlight)

以前のプレイヤーは、Microsoftが提供しているSilverlightというプラットフォームを利用して開発していました。
Silverlightは様々な動画サービスで古くから使われている技術で、ブラウザのPluginとして提供されており、DMMの動画配信サービスでもIEでストリーミング再生する場合はSilverlightを利用しています。

www.itmedia.co.jp

このSilverlightを採用していた理由は主に2つあり、1つ目は1ソースでWindows・macOS両方のデスクトップアプリケーションを提供できたことです。

Silverlightはブラウザのpluginとして動作する以外に、ブラウザー外実行(Out-Of-Browser)という機能を備えており、それを利用してデスクトップアプリケーションとして動作させることができます。また、SilverlightはWindows・macOSの両方の環境に対応しているため、Silverlightでプレイヤーを開発することで、1ソースでWindows用とmacOS用のプレイヤーを提供できます。

www.microsoft.com

2つ目は、DRMのかかった動画再生が可能なことです。
ダウンロード再生用のファイルは、購入したユーザー以外は再生できないようにしたり、再生回数や期間を制限するためにDRMをかけたりする必要があり、動画を再生するプレイヤーもそれに対応していなければなりません。以前のプレイヤーの開発にあたっていた当時は、現実的に扱えるDRMがMicrosoftから提供されているPlayReady(昔はWMDRM)くらいで、このPlayReadyがかかったwmvファイルであればMicrosoftから再生するための機能がSilverlight用に提供されていました。

以上の理由から、ダウンロード再生用のプレイヤーはSilverlightで作成することが最適だったというわけです。

しかし、Silverlightは2021年10月12日にサポートを終了することが発表されており、それ以降はバグフィックスやセキュリティに関するアップデートが行われなくなるため、安定して安全なプレイヤーを提供するためにサポート終了までに対応する必要がありました。

https://support.microsoft.com/ja-jp/help/4511036/silverlight-end-of-supportsupport.microsoft.com

さらに、macOS版のSilverlightは32bit版にしか対応していませんが、2019年10月にリリースされたmacOS Catalina以降のバージョンでは32bitアプリケーションが動作しなくなることが分かっていたため、そのリリース時期までにSilverlight以外の方法でプレイヤーを提供する必要がありました。

support.apple.com

また、Silverlightのプレイヤー用にwmvファイルを配信していますが、このwmvファイルはPCのダウンロード再生用にしか利用しておらず、そのためだけにエンコードやファイルを保持するためのストレージ(現状約3PB)のコストがかかっています。
そのため、wmvファイルの配信を止めたいという事情もあり、プレイヤーを刷新する流れになりました。

新しいプレイヤーはElectron製

新しいプレイヤーは、Electronという技術を利用しています。
ElectronはNode.js + Chromiumで構成されており、AtomやVisual Studio Codeといったエディタや、Slackクライアント、SkypeクライアントもElectronで作成されています。

electronjs.org

Electronで開発することになった理由は、大きく分けて主に以下の3つになります。

動画再生機能

最低限の要件として、H.264 + AACの動画(現状の最高画質で1080p)を再生できる必要があり、さらに、ダウンロード再生用の動画データはDRMがかかっているため、DRMに対応している必要があります。

クロスプラットフォーム対応

Silverlightでは1ソースでWindows・macOS用のプレイヤーを提供できていましたが、もしWindowsとmacOSでソースが別になってしまえば、開発や維持管理のコストが大きくなると考えられます。その結果、何をするにしてもフットワークが重くなるのではという懸念がありました。
そのため、1ソースでWindows・macOS両方のOS用のバイナリがビルドできることも大きなポイントになっていました。

学習コスト・メンテナンス性

利用する言語の学習コストが高かったり、特定の環境でないと開発やビルドができない(Visual Studioの最新版が入っている環境でないといけないなど)といった、先々メンテナンスしていくうえで問題となり得る点は少ない方が良いという考えもありました。

上記を踏まえたうえで、どのようなプラットフォーム・言語・フレームワークが適切なのかを調査したところ、JavaScript+HTML+CSSというWebの技術で構築でき、MSE+EMEで動画再生できるElectronが良さそうだという結論になりました。

プレイヤーの技術的な部分

Electronで開発されているということはお伝えしましたが、他にどんな技術が使われているのかなどについても紹介したいと思います。

再生コア部分について

ここでは動画プレイヤーとしてメインの機能である再生部分について紹介したいと思います。

新しいダウンロード再生用のファイルはCMAF準拠のFragmented MP4になっており、かつWidevineというDRMがかかっているため、そのままではvideo要素で再生できません(CMAFについては次の丸山さんの記事で詳細に説明されます)。
そこで登場するのがMSE(Media Source Extensions)とEME(Encrypted Media Extensions)というAPIになります。

www.w3.org

www.w3.org

MSEは分割された動画データ(以降セグメントと呼びます)を動的にvideo要素に食わせる機能を持っており、EMEはCDMと呼ばれるDRMの処理を行うモジュールを利用して暗号化されたセグメントを復号するためのやり取りをする機能を持っています。この2つのAPIを利用することで、DRMのかかったファイルをvideo要素で再生させることができます。

再生するまでの大まかな処理の流れですが、まず入力されたファイルの構造を解析し、メタ情報やセグメントを抽出します。ファイル解析については、特にライブラリなどは使っておらず、全て自前のプログラムで解析しています。ここで破損したファイルや不正なファイルをはじくことができ、また、メタ情報からどの作品なのか・何パート目のファイルなのかなどを特定できます。

ファイルを解析した後に、その解析したデータを元にMSEにセグメントを突っ込むと再生される…とはならず、前述のとおりセグメントにはDRMがかかっているため、セグメントを再生できる状態に復号するための情報(=ライセンス情報)が必要となります。

ライセンス情報を取得するためには、まずEME対してどのコンテンツをどのDRMで再生させたいというリクエストを行い、ライセンスサーバとやり取りを行うためのメッセージをEMEから取得します。そのメッセージとユーザ情報をライセンスサーバに送ることで、ライセンスサーバが再生しても問題ないと判断した場合はライセンスサーバからライセンス情報が返ってきます。そうして最終的に取得できたライセンス情報をEMEに設定してあげることで、やっと再生できます。動画を再生するために重要なこのあたりの制御も、全て自前のプログラムで行われています。

再生が始まった後も、video要素だけで再生させている場合と異なり細かく制御する必要があります。例えばvideo要素だけで再生している場合は動画の再生が終わると自動的にendedというイベントが発生しますが、MSEで再生している場合は自動では発生しません。そのため、MSEへのセグメントの追加が終了したタイミングでMSEのendOfStream()というメソッドを呼んであげる必要があります。

また、ライセンスサーバとのやり取りを行う途中で入力フォームを表示してユーザーの認証を行ったり、ライセンスサーバから返ってきた結果によってメッセージを表示したりするようなビューの絡む処理が発生します。そこでMSEやEMEを扱う再生コア部分とビューが密結合していると、ビューだけ変えたいのに再生コア部分を触らないといけない、再生コア部分だけ再利用したいのにビューが邪魔になるといった問題が発生します。 そのため、ビューの絡むような処理は再生コア部分の外から渡せるようになっており、再生コア部分とビューが上手い具合に分離できるような作りになっています。

余談 ~プレイヤーにWidevineCDMを同梱できるかGoogleに確認した話~

Electron + Widevineでプレイヤーを提供するために、初期のElectronではWidevineCDMをプレイヤーに同梱する必要がありましたが、開発前の時点では同梱しても問題ないかが不明でした。 そのため、Widevineを扱うためのCWIPという資格を取得するためにGoogle Kirklandで研修を受けた際に、上記についてGoogleの方に直接確認をしました。
もしCDMを同梱することがライセンス的に不可能な場合は開発の方針を変えざるを得ないため、意図した内容が正確に伝わるように(英語でやり取りするため)尋ねるべきことを事前に図や文章にまとめて質問しましたが、こちらの心配とは裏腹にあっさりOKと言われました。

利用しているフレームワークやライブラリについて

ここでは、プレイヤーを開発するにあたりどのようなフレームワークやライブラリを利用しているのかを紹介したいと思います。

UIロジックの分離やコンポーネントの再開発を防ぐためにVue.jsを導入

UI部分を作りこんでいくと、どうしてもビジネスロジックだけではなくUIを操作するためのロジックも多くなり、また、リスト表示やスライドバー、アイコンのTooltip表示のようなどこかで見たことあるような決まった動きするUIを実装する必要が出てきました。
自前でUIライブラリのようなものを作成することもできますが、そのための工数や今後メンテナンスしていくことを考えると得策ではないため、何かUIをうまく構築できるフレームワークを使うことを検討しました。その結果、使用頻度の高そうなUIが数多く提供されており、かつ配信基盤チーム内のシステムで既にVue.jsの技術が利用されているという点から、Vue.js(+Vuetify.js)というフレームワークを導入しました。

jp.vuejs.org

vuetifyjs.com

コードの可読性・メンテナンス性の向上のためTypeScriptで開発

ElectronはJavaScriptでコードを書きますが、JavaScriptは動的型付けで変数の型やスコープを常に把握していないと不具合を起こす原因となりかねません。コード量が少ないうちはまだ良いのですが、コード量が多くなるにつれて全ての変数を把握することがだんだんと辛くなってきます。
さらに、Electronはデバッグ時に毎回ビルド→アプリ起動をする必要があり(ユニットテストではなくUIの動作を確認する場合)、コードを修正してから確認するまでに若干時間がかかります。 そのため、変数の中に想定してしないデータを代入してしまっているような不具合だった場合、ビルドする時間ばかり増えて開発効率が悪くなります。

上記のような問題を解消するために、コードで型を宣言できるTypeScriptでコードを書くように切り替えました。
TypeScriptにしたことで、コーディング時やビルドする時点で間違いやエラーに気づけるようになり、エディタのコード補完機能も使えるようになったので、開発効率がかなり良くなったと思います。 特にElectronの場合は、規模が大きくなるにつれて動作確認するためのアプリの起動時間も増していくため、起動する前のタイミングで簡単なバグなどを潰せるのは意義が大きかったです。

www.typescriptlang.org

Electron Tips

ここでは、Electronを利用して開発するなかで遭遇した問題や、そこでの対応についていくつか紹介したいと思います。

メニューバーに関する設定はOSごとに変える

Windowsでは、ウィンドウ右上の閉じるボタン押すなどアプリケーションを閉じる操作をした場合はプロセスを完全に終了という動きで統一しています。一方macOSでは、アプリケーションを完全に終了(Cmd + Q等)した場合と左上の閉じるボタンを押した場合で動作が異なり、左上の閉じるボタンの場合はプロセスが完全には終了せずにDockに残ったままにするという動作にしています。
そのため、Windowsは通常ウィンドウの上部にあるメニューバーは必要ないので非表示にしていますが、macOSの場合はウィンドウの中ではなく画面全体の方のメニューバーの表示になるので、Quitだけ表示するようにしています。

f:id:yanoshi:20200127120805p:plain
macOSのメニューバー

ファイルの読み込みイベントはOSによって違う

動画ファイルをダブルクリックすると、プレイヤーが起動中の場合はそのまま再生が始まり、起動していない場合はプレイヤーを起動した後に自動で再生が始まるという仕様がありますが、このあたりの制御もWindowsとmacOSで異なっています。
Windowsの場合、動画ファイルからプレイヤー起動した場合はプロセスの引数からファイルパスが取得でき、プレイヤー起動中にファイルをダブルクリックするとsecond-instanceというイベントが発火されるので、そのイベントハンドラの中でファイルを再生させる処理を始めることになります。
macOSの場合は、動画ファイルから起動した時でもプレイヤー起動中に動画ファイルをダブルクリックした時でも、open-fileというイベントが発生するようになっており、そのイベントハンドラの中でファイルを再生させる処理を始めます。
ただし、macOSの場合は上述のとおり、Dockに残ったままの状態からウィンドウを表示するといった動きも考慮しなければならず、mainプロセス側で起動時の動画ファイルパスを保持しておいて、rendererプロセス側で準備ができたタイミングでmainプロセスから動画ファイルパスを受け取って再生を始めるといったややこしい処理もしています。

Vue CLIによるプロジェクトの初期構築

Vue.jsで開発する場合、Vue CLIというツールを利用すると開発環境を簡単に構築できますが、vue-cli-plugin-electron-builderというElectronのプロジェクトを作成するためのVue CLIのプラグインが公開されています。 このプラグインを利用すると、コマンドを叩くだけであっという間にElectron + Vue.jsのプロジェクトが構築できます。

github.com

多重起動を防止する機能を実装したためにプロセスが無限増殖

現状では、Electronは同じアプリケーションを多重起動すると一部の挙動がおかしくなるため、DMM Player v2では多重起動できないようにしています。その処理を当初はElectronのドキュメントで紹介されている方法で実装しましたが、多重起動しようとした数だけ裏でElectronのプロセスが増え続けるという現象が発生しました。
そこで、いろいろと試した結果、多重起動を防止するタイミングでapp.quit()ではなく、app.exit()でプロセスを終了すると、プロセスが増え続けるという現象は発生しなくなりました。

ビルドからインストーラーの作成までコマンド1つ

Silverlight製のDMM PlayerではアプリケーションのビルドはVisual Studioで行い、ビルドしたファイルのWindowsとmacOS用のインストーラーをまた別のプロダクトを利用して作成するという手間がかかっていました。

そのため、Electronの場合にそのあたりの流れを楽にできないかと調査したところ、electron-builderというモジュールを利用することで、ビルドするタイミングでインストーラーを作成できることが分かりました。

www.electron.build

このelectron-builderを利用し、ビルドやインストーラー生成に必要なスクリプトや設定を事前に用意しておくことで、npm run build:prdといったコマンドを実行するだけでインストーラーが作成できるようになっています。

electron-builderはかなりの数のオプションがあり、インストール時に表示するライセンス規約表示や管理者権限でインストールさせるなどについても設定を書くだけでそれらしいインストーラーができあがります。
Windowsの場合は64bit版、32bit版の同梱にも対応しており、実際DMM Player v2はインストーラーとしては1ファイルだけですが、インストール時に適切なほうが自動でインストールされるようになっています。

まとめ

長い間活躍してきたSilverlight製のDMM Playerは、Silverlightのサポート切れや動画配信環境の移り変わりとともに役目を終え、これからはElectron製のDMM Player v2が使われていくことになります。
ひとまずリリースはされましたが、リリース自体がゴールではなくスタートだという考えのもと、今後も継続的に改善をしていければと考えています。