Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
「騒ゲーハイライト」について【MIXI TECH CONFERENCE 2023】
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
YuukiARIA
March 02, 2023
Technology
1.9k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
「騒ゲーハイライト」について【MIXI TECH CONFERENCE 2023】
MIXI TECH CONFERENCE 2023 Day 1 でお話したスライドです。
https://techcon.mixi.co.jp/d1-3
YuukiARIA
March 02, 2023
More Decks by YuukiARIA
See All by YuukiARIA
Game Development Tutorial 2021
yuukiaria
0
26k
リアルタイム通信をささえるブラナイのサーバー開発と運用(前編)
yuukiaria
1
1k
Other Decks in Technology
See All in Technology
AIエージェント時代のPlatform as a Product —— テックリードがPdMとして回す発見・導入・計測 / Platform as a Product in the AI Agent Era
toshi0607
1
390
AI coding 整合正規方法
philipz
0
550
Antigravity SDK for the Java Developer
glaforge
0
210
メルカリにおけるAI時代の高速プロトタイピング基盤「Arca」
ryotarai
15
12k
いちAWSエンジニアのAI活用を振り返る #devio2026 / devio osaka 2026 kawahara
masahirokawahara
1
110
AI臭い文章とは何なのか
nasuvitz
4
1.3k
TiDBファミリーにDWHが新登場!! TiDB最新情報 / TiDB update 202609
yoshiakiyamasaki
0
120
あけおめLINE 傾向とその対策
nasa9084
0
340
Cloudflare Workers 向けアプリを C# で構築する ~WASM Native AOT への道~
nenonaninu
1
1.7k
Apache Iceberg が拓く AI 時代のオープンレイクハウス
tomtanaka
0
110
C#コードの結合を可視化する Roslyn解析による設計改善と リファクタリング判断
dora56
0
830
認知負荷を吸収し、プロダクトをまたぐPR Preview基盤の設計事例
taiki45
2
430
Featured
See All Featured
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
35
2.8k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
450
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.6k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
410
A Soul's Torment
seathinner
8
3.6k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.7k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
2
290
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
550
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.8k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
Optimising Largest Contentful Paint
csswizardry
37
4k
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