来自图形缓冲区的memcpy在Android中很慢

时间:2014-03-13 15:34:39

标签: android c++ multimedia

我希望捕获视频中的每一帧以在Android设备中渲染之前进行一些修改,例如Nexus 10.据我所知,android使用硬件解码并渲染特定设备中的帧,所以我应该得到来自GraphicBuffer的帧数据,在渲染数据之前将是YUV格式。

我还在AwesomePlayer.cpp中编写了一个静态方法来实现捕获帧数据/修改帧/将其写回到GraphicBuffer中进行渲染。

这是我的演示代码

static void handleFrame(MediaBuffer *buffer) {

    sp<GraphicBuffer> buf = buffer->graphicBuffer();

    size_t width = buf->getWidth();
    size_t height = buf->getHeight();
    size_t ySize = buffer->range_length();
    size_t uvSize = width * height / 2;

    uint8_t *yBuffer = (uint8_t *)malloc(ySize + 1);
    uint8_t *uvBuffer = (uint8_t *)malloc(uvSize + 1);
    memset(yBuffer, 0, ySize + 1);
    memset(uvBuffer, 0, uvSize + 1);

    int const *private_handle = buf->handle->data;

    void *yAddr = NULL;
    void *uvAddr = NULL;

    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, &yAddr);
    uvAddr = mmap(0, uvSize, PROT_READ | PROT_WRITE, MAP_SHARED, *(private_handle + 1));

    if(yAddr != NULL && uvAddr != NULL) {

      //memcpy data from graphic buffer
      memcpy(yBuffer, yAddr, ySize);
      memcpy(uvBuffer, uvAddr, uvSize);

      //modify the YUV data

      //memcpy data into graphic buffer
      memcpy(yAddr, yBuffer, ySize);
      memcpy(uvAddr, uvBuffer, uvSize);
    }

    munmap(uvAddr, uvSize);
    buf->unlock();

    free(yBuffer);
    free(uvBuffer);

}

我打印了memcpy函数的时间戳,我意识到来自GraphicBuffer 的 memcpy比 memcpy数据花费更多时间到GraphicBuffer 即可。以分辨率为1920x1080的视频为例,来自GraphicBuffer memcpy大约需要30毫秒,这对于正常的视频播放来说是不可接受的。

我不知道为什么需要这么多时间,也许它会从GPU缓冲区复制数据,但 将数据复制到GraphicBuffer 看起来很正常。

在android中熟悉硬件解码的其他人是否可以看看这个问题? 非常感谢。

更新 我发现我没有必要使用GraphicBuffer来获取YUV数据,我只是使用硬件解码视频源并将YUV数据存储到内存中,这样我就可以直接从内存中获取YUV数据了。非常快。 实际上你可以在AOSP源代码或开源视频显示应用程序中找到类似的解决方案。我只是分配内存缓冲区而不是图形缓冲区,然后使用硬件解码器。 AOSP中的示例代码:frameworks / av / cmds / stagefright / SimplePlayer.cpp

link:https://github.com/xdtianyu/android-4.2_r1/tree/master/frameworks/av/cmds/stagefright

1 个答案:

答案 0 :(得分:2)

最有可能的是,从CPU到图形内存的数据路径(a.k.a.databus)已经过优化。从图形存储器到CPU的路径可能无法优化。优化可能包括不同速度的内部数据总线,1级或2级缓存以及等待状态。

电子设备(硬件)设置了将数据从图形内存传输到CPU的最大速度。 CPU的内存可能比图形内存慢,因此可能存在等待状态,以使图形内存与CPU内存的较慢速度相匹配。

另一个问题是所有设备共享数据总线。想象一下城市之间的共享高速公路。为了优化流量,流量只允许一个方向。交通信号或人,监控交通。为了从城市A到城市C,必须等到交通信号或导演,清除剩余的交通并将城市A的路线优先考虑到城市C.在硬件方面,这称为总线仲裁

在大多数平台中,CPU正在寄存器和CPU内存之间传输数据。这是在程序中读取和写入变量所必需的。传输数据的慢速路径是CPU将存储器读入寄存器,然后写入图形存储器。更有效的方法是在不使用CPU的情况下传输数据。可能存在设备DMA(直接存储器访问),其可以在不使用CPU的情况下传输数据。您告诉它源和目标内存位置,然后启动它。它将在不使用CPU的情况下传输数据。

不幸的是,DMA必须与CPU共享数据总线。这意味着CPU的任何数据总线请求都会降低数据传输速度。它仍然比使用CPU传输数据更快,因为当CPU执行不需要数据总线的指令时,DMA可以传输数据。

<强>摘要
如果您没有DMA设备,您的内存传输可能会很慢。无论是否使用DMA,数据总线都由多个设备共享并进行流量仲裁。这设置了传输数据的最大总速度。存储器芯片的数据传输速度也可以有助于数据传输速率。硬件方面,有速度限制。

<强>优化
1.如果可能,请使用DMA 2.如果仅使用CPU,则CPU可以传输最大的块     这意味着使用专门用于复制存储器的指令 3.如果您的CPU没有专门的复制指令,请使用处理器的字大小进行传输     如果处理器具有32位字,则一次传输4个字节,而不是使用4个8位副本 4.在传输过程中减少CPU需求和中断     暂停任何应用程序;如果可能,禁用中断 5.分工:有一个核心传输数据而另一个核心正在执行你的程序 6.由于调度操作系统涉及操作系统,因此单个内核上的线程实际上可能会减慢传输速度。线程切换需要时间,这会增加传输时间。