【问题标题】:android ffmpeg opengl es render movieandroid ffmpeg opengl es 渲染电影
【发布时间】:2012-02-10 15:54:09
【问题描述】:

我正在尝试通过 NDK 渲染视频,以添加一些 sdk 中不支持的功能。我正在使用 FFmpeg 对视频进行解码,并且可以通过 ndk 对其进行编译,并使用 this 作为起点。我已经修改了该示例,而不是使用 glDrawTexiOES 来绘制纹理,而是设置了一些顶点并在其之上渲染纹理(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 上的本机 Bitmap 对象。使用这种方法,我有一个后台线程,它运行处理下一帧并在本机端填充位图对象。我认为这有潜力,但我需要提高将 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;
}
}

【问题讨论】:

  • UPDATE:: 我得到了更好的帧速率,但是当使用上面的 fill_bitmap 方法从 ffmpeg 的帧输出转换图像时,图像是黑白的,并且我得到了图像的副本一边。

标签: android opengl-es android-ndk ffmpeg


【解决方案1】:

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

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

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

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

【讨论】:

  • 感谢您的建议,请参阅我对上述问题的更新。
  • 位图方法让我的帧率接近我想要的。现在的问题是,使用原始问题中的方法将输出帧从 mmpeg 转换为本机位图时,我得到了黑白图像。有什么想法吗?
  • 我对ffmpeg不熟悉,但也许你的像素格式不同。 ffmpeg 是否解码为 RGB?还要检查是否使用正确的像素格式 - ARGB8 创建了位图。
  • 是的,就是这样,在尝试加载像素之前,我没有从原生格式转换为 ARGB8。
【解决方案2】:

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

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

这将为您节省一些周期。

【讨论】:

    【解决方案3】:

    添加一些小内容可以解决您的问题,首先使用 swscale 将您的 AVFrame 转换为 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);
    }
    

    【讨论】:

      【解决方案4】:

      哦,也许你可以使用 jnigraphics 作为https://github.com/havlenapetr/FFMpeg/commits/debug。 但是如果你解码帧后得到yuv数据,你应该把它转换成RGB555,太慢了。使用android的媒体播放器是个好主意

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多