Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
「騒ゲーハイライト」について【MIXI TECH CONFERENCE 2023】
Search
YuukiARIA
March 02, 2023
Technology
1
1.3k
「騒ゲーハイライト」について【MIXI TECH CONFERENCE 2023】
MIXI TECH CONFERENCE 2023 Day 1 でお話したスライドです。
https://techcon.mixi.co.jp/d1-3
YuukiARIA
March 02, 2023
Tweet
Share
More Decks by YuukiARIA
See All by YuukiARIA
Game Development Tutorial 2021
yuukiaria
0
25k
リアルタイム通信をささえるブラナイのサーバー開発と運用(前編)
yuukiaria
1
980
Other Decks in Technology
See All in Technology
遷移の高速化 ヤフートップの試行錯誤
narirou
6
1.2k
設計を積み重ねてシステムを刷新する
sansantech
PRO
0
160
システム・ML活用を広げるdbtのデータモデリング / Expanding System & ML Use with dbt Modeling
i125
1
320
JAWS FESTA 2024「バスロケ」GPS×サーバーレスの開発と運用の舞台裏/jawsfesta2024-bus-gps-serverless
ma2shita
3
200
Windows の新しい管理者保護モード
murachiakira
0
200
Active Directory攻防
cryptopeg
PRO
8
5.5k
AWSアカウントのセキュリティ自動化、どこまで進める? 最適な設計と実践ポイント
yuobayashi
7
620
Potential EM 制度を始めた理由、そして2年後にやめた理由 - EMConf JP 2025
hoyo
2
2.6k
1行のコードから社会課題の解決へ: EMの探究、事業・技術・組織を紡ぐ実践知 / EM Conf 2025
9ma3r
11
3.8k
Visualize, Visualize, Visualize and rclone
tomoaki0705
9
83k
MIMEと文字コードの闇
hirachan
2
1.4k
依存パッケージの更新はコツコツが勝つコツ! / phpcon_nagoya2025
blue_goheimochi
3
220
Featured
See All Featured
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Building Your Own Lightsaber
phodgson
104
6.2k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
30
2.2k
The World Runs on Bad Software
bkeepers
PRO
67
11k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Java REST API Framework Comparison - PWX 2021
mraible
29
8.4k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
160
15k
Typedesign – Prime Four
hannesfritz
40
2.5k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
233
17k
Music & Morning Musume
bryan
46
6.4k
Designing Experiences People Love
moore
140
23k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Transcript
©MIXI MIXI TECH CONFERENCE 2023 「騒ゲーハイライト」について 開発本部 CTO室 クライアントグループ Yuki
Shinobu
©MIXI Yuki Shinobu / YuukiARIA 株式会社MIXI 開発本部CTO室クライアントグループ 2014年新卒入社。 直近ではゴーストスクランブルのテクニカルリードとして 主にクライアントの技術課題に取り組む。
2 自己紹介
©MIXI 本日の内容 • ゴーストスクランブルと「騒ゲーハイライト」システム • Unityゲーム画面のキャプチャ • Androidでmp4動画を出力するまで • 様々な不具合対応
3
©MIXI ゴーストスクランブルについて
©MIXI ゴーストスクランブル モンストシリーズの協力アクションスマホゲーム ストブルと略します 5 ゲームエンジン グラフィック サウンド Unity 2020.3
Universal Render Pipeline (URP) CRI ADX
©MIXI 「騒ゲーハイライト」とは? ゲームプレイの特定の15秒を動画として保存できる機能 6
©MIXI 基本の設計コンセプト
©MIXI システムの要件 • キャプチャ実行時,直前の10秒程度を含む前後の動画を保存 • インゲームプレイ中,いつでもキャプチャ実行可能 • 保存した動画はSNS等へ投稿できる形式 ◦ Unity等に依存しない,一般的な
mp4 (H.264) 形式 8
©MIXI システムの要件 • キャプチャ実行時,直前の10秒程度を含む前後の動画を保存 • インゲームプレイ中,いつでもキャプチャ実行可能 • 保存した動画はSNS等へ投稿できる形式 ◦ Unity等に依存しない,一般的な
mp4 (H.264) 形式 9 これは ドライブレコーダーだ! 最近はゲーム機に搭載されていることも多い
©MIXI 基本の設計コンセプト 10
©MIXI 基本の設計コンセプト 11 15秒分の固定長の循環バッファを作り 常時録画・録音
©MIXI 基本の設計コンセプト 12 Android/iOSのコーデックAPIを利用
©MIXI ゲーム画面のキャプチャ
©MIXI ゲーム画面のキャプチャ 当初はRendererFeatureとして実装 RendererFeature: Universal Render Pipelineでカスタムの描画パスを挿入する仕組み 14 キャプチャ処理の実体 パラメータ定義
Rendererにパスを挿入するコンポーネント
©MIXI ゲーム画面のキャプチャ CameraCaptureBridgeを使うとデリゲートを登録するだけで簡単 にキャプチャ処理を挟むことができる 15 void Execute(RenderTargetIdentifier renderTargetIdentifier, CommandBuffer commandBuffer)
{ using (new ProfilingScope(commandBuffer, _profilingSampler)) { commandBuffer.Blit(renderTargetIdentifier, _captureOutputRenderTarget, _material); } } CameraCaptureBridge.AddCaptureAction(_targetCamera, Execute);
©MIXI mp4ファイルを作る
©MIXI コーデックAPI 連続画像を動画にエンコードするために,低レベルAPIを使う 17 iOS VTCompressionSession AVAssetWriter Android MediaCodec MediaMuxer
©MIXI コーデックAPI 連続画像を動画にエンコードするために,低レベルAPIを使う 18 iOS VTCompressionSession AVAssetWriter Android MediaCodec MediaMuxer
端末ごとの差異も少なく,それほどトラブルが発生しなかった
©MIXI コーデックAPI 連続画像を動画にエンコードするために,低レベルAPIを使う 19 iOS VTCompressionSession AVAssetWriter Android MediaCodec MediaMuxer
大変だった
©MIXI android.media.MediaCodec
©MIXI MediaCodecの入力データ • android.view.Surface ◦ MediaProjectionと相性が良いが今回の要件には合わない • android.media.Image ◦ ピクセルフォーマットを気にせずYUVの各Planeを扱えるが遅い
• java.nio.ByteBuffer ◦ 最速だがピクセルフォーマットを自分で組み立てる必要がある 21
©MIXI ByteBufferを使おう
©MIXI ByteBufferで入力するには… MediaCodecが直接扱える生データを入力する必要がある 以下のことを自分でやらなければならない • キャプチャした画像の色空間をRGBからYUVに変換 • YUVデータの色差成分をサブサンプリングしてYUV420に • YUV420データを適切なピクセルフォーマットにパック
23
©MIXI • キャプチャした画像の色空間をRGBからYUVに変換 • YUVデータの色差成分をサブサンプリングしてYUV420に • YUV420データを適切なピクセルフォーマットにパック 24
©MIXI YUV色空間 映像分野で標準的な色空間 輝度成分 Y と色差成分 U, V から成る ITU-R
BT.601 の係数を使って以下のように変換 25
©MIXI YUV色空間 キャプチャ時にYUV変換してRGB値としてRenderTextureに出力 U, V の範囲は –0.5~0.5 なので +0.5 している
26 YUVをRenderTextureのRGBとして出力 YUV変換 Capture Y U V
©MIXI • キャプチャした画像の色空間をRGBからYUVに変換 • YUVデータの色差成分をサブサンプリングしてYUV420に • YUV420データを適切なピクセルフォーマットにパック 27
©MIXI クロマサブサンプリング 色差成分を間引くことで圧縮 どのように間引くかによって YUV422, YUV420, YUV411 等の種類がある MediaCodecの入力フォーマットでは基本的にYUV420を使う 実際の間引きはピクセルフォーマット整形時に行う
28 U,V は 2×2 ピクセルあたり 1ピクセル分しか取らない https://developer.android.com/reference/android/media/MediaCodec
©MIXI • キャプチャした画像の色空間をRGBからYUVに変換 • YUVデータの色差成分をサブサンプリングしてYUV420に • YUV420データを適切なピクセルフォーマットにパック 29
©MIXI ピクセルフォーマット 同じ色空間のデータでもバイナリデータの配置方法は様々 RGBデータでも, という配置だけではなく,色ごとに分離した のような配置方法も場合によっては便利 30 Packed (Interleaved) Planar
©MIXI NV12 YUV420のピクセルフォーマットの一つ • Y-Plane と UV-Plane からなる Semi-planar 形式
• UV-Plane では UVUV... のように並ぶ 31 Packed UV Planar Y Semi-planar YUV420のピクセルフォーマットたち • NV12 YYYY...UVUV... • NV21 YYYY...VUVU... • I420 YYYY...UU...VV... • YV12 YYYY...VV...UU...
©MIXI MediaCodecへの入力まとめ 32
©MIXI 音声のキャプチャ レコーダー側は音声データを受け取るのみで,録音の具体的な方法 には踏み込まない ◦ Unity Audioの場合はOnAudioFilterReadを使う ◦ CRI ADXの場合はCriAtomExOutputAnalyzerを使う
ストブルはこちらの方法 音声はUnity C#側でリングバッファを用意して常時録音 mp4出力時にエンコーダへ音声データを書き込む 33
©MIXI mp4ファイル出力とシェア
©MIXI mp4ファイル書き出し I-フレーム(キーフレーム)が開始フレームになるように注意 補間 (P, B) フレームから開始すると動画の頭に乱れが生じる 最終的な動画の範囲はI-フレームを基準として決め,その時間に音声を合わせる 35 48,000
frames/sec 30 frames/sec
©MIXI OS機能のサポート • 他のアプリへファイル・テキストを共有 ◦ iOS: UIActivityViewController ◦ Android: Intent.createChooser
• 「アルバム」へファイルを保存 ◦ iOS: PHPhotoLibrary ◦ Android: MediaStore API 36 特にiOSのアプリ内データはユーザーが触れづらいので, 外側に取り出す仕組みが必要。 もちろん開発時のデータ確認でも必要だった。
©MIXI MediaStore API • Android 10 以降で Scoped Storage が有効
◦ 生パスではなくコンテンツURIを介してファイルアクセス ◦ WRITE_EXTERNAL_STORAGE 権限が不要 • Android 9 以前では直接 Movies フォルダへコピー ◦ WRITE_EXTERNAL_STORAGE 権限が必要 37
©MIXI システムのまとめ
©MIXI 動画エンコードパート 39
©MIXI 音声エンコードパート 40
©MIXI mp4ファイル出力パート 41
©MIXI ドキドキの多端末検証
©MIXI 様々な問題が発生しました
©MIXI デフォルトが NV12 ではない端末がある • Zenfone 8 ◦ I420: Y,
U, V の順の Planar 形式 • Y は正常 • U と V の取り方がかなり異なる ◦ 格子状に拡大したような状態になる 44
©MIXI アライメント問題 • 一部端末では横幅が16の倍数でなければならない ◦ Pixel 3a • パディングが必要 ◦
パディングが欠けているため斜めに歪む 45
©MIXI 続・アライメント問題 • さらに一部の端末では16の倍数でもだめなことがある ◦ Pixel 3a XL (≧ Android
12) • MediaCodec#getInputFormat の戻り値に含まれる stride, slice-height の値を使用する必要がある • stride, slice-height は含まれていないこともある ◦ その場合は16の倍数にアラインするのみとした 46
©MIXI slice-height の考慮 • Plane間にも適切なパディングが必要なことがある • 高さが適切でないと下部に緑色の領域が現れる事が多い ◦ U, V
が足りず途中で0になるため 47
©MIXI 入力バッファのサイズ getInputBufferで返されるByteBufferは stride, slice-height を考 慮した必要十分なサイズが確保されている 48
©MIXI 入力バッファのサイズ Y-Plane のサイズは 768 × 864 = 663,552 UV-Plane
のサイズは 768 × 848 / 2 = 325,632 ∴計 989,184 49 問題 width = 752, height = 848 で MediaCodec を初期化したところ, 入力フォーマットに stride = 768, slice-height = 864 という値が含まれ ていた。このとき,入力バッファのサイズは何バイトか。
©MIXI 入力バッファのサイズ Y-Plane のサイズは 768 × 864 = 663,552 UV-Plane
のサイズは 768 × 848 / 2 = 325,632 ∴計 989,184 50 問題 width = 752, height = 848 で MediaCodec を初期化したところ, 入力フォーマットに stride = 768, slice-height = 864 という値が含まれ ていた。このとき,入力バッファのサイズは何バイトか。 しかし実際には 989,169 15バイト少ない…
©MIXI 入力バッファのサイズ getInputBufferで返されるByteBufferは stride, slice-height を考 慮した必要十分なサイズが確保されている 足りない15バイトは 768 (stride)
– 752 (width) = 15 UV-Plane の最終行のパディングが不要 51
©MIXI stride, slice-height まとめ 52
©MIXI パフォーマンス問題 NV12データを Unity C# から Android Java へ渡す部分 53
byte[] frameData; AndroidJavaClass pluginClass; pluginClass.CallStatic("writeFrame", frameData); Unity 2019 vs 2020 で性能が2~3桁違う! 400,000 バイトの配列を渡してみると… Unity 2019 350 ms Unity 2020 0.5 ms (x700 faster!)
©MIXI パフォーマンス問題 Unity 2020.1.0 リリースノート https://unity.com/ja/releases/editor/whats-new/2020.1.0 54
©MIXI ということで
©MIXI 完成!!
©MIXI