我希望捕获视频中的每一帧以在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
答案 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.由于调度操作系统涉及操作系统,因此单个内核上的线程实际上可能会减慢传输速度。线程切换需要时间,这会增加传输时间。