blog

DeNAのエンジニアが考えていることや、担当しているサービスについて情報発信しています

2022.06.14 技術記事

ライブサービスのエンターテインメント性を支えるグラフィックスの工夫

by tomoya.hirano

#pococha #graphics #animation

はじめに

こんにちは。DeNA Pococha事業部エンジニアの平野です。主にPocochaにおける、iOS版アプリの動作パフォーマンスの改善チームを担当しています。 当記事では、Pococha内で使用されるアイテム再生の実装における創意工夫について紹介させていただきたいと思います。

※当記事の執筆にあたり同チームメンバーの郭にも協力してもらいました。

配信の盛り上がりに不可欠なアイテムの存在

Pocochaの特徴的な機能の一つとして、配信を視聴中にコインを消費して使うことのできるアイテムの存在があります。 アイテムは配信を盛り上げるのには不可欠で、これによってライバーとリスナーは画面越しに一体感を感じながら同じ時間を過ごすことができます。 アイテムは画面左端からスライドインするアイテムの小アイテムと、画面全体で再生されるアイテムの大アイテムがあります。 どちらもiOS, Android, ブラウザといったマルチプラットフォーム上で同じように動作します。 また、アイテムは定期的に新作が登場し、現在は大、小合わせて440を超える数のアイテムをPocochaで使うことができます。

▲小アイテムの一部リスト(左)と大アイテムの使用イメージ(右)

小アイテムについて

小アイテムは主に自分とライバーだけでなく、他の人の会話に対してのリアクションにも使用されます。 種類も多く消費するコインも少ないため、使われるタイミングが多いのが特徴です。 そこで、Pocochaではデータサイズが小さくマルチプラットフォームで利用できるWebPを採用しています。 アイテムは自分が使った時以外にも、他のリスナーが使った場合も再生する必要があります。 データサイズの小ささは、アイテムの使用から表示までの時間を短くしリアルタイム感を向上させるのに貢献します。

大アイテムについて

大アイテムはダイナミックな演出が特徴で、配信枠にいる全員の注目を集めることができ、数秒間の間、デバイスの全画面を占有して再生されます。 Pocochaはサービス立ち上げの段階では、大アイテムは花火エフェクトの1種類しかありませんでした。 当時は実現可能性を優先し透過PNGをフレーム数分用意してシーケンスアニメーションすることで大アイテムの再生を実現していました。

imageView.animationImages = images
imageView.duration = 3
imageView.startAnimation()

しかし、これにはいくつかの問題がありました。 まず、デバイスの全画面を覆うほど大きなPNGを毎フレーム描画するというのは当時のデバイスには難しいことです。 特にデバイスはカメラやネットワークといった重い処理を並行して実行しており、この状態では12fps程度が現実的なものでした。 加えて、PNGはデータサイズが大きいという問題があります。500MB程度の非圧縮の透過付きmovを連番PNGにすると、およそ300MB程度になります。 アイテムの種類を増やすには、オンデマンドリソースとしてネットワークごしにアイテムデータを取得できる程度に軽量なデータサイズにする必要がありました。 そこで、いくつかのフォーマットを試すことにしました。 最初にアニメーションに対応したPNGということで、APNGもアニメーションをサポートしていますが、半分の150MB程度にしかなりませんでした。 小アイテムで利用しているWebPも、当時はソフトウェアデコードする必要があり全画面という大きなシーケンスを再生するには不向きでした。 また、有名なアニメーションライブラリのLottieを選択することも出来ましたが、Lottieはベクターアニメーション特有の表現の癖がありユーザーがどんなエフェクトを受け入れるか分からない以上、ラスタライズされたシーケンスを使うことを重要視しました。

このような検証を元に、ハードウェアでデコード出来て圧縮率も高いフォーマットが必要ということがわかり動画ファイルであるmp4を利用することにしました。

mp4の場合はキーフレームの数を減らすことで1.4MB程度までデータサイズを抑えることができました。

アイテム再生プレーヤの開発とフォーマットの策定

動画ファイルが適していることが判明したものの、導入にはまだ課題がありました。 それは、iOSもAndroidも標準のプレーヤーでは背景を透過させて動画ファイルを再生する手段がないという点です。 また、mp4自体もアルファチャンネルを持つことが出来ません。 そこで、独自のアイテム再生プレーヤーと動画の扱いを策定する必要が出てきました。 まず、動画に関しては色情報を持ったbase.mp4とアルファチャンネルだけを持ったalpha.mp4というルールに則った2つのファイルを扱うことにしました。

アイテム再生プレーヤーは、この2つの動画ファイルから同タイミングのフレームを取り出して合成します。 合成は次のようなOpenGLESのシェーダ上で行われ、背景透過が可能なビューにレンダリングされます。

varying highp vec2 textureCoordinate;
uniform sampler2D videoFrame;
uniform sampler2D videoFrame2;
void main() {
  highp vec4 color = texture2D(videoFrame, textureCoordinate);
  highp vec4 colorAlpha = texture2D(videoFrame2, textureCoordinate);
  gl_FragColor = vec4(color.r, color.g, color.b, colorAlpha.r);
}

ベクターのLottieと比較して解像度が劣るものの、圧縮された配信映像の上で表示するため上手に溶け込みました。 このプレーヤーはKitsunebiという名前を付け、OSSにて公開しました。

https://github.com/noppefoxwolf/Kitsunebi

アイテム再生プレーヤのその後

Pocochaは配信、映像のデコードやデジタル化粧の処理、リアルタイムにコメントなどのメタデータの送受信などの負荷の高い処理を同時に行なっています。 デバイスのリソースは有限なため、それぞれの機能は常に最適化を続けています。アイテムプレーヤも例に漏れず、継続的に最適化を繰り返していました。 Pocochaはしばらく前項で解説したプレーヤーを使っていましたが、2020年にはiOSはOpenGLESからMetalへの移行を完了しました。 さらに2021年には、iOSとAndroidで別々のアプローチを行うことで、よりパフォーマンスを改善することができました。 iOSは、HEVC Video with alphaを利用することにしました。このフォーマットはAppleが開発したビデオフォーマットでalphaチャンネルを含むことができ、さらに圧縮率の高いビデオフォーマットです。

このフォーマットを利用することで、これまでbase.mp4とalpha.mp4の2つをデコードしていましたが、main.hevcをデコードするだけでビデオのアルファ付きフレームを取り出すことが出来るようになったため、デコードコストは最大35%程度改善しました。 また、圧縮率も向上しこれまでのアイテムのデータと同じサイズでも、高品質な映像を扱うことが出来るようになりました。 ここで改善されたデータサイズは、ユーザーのネットワークトラフィックを改善しその分を映像の視聴や配信の安定性に寄与することができます。 この新プレーヤもHEVCPlayerViewとして、OSSで公開しています。

https://github.com/DeNA/HEVCPlayerView

Androidは対応機種が多いため、特殊なデコーダを必要としないmp4を引き続き採用することにしました。 ただし、ファイルは2つではなくiOSのhevcのように色情報とalpha情報を一つのビデオファイルにレイアウトすることにしました。内部ではこの方式をAlpha Packed formatと呼称しています。 これによって26%程度デコードコストが減り、また2つのデコーダによる微妙なデコード時間のズレが0になりました。

▲左側にカラー情報・右側にアルファチャンネルを格納している。

さいごに

Pocochaの大アイテムの実装では、MetalやOpenGLESといった一般的なアプリ開発からすると低レイヤーな技術が使われています。 これらの技術は、最初から熟知したエンジニアがいたわけではありません。 ユーザーの体験の向上は、自分の持ちうる能力だけを使っているだけでは得られないものです。 Pocochaではユーザー体験をより良いものにするために、苦労を惜しまずに学び続け達成する文化があります。 これからも、革新的な体験を実現するためにしなくてはならないことがたくさんあります。 幅広い領域に関心を持ち、Pocochaが提供する価値を最大化できるよう探求を続けていきます。

最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。

recruit

DeNAでは、失敗を恐れず常に挑戦し続けるエンジニアを募集しています。