在Android上以30fps的速度从SDCard显示450个图像文件

时间:2013-12-10 09:21:43

标签: android image animation video ffmpeg

我正在尝试开发一款需要15秒视频的应用,允许用户应用不同的滤镜,显示效果的预览,然后允许将处理后的视频保存到SD卡。我使用ffmpeg将视频分割成JPEG帧,使用GPUImage将所需的滤镜应用到所有帧,然后使用ffmpeg将帧编码回视频。除了用户选择过滤器的部分外,一切正常。当用户选择过滤器时,应用程序应显示应用了过滤器的视频预览。虽然450帧可以相当快地应用滤波器,但以30 fps顺序显示图像(以使用户感觉视频正在播放)表现不佳。我尝试了不同的方法,但即使在最快的设备上,我可以达到的最大帧速率是10到12 fps。

在这种情况下,AnimationDrawable技术不起作用,因为它需要将整个图像缓冲到内存中,在这种情况下是巨大的。应用程序崩溃。

以下代码是目前表现最佳的代码(10到12 fps)。

package com.example.animseqvideo;
import ......

public class MainActivity extends Activity {
    Handler handler;
    Runnable runnable;
    final int interval = 33; // 30.30 FPS
    ImageView myImage;
    int i=0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myImage = (ImageView) findViewById(R.id.imageView1);

        handler = new Handler();
        runnable = new Runnable(){
            public void run() {

                i++;  if(i>450)i=1;

                File imgFile = new  File(Environment.getExternalStorageDirectory().getPath() + "/com.example.animseqvideo/image"+ String.format("%03d", i)   +".jpg");
                if(imgFile.exists()){
                    Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
                    myImage.setImageBitmap(myBitmap);
                }
//SOLUTION EDIT - MOVE THE BELOW LINE OF CODE AS THE FIRST LINE OF run() AND FPS=30 !!!

                handler.postDelayed(runnable, interval);
            }
        };
        handler.postAtTime(runnable, System.currentTimeMillis()+interval);
        handler.postDelayed(runnable, interval);
    }
}

据我所知,从SDCard获取图像,对其进行解码,然后将其显示在屏幕上的过程涉及SDCard读取的性能,CPU的性能和设备的图形性能。但我想知道是否有一种方法可以在每次迭代中节省几毫秒。在这一点上,任何建议都会有很大的帮助。

3 个答案:

答案 0 :(得分:1)

我也做了类似的事情。我使用了ValueAnimator。图像是图像ID的数组。例如:R.drawable.park1

ValueAnimator animation = ValueAnimator.ofInt(0,44);
            animation.setInterpolator(new LinearInterpolator());
            animation.setDuration(1000);
            animation.setRepeatCount(200);
            animation.addUpdateListener(new AnimatorUpdateListener() {
                int i;          
                public void onAnimationUpdate(ValueAnimator animation) {
                    Log.i("TAG", "::onAnimationUpdate: "+(++i)+" value = "+animation.getAnimatedValue());               
                    mImageViewPreview.setImageResource(images[(Integer) animation.getAnimatedValue()]);
                    mImageViewPreview.invalidate();

                    if(mImageViewPreview.getVisibility()!=View.VISIBLE){
                        animation.cancel();                 
                    }
                }           

            });
            animation.start();  

答案 1 :(得分:1)

你说SDCard阅读的速度很慢是正确的。实际上,看看你想做多少事情 - 从外部位置创建一个新文件,解码它并创建一个新的位图。所有这些都在UI线程上完成。

我不知道您的图片有多大,但您是否尝试过将其中一些图片预加载,将它们放入列表中,然后从列表中选择它们并将其显示在处理程序代码中?

我意识到在内存中存储450张图片相当多,但任务可能会分开。假设您已预加载100位图并开始显示它们,然后让异步任务同时将其他100位预加载到下一个列表(或可能是同一列表)中。

答案 2 :(得分:0)

我的坏!找到了罪魁祸首。习惯于iOS上的NSTimer,我忽略了代码行的重要性:

handler.postDelayed(runnable, interval);

在从SDCard读取当前帧,解码并显示在imageview上之后,正在执行负责调度下一帧处理的代码。

即。如果显示当前帧的整个过程花费25毫秒,则在前一帧之后的25 + 33 = 58ms之后安排下一帧处理。

我将那行代码移动到runnable线程的run {}方法的第一行,并且BINGO !!即使在100美元的Android设备上,FPS也提升到24-30 FPS。

无论如何,感谢@Valentin,@ Pedro Bernardo和@Shashika指出了一些有效的解决方案。