在C / C ++中编写YUV图像帧的问题 [英] Issue with writing YUV image frame in C/C++
问题描述
我试图将一个RGB帧从OpenGL glReadPixels()转换为YUV帧,并将YUV帧写入文件(.yuv)。后来我想把它写到一个named_pipe作为FFMPEG的输入,但现在我只想写它到一个文件,并使用YUV图像查看器查看图像结果。所以现在只是忽略了写管道。
I am trying to convert an RGB frame, which is taken from OpenGL glReadPixels(), to a YUV frame, and write the YUV frame to a file (.yuv). Later on I would like to write it to a named_pipe as an input for FFMPEG, but as for now I just want to write it to a file and view the image result using a YUV Image Viewer. So just disregard the "writing to pipe" for now.
运行我的代码后,遇到以下错误:
After running my code, I encountered the following errors:
-
YUV图像查看器软件中显示的帧数总是我在程序中声明的帧数的1/3。当我声明fps为10,我只能查看3帧。当我宣布fps为30,我只能查看10帧。但是,当我在文本编辑器中查看文件时,我可以看到我在文件中打印了正确数量的单词FRAME。
这是我得到的示例输出: http://www.bobdanani.net/image。 yuv
我看不到正确的图片,只是一些扭曲的绿色,蓝色,黄色和黑色像素。
I could not see the correct image, but just some distorted green, blue, yellow, and black pixels.
我从 http://wiki.multimedia.cx/index.php?title=YUV4MPEG2 和 http://www.fourcc.org/fccyvrgb.php#mikes_answer 和 http://kylecordes.com/2007/pipe-ffmpeg
这是我到目前为止的尝试。我知道这种转换方法是非常低效的,我可以优化它以后。现在我只想让这个朴素的方法工作,并正确显示图像。
Here is what I have tried so far. I know that this conversion approach is quite in-efficient, and I can optimize it later. Now I just want to get this naive approach to work and have the image shown properly.
int frameCounter = 1;
int windowWidth = 0, windowHeight = 0;
unsigned char *yuvBuffer;
unsigned long bufferLength = 0;
unsigned long frameLength = 0;
int fps = 10;
void display(void) {
/* clear the color buffers */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* DRAW some OPENGL animation, i.e. cube, sphere, etc
.......
.......
*/
glutSwapBuffers();
if ((frameCounter % fps) == 1){
bufferLength = 0;
windowWidth = glutGet(GLUT_WINDOW_WIDTH);
windowHeight = glutGet (GLUT_WINDOW_HEIGHT);
frameLength = (long) (windowWidth * windowHeight * 1.5 * fps) + 100; // YUV 420 length (width*height*1.5) + header length
yuvBuffer = new unsigned char[frameLength];
write_yuv_frame_header();
}
write_yuv_frame();
frameCounter = (frameCounter % fps) + 1;
if ( (frameCounter % fps) == 1){
snprintf(filename, 100, "out/image-%d.yuv", seq_num);
ofstream out(filename, ios::out | ios::binary);
if(!out) {
cout << "Cannot open file.\n";
}
out.write (reinterpret_cast<char*> (yuvBuffer), bufferLength);
out.close();
bufferLength = 0;
delete[] yuvBuffer;
}
}
void write_yuv_frame_header (){
char *yuvHeader = new char[100];
sprintf (yuvHeader, "YUV4MPEG2 W%d H%d F%d:1 Ip A0:0 C420mpeg2 XYSCSS=420MPEG2\n", windowWidth, windowHeight, fps);
memcpy ((char*)yuvBuffer + bufferLength, yuvHeader, strlen(yuvHeader));
bufferLength += strlen (yuvHeader);
delete (yuvHeader);
}
void write_yuv_frame() {
int width = glutGet(GLUT_WINDOW_WIDTH);
int height = glutGet(GLUT_WINDOW_HEIGHT);
memcpy ((void*) (yuvBuffer+bufferLength), (void*) "FRAME\n", 6);
bufferLength +=6;
long length = windowWidth * windowHeight;
long yuv420FrameLength = (float)length * 1.5;
long lengthRGB = length * 3;
unsigned char *rgb = (unsigned char *) malloc(lengthRGB * sizeof(unsigned char));
unsigned char *yuvdest = (unsigned char *) malloc(yuv420FrameLength * sizeof(unsigned char));
glReadPixels(0, 0, windowWidth, windowHeight, GL_RGB, GL_UNSIGNED_BYTE, rgb);
int r, g, b, y, u, v, ypos, upos, vpos;
for (int j = 0; j < windowHeight; ++j){
for (int i = 0; i < windowWidth; ++i){
r = (int)rgb[(j * windowWidth + i) * 3 + 0];
g = (int)rgb[(j * windowWidth + i) * 3 + 1];
b = (int)rgb[(j * windowWidth + i) * 3 + 2];
y = (int)(r * 0.257 + g * 0.504 + b * 0.098) + 16;
u = (int)(r * 0.439 + g * -0.368 + b * -0.071) + 128;
v = (int)(r * -0.148 + g * -0.291 + b * 0.439 + 128);
ypos = j * windowWidth + i;
upos = (j/2) * (windowWidth/2) + i/2 + length;
vpos = (j/2) * (windowWidth/2) + i/2 + length + length/4;
yuvdest[ypos] = y;
yuvdest[upos] = u;
yuvdest[vpos] = v;
}
}
memcpy ((void*) (yuvBuffer + bufferLength), (void*)yuvdest, yuv420FrameLength);
bufferLength += yuv420FrameLength;
free (yuvdest);
free (rgb);
}
这只是非常基本的方法,我可以稍后优化转换算法。
任何人都可以告诉我在我的方法是什么错误?我的猜测是,其中一个问题是与outstream.write()调用,因为我将unsigned char *数据转换为char *数据,它可能会丢失数据精度。但如果我不把它转换为char *我会得到一个编译错误。然而,这并不能解释为什么输出帧被破坏(只占总帧数的1/3)。
This is just the very basic approach, and I can optimize the conversion algorithm later. Can anyone tell me what is wrong in my approach? My guess is that one of the issues is with the outstream.write() call, because I converted the unsigned char* data to char* data that it may lose data precision. But if I don't cast it to char* I will get a compile error. However this doesn't explain why the output frames are corrupted (only account to 1/3 of the number of total frames).
推荐答案
在我看来,对于4:2:0数据,每帧有太多字节。根据您链接的规范,200x200像素4:2:0帧的字节数应为200 * 200 * 3/2 = 60,000。但你有〜90,000字节。看看你的代码,我看不到你从4:4:4转换为4:2:0。所以你有两个选择 - 或者将标题设置为4:4:4,或者在写出之前将YCbCr数据转换为4:2:0。
It looks to me like you have too many bytes per frame for 4:2:0 data. ACcording to the spec you linked to, the number of bytes for a 200x200 pixel 4:2:0 frame should be 200 * 200 * 3 / 2 = 60,000. But you have ~90,000 bytes. Looking at your code, I don't see where you are convert from 4:4:4 to 4:2:0. So you have 2 choices - either set the header to 4:4:4, or convert the YCbCr data to 4:2:0 before writing it out.
这篇关于在C / C ++中编写YUV图像帧的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!