自 7 月以来,我开发了 Android 应用程序来编辑 .avi、.flv 等视频文件。我使用 FFMPEG 和 OpenGL ES 2.0 来实现这个应用程序。
由于CPU执行“Blur”之类的滤镜效果需要太多的计算,所以我决定使用OpenGl ES 2.0通过GPU和Shader对一帧视频应用滤镜效果。
我尝试做的是“使用着色器将滤镜效果应用于视频帧并获取存储在帧缓冲区中的像素”。
所以我必须使用 glReadPixels only OpenGL ES 2.0 方法,该方法可用于从 FrameBuffer 获取像素。但是根据许多 GPU 开发指南,不推荐使用 glReadPixels,并且指南书警告说使用 glReadPixels 时存在潜在风险。此外,glReadPixels 的性能因 GPU 版本和供应商而异。我无法具体决定使用 glReadPixels 并试图找到其他获取像素的方法,这是 GPU 计算的结果。
几天后,我发现了使用 Android GraphicBuffer 获取像素数据的 hacky 方法。
这是链接。
从这个链接,我尝试了Karthik 的方法来编写我的代码。
唯一的区别是:
//render method I made.
void renderFrame(){
/* some codes to init */
glBindFramebuffer(GL_FRAMEBUFFER, iFBO);
/* Set the viewport according to the FBO's texture. */
glViewport(0, 0, mTexWidth , mTexHeight);
/* Clear screen on FBO. */
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Different Code compare to Karthik's.
contents->setTexture();
contents->draw(mPositionVarIndex, mTextrueCoIndex);
contents->releaseData();
/* And unbind the FrameBuffer Object so subsequent drawing calls are to the EGL window surface. */
glBindFramebuffer(GL_FRAMEBUFFER,0);
LOGI("Read Graphic Buffer");
// Just in case the buffer was not created yet
void* vaddr;
// Lock the buffer and retrieve a pointer where we are going to write the data
buffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &vaddr);
if (vaddr == NULL) {
LOGE("lock error");
buffer->unlock();
return;
}
/* some codes that use the pixels from GraphicBuffer...*/
}
void setTexture(){
glGenTextures(1, mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, mData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
void releaseData(){
glDeleteTextures(1, mTexture);
glDeleteBuffers(1, mVbo);
}
void draw(int positionIndex, int textureIndex){
mVbo[0] = create_vbo(lengthOfArray*sizeOfFloat*2, NULL, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, mVbo[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, lengthOfArray*sizeOfFloat, this->vertexData);
glEnableVertexAttribArray(positionIndex);
// checkGlError("glEnableVertexAttribArray");
glVertexAttribPointer(positionIndex, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// checkGlError("glVertexAttribPointer");
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, mVbo[0]);
glBufferSubData(GL_ARRAY_BUFFER, lengthOfArray*sizeOfFloat, lengthOfArray*sizeOfFloat, this->mImgTextureData);
glEnableVertexAttribArray(textureIndex);
glVertexAttribPointer(textureIndex, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(lengthOfArray*sizeOfFloat));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTexture[0]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
checkGlError("glDrawArrays");
}
我使用纹理和渲染帧来填充缓冲区。我有 2 部测试手机,一部是三星 Galaxy S 2,渲染器是 Mali-400MP。另一个是 LG Optimus G Pro,渲染器是 Adreno(TM) 320。Galaxy S2 与上述代码和 Karthik 的方法配合得很好。但如果是 LG 智能手机,则存在一些问题。
E/libgenlock(17491): perform_lock_unlock_operation: GENLOCK_IOC_DREADLOCK failed (lockType0x1,err=Connection timed out fd=47)
E/gralloc(17491): gralloc_lock: genlock_lock_buffer (lockType=0x2) failed
W/GraphicBufferMapper(17491): lock(...) failed -22 (Invalid argument)
根据此链接,
在 Android-4.2 之前的 Qualcomm 硬件上,使用了一种名为 Genlock 的 Qualcomm 特定机制。
只有我能看到与GenLock相关的错误,所以我仔细猜测了GraphicBuffer和Qualcomm GPU之间的一些问题。之后,我搜索并阅读了 Gralloc.cpp、GraphicBufferMapper.cpp、GraphicBuffer.cpp 和 *.h 的代码以查找这些错误的原因,但失败了。
我的问题是:
从GPU计算中获得过滤效果的方法是否正确?如果没有,如何获得像“模糊”这样需要大量计算的滤镜效果?
Karthik 的方法不适用于高通 GPU 吗?我想知道为什么这些错误只发生在高通 GPU、Adreno 上。