フレームを取得し、ffmpegを使用してビデオからOpenGLテクスチャーに変換してクワッドに配置しようとしています。私はグーグルを使い果たして答えを見つけられませんでした。よく答えを見つけましたが、どれもうまくいっていないようです。
基本的に、私はavcodec_decode_video2()
を使用してフレームをデコードし、次にsws_scale()
を使用してフレームをRGBに変換してから、glTexSubImage2D()
を使用してopenGLテクスチャを作成していますが、何でも動作させる。
「宛先」AVFrameがSWSコンテキストセットアップで2次元のパワーを持つことを確認しました。これが私のコードです:
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ファイルに保存され、実際にレンダリングされていることを確認しています。
使用されているファイルは854x480の.wmvですが、これは問題でしょうか? 512x256に変更するように言っているだけですか?
追伸私はこれを見ました Stack Overflowの質問 しかし、それは助けにはなりませんでした。
また、glEnable(GL_TEXTURE_2D)
も使用しており、通常のbmpをロードするだけでテストしました。
[〜#〜]編集[〜#〜]
画面に画像が表示されますが、それは文字化けした混乱です。2の累乗に変更することで何かを推測しています(コードでは、デコードでは、swscontext
とgluBuild2DMipmaps
)。 。私は通常、上記とほぼ同じコードですが、glTexSubImage2D
をgluBuild2DMipmaps
に変更し、タイプをGL_RGBA
に変更しただけです。
フレームは次のようになります。
再度編集
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);
avgpicture_get_size
のPixelFormat
をPIX_FMT_RGB24
に変更したので、SwsContext
でも変更し、GluBuild2DMipmaps
をGL_RGB
に変更して、少し良い画像を取得しましたが、まだ線が足りないようで、まだ少し引き伸ばされています。
別の編集
Mackeのアドバイスに従い、実際の解像度をOpenGLに渡した後、フレームはほぼ適切ですが、多少ゆがみがあり、白黒になっています。また、110fpsではなく6fpsしか取得していません。
追伸.
私はsws_scale()
の後に画像にフレームを保存する機能を持っていますが、それらはカラーとしてすべてうまくいき、OGLで何かが白黒になっています。
最終編集
ワーキング!さて、これで機能しました。基本的に、テクスチャを2の累乗でパディングするのではなく、ビデオの解像度を使用します。
幸運な推測で正しいglPixelStorei()でテクスチャが正しく表示されました
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
また、他の誰かが私のようにsubimage()
で空白の問題を示している場合は、テクスチャを少なくとも1回glTexImage2D()
で埋める必要があるため、ループで1回使用してからglTexSubImage2D()
。
Mackeとdatenwolfに感謝します。
回答済みglTexSubImage2D
を呼び出すと、テクスチャが初期化されますか?テクスチャオブジェクトを初期化するには、glTexImage2D
(Subではない)を1回呼び出す必要があります。データポインタにNULLを使用すると、OpenGLはデータをコピーせずにテクスチャを初期化します。
[〜#〜]編集[〜#〜]
ミップマッピングレベルを指定していません。ミップマッピングを無効にしましたか?
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILER, linear_interpolation ? GL_LINEAR : GL_NEAREST);
EDIT 2ほとんどの画像フォーマットは左上に原点があるので、OpenGLはテクスチャ画像の原点を下に置くので、画像が上下逆であることは驚くべきことではありません左。あなたがそこに見るそのバンディングは間違った行ストライドのように見えます。
EDIT 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;
}
私はこれをプロジェクトで使用していますが、そこで機能するため、このコードはテスト済みです。
使用されているファイルは854x480の.wmvですが、これは問題でしょうか? 512x256に変更するように言っているだけですか?
はい!
縞模様は、データサイズ(行サイズ)が一致していないことを示しています。 (色が正しいので、RGB vs BGR vs BGRAおよびnコンポーネントは正しいです。)
アップロードしているテクスチャが512x256(AFAICTではない)であることをOpenGLに伝えています。実際の寸法を使用します(NPOT、カードが古くない場合はサポートする必要があります)。
それ以外の場合は、データのサイズ変更/パディングbefore 1024x512テクスチャとしてアップロードします。
更新
私はあなたが呼んでいる他の関数よりOpenGLに精通しています。
sxs_scaleはあなたが望むものになるかもしれません(つまり、画像をポットサイズに縮小します)。ただし、各フレームのスケーリングは遅い場合があります。
代わりにパディングを使用します(つまり、小さな画像(ビデオ)を大きなテクスチャ(opengl)の一部にコピーします
その他のヒント:
glPixelStore
を調べてピクセルと行のストライドを設定します。 sxs_scale/wmwから与えられたデータには、各行の終わり(黒い線)にパディングがあると思います。おそらく、各行が8-16-32バイトの偶数境界で始まるようにするためです。