2012-01-15 121 views
3

我想通过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; 
} 
} 
+0

UPDATE ::使用图像上方的fill_bitmap方法是黑白从FFMPEG的帧输出转换图像时我得到更好的帧率,但是,我通过侧得到像侧的重复。 – broschb 2012-01-19 05:30:48

回答

6

是的,纹理(和缓冲区,着色器和帧缓冲区)的创建速度很慢。

这就是为什么你只应该创建纹理一次。创建后,您可以通过调用glSubTexImage2D来修改其数据。

为了更快地上传纹理数据 - 创建两个纹理。当你使用一个来显示时,将纹理数据从ffmpeg上传到第二个。当你显示第二个时,上传数据到第一个。并从头开始重复。

我认为它仍然不会很快。您可以尝试使用允许从NDK访问位图对象像素的jnigraphics库。之后 - 您只需在java侧的屏幕上显示此位图。

+0

感谢您的建议,请参阅我对上述问题的更新。 – broschb 2012-01-17 14:58:02

+0

位图方法给了我接近我想要的帧速率。现在的问题是,当我使用原始问题中的方法将输出帧从mmpeg转换为本地位图时,我得到的是黑白图像。有什么想法吗? – broschb 2012-01-19 05:29:38

+0

我不熟悉ffmpeg,但也许你的像素格式是不同的。 ffmpeg是否可以解码为RGB?同时检查是否使用正确的像素格式创建了位图 - ARGB8。 – 2012-01-19 06:38:12

0

是的,你可以优化这段代码:

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]; 
} 
} 

这将为您节省一些周期。

1

一些小的添加将解决您的问题,首先将您的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); 
}