【发布时间】:2014-03-13 15:34:39
【问题描述】:
我想捕获视频中的每一帧以在 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 函数的时间戳,我意识到 memcpy from GraphicBuffer 比 memcpy 数据到 GraphicBuffer花费更多的时间>。以分辨率为 1920x1080 的视频为例,memcpy from GraphicBuffer大约需要 30ms,对于正常的视频播放来说是无法接受的。
我不知道为什么要花这么多时间,也许它从 GPU 缓冲区复制数据,但 将数据复制到 GraphicBuffer 看起来很正常。
其他熟悉android中硬件解码的人可以看看这个问题吗? 非常感谢。
更新: 我发现我不必使用GraphicBuffer来获取YUV数据,我只是使用硬件解码视频源并将YUV数据存储到内存中,这样我就可以直接从内存中获取YUV数据,速度非常快。 实际上,您可以在 AOSP 源代码或开源视频显示应用程序中找到类似的解决方案。我只是分配内存缓冲区而不是图形缓冲区,然后使用硬件解码器。 AOSP 中的示例代码:frameworks/av/cmds/stagefright/SimplePlayer.cpp
链接:https://github.com/xdtianyu/android-4.2_r1/tree/master/frameworks/av/cmds/stagefright
【问题讨论】:
-
这可能是因为它只是很多数据。 1920x1080 是每帧 2.3M,仅 8 位/像素。如果您有完整的 ARGB,预计每帧将达到 8.2M。假设您每秒甚至可以达到 15 帧 - 您要求您的平板电脑每秒移动 124Mbytes。需要在屏幕上移动和绘制大量数据、推送到文件或您正在使用它做什么
-
感谢您的回复@Martin,也许我没有清楚地描述我的意图。首先,我只是处理 YUV 数据,修改后写入原始缓冲区。另外,我在分配的缓冲区之间测试 memcpy 相同的数据,它只需要很少的时间,同一个视频只需大约 5 毫秒。所以我假设它需要很长时间,因为它从 GPU 复制数据,但是将数据复制回 GPU 花费的时间并不多,大约 6 毫秒或 7 毫秒是没有意义的。很困惑。
-
由于使用的缓存模型,从图形单元复制数据通常要慢一个数量级。在某些平台(这里我指的是图形设备和系统板)上,您甚至可能无法将其复制回系统内存。
-
感谢@Calvin,我对硬件级别的机制了解不多,所以找不到根本原因。但我真的尝试将帧数据(YUV)复制到系统内存(就像我用 malloc()分配的上述缓冲区),修改数据,并将其复制回原始 GraphicBuffer,它在 Nexus 10 上运行良好,除了它由于时间延迟很多,丢了很多帧。关于缓存模型,你想告诉我更多吗?我对此一无所知。再次感谢。
-
如果您可以使用着色器对逻辑进行编程,您可以将内容保存在显存中并避免所有传输。
标签: android c++ multimedia