android ffmpeg opengl es渲染电影
我想通过NDK呈现视频,添加一些功能,只是不支持在sdk中。我使用FFmpeg来解码视频,并且可以通过ndk进行编译,并以this作为起点。我已经修改了这个例子,而不是使用glDrawTexiOES来绘制纹理,我已经设置了一些顶点并且在这个顶点上渲染了纹理(opengl es渲染四边形的方式)。android ffmpeg opengl es渲染电影
下面是我正在做的渲染,但创建glTexImage2D是缓慢的。我想知道是否有任何方法可以加快速度,或者提高速度,例如试图在背景中设置一些纹理并渲染预先设置的纹理。或者,如果有其他方法可以更快速地将视频帧绘制到Android屏幕上?目前我只能获得12fps左右。
glClear(GL_COLOR_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, textureConverted);
//this is slow
glTexImage2D(GL_TEXTURE_2D, /* target */
0, /* level */
GL_RGBA, /* internal format */
textureWidth, /* width */
textureHeight, /* height */
0, /* border */
GL_RGBA, /* format */
GL_UNSIGNED_BYTE,/* type */
pFrameConverted->data[0]);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
编辑 我改变了我的代码来初始化一个gltextImage2D只有一次,并与glSubTexImage2D修改它,它并没有太大的改进,帧率的。
然后我修改了代码来修改NDK上的本地位图对象。通过这种方法,我有一个后台线程运行处理下一帧并在本机侧填充位图对象。我认为这有潜力,但我需要提高将AVFrame对象从FFmpeg转换为本地位图的速度。下面是目前我正在使用的一种蛮力方法。有什么方法可以提高速度或优化此转换吗?
static void fill_bitmap(AndroidBitmapInfo* info, void *pixels, AVFrame *pFrame)
{
uint8_t *frameLine;
int yy;
for (yy = 0; yy < info->height; yy++) {
uint8_t* line = (uint8_t*)pixels;
frameLine = (uint8_t *)pFrame->data[0] + (yy * pFrame->linesize[0]);
int xx;
for (xx = 0; xx < info->width; xx++) {
int out_offset = xx * 4;
int in_offset = xx * 3;
line[out_offset] = frameLine[in_offset];
line[out_offset+1] = frameLine[in_offset+1];
line[out_offset+2] = frameLine[in_offset+2];
line[out_offset+3] = 0;
}
pixels = (char*)pixels + info->stride;
}
}
是的,纹理(和缓冲区,着色器和帧缓冲区)的创建速度很慢。
这就是为什么你只应该创建纹理一次。创建后,您可以通过调用glSubTexImage2D来修改其数据。
为了更快地上传纹理数据 - 创建两个纹理。当你使用一个来显示时,将纹理数据从ffmpeg上传到第二个。当你显示第二个时,上传数据到第一个。并从头开始重复。
我认为它仍然不会很快。您可以尝试使用允许从NDK访问位图对象像素的jnigraphics库。之后 - 您只需在java侧的屏幕上显示此位图。
哦,也许你可以使用jnigraphics作为https://github.com/havlenapetr/FFMpeg/commits/debug。 但如果当你解码帧后得到的YUV数据,你应该把它转换为RGB555,实在是太slowly.Use Android的媒体播放器是一个好主意
是的,你可以优化这段代码:
static void fill_bitmap(AndroidBitmapInfo* info, void *pixels, AVFrame *pFrame)
{
uint8_t *frameLine;
int yy;
for (yy = 0; yy < info->height; yy++)
{
uint8_t* line = (uint8_t*)pixels;
frameLine = (uint8_t *)pFrame->data[0] + (yy * pFrame->linesize[0]);
int xx;
for (xx = 0; xx < info->width; xx++) {
int out_offset = xx * 4;
int in_offset = xx * 3;
line[out_offset] = frameLine[in_offset];
line[out_offset+1] = frameLine[in_offset+1];
line[out_offset+2] = frameLine[in_offset+2];
line[out_offset+3] = 0;
}
pixels = (char*)pixels + info->stride;
}
}
就像这样:
static void fill_bitmap(AndroidBitmapInfo* info, void *pixels, AVFrame *pFrame)
{
uint8_t *frameLine = (uint8_t *)pFrame->data[0];
int yy;
for (yy = 0; yy < info->height; yy++)
{
uint8_t* line = (uint8_t*)pixels;
int xx;
int out_offset = 0;
int in_offset = 0;
for (xx = 0; xx < info->width; xx++) {
int out_offset += 4;
int in_offset += 3;
line[out_offset] = frameLine[in_offset];
line[out_offset+1] = frameLine[in_offset+1];
line[out_offset+2] = frameLine[in_offset+2];
line[out_offset+3] = 0;
}
pixels = (char*)pixels + info->stride;
frameLine += pFrame->linesize[0];
}
}
这将为您节省一些周期。
一些小的添加将解决您的问题,首先将您的AVFrame转换为swscale的RGB,然后直接将其应用于您的纹理,即:
AVPicture *pFrameConverted;
struct SwsContext img_convert_ctx;
void init(){
pFrameConverted=(AVPicture *)avcodec_alloc_frame();
avpicture_alloc(pFrameConverted, AV_PIX_FMT_RGB565, videoWidth, videoHeight);
img_convert_ctx = sws_getCachedContext(&img_convert_ctx,
videoWidth,
videoHeight,
pCodecCtx->pix_fmt,
videoWidth,
videoHeight,
AV_PIX_FMT_RGB565,
SWS_FAST_BILINEAR,
NULL, NULL, NULL);
ff_get_unscaled_swscale(img_convert_ctx);
}
void render(AVFrame* pFrame){
sws_scale(img_convert_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pFrame->height, pFrameConverted->data, pFrameConverted->lineSize);
glClear(GL_COLOR_BUFFER_BIT);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoWidth, videoHeight, GL_RGB, GL_UNSIGNED_BYTE, pFrameConverted->data[0]);
glDrawTexiOES(0, 0, 0, videoWidth, videoHeight);
}
UPDATE ::使用图像上方的fill_bitmap方法是黑白从FFMPEG的帧输出转换图像时我得到更好的帧率,但是,我通过侧得到像侧的重复。 – broschb 2012-01-19 05:30:48