在Android中,图形缓冲区中的memcpy速度较慢

在Android中,图形缓冲区中的memcpy速度较慢,android,c++,multimedia,Android,C++,Multimedia,我想从视频中捕获每一帧,在Android设备(如Nexus10)渲染之前进行一些修改。据我所知,android使用硬件在特定设备中解码和渲染帧,因此我应该从GraphicBuffer获取帧数据,在渲染之前,数据将为YUV格式 我还在AwesomePlayer.cpp中编写了一个静态方法来实现捕获帧数据/修改帧/将其写回GraphicBuffer以进行渲染 这是我的演示代码 static void handleFrame(MediaBuffer *buffer) { sp<Grap

我想从视频中捕获每一帧,在Android设备(如Nexus10)渲染之前进行一些修改。据我所知,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);

}
static void handleFrame(MediaBuffer*buffer){
sp buf=buffer->graphicsbuffer();
size\u t width=buf->getWidth();
size\u t height=buf->getHeight();
size\u t ySize=buffer->range\u length();
尺寸=宽度*高度/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(一般用法通常读取,一般用法通常写入,&yAddr);
uvAddr=mmap(0,uvSize,PROT_READ | PROT_WRITE,MAP_SHARED,*(private_handle+1));
if(yAddr!=NULL&&uvAddr!=NULL){
//来自图形缓冲区的memcpy数据
memcpy(yBuffer、yAddr、ySize);
memcpy(uvBuffer、uvAddr、uvSize);
//修改YUV数据
//将数据存储到图形缓冲区中
memcpy(yAddr、yBuffer、ySize);
memcpy(uvAddr、uvBuffer、uvSize);
}
munmap(uvAddr,uvSize);
buf->unlock();
免费(yBuffer);
自由(uvBuffer);
}
我打印了memcpy函数的时间戳,我意识到从GraphicBuffer输入memcpy要比从GraphicBuffer输入memcpy数据花费的时间多得多。以分辨率为1920x1080的视频为例,memcpy from GraphicBuffer大约需要30毫秒,这对于正常的视频播放是不可接受的

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

其他熟悉安卓硬件解码的人能看看这个问题吗? 非常感谢

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


链接:

最有可能的是,从CPU到图形内存的数据路径(也称为数据总线)已优化。从图形内存到CPU的路径可能未优化。优化可能包括不同速度的内部数据总线、1级或2级缓存以及等待状态

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

另一个问题是共享数据总线的所有设备。想象一下,城市之间有一条共用的高速公路。为了优化交通,只允许单向交通。交通信号灯或人,监控交通。要从A市前往C市,必须等到交通信号灯或指挥灯亮起,清除剩余交通,并优先选择A市至C市的路线。在硬件方面,这称为总线仲裁

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

不幸的是,DMA必须与CPU共享数据总线。这意味着您的数据传输将因CPU对数据总线的任何请求而减慢。它仍然比使用CPU传输数据快,因为DMA可以在CPU执行不需要数据总线的指令时传输数据

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

优化
1.如果可能,请使用DMA。
2.如果只使用CPU,则让CPU传输尽可能大的数据块。
这意味着使用专门用于复制内存的指令。
3.如果您的CPU没有专门的复制指令,请使用处理器的字号进行传输。
如果处理器有32位字,则使用1个字一次传输4个字节,而不是使用4个8位副本。
4.减少传输过程中的CPU需求和中断。
暂停任何申请;如果可能,禁用中断。
5.分工合作:让一个内核在另一个内核执行程序时传输数据。

6.单核上的线程实际上可能会减慢传输速度,因为操作系统会因为调度而卷入其中。线程切换需要时间,这会增加传输时间

这可能是因为它只是大量的数据