Подтвердить что ты не робот

Ffmpeg video to opengl texture

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

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

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

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?

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

Кроме того, у меня есть 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

Другое Редактирование

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

enter image description here

P.S.

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

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

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

Я получил текстуру, которая появляется правильно с удачной предпосылкой правильного glPixelStorei()

glPixelStorei(GL_UNPACK_ALIGNMENT, 2);

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

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

4b9b3361

Ответ 1

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

ИЗМЕНИТЬ

Вы не поставляете уровни mipmaping. Так вы отключили mipmaping?

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

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

Я использую это в проекте, и он работает там, поэтому этот код протестирован, вроде.

Ответ 2

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

Да!

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

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

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

Update

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

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

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

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

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