J'essaie de rendre les images saisies et converties à partir d'une vidéo en utilisant ffmpeg en une texture OpenGL à mettre sur un quad. J'ai à peu près épuisé Google et je n'ai pas trouvé de réponse, eh bien j'ai trouvé des réponses, mais aucune d'entre elles ne semble avoir fonctionné.
Fondamentalement, j'utilise avcodec_decode_video2()
pour décoder le cadre, puis sws_scale()
pour convertir le cadre en RVB, puis glTexSubImage2D()
pour créer une texture openGL à partir de celui-ci mais ne peut pas ' Il semble que rien ne fonctionne.
Je me suis assuré que l'AVFrame "de destination" a une puissance de 2 dimensions dans la configuration du contexte SWS. Voici mon code:
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();
Comme vous pouvez le voir dans ce code, j'enregistre également les images dans des fichiers .ppm juste pour m'assurer qu'elles sont réellement rendues, ce qu'elles sont.
Le fichier utilisé est un .wmv à 854x480, cela pourrait-il être le problème? Le fait que je lui dise juste de passer en 512x256?
P.S. J'ai regardé ça Stack Overflow question mais ça n'a pas aidé.
J'ai également glEnable(GL_TEXTURE_2D)
et je l'ai testé en chargeant simplement dans un bmp normal.
[~ # ~] modifier [~ # ~]
Je reçois maintenant une image à l'écran, mais c'est un désordre confus, je suppose que quelque chose à voir avec le changement des choses à une puissance de 2 (dans le décodage, swscontext
et gluBuild2DMipmaps
comme indiqué dans mon code). Je suis généralement presque exactement le même code que celui indiqué ci-dessus, seulement j'ai changé glTexSubImage2D
En gluBuild2DMipmaps
Et changé les types en GL_RGBA
.
Voici à quoi ressemble le cadre:
MODIFIER ENCORE
Je viens de réaliser que je n'ai pas montré le code de configuration de 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);
Maintenant que j'ai changé le PixelFormat
dans avgpicture_get_size
En PIX_FMT_RGB24
, Je l'ai fait aussi dans SwsContext
et j'ai changé GluBuild2DMipmaps
En GL_RGB
Et j'obtiens une image légèrement meilleure mais on dirait qu'il me manque encore des lignes et c'est encore un peu étiré:
Un autre montage
Après avoir suivi les conseils de Macke et passé la résolution réelle à OpenGL, je reçois les images presque correctes mais toujours un peu asymétriques et en noir et blanc, il n'obtient maintenant que 6 images par seconde plutôt que 110 images par seconde:
P.S.
J'ai une fonction pour enregistrer les cadres dans l'image après sws_scale()
et ils sortent bien en tant que couleur et tout, donc quelque chose dans OGL le rend en noir et blanc.
DERNIÈRE ÉDITION
Travail! D'accord, je le fais fonctionner maintenant, fondamentalement, je n'améliore pas la texture à une puissance de 2 et j'utilise simplement la résolution de la vidéo.
J'ai obtenu la texture s'affichant correctement avec une chance sur le bon glPixelStorei ()
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
De plus, si quelqu'un d'autre a la subimage()
montrant un problème vierge comme moi, vous devez remplir la texture au moins une fois avec glTexImage2D()
et donc je l'utilise une fois dans la boucle puis j'utilise glTexSubImage2D()
après cela.
Merci Macke et datenwolf pour toute votre aide.
La texture est-elle initialisée lorsque vous appelezréponduglTexSubImage2D
? Vous devez appelerglTexImage2D
(pas Sub ) une fois pour initialiser l'objet texture. Utilisez NULL pour le pointeur de données, OpenGL initialisera alors une texture sans copier les données.
[~ # ~] modifier [~ # ~]
Vous ne fournissez pas de niveaux de mipmap. Avez-vous donc désactivé le mipmaping?
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILER, linear_interpolation ? GL_LINEAR : GL_NEAREST);
EDIT 2 l'image étant à l'envers n'est pas une surprise car la plupart des formats d'image ont l'origine en haut à gauche, tandis qu'OpenGL place l'origine de l'image de texture en bas la gauche. Cette bande que vous voyez là ressemble à une mauvaise foulée.
EDIT 3
J'ai fait ce genre de choses moi-même il y a environ un an. Je me suis écrit un petit wrapper pour ffmpeg, je l'ai appelé aveasy https://github.com/datenwolf/aveasy
Et voici du code pour mettre les données extraites à l'aide d'aveasy dans des textures 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;
}
J'utilise ceci dans un projet et cela fonctionne là-bas, donc ce code est testé, en quelque sorte.
Le fichier utilisé est un .wmv à 854x480, cela pourrait-il être le problème? Le fait que je lui dise juste de passer en 512x256?
Oui!
Le motif rayé est une indication évidente que vous ne correspondez pas à la taille des données (taille de ligne). (Étant donné que les couleurs sont correctes, RVB vs BGR vs BGRA et n-composants est correct.)
Vous dites à OpenGL que la texture que vous téléchargez est 512x256 (ce qui n'est pas le cas, AFAICT). Utilisez les dimensions réelles (NPOT, votre carte doit la supporter si elle n'est pas ancienne).
Sinon, redimensionnez/complétez vos données avant de les télécharger en tant que texture 1024x512.
Mettre à jour
Je connais mieux OpenGL que les autres fonctions que vous appelez.
sxs_scale pourrait correspondre à ce que vous voulez (c'est-à-dire réduire l'image à la taille d'un pot). Cependant, la mise à l'échelle de chaque image peut être lente.
J'utiliserais plutôt le rembourrage (ce qui signifie, copiez une petite image (votre vidéo) dans une partie d'une grande texture (opengl)
Quelques autres conseils:
glPixelStore
pour définir les foulées de pixels et de lignes . Je soupçonne que les données fournies par sxs_scale/wmw ont un remplissage à la fin de chaque ligne (la ligne noire). Probablement pour que chaque ligne commence sur une limite paire de 8-16-32 octets.