どうもこんばんわ。
D.C.5 攻略しました(ぜんねんれい作品)
全年齢だけど結構攻めたイベントCGが多かった気がする(こんなもんなんですかね
(顔つきが大人っぽくなりましたね..!)
今回の主人公、白河灯莉ちゃん以外のヒロインが同じ家にいるだと
灯莉ちゃん√がめっちゃ可愛かったのでおすすめです。
(自前のスクショをとるアプリの調子がわるく普通に写り込んでますが気にせず...)
かわいい
ここのお話がお気に入りです
あと個別ルートで愛乃亜ちゃんかわいいくなっていくのでぜひ。いいなあ...
D.C.4 をやってなくても楽しめると思うのでぜひ!
いくつか謎が残っていますがFDとか6とかで明かされるのでしょうか、楽しみです!
(瑞花verのめぐり逢えたね、単品でほしい)
本題
少し前に動画の上にCanvasで落書きするような記事を書きましたが、今回はそもそもCanvasから動画を作ってみようの話です。
スライドショーっぽい動画を作るアプリが作れるかも?
環境
なまえ | あたい |
---|
端末 | Xperia Pro-I |
Android | 13 |
言語 | Kotlin |
OpenGL
を使いますが使わない方法もあります。
あとJetpack Compose
を使いますが別に使わなくても良いです。
めんどうなんだけど
最新バージョンは↓から、すでに今回やったことは MavenCentral にあるので、、、
あとはいい感じに乗っかれば作れるはず
Android で Canvas から動画を作るには
MediaCodec
とOpenGL
を使うか、難しいのを無しにしてMediaRecorder
とSurface#lockHardwareCanvas
を使う方法があると思います。
- MediaCodec / MediaMuxer の用意をする
- MediaCodec の Surface に OpenGL で描画する
- Surface と OpenGL の橋渡しをするためのコードは書かないといけない(AOSPそのまま)
- Canvas で書いて、Bitmapにして、テクスチャとして描画します。
- OpenGL のシェーダーもAOSPから借りてくることにします(ならお前は何をするんだ)
- MediaCodec から出てきたエンコード済みデータを MediaMuxer を使い
mp4
(コンテナフォーマット)に格納する
OpenGL
の用意と、シェーダーを書かないといけない(ただコピペできる)ので、普通に難しい。
あとやっぱり低レベルAPIなので、MediaRecorder
では出てこないコーデック やら コンテナ やら
と向き合わないといけない。
が、おそらく正規ルート感はある。(後者は動かない端末があるらしい)
というか OpenGL
の代わりに lockHardwareCanvas
を使う方法もあります。Android 6 以上なので、まぁ選んでも良いでしょう
- MediaRecorder を初期化する
- MediaRecorder の Surface を取得する
- Surface にある
lockHardwareCanvas
関数を呼ぶと Canvas
が貰えるので、よしなに書く
unlockCanvasAndPost
の引数に Canvas
を入れる
高レベルAPIで保守しやすそう!!!
lockHardwareCanvas
を使わないといけないです(ハードウェアアクセラレーションされていないとだめ)
また、一部の端末ではこの方法が使えないらしい(以下の Issue
参照、多分 OpenGL
を使うしかなさそう?)
https://issuetracker.google.com/issues/111433520
作ってみる
Android Studio
更新したら Empty Activity
が JetpackCompose
になってる、、
適当なプロジェクトを作ってください。
MediaCodec
- エンコード / デコード するためのクラス
- H.264 / H.265 とかにエンコードできる
- パラパラ漫画にするよりも動画にするほうが容量が小さいのはコーデックが圧縮してくれているから
- エラーメッセージが役に立たない
MediaMuxer
- コンテナフォーマットへ保存するためのクラス
- コンテナフォーマットは音声と映像を一つのファイルにするための技術(
mp4
など)
MediaCodec
から出てきたエンコード済みデータをmp4 (webM とかでも可)
に格納する
addTrack
は スタート前に呼ぶ必要がある
AOSP の以下のコードそのままです(多分 GLSurfaceView の内部でやっていること)
https://cs.android.com/android/platform/superproject/+/master:cts/tests/tests/media/common/src/android/media/cts/InputSurface.java
↑ の Kotlin化 と若干の修正が入っているだけです。
これはエンコーダー MediaCodec
の入力にOpenGL
を使えるようにするためのクラスです。わかりません。
TextureRenderer
OpenGL
でCanvas
をBitmap
にしてテクスチャとして描画するためのクラス。
OpenGL
、、何もわからん(アルファブレンド設定が有効だと真っ暗な映像が出来た...)
CanvasProcessor
これがCanvas
から動画にするためのクラスですね。
記事冒頭に書いたライブラリと同じやつです。
- エンコーダー(
MediaCodec
)を用意
OpenGL
の用意
- エンコーダーを開始し
- 終わるまで以下の作業をループ
- エンコーダーからエンコードしたデータがあれば取り出す
MediaMuxer
に入れてコンテナフォーマットに格納
MediaMuxer
に始めていれる場合はaddTrack
する(おそらくエンコードしたデータよりも先に呼ばれる)
Canvas
に好きなように描く
- 関数を引数にしているので、呼び出し側で好きなように描けます
OpenGL
で描画し、swapBuffers
でエンコーダーに渡す?
- もう描画しない場合はループを抜ける
- 後片付け
多分他のMediaCodecシリーズ
と同じようなコードだと思う。
MainActivity
呼び出し側でCanvas
にお絵かきをします
Jetpack Compose
でプロジェクトを作ったために使ってますが、別に使う必要はないです
(むしろエンコードは時間がかかるのでAcitivty
ではなく、Foreground Service
などにやらせるべきです。)
まーでも記事を書く側としては、xml
側のコードをわざわざ載せなくても、一つのkt
ファイルを記事に乗せておけば良いというのは結構良いですね。
保存先ですが、getExternalFilesDir
なので、/sdcard/Android/data/{アプリケーションID}/files
の中にあるはずです。
保存先↓
Android
標準のファイラー(Files by Google の事ではない) com.android.documentsui
の方のアプリを使うことで多分 /sdcard/Android/data
を開くことが出来ると思います。
(ただホーム画面にアイコンがないので、Intent
を投げるアプリが別途必要(ショートカットを作れるアプリなど))
またX-plore
でも権限を渡すことで見れます。
どうでしょう、動画ちゃんと出来ていますか!?
こっちは高レベルAPIなので、難しいことは無いと思う。
CanvasProcessorHighLevelApi
命名センスが終わっている。OpenGL
版をもとに作りました。合ってるかはわからないです。
コードを見てもらえるとなんですが、MediaRecorder
とCanvas
を使っています。OpenGL
とMediaCodec / MediaMuxer
の難しいコードはありません。
MainActivity
呼び出し側もあんまり変わってないです。
(Context
が必要になったぐらいで、APIはだいたい同じ)
ただ、私の作りが悪いのか、少しずつ時間がずれていっている気がします、、、改善したほうが良さそう。
API はだいたい同じなので、以下のように実行時に切り替えるみたいなことも出来ると思います(誰得?)
端末の動画フォルダに保存して欲しい
このままだと毎回ファイルマネージャーを開かないといけないので、端末の動画フォルダー(/sdcard/Movies
)に移動するようにしましょう。
ここに移動することで、Googleフォト等の写真ビューアーで見ることが出来ます。
これをエンコーダーの下に描けば良いはず
これで動画フォルダーに保存されるようになったはず。
というかこれ前も書いた気がする
ベンチマーク(動作確認)
時間測ってみます。三回くらい試しました。リリースビルドではないので正しくは無いかも?
測ってみましたが差がないのでどっちを使ってもいいと思いました。
たんまつ | OpenGL 版 | lockHardwareCanvas 版 |
---|
Xperia Pro-I (SDM 888) | 10111 ms / 10109 ms / 10121 ms | 10161 ms / 10133 ms / 10121 ms |
Google Pixel 6 Pro (Google Tensor G1) | 10185 ms / 10131 ms / 10117 ms | 10717 ms / 10649 ms / 10654 ms |
Google Pixel 3 XL (SDM 845) | 10381 ms / 10133 ms / 10099 ms | 10552 ms / 10404 ms / 10391 ms |
Xperia XZ1 Compact (SDM 835) | 10932 ms / 10204 ms / 10186 ms | 10420 ms / 10412 ms / 10435 ms |
Xperia Z3 Compact (SDM 801) | 10634 ms / 10559 ms / 10419 ms | Android 5 なので使えない |
たまたま Z3 Compact の電源が入っていたのでついでにやってみました、もう9年くらい前になるの...!? | | |
ファイルサイズ
速度はあんまり変わらないのですが、できた動画サイズが全部バラバラなんですよね(なぜ?)
OpenGL -> lockHardwareCanvas を三回繰り返したので、偶数がOpenGL、奇数がlockHardwareCanvasになります
うーん、OpenGL 版
のほうが若干ファイルサイズが小さい?
- Xperia Pro-I
- Google Pixel 6 Pro
- Google Pixel 3 XL
- Xperia XZ1 Compact
- Xperia Z3 Compact
他にも Xiaomi とかでも見てみましたが特に問題なくどっちでも動いていそうです。(Issueの件再現せず)
スライドショーの動画を作るアプリを作る
Jetpack Compose
でUIを作ります。
画像を選択するボタンと、エンコードボタンを設置しました。
押したらエンコードするようにしています。多分 UI に書くことじゃないと思います(時間がかかるのでService
でやるべきですねはい。)
特に難しいことはしてない(Canvas
に書いてるだけ)ので、詳しくはコードのコメント見てください。
Bitmap
をCanvas
の真ん中に描くのが地味に面倒だった。
Uri から Bitmap
はなんか面倒だったのでGlide
とかCoil
とか入れて楽しても良いかも。
まぁ切り替えアニメーションがないんで、見た目があれですが面倒そうなので、、、
(今思ったけどアニメーションなくてもスライドショーって呼んで良いのか・・・?)
エンドロール?スタッフロール?の動画も作れます
エンディングのあれも作れそう、作ります。
適当に画像を選ぶボタンと、エンコードボタンと、エンドロール?で流す文字を入力するテキストボックスを置きました。
これもCanvas
にいい感じに書いてるだけなので、特に難しいことはしてないはず。positionMs
を見てそれっぽく上に文字を移動させているだけです、、
こんな感じのUIになるはず
実際に作るとこんな感じです。
そーすこーど
最低限のUIを作りました、スライドショーとエンドロールを作成する画面を開くことができるはずです。
https://github.com/takusan23/CanvasToVideo
おわりに
Windows 用 Nearby Share
、めっちゃ便利ですね。
USB
にしてもUSB接続をファイル転送
にしないといけないし、adb
だとプレビューないからファイルの名前を知らないと行けないから、結構どれを取っても面倒だったんですよね。
少し前なら Googleフォト へバックアップしてパソコンでダウンロードする方法がありましたが、無制限じゃなくなったからなあ。
すごく便利です。