видео ffmpeg в текстуру opengl - PullRequest
38 голосов
/ 27 июня 2011

Я пытаюсь визуализировать кадры, захваченные и преобразованные из видео с помощью ffmpeg, в текстуру OpenGL для размещения на четырехугольнике.Я в значительной степени исчерпал Google и не нашел ответа, ну, я нашел ответы, но ни один из них, кажется, не работал.

В основном, я использую avcodec_decode_video2() для декодирования фрейма, а затем sws_scale() чтобы преобразовать кадр в RGB, а затем glTexSubImage2D(), чтобы создать из него текстуру openGL, но, похоже, ничего не получилось.

Я убедился, что AVFrame "назначения" имеет мощность 2размеры в настройках SWS Context.Вот мой код:

SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width,
                pCodecCtx->height, pCodecCtx->pix_fmt, 512,
                256, PIX_FMT_RGB24, SWS_BICUBIC, NULL,
                NULL, NULL);

//While still frames to read
while(av_read_frame(pFormatCtx, &packet)>=0) {
    glClear(GL_COLOR_BUFFER_BIT);

    //If the packet is from the video stream
    if(packet.stream_index == videoStream) {
        //Decode the video
        avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

        //If we got a frame then convert it and put it into RGB buffer
        if(frameFinished) {
            printf("frame finished: %i\n", number);
            sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

            glBindTexture(GL_TEXTURE_2D, texture);
            //gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pCodecCtx->width, pCodecCtx->height, GL_RGB, GL_UNSIGNED_INT, pFrameRGB->data);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, 512, 256, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
            SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, number);
            number++;
        }
    }

    glColor3f(1,1,1);
    glBindTexture(GL_TEXTURE_2D, texture);
    glBegin(GL_QUADS);
        glTexCoord2f(0,1);
        glVertex3f(0,0,0);

        glTexCoord2f(1,1);
        glVertex3f(pCodecCtx->width,0,0);

        glTexCoord2f(1,0);
        glVertex3f(pCodecCtx->width, pCodecCtx->height,0);

        glTexCoord2f(0,0);
        glVertex3f(0,pCodecCtx->height,0);

    glEnd();

Как вы можете видеть в этом коде, я также сохраняю фреймы в файлы .ppm просто для того, чтобы убедиться, что они на самом деле рендеринга, что они и есть.

Используемый файл - .wmv в 854x480, это может быть проблемой?То, что я просто говорю, чтобы это было 512x256?

PS Я смотрел на этот вопрос переполнения стека , но это не помогло.

Кроме того, яИметь glEnable(GL_TEXTURE_2D) и проверить его, просто загрузив в обычном BMP.

РЕДАКТИРОВАТЬ

Я получаю изображение на экране сейчас, но этоискаженный беспорядок, я думаю, что-то делать с изменением степени до 2 (в декодировании, swscontext и gluBuild2DMipmaps, как показано в моем коде).Я обычно почти такой же код, как показано выше, только я изменил glTexSubImage2D на gluBuild2DMipmaps и изменил типы на GL_RGBA.

Вот как выглядит кадр:

Ffmpeg as OpenGL Texture garbled

ВНОВЬ РЕДАКТИРОВАТЬ

Только что понял, что я не показал код для настройки pFrameRGB:

//Allocate video frame for 24bit RGB that we convert to.
AVFrame *pFrameRGB;
pFrameRGB = avcodec_alloc_frame();

if(pFrameRGB == NULL) {
    return -1;
}

//Allocate memory for the raw data we get when converting.
uint8_t *buffer;
int numBytes;
numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
buffer = (uint8_t *) av_malloc(numBytes*sizeof(uint8_t));

//Associate frame with our buffer
avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24,
    pCodecCtx->width, pCodecCtx->height);

Теперь, когда я изменил PixelFormat в avgpicture_get_size на PIX_FMT_RGB24, я также сделал это в SwsContext и изменил GluBuild2DMipmaps на GL_RGB, и я получаю немного лучшее изображение, но оно выглядиткак будто я все еще пропускаю строки, и это все еще немного растянуто:

Ffmpeg Garbled OpenGL Texture 2

Другое Правка

После следования совету Макке и прохожденияфактическое разрешение для OpenGL Я получаю кадры почти правильные, но все еще немного искаженные и черно-белые, а теперь он получает только 6 кадров в секунду вместо 110 кадров в секунду:

enter image description here

PS

У меня есть функция для сохранения кадров в изображение после sws_scale(), и они выглядят хорошо как цвет и все такое, что яn OGL делает это черно-белым.

ПОСЛЕДНИЕ РЕДАКТИРОВАТЬ

Работает!Хорошо, теперь у меня все работает, в основном я не добавляю текстуру к степени 2, а просто использую разрешение видео.

Я получил текстуру, отображаемую правильно с удачным предположением в правильном glPixelStorei()

glPixelStorei(GL_UNPACK_ALIGNMENT, 2);

Кроме того, если у кого-то еще, как и у меня, есть проблема с отображением subimage(), как вам, вы должны заполнить текстуру хотя бы один раз с помощью glTexImage2D(), поэтому я использую ее один раз в цикле изатем используйте glTexSubImage2D().

Спасибо Маке и datenwolf за вашу помощь.

Ответы [ 2 ]

9 голосов
/ 27 июня 2011

Инициализируется ли текстура при вызове glTexSubImage2D?Вам нужно вызвать glTexImage2D (не Sub ) один раз, чтобы инициализировать объект текстуры.Используйте NULL для указателя данных, затем OpenGL инициализирует текстуру без копирования данных. ответил

РЕДАКТИРОВАТЬ

Выне поставляешь мипмаппинг уровней.Так вы отключили мипмапинг?

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILER, linear_interpolation ? GL_LINEAR : GL_NEAREST);

РЕДАКТИРОВАТЬ 2 изображение вверх ногами не является сюрпризом, так как большинство форматов изображений имеют источник в верхнем левом углу, в то время как OpenGL помещает источник изображения текстурыв левом нижнем углу.Эта полоска, которую вы видите там, выглядит как неправильный ход по ряду.

РЕДАКТИРОВАТЬ 3

Я сам делал подобные вещи около года назад.Я написал небольшую оболочку для ffmpeg, я назвал ее aveasy https://github.com/datenwolf/aveasy

И вот какой-то код для помещения данных, выбранных с помощью aveasy, в текстуры OpenGL:

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <GL/glew.h>

#include "camera.h"
#include "aveasy.h"

#define CAM_DESIRED_WIDTH 640
#define CAM_DESIRED_HEIGHT 480

AVEasyInputContext *camera_av;
char const *camera_path = "/dev/video0";
GLuint camera_texture;

int open_camera(void)
{
    glGenTextures(1, &camera_texture);

    AVEasyInputContext *ctx;

    ctx = aveasy_input_open_v4l2(
        camera_path,
        CAM_DESIRED_WIDTH,
        CAM_DESIRED_HEIGHT,
        CODEC_ID_MJPEG,
        PIX_FMT_BGR24 );
    camera_av = ctx;

    if(!ctx) {
        return 0;
    }

    /* OpenGL-2 or later is assumed; OpenGL-2 supports NPOT textures. */
    glBindTexture(GL_TEXTURE_2D, camera_texture[i]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(
        GL_TEXTURE_2D,  
        0,
        GL_RGB, 
        aveasy_input_width(ctx),
        aveasy_input_height(ctx),
        0,
        GL_BGR,
        GL_UNSIGNED_BYTE,
        NULL );

    return 1;
}

void update_camera(void)
{
    glPixelStorei( GL_UNPACK_SWAP_BYTES, GL_FALSE );
    glPixelStorei( GL_UNPACK_LSB_FIRST,  GL_TRUE  );
    glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
    glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0);
    glPixelStorei( GL_UNPACK_SKIP_ROWS, 0);
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1);

    AVEasyInputContext *ctx = camera_av;
    void *buffer;

    if(!ctx)
        return;

    if( !( buffer = aveasy_input_read_frame(ctx) ) )
        return;

    glBindTexture(GL_TEXTURE_2D, camera_texture);
    glTexSubImage2D(
        GL_TEXTURE_2D,
        0,
        0,
        0,
        aveasy_input_width(ctx),
        aveasy_input_height(ctx),
        GL_BGR,
        GL_UNSIGNED_BYTE,
        buffer );
}


void close_cameras(void)
{
    aveasy_input_close(camera_av);
    camera_av=0;
}

I'mиспользуя это в проекте, и он работает там, так что этот код проверен, вроде.

5 голосов
/ 28 июня 2011

Используемый файл является .wmv в 854x480, это может быть проблемой? Тот факт, что я просто говорю это идти 512x256

Да!

Полосатый рисунок является очевидным признаком того, что вы не соответствует размерам данных (размеру строки). (Так как цвета правильные, RGB против BGR против BGRA и n-компонентов верны.)

Вы говорите OpenGL, что загружаемая текстура имеет размер 512x256 (что не так, AFAICT). Используйте реальные размеры (NPOT, ваша карта должна поддерживать его, если он не древний).

В противном случае измените размер / добавьте данные перед тем, как загрузите их как текстуру 1024x512.

Обновление

Я больше знаком с OpenGL, чем с другими функциями, которые вы вызываете.

sxs_scale может соответствовать тому, что вы хотите (т. Е. Масштабировать изображение до размера банка). Однако масштабирование каждого кадра может быть медленным.

Вместо этого я бы использовал отступы (что означает копирование небольшого изображения (вашего видео) в часть большой текстуры (opengl)

Некоторые другие советы:

  • Тебе действительно нужны мипмапы? Создавайте их только в том случае, если вам нужно плавно уменьшить текстуру (обычно это требуется только при использовании 3d-геометрии).
  • Избегайте генерации mipmap во время выполнения, если вы визуализируете видео (особенно, не используйте gluBuildMipMaps2D, так как это может работать в программном обеспечении. Существуют другие способы, которые работают быстрее, , если , вам нужно mipmapping ( например, использование параметра текстуры GL_GENERATE_MIPMAP). См. этот поток для получения дополнительной информации.
  • Избегайте повторного вызова glTexImage, так как это создает новую текстуру. glTexSubImage просто обновляет часть текстуры, которая может работать лучше для вас.
  • Если вы хотите загрузить текстуру за один шаг (что предпочтительно по соображениям производительности), но данные не совсем подходят, посмотрите glPixelStore, чтобы установить шаг в пикселях и рядах. Я подозреваю, что данные, полученные из sxs_scale / wmw, имеют некоторые отступы в конце каждой строки (черная линия). Вероятно, так, что каждая строка начинается с четной границы 8-16-32 байта.
...