【问题标题】:Display a stream of bitmaps at 60fps smoothly on Android 4.x在 Android 4.x 上以 60fps 流畅显示位图流
【发布时间】:2013-07-18 21:53:49
【问题描述】:

(这是由于我将使用的服务器软件的限制,如果我可以更改它,我会)。

我正在通过套接字接收一系列 720x480 JPEG 文件(大小约为 6kb)。我对网络进行了基准测试,发现我能够以 60FPS 顺利接收这些 JPEG。

我当前的绘图操作是在 2560x1600 的 Nexus 10 显示器上,这是我的解码方法,一旦我从套接字接收到字节数组:

public static void decode(byte[] tmp, Long time) {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferQualityOverSpeed = false;
        options.inDither = false;
        Bitmap bitmap = BitmapFactory.decodeByteArray(tmp, 0, tmp.length, options);
        Bitmap background = Bitmap.createScaledBitmap
                (bitmap, MainActivity.screenwidth, MainActivity.screenheight, false);
        background.setHasAlpha(false);
            Canvas canvas = MainActivity.surface.getHolder().lockCanvas();
            canvas.drawColor(Color.BLACK);
            canvas.drawBitmap(background, 0, 0, new Paint());
            MainActivity.surface.getHolder().unlockCanvasAndPost(canvas);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

如您所见,我正在从 SurfaceView 中清除画布,然后将 Bitmap 绘制到 SurfaceView。我的问题是它非常非常慢。

基于在锁定操作之前和之后添加 System.currentTimeMillis() 的一些测试导致获取画布、绘制位图和推回画布之间大约有 30 毫秒的差异。显示的SurfaceView非常卡顿,有时会来回跳动,帧率很糟糕。

有没有这样的绘图方法?同样,我无法修改从服务器获取的内容,但我希望尽可能以 60FPS 显示位图。

(我尝试设置 ImageView 的内容,并收到类似的结果)。我在 SurfaceView 中没有其他可能影响这一点的代码。我已将支架设置为 RGBA_8888 格式:

    getHolder().setFormat(PixelFormat.RGBA_8888);

是否可以将此位图流转换为 VideoView?那会更快吗?

谢谢。

【问题讨论】:

    标签: android bitmap android-canvas surfaceview frame-rate


    【解决方案1】:

    每当您遇到性能问题时,请使用Traceview 准确找出问题所在。使用System.currentTimeMillis() 就像试图用锤子修剪牛排。

    她的第一件事是从主应用程序线程中获取位图解码。在后台线程中执行此操作。您的主应用程序线程应该只是在绘制位图,将它们从由该后台线程填充的队列中拉出。从 Android 4.1(又名“Project Butter”)开始,Android 的主应用程序线程设置为以 60fps 的速度渲染,因此只要您可以在几毫秒内绘制 Bitmap,并假设您的网络和解码可以保持你的队列最新,你应该得到 60fps 的结果。

    此外,当您的图像大小一致时,始终在 Android 3.0+ 上使用 inBitmapBitmapFactory.Options,因为您的问题的一部分是 GC 占用 CPU 时间。处理您循环使用的 Bitmap 对象池,这样您生成的垃圾就会更少,并且不会对堆进行太多碎片化。

    我怀疑让 Android 在ImageView 中为您缩放图像(或仅通过绘制到View 画布)比让BitmapFactory 缩放图像更好,因为 Android 可以采取渲染硬件图形加速的优势,BitmapFactory 不能。同样,Traceview 是您的朋友。

    关于:

    并且发现我能够以 60FPS 顺利接收这些 JPEG。

    这只是有时是真的。移动设备往往是移动的。假设“6kb”的意思是 6KB(6 KB),那么您假设的连接速度约为 3Mbps(每秒 3 兆比特),这还远不能确定。

    关于:

    是否可以将此位图流转换为 VideoView?

    VideoView 是播放视频的小部件,您没有视频。

    迫在眉睫,您可能需要下拉到 NDK 并使用本机代码执行此操作,但我不希望这样做。

    【讨论】:

    • 对不起,我忘了说,这个电话来自我的网络线程。位图解码在 UI 线程之外完成。但是,如果我确实使用了您的 imageview 建议,我将如何从另一个线程更新 ImageView?至于网络,这是在一个 54Mb/s 的 LAN 上,所以我确信我不需要担心这个。这将通过 wifi 进行,不适合一般用途。再次感谢您的帮助,我将看看如何从单独的线程设置 ImageView。
    • @user2592851:你不会的。我会尝试让解码线程生成Bitmap 对象并将它们放在LinkedBlockingQueue 上。您的自定义View(或者可能是ImageView 的子类)将poll() 在其onDraw() 方法中获取Bitmap,如果需要,可能等待几毫秒,然后渲染它。这会将渲染放在主应用程序线程上,与您的解码无关。基本上,您有一个管道:网络线程将字节数组传输到解码线程的队列,解码线程将Bitmaps 传输到队列进行渲染。
    • @user2592851:“这将通过 wifi”——您需要参加更多会议并体验大型 WiFi 网络的乐趣。 :-)
    • 所以我会在主线程中放一个public static LinkedBlockingQueue queue,然后在网络/解码线程上,我调用put(bitmap)。我对绘图过程不太熟悉,我是从我的方法中手动调用 onDraw() 还是自动调用它?再一次感谢你的帮助。至于inBitmap,我假设我会把之前的位图设置成那样?
    • @ylin:因为Bitmap 可能正在使用中。您处于多线程情况。您需要确保 Bitmap 一次只被一个线程使用。并且您需要确保在完成之前不要通过inBitmap 重用Bitmap(即,您在主应用程序线程上的代码已移动到另一个Bitmap 并且不再依赖于您的当前一个)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多