在 C 中对 OpenGL 捕获的缓冲区进行编码
Encode buffer captured by OpenGL in C
我正在尝试使用 OpenGL 捕获计算机屏幕的后台缓冲区,然后使用 FFMPEG 的 libavcodec
库对缓冲区进行 H.264 编码。我遇到的问题是,我想将视频编码为AV_PIX_FMT_420P
,但OpenGL glReadPixels()
提供的后台缓冲区捕获功能仅支持GL_RGB
等格式。如下所示,我尝试使用 FFMPEG 的 swscale()
函数将 RGB 转换为 YUV,但以下代码在 swscale()
行崩溃。关于如何编码 OpenGL 后备缓冲区的任何想法?
// CAPTURE BACK BUFFER USING OPENGL
int width = 1280, height = 720;
BYTE* pixels = (BYTE *) malloc(sizeof(BYTE));
glReadPixels(0, 720, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
//CREATE FFMPEG VARIABLES
avcodec_register_all();
AVCodec *codec;
AVCodecContext *context;
struct SwsContext *sws;
AVPacket packet;
AVFrame *frame;
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
context = avcodec_alloc_context3(encoder->codec);
context->dct_algo = FF_DCT_FASTINT;
context->bit_rate = 400000;
context->width = width;
context->height = height;
context->time_base.num = 1;
context->time_base.den = 30;
context->gop_size = 1;
context->max_b_frames = 1;
context->pix_fmt = AV_PIX_FMT_YUV420P;
avcodec_open2(context, codec, NULL);
// CONVERT TO YUV AND ENCODE
int frame_size = avpicture_get_size(AV_PIX_FMT_YUV420P, out_width, out_height);
encoder->frame_buffer = malloc(frame_size);
avpicture_fill((AVPicture *) encoder->frame, (uint8_t *) encoder->frame_buffer, AV_PIX_FMT_YUV420P, out_width, out_height);
sws = sws_getContext(in_width, in_height, AV_PIX_FMT_RGB32, out_width, out_height, AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, 0, 0, 0);
uint8_t *in_data[1] = {(uint8_t *) pixels};
int in_linesize[1] = {width * 4};
// PROGRAM CRASHES HERE
sws_scale(encoder->sws, in_data, in_linesize, 0, encoder->in_height, encoder->frame->data, encoder->frame->linesize);
av_free_packet(&packet);
av_init_packet(&packet);
int success;
avcodec_encode_video2(context, &packet, frame, &success);
您的pixels
缓冲区太小;你 malloc
只有 一个 BYTE
而不是 width*height*4
字节:
BYTE* pixels = (BYTE *) malloc(width*height*4);
你的glReadPixels
调用也不正确:
- 传递
y=720
会导致它在 window 之外读取。请记住,OpenGL 坐标系的 y 轴指向上方。
AV_PIX_FMT_RGB32
期望每个像素四个字节,而 GL_RGB
每个像素写入三个字节,因此您需要 GL_RGBA
或 GL_BGRA
.
- 在这两个中,我很确定它应该是
GL_BGRA
:AV_PIX_FMT_RGB32
将像素视为 32 位整数,因此在小端优先级上,蓝色排在第一位。 OpenGL 将每个通道视为一个字节,因此应该 GL_BGRA
才能匹配。
总结尝试:
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
此外,由于 OpenGL y 轴指向上方,而 ffmpeg y 轴指向下方,您可能需要翻转图像。可以使用以下技巧来完成:
uint8_t *in_data[1] = {(uint8_t *) pixels + (height-1)*width*4}; // address of the last line
int in_linesize[1] = {- width * 4}; // negative stride
我正在尝试使用 OpenGL 捕获计算机屏幕的后台缓冲区,然后使用 FFMPEG 的 libavcodec
库对缓冲区进行 H.264 编码。我遇到的问题是,我想将视频编码为AV_PIX_FMT_420P
,但OpenGL glReadPixels()
提供的后台缓冲区捕获功能仅支持GL_RGB
等格式。如下所示,我尝试使用 FFMPEG 的 swscale()
函数将 RGB 转换为 YUV,但以下代码在 swscale()
行崩溃。关于如何编码 OpenGL 后备缓冲区的任何想法?
// CAPTURE BACK BUFFER USING OPENGL
int width = 1280, height = 720;
BYTE* pixels = (BYTE *) malloc(sizeof(BYTE));
glReadPixels(0, 720, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
//CREATE FFMPEG VARIABLES
avcodec_register_all();
AVCodec *codec;
AVCodecContext *context;
struct SwsContext *sws;
AVPacket packet;
AVFrame *frame;
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
context = avcodec_alloc_context3(encoder->codec);
context->dct_algo = FF_DCT_FASTINT;
context->bit_rate = 400000;
context->width = width;
context->height = height;
context->time_base.num = 1;
context->time_base.den = 30;
context->gop_size = 1;
context->max_b_frames = 1;
context->pix_fmt = AV_PIX_FMT_YUV420P;
avcodec_open2(context, codec, NULL);
// CONVERT TO YUV AND ENCODE
int frame_size = avpicture_get_size(AV_PIX_FMT_YUV420P, out_width, out_height);
encoder->frame_buffer = malloc(frame_size);
avpicture_fill((AVPicture *) encoder->frame, (uint8_t *) encoder->frame_buffer, AV_PIX_FMT_YUV420P, out_width, out_height);
sws = sws_getContext(in_width, in_height, AV_PIX_FMT_RGB32, out_width, out_height, AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, 0, 0, 0);
uint8_t *in_data[1] = {(uint8_t *) pixels};
int in_linesize[1] = {width * 4};
// PROGRAM CRASHES HERE
sws_scale(encoder->sws, in_data, in_linesize, 0, encoder->in_height, encoder->frame->data, encoder->frame->linesize);
av_free_packet(&packet);
av_init_packet(&packet);
int success;
avcodec_encode_video2(context, &packet, frame, &success);
您的pixels
缓冲区太小;你 malloc
只有 一个 BYTE
而不是 width*height*4
字节:
BYTE* pixels = (BYTE *) malloc(width*height*4);
你的glReadPixels
调用也不正确:
- 传递
y=720
会导致它在 window 之外读取。请记住,OpenGL 坐标系的 y 轴指向上方。 AV_PIX_FMT_RGB32
期望每个像素四个字节,而GL_RGB
每个像素写入三个字节,因此您需要GL_RGBA
或GL_BGRA
.- 在这两个中,我很确定它应该是
GL_BGRA
:AV_PIX_FMT_RGB32
将像素视为 32 位整数,因此在小端优先级上,蓝色排在第一位。 OpenGL 将每个通道视为一个字节,因此应该GL_BGRA
才能匹配。
总结尝试:
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
此外,由于 OpenGL y 轴指向上方,而 ffmpeg y 轴指向下方,您可能需要翻转图像。可以使用以下技巧来完成:
uint8_t *in_data[1] = {(uint8_t *) pixels + (height-1)*width*4}; // address of the last line
int in_linesize[1] = {- width * 4}; // negative stride