たくさんの自由帳

ブラウザへAndroidの画面をミラーリングできるアプリ、ぜろみらーを作った

投稿日 : | 0 日前

文字数(だいたい) : 4599

KotlinAndroidMediaCodec
Twitterで共有GitHubで開く

どうもこんばんわ、(お久しぶりです)
D.C.4 Sweet Harmony ~ダ・カーポ4~ スイートハーモニー 攻略しました。
めっちゃいいですね、ヒロインみんなかわいい!

ここの話ほんとかわいい

Imgur

ここほんとすき

Imgur

あぁぁぁ

Imgur

本編では焦点があまり当てられなかったヒロインたちのストーリーが本作でちゃんとしてていい!

Imgur

実用性もあるとおもう

本題

ぜろみらーを作りました。ミラーリングアプリです。
とりあえずリリースしましたが、多分動く端末のほうが少ないと思う←!?

Imgur

ニコ生のコミュの名前にありそうですが関係ないです。

ダウンロード

機能

  • ブラウザがあれば見れる、同じWi-Fi (同じLAN) に接続している端末のブラウザから見れます
  • 設定はほぼない
  • 画面だけじゃなくて、端末内音声も共有される(Android 10 以上、がんばった)
  • 一応おまけ機能として、VP9でエンコードする機能もあります。H.264と比べて同じビットレートだと多分きれいな気がする...
  • ブラウザ側は簡単なHTMLとJavaScriptが書いてあるだけ
    • WebSocketクライアント、HTTPクライアント、動画再生機能、インターネット接続 があればブラウザ以外でも視聴側は移植できるかも

ブラウザがあれば使える → ほぼ設定がない → Zero ( Config ) Mirroring → ぜろみらー
結構無理やりなので負荷がやばいかも
あと動画を細切れにしている以上、切り替えで一瞬ロードされちゃいます...

ざっくり仕組み

端末の画面録画と(あれば)端末内音声を集めて、動画を細切れにして、ブラウザへWebSocketで送信してます。
もっといい方法があったかもしれない...

Imgur

真面目に仕組み

Androidの画面録画、端末内音声の内容をMediaProjectionで受け取ります。
(画面録画はSurface、端末内音声はPCM

受け取ったらMediaCodecH.264か(設定したら)VP9にエンコードします。
エンコードしたらMediaMuxerを使いmp4webmの動画ファイルにします、一定期間経ったらファイルを切り替えてまた保存します。
できた動画ファイルをWebSocketを使ってブラウザへ通知して(動画ファイルのパス)、ブラウザの<video> タグで再生します。

動画ファイルを細切れで作って、ブラウザで再生しているだけです。はい

もっといい方法ないの

HLS / MPEG-DASH

リアルタイム配信といえばこれ?

HLSMediaMuxerMPEG2-TSのコンテナフォーマットに対応していないので無理です!(これは後述)
MPEG-DASHMediaMuxerFragmented MP4を作れないので多分無理です。

ブラウザで見たかったので(追加のアプリが必要とかは敷居が高い!)今回は動画ファイルを細切れにして送ることにした。
iOSだと出来るみたいなのですが残念ですね...

(ffmpeg? バイナリが大きくなる上にライセンスがね...)

大変だったこと

MediaMuxer

MediaMuxer#addTrackがスタート前じゃないと呼べないため、使いにくい!
それとは別なのですが、高レベルAPIのMediaRecorderにはMPEG2-TSのコンテナフォーマットに対応しているのですよね。
一方今回使った低レベルAPIのMediaMuxerにはMPEG2-TSのサポートはありません!え逆では

ならMediaMuxer / MediaCodecとかを使わずに、MediaRecorderを使えばHLSで配信出来たの!?って話になるんですが、
ならないです。内部音声を収録して一緒の動画ファイルにしたかったので。残念。
それに加えて、もし内部音声がなかった場合でも動画を短い間隔で作り直すのはMediaRecorderでは多分難しいような気がします。遅延が大きくなりそう。

MediaCodec

MediaMuxerのインスタンスを作り直した場合は、MediaCodec.BufferInfopresentationTimeUs0からスタートするようにする必要があるみたいです。
https://github.com/takusan23/ZeroMirror/commit/3718678180bea6037c0e23d2686b2265b2d4e58f

あとVP9の場合は解像度が厳しいです(1920x1080、1280x720 なら動く)、ディスプレイの画面解像度をそのまま入れたら落ちてしまう。

MP4ファイルがストリーミング出来ない

mp4ファイルをストリーミングできるように(ダウンロードしながら再生)するには、mp4ファイルの先頭にmoovブロックを置く必要があるらしい(?)のですが、
MediaMuxerの場合は最後にmoovブロックを置くため、ストリーミング出来ません(全部ダウンロードしてから再生してしまう) ffmpegが入ってる場合は以下のコマンドを叩くことで、moovブロックの位置が分かります。

ffmpeg -v trace -i ファイル.mp4 2>&1 | grep -e type:\'mdat\' -e type:\'moov\'

多分こうなっていれば正解なのですが

$ ffmpeg -v trace -i  publish74.mp4  2>&1 | grep -e type:\'mdat\' -e type:\'moov\'
[mov,mp4,m4a,3gp,3g2,mj2 @ 00000133f22a9a40] type:'moov' parent:'root' sz: 3844 32 1896645
[mov,mp4,m4a,3gp,3g2,mj2 @ 00000133f22a9a40] type:'mdat' parent:'root' sz: 1889577 7076 1896645

MediaMuxerで出来たファイルはmoovブロックが一番下なのですよね...

$ ffmpeg -v trace -i  publish29.mp4  2>&1 | grep -e type:\'mdat\' -e type:\'moov\'
[mov,mp4,m4a,3gp,3g2,mj2 @ 000002461b249a40] type:'mdat' parent:'root' sz: 1420893 3232 1427957
[mov,mp4,m4a,3gp,3g2,mj2 @ 000002461b249a40] type:'moov' parent:'root' sz: 3840 1424125 1427957

この問題はすでに先駆け者さんが対応してくれています!
すごい!

上記のプログラムをお借りすることで、moovブロックを先頭に移動できました、ありがとうございます!

$ ffmpeg -v trace -i  publish74.mp4  2>&1 | grep -e type:\'mdat\' -e type:\'moov\'
[mov,mp4,m4a,3gp,3g2,mj2 @ 00000133f22a9a40] type:'moov' parent:'root' sz: 3844 32 1896645
[mov,mp4,m4a,3gp,3g2,mj2 @ 00000133f22a9a40] type:'mdat' parent:'root' sz: 1889577 7076 1896645

ちなみにブラウザでもストリーミング再生なのでバッファリングの表示がされるようになります。

Imgur

なおWebMの場合は特に何もせずともストリーミング可能なファイルにしてくれるみたい

Imgur

よくわからない

端末の差がある

新しめの端末じゃないと動かないっぽい?
動かない理由もよくわからん(MediaCodecのクラッシュログが不親切)

Opusだと音が高い?

これはまじで謎です。
なんかエンコーダーの設定間違えたのかな。

VP9でエンコードした動画がなんかFirefoxだけ再生できる

わかりません、Chromeだと再生されませんでした。

終わりに

Pixel 6 Pro のディスプレイちょっとだけ画面傷入っちゃって悲しい。

D.C.5 !?

Imgur