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.2k
「騒ゲーハイライト」について【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
SREが投資するAIOps ~ペアーズにおけるLLM for Developerへの取り組み~
takumiogawa
1
190
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
0
130
CysharpのOSS群から見るModern C#の現在地
neuecc
2
3.2k
ドメインの本質を掴む / Get the essence of the domain
sinsoku
2
150
EventHub Startup CTO of the year 2024 ピッチ資料
eventhub
0
110
Amazon Personalizeのレコメンドシステム構築、実際何するの?〜大体10分で具体的なイメージをつかむ〜
kniino
1
100
インフラとバックエンドとフロントエンドをくまなく調べて遅いアプリを早くした件
tubone24
1
430
リンクアンドモチベーション ソフトウェアエンジニア向け紹介資料 / Introduction to Link and Motivation for Software Engineers
lmi
4
300k
サイバーセキュリティと認知バイアス:対策の隙を埋める心理学的アプローチ
shumei_ito
0
380
強いチームと開発生産性
onk
PRO
34
11k
AWS Media Services 最新サービスアップデート 2024
eijikominami
0
200
Security-JAWS【第35回】勉強会クラウドにおけるマルウェアやコンテンツ改ざんへの対策
4su_para
0
180
Featured
See All Featured
The Power of CSS Pseudo Elements
geoffreycrofte
73
5.3k
What's new in Ruby 2.0
geeforr
343
31k
For a Future-Friendly Web
brad_frost
175
9.4k
A Philosophy of Restraint
colly
203
16k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
191
16k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
131
33k
Designing on Purpose - Digital PM Summit 2013
jponch
115
7k
Visualization
eitanlees
145
15k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Git: the NoSQL Database
bkeepers
PRO
427
64k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
38
1.8k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
0
89
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