Drawableのビデオシーケンス

JPEGでビデオをエンコードするためのAppleのアプローチについての記事を投稿した後、Android向けの似たような「バイク」について話すことにしました。



私たちのモバイルプロジェクトでは、武器のプレビューを静止画ではなくビデオにすることを決めました。 アーティストは、おそらく3Dでさえ美しいアニメーションを描くと理解されていましたが、何かがうまくいかず、256x256の解像度で最も単純な1〜​​1.5秒のループクリップが与えられました。 iOSバージョンではうまく構築されていましたが、AndroidではMediaPlayerとSurfaceViewと戦わなければなりませんでしたが、それでも「不器用さ」が発生しました。SurfaceViewの内容は親Viewの後に移動しません。



合理的な解決策は、アニメーションをフレームに分割し、AnimationDrawable用にxmlでフォーマットすることですが、15種類の武器の場合、これはそれぞれ10〜15 kbの5000+フレームのゴミ箱を意味します。 そのため、AnimationDrawableの独自の実装が行われ、スプライトシートとビデオをそのような形式に変換する比較的迅速な方法を使用しました。



スプライトシート



さらに苦労せずに、彼らは記述ファイルなしで、スプライトのマップを水平にすることを決めました。 これは理想的ではありませんが、1〜2秒のアニメーションでは重要ではありません。

元のビデオは、ffmpegを介してスプライトに分割されます。



ffmpeg -i gun.avi -f image2 gun-%02d.png







32を超えるフレームが取得された場合、fpsを減らすために-r 25または-r 20オプションが追加されます。 32フレームの制限は、8192ピクセルの合理的な最大水平画像サイズから取得されます。 これはスプライトのより複雑な配置によって回避できますが、1〜1.5秒のシーケンスではこれで十分です。



このファイルの散在がわかります:







Zwoptexを使用してスプライトシートを作成しますが、同様のツール、または自作のスクリプトでも作成できます。







ピストルを使用した例では、サイズが257kbで解像度が8192x256のPNGファイルが取得されました。 7424x256にトリミングしてTinyPNG Webサイト処理した後、品質を損なうことなく101kbに減少しました。 必要に応じて、品質をわずかに低下させてJPGで保存し、50〜70 kbに減らすこともできます。 比較のために、高品質の.MP4の元のビデオは同じ100kbを使用します。 より複雑なPNGアニメーションの場合、元のビデオの2〜3倍を取得できますが、実際には重要ではありません。







ネイティブAnimationDrawable



元のバージョンでは、 Bitmap.createBitmapは新しい画像を作成せず、説明に従って既存の画像のサブセットを作成するという事実に賭けられました。



ソースビットマップの指定されたサブセットから不変ビットマップを返します。



デザイナーは画像をロードし、フレームに分割してAnimationDrawableに追加します。 この場合のアニメーションは、名前でアクセスするためにアセットに保存されますが、コードはR.drawableでの動作に非常に簡単に適応できます。



 public class AssetAnimationDrawable extends AnimationDrawable { public AssetAnimationDrawable(Context context, String asset, int frames, int fps) throws IOException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Config.RGB_565; // AG: use 16-bit mode without alpha for animations this.bitmap = BitmapFactory.decodeStream(context.getResources() .getAssets().open(asset), null, options); int width = bitmap.getWidth() / frames; int height = bitmap.getHeight(); int duration = 1000 / fps; // AG: note the little gap cause of integer division. // ie duration would be 33 for 30fps, meaning 990ms for 30 frames. for (int i = 0; i < frames; i++) { Bitmap frame = Bitmap.createBitmap(bitmap, i * width, 0, width, height); addFrame(new BitmapDrawable(frame), duration); } } }
      
      







通常のAnimationDrawableのようなクラスが使用されます。



  AnimationDrawable animation = new AssetAnimationDrawable(getContext(), "movies/gun.jpg", 28, 25); animation.setOneShot(false); previewImage.setImageDrawable(animation); animation.start();
      
      







残念ながら、このソリューションは多くの状況でうまく機能しますが、オリジナルへの不変のリンクが作成されるのではなく、各フレームの新しい画像が作成されることを示しました。



次のオプションはすでにかなり複雑で、Drawableから直接継承しています。 コンストラクターでは、スプライトシートがクラスメンバーに読み込まれ、現在のフレームがdrawメソッドで描画されます。 このクラスは、アニメーション用の元のAnimationDrawableと同様のRunnableインターフェイスも実装します。



  @Override public void draw(Canvas canvas) { canvas.drawBitmap(m_bitmap, m_frameRect, copyBounds(), m_bitmapPaint); } @Override public void run() { long tick = SystemClock.uptimeMillis(); if (tick - m_lastUpdate >= m_duration) { m_frame = (int) (m_frame + (tick - m_lastUpdate) / m_duration) % m_frames; m_lastUpdate = tick; // TODO: time shift for incomplete frames m_frameRect = new Rect(m_frame * m_width, 0, (m_frame + 1) * m_width, m_height); invalidateSelf(); } scheduleSelf(this, tick + m_duration); } public void start() { run(); } public void stop() { unscheduleSelf(this); } public void recycle() { stop(); if (m_bitmap != null && !m_bitmap.isRecycled()) m_bitmap.recycle(); }
      
      







run()メソッドは現在のフレームを計算し、タスクをキューに入れます。 フレームの小数時間は考慮されないため(たとえば、tick-m_lastUpdateが期間より1ミリ秒短い場合)、指定されたコードの精度は理想的ではありませんが、これはタスクに関係なく、希望する人は自分でクラスを変更できます。

paste2の完全なコード: paste2.org/p/2240487



m_bitmapをクリアするrecycle()メソッドに注意を払いたいです。 ほとんどの場合、これは必要ありませんが、複数のAssetAnimationDrawableが作成され、メモリが不足する可能性があるため、ストアで購入をクリックすることができます。したがって、新しいアニメーションを作成するときに、古いアニメーションのリソースをクリアします。



長所と短所



もちろん、このアプローチは理想とはほど遠いものであり、大きなアニメーションや大幅に異なるフレームには適していませんが、私たちのタスクにとっては、プロジェクトや視覚的なバグが目立って増加することなく完全に実現しました。



短所:





長所:





PS



これが誰かにとって興味深い場合は、iOSプロジェクトのMonoTouchのGUIに小さなビデオを統合した経験について個別に説明できます。 Monoのドキュメントは比較的小さく、そこには多くの落とし穴があります。



All Articles