Android FFMPEG OpenGLES рендеринга фильма - PullRequest
3 голосов
/ 15 января 2012

Я пытаюсь визуализировать видео через NDK, чтобы добавить некоторые функции, которые просто не поддерживаются в SDK. Я использую FFmpeg для декодирования видео и могу скомпилировать это через ndk, и использовал this в качестве отправной точки. Я изменил этот пример и вместо того, чтобы использовать glDrawTexiOES для рисования текстуры, я настроил некоторые вершины и рендеринг текстуры поверх этого (открытый способ рендеринга четырехугольника).

Ниже показано, что я делаю для рендеринга, но создание glTexImage2D идет медленно. Я хочу знать, есть ли способ ускорить это или создать видимость ускорения, например, попытаться настроить некоторые текстуры на заднем плане и визуализировать предварительно настроенные текстуры. Или, если есть какой-то другой способ быстрее нарисовать видеокадры на экране в Android? В настоящее время я могу получить только около 12 кадров в секунду.

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);

EDIT Я изменил свой код, чтобы инициализировать 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;
}
}

Ответы [ 4 ]

6 голосов
/ 17 января 2012

Да, создание текстур (и буфера, и шейдера, и кадрового буфера) идет медленно.

Вот почему вы должны создавать текстуру только один раз.После того, как он создан, вы можете изменить его данные, вызвав glSubTexImage2D .

И чтобы ускорить загрузку данных текстуры - создайте две текстуры.Пока вы используете один для отображения, загрузите данные текстуры из ffmpeg во второй.Когда вы отображаете второй, загрузите данные в первый.И повтори с начала.

Я думаю, что все равно будет не очень быстро.Вы можете попробовать использовать библиотеку jnigraphics, которая позволяет получить доступ к пикселям объекта Bitmap из NDK.После этого вы просто отображаете этот растр на экране на стороне Java.

1 голос
/ 18 января 2013

Несколько незначительных дополнений решат вашу проблему, сначала конвертируйте ваш AVFrame в RGB с помощью swscale, а затем примените его непосредственно к вашей текстуре, т.е.

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);
}
0 голосов
/ 03 июня 2012

Да, вы можете оптимизировать этот код:

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

Это сэкономит вам несколько циклов.

0 голосов
/ 17 января 2012

О, может быть, вы можете использовать jnigraphics как https://github.com/havlenapetr/FFMpeg/commits/debug. но если, когда вы получаете данные yuv после декодирования кадра, вам следует преобразовать его в RGB555, это будет слишком медленно. Используйте медиаплеер для Android - хорошая идея

...