yutopp's blog

サンドバッグになりたい

AvatarMakerを支える技術 (cluster)

これは クラスター Advent Calendar 2021、8日目の記事です。

qiita.com


yutoppです。今はクラスターでソフトウェアエンジニアをしています。

さて、11/1にAvatarMakerという機能がリリースされました。めでたいですね。

note.com

今回は、このAvatarMakerの技術的な背景の一部分を紹介したいと思います。こんな感じで動いているんだな~と楽しんでいただけたら幸いです。

要件

基本的な部分に関しては以下のようになっています。

  1. cluster内でアバターを作成できること (VR/NonVR)
  2. 服装を切り替えられること
  3. 顔の造形を変更できること
  4. 社内外のクリエイターがアバターのパーツをアップロードできること

03の 顔の造形を変更できること は、プロシージャルに目のパーツや輪郭などが動くようになるわけでアバターの表現力が広がります。 一方で、メッシュのめり込みなど発生しやすくなりますし、デザイン・3Dモデル作成の量産・品質調整の難易度とのトレードオフになります。

04の 社内外のクリエイターがアバターのパーツをアップロードできること は、シンプルにやることが多くなります。 データの作成を開かれた技術・適切な権限で、セットアップの手順やトラブルシュートも社外の第三者が問題なく行える品質を見据えて設計・実装をする必要があります。

大変ですね。

設計

f:id:yutopp:20211207181752p:plain
AvatarMaker増築前

f:id:yutopp:20211207181906p:plain
AvatarMaker増築後

新たにglTFをやり取りするフローと、clusterからVRMをアップロードするフローを追加しています。

ユースケースとしては、

  • クリエイターはDCCツールとUnityでアバターパーツを作り、glTFで出力
  • AvatarMakerは、そのglTFを組み合わせてアバター全体を編集
  • 編集が終わったらAvatarMakerがVRMとしてアバターを保存
  • 以降、通常のアバターとして利用可能

を考えていて、clusterの既存のアバターのエコシステムと共存するようにしています。

実は、別にAvatarMakerはホームでなくても実行でき、VRMとして保存しなくてもアバターで動き回れる設計にしてあるので、そのうちワールドやイベントでリアルタイムに着替えながら遊べる未来もできたらいいですね。今後に期待…

実装

全体

f:id:yutopp:20211207182129p:plain
コード構成

AvatarMakerはライブラリとスタンドアロンの開発実験アプリとして独立して作りはじめ、その後clusterにライブラリとして組み込みました。

クリエイターがパーツをアップロードできるようにするためには、アップロード部分のコードを配布する必要がありますし、動作確認用のミニマルなソフトウェアも必要になることを見越してこうしています。

メリットとしては、最小限の依存関係でビルドが高速なので、開発イテレーションをたくさん回せる点。また、ライブラリとして設計することにより境界が明確になるため、アプリケーション本体への影響が読みやすい点などを感じています。

データ形式:glTF

独自形式にするとUGCとの親和性が微妙なので、今回はオープンなデータ形式であるglTF2.0を採用しました。

f:id:yutopp:20211207182419p:plain

AvatarMakerのパーツは上記のようにhumanoidの状態でメッシュ分割されており、それぞれが独立したglTFのバイナリ形式で配布されます(clusterのVRMアバターと同じく難読化されます)。 それぞれに、”瞳のテクスチャはどれ” だとか ”肌のマテリアルはどうなっている” といった様々な情報を、glTFのextrasに詰めています。

VJson, VGltf

上記で書いたとおりglTFの拡張をかなり利用するつもりだったので、丁度いい実装はないかなということで自作のライブラリをプロダクトに導入しました。glTFのimport/exportと、VRMのexportに利用しています。

github.com

github.com

この点は技術選定で弊社TechLeadにも相談したのですが、自作ライブラリの導入にGoサインが出るのはなかなか狂気で良いなと密かに思いました。

forkせずに公開されている実装をそのまま使っています。glTFの拡張をヘビーに使っていますが、ライブラリのコードベースには一切手を加えずに対応できているので便利だと思います。

サーバーサイド合成 vs クライアントサイド合成

有料のパーツなどが発生した場合にサーバーサイドでパーツを合成したほうが堅牢だという話がありましたが、今回はクライアントサイドで合成しています。

既存VRMアバターと同様の難読化形式で配信する前提を置けば、Unity側で合成したほうがリアルタイムに着替えられますし、パーツごとに細かくデータをロードできてさわり心地も良いと思われます。デバッグもしやすいですしね。

パーツ合成、切り替えで毎回やるか・export時に一度か

AvatarMakerのパーツは切り替えるたびにhumanoidのskeletonを差し替えて1つのAvatarに合成しています。

最初は複数のパーツごとにhumanoidを重ねて描画していました。これは、skeletonの付け替えコストが重たいと想像していたためです。
しかし、skeletonの付け替えを試したところ全くコストにならず、フレーム落ちにも気づかない程度だったので、今はhumanoidのボーン構造をマージしたものに付け替えて単一のhumanoidを作るようになっています。

テクスチャ合成・カラーバリエーション

AvatarMakerは、瞳・ハイライト・眉毛・まつ毛・アイラインなどなど(他にもたくさんあります)、1つのパーツにテクスチャがもりもりに入っています。

f:id:yutopp:20211207182754p:plain

これらの複数のテクスチャを重ねて描画し・色味の変換も行うシェーダを今回実装しています。シェーダで描画しているのでリアルタイムに合成を行っても高速で感動しました。

最初はイケイケのCustomRenderTextureを用いて実装していたのですが、モバイルで真っ黒になるなど破滅したので、古典的な1x1のmeshをorthographicに描画するシェーダに途中で書き換えています。悲しいですが枯れた技術は今も最高ということです。

answers.unity.com

マテリアル問題

clusterのカスタムアバター制限に収まるようにテクスチャとマテリアルを設計しています。AvatarMaker専用の特製アバターアップロードAPIなどはなく、平等に同じAPI経由で作っています。

マテリアルの質感が同じ部分は、上記のシェーダでテクスチャを1枚に重ねてマテリアルを1つにまとめるなど工夫しています。アバター上限解放が先にリリースされていて欲しかったですね(乞うご期待…)

カスタムアバターの制限

メモリ枯渇問題

開発中は最高のテクスチャをそのままに2kでデータを配布していましたが、案の定 iOSバイス がメモリ枯渇でクラッシュするようになりました(それはそう)。
なので、すべて1kテクスチャに変換し合成時に1テクスチャのみ2kにupconvertするようにしています。なんと、2kのテクスチャを1kにするとメモリ使用量が4分の1になります(それはそう)。

いまよく考えるとupconvertもしなくても良い気がしてきたので、今後軽量化する可能性があります。

clusterでVRMアバターにした状態で使う場合は、サーバーで軽量化されて配信されているのでそこまで負荷にはなっていないはずです。

IL2CPP対応

想像の5億倍 IL2CPPで問題が発生しました。やはり自作ライブラリを動かすときはIL2CPPが鬼門になると学びましたね…。

一部ですがどのような罠にハマったかを紹介します(Unityは2019.4.22fです)。

最後の方は (Windows, Mac, iOS, Android, Linux) x IL2CPPビルドで動作確認するような用心深さになりました。

さいごに

いかがでしたか?

開発以外にもたくさんの人々の力によってリリースされたAvatarMakerの技術的裏側の紹介でした。今後も開発を続けていくので、よろしくお願いいたします。

クラスター広告

クラスターは本物の3DCGクリエイターを募集しています。

herp.careers

herp.careers

herp.careers

謝辞

【Jump King】最難関超鬼畜ゲーをクリアします!!!ぺこ!【ホロライブ/兎田ぺこら】 - YouTube

開発初期にこの配信を見て精神を保っていました。感謝します。

2020年振り返り

2018年から振り返っているので、今年2020年も振り返っていきます。

今年は虚無の年でした…。

開発

Go、HCL、Rust、Elm、OCamlC#、HLSL、TypeScript あたりを書いていたように思います。特段あたらしい言語に手を出した感じしないですね。

基本的に、CI/CDの仕組みを楽にしたり、ビルドやテスト時間の短縮、複数人での設計や品質まわりのバランスを無限に考えていた気がします。これどうする…?

あとは今まで適当に書いていた部分の調べ直しをしたり、GCPで消耗なくサービスのデプロイができないかの試行錯誤や雑スクリプトを書きなぐったり、ちょいちょいOSSにPRを出したりでしょうか…。形にしたい。

趣味で作ったリポジトリ

toy-runner

github.com

RustのFutureのexecutorの本当コアな部分を、Fahrenheit | Futures-rs を参考に書いていました。

Wakerあたりの気持ちが分かった気になっていましたが、今読むと疑問点がいくつかありますね… (スケジューラあたり)。

こういうものを書いたらすぐに記事にしよう!

yterm

github.com

Rustでgtkを用いてターミナルエミュレータを自作しようと思い立って途中まで書いたものです。UIスレッドとロジックのスレッドが分離しています。IMも使えます。

純粋にテキストエディタレンダリング部分をどう書くのか調べようと思いたち、その前にCUIテキストエディタレンダリングできるターミナルエミュレータを作ればいいのでは?!と道を逸れていきました。常用くらいまで作りたいですが、いつになるか。

同人誌

今年も同人誌として合同誌を2冊出しました。

shinchoku-tairiku.github.io

進捗大陸07の自分の担当では、自作言語の改造ログを書きました。型クラスの実装がかなり大変でした…。

shinchoku-tairiku.github.io

進捗大陸08では今までとは毛色を変えて、UnityURP でトゥーンシェーダを書く解説を書きました。自分でシェーダを書く際に調べて気になったところにはすべて解説を差し込んだので、役に立ってほしい!

全員分面白い記事が集まっているので、よろしければ手にとっていただけると嬉しいです(宣伝)。

モデリング

去年まで作っていた3Dモデルに更に手を加えて、iOSのARKitのデータを使ってリアルタイムに現実と同期して表情を動かせるようにしていました。

blenderのfacial rigももっと改良したり、モーションデータも取得して動かすようにしたい…!

今年は転をしていない。来年は…

まとめ

今年は本当に進捗が少ない年でした。まずいです。

主な原因は謎の疲れにより色々とダラダラ後回しにしたことだと思っています。謎の疲れは、おそらく環境の変化だったり、労だったり、タスクが頭の中で散らばって気疲れなどがありそうでした。

とりあえず反省を活かして、

  • タスクをGitHubのprojectで細かくメモする self · GitHub
  • WriteCodeEveryDayを毎日時間をとってDiscordでやる

をして、来年は毎日コツコツやっていく年を目指していきをしていきます。

それではよいお年を…

2019年振り返り

今年も振り返っていく。月ごとにまとめるの大変すぎるので思い出したことを記していく。

開発

C#、HLSL、Go、Rust、Elm、Haskell、HCL、OCaml を書いていたようだ。

C#HLSLは趣味Unityで。RustElmHaskellHCLは主に労働で。GoOCaml は趣味で。

趣味では3Dモデリングばかりしていたので、今年はあまり趣味コードを書いていないかも。来年はバランスを取りたい。

書いたコード

glTFのやつ

github.com

github.com

github.com

労働で使うつもりで書いていたライブラリ。UnityでglTFのimport/exportができる。

はじめてUnity向けのライブラリというのを書いたが、UnityはCircleCIなどでの自動テストがとにかく面倒でつらかった記憶がある。特にUnityEditorへの依存やライセンス周りの解決が(どうすればいいのだろう)。

そのため、Unityに依存する部分とそうでない部分を明確に切り分けて実装するなどの工夫を模索していたように思う。上記のコードはUnity依存がない部分はmsbuild.NET Coreでクロスプラットホームで手元のCLIからでもCIでも単体テストを回せるように書いているため、開発しやすかった。

一方未解決なのは、Unityのパッケージマネージャーが発展しておらず、.unitypackage丸コピーやgit submodule埋め込みしか個人が気軽に試せるパッケージ管理の方法がなさそうというのがある。上記の通り、自作ライブラリはスタンドアロンで開発できるようにパッケージはNuGetに乗せているので、https://github.com/GlitchEnzo/NuGetForUnity などを使ってNuGetパッケージをUnityに持ってくることができれば幸せになれるのかもしれない(知見のある方教えていただけると嬉しいです)。

(VVrmは放置してしまっているが…)それ以外は個人的にまあまあ使えているので満足。

アセンブラ

github.com

Linux上でセルフホストできるx86_64のアセンブラアセンブリで書いていた。現実逃避は最高や。楽しかった。

これ以降、このアセンブラで既存のツールチェインに依存しないスタンドアロンな自作言語やライブラリを作ろうとしていたのだけど、力尽きている…。

LSP Serverの実験

github.com

自作言語でLSPでコード補完をしたかった実験跡。LSPのプロトコルより、パーサーのエラー回復の実装に近づけてよかったという副産物がでかい。

同人誌

複数人で合同誌を2冊作った(宣伝)。サークル進捗大陸のメンバーにはいつも感謝しています。

shinchoku-tairiku.github.io

shinchoku-tairiku.github.io

これは毎年言っていますが、来年は締切に追われないようにする。

モデリング

2019年、とにかくモデリングとシェーダを書くなどしていた。

プリチャンというゲームのマイキャラ(自分で作れるやつ)を参考に作っていた(オタク)。

始めたのが5月。

ここまでで12月。

半年間いろいろやっていた…。大雑把に学べた内容としては、

  • blender
    • Python拡張の書き方
    • facial-rigでの制御
  • Unity
    • シェーダまわり
    • glTF
    • 3Dモデルのデータ構造

これは次回の技術書典あたりのネタにしようと思っています。

旅行・登山

いろいろ行ったけど書く前に年越しそうなので飛ばす。出かけるのは最高なので来年もいろいろ行きたい。

5月に転をした。もうしばらくは転したくない。

まとめ

一年に一度まとめると大変なので月一くらいでメモ取るようにするか。来年もよろしくおねがいします。

2018年振り返り

今年は色々変わったなあという感じがするので思い出しながら書いていく.GitHubTwitterには断片的なログが残っており,こういうとき便利だ.

振り返り

1月

あまり記憶が無いが,VPS環境をAnsibleで全て自動で構築できるようにしていた気がする.この時期はDockerやAnsible,Consul,traefik,grafana,prometheusなど色々触っていた.今まで個人で使っていたツールを応用することもあれば初めて使うツールもあり,試行錯誤を楽しんでいたように思える.言語はGoをひたすら書いていた.

このVPS環境では自サイトであるyutopp.netがホストされている.確かHugoを使ってページを生成するようにしたので,ブログ未満ツイート以上の文章を書こうと意気込んでいたはずだが,現時点でも2記事しか書かれていない.ウケますね.
内容は,"今年こそオンラインコンパイラを作り直したい" と "Zenbook3を買った" というものだった.オンラインコンパイラは途中まで作って放置しているため,2018年の抱負はこのまま2019年に引き継ごうと思います.

振り返っていて早くも暗雲が立ち込めてきた.

以下は癒やしのツイートです.

twitter.com

2月

技術書典4へのサークル進捗大陸の参加が確定し,やっていきの月だった.この回は,@no_maddoさんや@pocketberserkerさんも参加してくださり,ワイワイといつにも増して楽しかった記憶がある.

twitter.com

言語は相変わらずGoメインで書いていたようだが,Rust齧ったりOCamlも触っていた.あとErlang VMの実装を読んで分散Erlangのフルメッシュ構造になる原因と解決策を調べていた.これは社内LTで発表した(色々手直しして公開しても良い気がしてきた).

あと旅行(広島→岡山→神戸)に行った.

twitter.com

もう一度優勝したい.

3月

心が比較的死んでいました.ウェザーロイドAiriさんの骨折動画を見て笑いながら会社でしいたけを育てていた.

twitter.com

あとは開発合宿にお邪魔させていただいてキツネ村に行ってきた.

twitter.com

mzp.hatenablog.com

とてもよかった.ここでtraefikにPRを出したり,進捗大陸の原稿をひたすら書いていた.

最後にMLDayでLTをした.内容は自作言語である文鳥言語の,失敗したなと思う部分とその改善策についての知見です.

OCamlで作り直す 自作言語のススメ - Google スライド

4月

心が比較的死んでいました.ウェザーロイドAiriさんの骨折配信を見て笑いつつ,技術書典の最後の追い込みでひたすら煽られながら原稿を書いていた.

twitter.com

結果的に締め切りには間に合い,当日も無事頒布でき良かった.改めて感謝です.

twitter.com

(宣伝: この時の内容は 言語処理系のススメ - 進捗大陸03 - 進捗大陸 - BOOTH にて入手可能です)

イベントでは,ニコニコ超会議2018に参加した.ここでVirtualCastを実際に体験したのだが,あまりの没入感に感動しVR機器を買いたい気持ちに支配されたのであった.

あとはリズと青い鳥を観て,旅行に2回行った.

twitter.com

1回目はゆるキャンに影響されたおたくと共に富士山の麓でキャンプをした.4月だというのに夜寒すぎて眠れなかったのが良い思い出.

twitter.com

2回目は四国に行った.車で四国の左下以外(高速がないため)の部分を巡った.香川のうどんの安さと奥道後温泉が印象的だったが,全体的に四国は良い.
川に財布落としたり,空港でじゃこカツをひたすら食べた.

5月

5月,色々やった.

最初に,VR ReadyなデスクトップPCとHTC Vive一式を購入した.秋葉原ツクモで購入し,即日使いたかったので自分で運んで持ち帰った.2018年で一番重かった.

twitter.com

VirtualCastでD言語くんアバターになって配信などしていたが,やはり自分で開発したくなり,UnityとC#Blenderをまともに触り始めた.
毎日朝5時位まで実装をしていて,5月では最終的に以下のような所まで作れることが分かった.

twitter.com

(このときモデルはウェザーロイドAiriさんのMMDデータをお借りしていました…)

UnityのMonoBehaviourのような挙動はErlangアクターモデルっぽいなと思いつつ実装していた.労働ではひたすらErlangを書いていた時期があったので,思わぬところで知見が役に立ってよかった. 全体的に今まで触ったことがないツールや開発の対象(VR)だったので,なにもかも非常に新鮮で楽しかった.

あとはRTMP周辺のライブラリをGoで書き始めた.

規格を読みながら現実のソフトウェアの挙動と照らし合わせながら実装していくのは大変ためになった.

それ以外で言えば,プリパラを布教されたのが5月辺りだったと思います.

6月

VR開発を継続してやっていた.

twitter.com

twitter.com

自分でモデリングもしてみたくなって,Blenderの人型のモデリングにも入門した.

twitter.com

twitter.com

6月時点で出来上がったのはこれ.

6月は生活が本当に破滅しており,朝6時位までUnityやBlenderを触ったりRTMPサーバを書きつつ,プリパラを2話くらい観て寝るみたいな感じだったのを覚えている.
このあたりからGitHubの草を毎日欠かさず生やすようになってきた.

7月

引き続き毎日Blenderによるモデリングを明け方までやって,ついでにBeatSaberに自作もモデルを読み込ませてこれも明け方までやっていたら体壊した月.虚無.

twitter.com

Vive Trackerも3つ買ってしまい,沼.

プリパラは80話まで見た.

8月

Rustで言語処理系を作ろうとしたけれど,グラフ構造扱うのが面倒すぎて挫折した月だった.やはりOCamlは最高や.

技術書典5へのサークル進捗大陸の参加が確定したので,RTMPサーバの実装と原稿に本腰を入れ始めた.本腰を入れられることが出来たのは,この月でスプラトゥーンのウデマエXを達成したため,実装に集中できるようになったという理由(厳しい).

あとはペンギンハイウェイを観て,富士山登ってきた(2回目).高山病はつらい.体力をつける.
振り返って思ったが,体力以前に直近数ヶ月の生活が壊れていたのが高山病の原因だったのでは…

twitter.com

プリはパラを全191話見て,チャンを見始めました.沼.心が弱っていたのだと思う.

9月

なんかCコンパイラフルスクラッチで書くのが流行っていたので便乗して自分も書いていた.

twitter.com

GitHub - yutopp/cc

技術書典5の原稿を書くのが最優先だったのだが,やはり現実逃避は最高や.しかし,C言語の構文は読めば読むほど実装する気が無くなってきたため途中で放置となってしまった.

月の真ん中で9連休を作り,そのうちの2日を使ってサークル進捗大陸で進捗キャンプをした.キャンプと言っているが,中身は千葉の旅館で缶詰になって原稿を書くというものであった.

twitter.com

/etc/hoststwitterドメインlocalhostに向けて潰していたため,この月はツイートが少ない.プリはリズをシーズン2まで見終えた.

10月

技術書典5の原稿は無事入稿でき,ウェブサイトもリニューアルした.初めてnuxt.jsを使った.小ネタなのだが,このウェブサイトで表示されるプログレスバー(現在の1/1からの日にちの累計/365)*100という式で計算しているため,今見るとほぼ100%になっているはず.2019年になったらまた0%になっていることでしょう.

twitter.com

twitter.com

当日も無事頒布でき良かった.こちらも改めて感謝です.
(宣伝: この時の内容も 進捗大陸04 - 進捗大陸 - BOOTH にて入手可能です)

技術書典が終わってからはひたすらRTMPサーバーのパフォーマンスチューニングをしていた気がする.
他にはクライアント側など足りない部分の実装を埋めている.たまにPRやStarを頂くのだが,初めてまともに作ろうとしたGoライブラリなので結構嬉しい.

あと旅行に2回行った.
1回目(?)は谷川岳に登山(旅行か?)をした.

twitter.com

紅葉がきれい.富士山で使ったガチ登山装備で挑んだので怪我なく楽しむことができた.

2回目は沖縄・石垣島竹富島に行った.

twitter.com

住みたい.沖縄では時計をあまり見ないで過ごした.土地の雰囲気も相まって時間がゆったりと流れているような開放的なあの感覚が忘れられない.

感想が谷川岳と合わせてエモいしか言ってなくて笑ってしまった.

11月

iPad ProとPixel3を買ったらクレジットカードが止まった月だった.

言語処理系勉強会 Vol.1でLTもした.言語処理系開発とUnityの知見を合わせて自分ができそうなことについて話した.
デモが本当にプリミティブなものしかできなかったので,これの開発は続けていきたい.来年まだどこかで発表できたらと思う.

twitter.com

それ以外は,心が弱っていたのでプリに没頭していた.筐体に手を出してしまったので,実用的なアイテム閲覧かつ重複チェックアプリをFlutterを使って作った.

twitter.com

Flutter,非常に使いやすくて推せるなという感想を持った.Android Studioとの連携は抜群で,補完もエラーもバッチリ出る.
Hot reloadも高速で,コードを書いたらすぐ画面にUIが反映される.UIも標準でMaterial designが提供されるので見た目が良い(生の描画もできる).

Android アプリもDartは久しぶりに書いた.スラスラなんの躓きもなく開発することができて非常に良かった.ぜひFlutterは試してみて欲しい.

12月

obeam というOCamlErlangのBEAMフォーマットをパースするライブラリの開発が活発だった.PR本当にありがとうございます.
ただやり取りを全て英語でしているのだが,自分の英語の不自由さに厳しい気持ちになっている.OCamlより英語のほうが難しい.解決せねば...

あとは中旬あたりに職場が変わった.いまはVR関連の開発をしている.とてもありがたい.
なにもかもがとても新鮮でかなり楽しい.5月頃の経験を下地に日々足りない知識を吸収しようとしている感じ.やっていきます.

まとめ

技術書典のサークル参加やVR開発,モデリング,ライブラリ開発にLT,旅行,プリ,転と思ったより色々充実していたのかもしれない.
言語も新しくRustやC#を深く触るきっかけが作れてよかった.OCamlやGoも理解が深まったと思う.

GitHubの草も半年近く連続で生やすことが出来たのもよかったと思う.

来年

心を元気にして開発速度を取り戻していきたい.あと英語.やる気元気寝起き!

RTMPのChunkStream, MessageStreamの話

yutoppです.

今回は,RTMPのChunkStreamとMessageStreamの2種類のストリームについて書きます.

それでは,一旦ストリームの種類の概要を紹介した後,RTMPの通信が2つのストリームの種類をどのように使い分けて通信するのかという説明していきます.細かいフロー(ChunkSizeやBandwidthの設定など)は割愛します.また,RTMPは実装によってパラメータがまちまちであるため,環境によってIDの番号などが異なると思いますので,適宜読み替えてください.

※この記事の指す"RTMP"は,AdobeのRTMPの仕様(v1.0)に基づくものとします.

RTMPのデータの送受信概要 (ChunkStream)

RTMPは,1つのTCPコネクションの上に細切れにしたデータ(Chunk)を多重化して送受信します.これによって,例えば映像などの大きいデータと音声などの小さいデータを送受信する際に,大きいデータがボトルネックとなって本来先に処理を行えるはずの小さいデータが中々届かないというケースを防ぐことができます.

f:id:yutopp:20180611040426p:plain

この多重化されて送られてくるchunkは,ChunkStreamと名付けられたストリームの単位で保持されます.このchunkには実際にはChunkStreamIDと呼ばれるIDがついており,上の図のようにChunkStream毎に振り分けられた後,読み込みが終了したものからデコードが開始できます.また,上記ではAudioとVideoのみですが,実際にはRTMPで処理されるメッセージ全てが対象となります(ハンドシェイクは除く).

RTMP論理ストリーム概要 (MessageStream)

上のChunkStreamはデータの送受信に使われる概念でしたが,RTMPには更にその上のレイヤでメッセージを振り分けるMessageStreamの概念が存在します.

f:id:yutopp:20180611071830p:plain

送受信を別のChunkStreamで行っていたとしても,例えば同じ動画に対するデータを処理する場合にMessageStreamとしては同じになるようなイメージです.

詳細

それでは,上記を踏まえてRTMPの通信を例にとって実際のデータ構造について説明していきます.

その前に,2つのストリームについての細かい補足です.

まず,ChunkStreamについてです.ChunkStreamは,ChunkStreamIDと呼ばれるIDによって区別されます.
このIDは2〜65599まで使うことが可能です(計65598ストリーム).しかし,その中でもChunkStreamID 2はRTMPによって予約されているため,ユーザーが自由に使うことはできません.それを除けば,ChunkStreamは生成や削除を明示的に行わなくても使うことが出来ます.

次に,MessageStreamについてです.MessageStreamはMessageStreamIDと呼ばれるIDによって区別されます.
このIDはuint32型の範囲で指定することが可能です.RTMPは,コネクションごとにコントロールストリームと呼ばれるストリームを必ず持ちます(MessageStreamIDは0).それ以外のストリームについては,自由に使うことはできませんcreateStreamコマンド(仕様: p36, 7.2.1.3)を用いて,サーバーからMessageStreamIDを発行してもらう必要があります.

では,実際のデータの流れを見ながらデータ構造を追っていきます.
前提として,通信の流れは以下のようなコマンドで得られたデータを用いています.

ffmpeg -re -i test.mp4 -codec copy -f flv rtmp://localhost:1935/live/hogehoge

また,ChunkStreamのChunkSize(Chunkのpayloadの大きさ)はデフォルトの128Bytesと仮定し,Ackなどのメッセージは無視するものとします.

1: ハンドシェイク

割愛.後述の参考文献にハンドシェイクについてのリンクがあります.

2: クライアント → サーバー: connectコマンドの送信

クライアントは,まずサーバーにconnectコマンド(仕様: p29, 7.2.1.1)を送信します.ffmpegではMessageStreamにはID 0(コントロールストリーム),ChunkStreamにはID 3を指定するようですね.
また,このconnectコマンドを含むメッセージはpayloadが157Bytesあるようで,ChunkSizeの128Bytesを上回っているため早速Chunkに分割されて送られてきています.

TCPで送られてくるデータは以下のようになっていました.

f:id:yutopp:20180611061247p:plain

赤い部分(03とc3の部分)がChunkの切れ目です.丁度payloadの長さが128Bytes(0x80Bytes)になるところでChunkが区切れているのが分かります.
これらのChunkのHeaderを見たら取り除き,payloadの部分のみをChunkStreamとしてバッファに保持していきます.メッセージの長さの分Chunkを読み終わったら,バッファをデコードし,MessageStreamIDに紐づくMessageStreamのメッセージとしてデータを処理します.
今回はMessageStreamIDは0なので,コントロールストリームにconnectコマンドのメッセージが来たものとして処理します(接続済みのステートにコネクションを変更する).

2.1: サーバー → クライアント: _resultコマンドの送信

割愛.connectに成功したので,NetConnectionの_resultをクライアントに返します.

3: クライアント → サーバー: createStreamコマンドの送信

動画データの送信を行うために,クライアントはcreateStreamコマンドをサーバーに送信します.メッセージの内容は特にありませんが,この時点でもMessageStreamIDは0を利用します.

3.1: サーバー → クライアント: _resultコマンドの送信

connectに成功したので,NetConnectionの_resultをクライアントに返します.
ここで,新しいMessageStreamとしてStreamIDとして1(とりあえず0以外)をクライアントに返します.MessageStreamID 0は既にコントロールのために用いているので,データ用に新しいストリームを作るということです.

f:id:yutopp:20180611064641p:plain

4: クライアント → サーバー: publishコマンドの送信

クライアントは,映像の配信を行うためにサーバーにpublishコマンド(仕様: p45, 7.2.2.6)を送信します.

f:id:yutopp:20180611065613p:plain

このとき,クライアントはcreateStreamで受け取ったStreamID 1をMessageStreamIDに指定していることが分かります.これ以降のNetStream系の操作は,createStreamによって得られたMessageStreamに対して行うようになります.

4.1: サーバー→クライアント: onStatusコマンドの送信

割愛.publishに成功したので,NetStreamのonStatusをクライアントに返します.

5: それ以降

クライアントは,ChunkStreamをvideoとaudio,metadataで変更しながら,MessageStreamIDはcreateStreamで得られたIDに固定でデータを送るようになります.

最後に

RTMPの仕様に出てくる2種類のストリームについて,実際に送受信するデータを追いつつストリームの用途を確認しました.

初めてRTMPの仕様を読んだときに,ChunkStreamとMessageStreamの用語と意味を噛み砕くのに少し苦労したので,今後迷わないように残しておきます.

参考文献

おまけ

GoでRTMPサーバーを書いているので,途中の知見を書いた.そのうちコードを公開するので,そのときにもっと細かく書けたら嬉しい.

技術書典3にサークル参加した

こんにちは。先日10/22に行われた技術書典3にサークル進捗大陸で参加しました。(youxkei氏、amutake氏、ありがとうございました!)

とても楽しかったです。お疲れ様でした。手に取ってくださった方々、スタッフの方々、本当にありがとうございました。

前回の技術書典2の参加記事や気持ちをもとに今回工夫したところがあったので、この記事も何かの参考になったら良いなと思って書きます。

表紙

前回 今回
f:id:yutopp:20170402131218p:plain:h240 f:id:yutopp:20171018192426p:plain:h240

前回は"タイトルから内容がわからない"という声があったので下の方に大きく内容を箇条書きしました。 メンバーのamutake氏が入稿の日に明け方までかけて作ってくれました(感謝)。おしゃれですね。

いま落ち着いてみると、"タイトルから内容がわからない" は解決できてないですね。おわり。次はうまくやります。

タイトルと内容は同じ方向を向いていて分かりやすいものが目に止まりやすいかもな、と思いました。

持ち物

前回 今回
(手ぶら) ブックスタンド、サークルカットぶら下げるやつ、ペン、紙

前回は何も持っていかなかったので、いい感じの本の展示もできず(ダンボールに立てかけていた)、文字も書けなくて辛かったです。上記の4つは必要だと思いました。

また、"サークルスペースに引く布"も必須な気がしました。敷物があるだけで雰囲気が全然変わります。
今回も敷物を忘れたので、納品された本が包まれていた紙を敷いて使っていました。

f:id:yutopp:20171024015936j:plain:h240

次はうまくやります。

決済システム

今回は、技術書典決済システムとpixiv payに対応して向かいました。

システム 回数
技術書典決済システム 8回
pixiv pay 0回

という結果でした。アプリはどちらも使いやすかったです! 技術書典決済システムもスムーズで便利でした(途中に操作ミスがあり申し訳なさがありました… 次はうまくやります)。

アプリは個人の端末にインストールしていったので、自分が昼ご飯を食べていると決済システムで自サークルの決済が出来ない感じになってしまいました。すみません。対策をしたい。

頒布部数 (余り/持ち込み数)

前回 今回
物理 0/30部 物理 1/80部
ダウンロードカード 50/98枚 なし

前回は正午過ぎ辺りに物理本が0部になったので、今回は多めに80部を持っていきました。結果はほぼ余りなく終わることが出来ました。ありがとうございました。

ぼく「30部でよさそう」amutake氏「80くらいいけるのでは」ぼく「ボ」という感じで決めました(結果オーライ)。

内容がニッチなので不安でいっぱいでしたが、技術季報のレポートにある平均あたりくらいの部数になった気がしました。

また、今回ダウンロードカードは作れませんでした。すみません。
前回は前日に自分がgoでダウンロードサーバを作って、夜中にamutake氏がシリアルコードを印刷していたのですが、今回は体力が尽きていました。

次回があるとしたら、S3に置いて物理本にシリアルコードの紙を挟むやつでやりたいです。

ページと印刷の値段

前回 今回
52ページ30部, 16200円 (540円/1部) 68ページ80部, 27300円 (341円/1部)

頒布は、前回も今回も1部500円を設定しました。

なんと前回は紙の本を売る度に赤字になる状態でした(大草原)。ダウンロードカードによってなんとか赤字を免れることが出来ました。

今回は大丈夫でした。サークル参加費や諸経費を考えると、自分のサークルではこのあたりの価格帯がギリギリな感じがしました。

印刷所

ねこのしっぽのねこスパークというプランを利用しました。
締め切りや値段、質、部数(なんと見本用で+2冊つけてくれました)を考えると本当に最of高でした。感謝します…

まとめ

次回は締め切り1ヶ月前に分かりやすいタイトルで良い記事を書きあげるぞ!!!

とにかく、今回の技術書典も本当に楽しかったです。ありがとうございました。

技術書典3に同人誌を出す回

この広告は、90日以上更新していないブログに表示しています。を消したい。

こんにちは、yutoppです。イカ2ばかりしていたら前の記事が200日前などとなっていますね。

さて、10/22に行われる技術書典3に再びサークル進捗大陸で参加します。ので宣伝をします。

今回のサークルページです。詳細についてはこちらを参照していただけると嬉しいです。
場所はお16です。

今回出す本は進捗大陸02です。

f:id:yutopp:20171018192426p:plain

500円です。!!!pixiv payと技術書典決済システムが利用可能です!!!
後日電子版をboothで頒布します。こちらは300円です。

今回の目次です。今回は目次を作りました。


  1. 自作言語 Rill と WebAssembly (@yutopp, 12ページ)

    1. 前回の進捗大陸 01 からの自作言語の進捗
    2. WebAssembly サポート
      1. シンプルに Hello world!
      2. ランタイム移植問題: リンクとlibc
      3. ランタイムビルド
      4. 全てリンクしたバイナリを作る
      5. 実行
      6. 問題点
      7. 今後の展望
    3. まとめ
  2. 抽象解釈による,再帰を持つ関数型言語の符号解析 (@youxkei, 22ページ)

    1. はじめに
    2. 再帰を持つトイ言語
      1. 構文
      2. 型付け規則
      3. 意味論
      4. 型システムの健全性
      5. 符号解析
    3. 符号解析
      1. 抽象値
      2. 抽象環境
      3. 符号解析の定義
      4. 符号解析の well-defined 性
      5. 符号解析の健全性
    4. 符号解析の例
    5. まとめ
  3. 証明付き分散システムを作ろう! (@amutake, 26ページ)

    1. イントロダクション
      1. 本稿の流れ
    2. Coq の概要
      1. Coq のインストール方法
      2. Coq を使ってみる
      3. コード抽出(extraction)
    3. Verdi の概要
      1. Verdi の使い方
      2. ネットワーク意味論
      3. VST
      4. まとめ
    4. Verdi を使って分散システムを作る
      1. Verdi のインストール
      2. ブール値保管システム BoolServ
    5. Verdi による Raft の証明
      1. Raft の概要
      2. Verdi チームが証明した定理
      3. Verdi による Raft の実装
      4. Verdi による Raft の証明の流れ
    6. まとめ

以上の本体60ページくらいの本になります。読み応えがあります。

よろしくお願いします!!!!