Reputation: 175
For my project I need to convert a RGB (GL_RGB
) Image generated by glReadPixels
into a AVFrame
. I've googled it and found just examples of the other way around. But in this case I need from GL_RGB
to AVFrame
.
Here's my code:
Here's how I set my codec:
/* Allocate resources and write header data to the output file. */
void ffmpeg_encoder_start(AVCodecID codec_id, int fps, int width, int height) {
const AVCodec *codec;
int ret;
codec = avcodec_find_encoder(codec_id);
if (!codec ) {
std::cerr << "Codec not found" << std::endl;
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
std::cerr << "Could not allocate video codec context" << std::endl;
exit(1);
}
c->bit_rate = 400000;
c->width = width;
c->height = height;
c->time_base.num = 1;
c->time_base.den = fps;
c->keyint_min = 600;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (avcodec_open2(c, codec, NULL) < 0) {
std::cerr << "Could not open codec" << std::endl;
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
std::cerr << "Could not allocate video frame" << std::endl;
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
if (ret < 0) {
std::cerr << "Could not allocate raw picture buffer" << std::endl;
exit(1);
}
}
Fetching the pixels and setting the new frame:
BYTE* pixels = new BYTE[3 * DEFAULT_MONITOR.maxResolution.width * DEFAULT_MONITOR.maxResolution.height];
glReadPixels(0, 0, DEFAULT_MONITOR.maxResolution.width, DEFAULT_MONITOR.maxResolution.height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
screenSrc->setNextFrame(pixels, DEFAULT_MONITOR.maxResolution.width, DEFAULT_MONITOR.maxResolution.height);
And the function that I have for the conversion:
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) {
const int in_linesize[1] = { 3 * c->width };
sws_context = sws_getCachedContext(sws_context,
c->width, c->height, AV_PIX_FMT_RGB24,
c->width, c->height, AV_PIX_FMT_YUV420P,
0, 0, 0, 0);
sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0,
c->height, frame->data, frame->linesize);
}
All the code can be found here Here is the line that results in segmentation fault.
Unfortunately, the function gives me a segmentation fault. Do you have an idea how to solve this problem?
Upvotes: 0
Views: 604
Reputation: 794
There seems to be no apparent error in the posted code snippets, that could be verified like this:
#include <iostream>
extern "C" {
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavcodec/avcodec.h>
}
AVFrame *frame = nullptr;
AVCodecContext *c = nullptr;
SwsContext *sws_context = nullptr;
/* <copy_of_original_code> */
void ffmpeg_encoder_start(AVCodecID codec_id, int fps, int width, int height) { ... }
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) { ... }
/* </copy_of_original_code> */
int main()
{
const int width = 640, height = 480;
uint8_t rgb_in[width * height * 3] = { 0 };
ffmpeg_encoder_start(AV_CODEC_ID_RAWVIDEO, 1, width, height);
ffmpeg_encoder_set_frame_yuv_from_rgb(rgb_in);
std::cout << "OK" << std::endl;
/* TODO: deallocate all resources here */
return 0;
}
And checked like this:
$ g++ -Wall -pedantic -o prg prg.cpp -lswscale -lavcodec -lavutil
$ ./prg
OK
So, it is difficult to say where the issue originates. Possible questions that come to mind:
DEFAULT_MONITOR.maxResolution.width
and DEFAULT_MONITOR.maxResolution.height
values valid? Is it necessary to use the heap?AVFrame
, AVCodecContext
, sws_context
pointer variables been initialized prior to their use?sws_scale()
? (That could be checked by using a debug version and a debugger I guess, to see where the program crashes by drilling into that function...)The issue seems to be not directly related to OpenGL. FFmpeg's libswscale library documentation can be found here.
In order to find a better answer, it would help if the problem could be replicated by others with a similar minimal reproducible example.
Upvotes: 1
Reputation: 32114
The second argument of sws_scale
is an array of pointers: const uint8_t *const srcSlice[]
Instead of pointer cast: (const uint8_t * const *)&rgb
, place rgb
pointer inside an array of pointers:
const uint8_t* const src_data[] = { rgb };
sws_scale(sws_context, src_data, in_linesize, 0,
c->height, frame->data, frame->linesize);
Example for converting RGB to YUV420P:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
extern "C"
{
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
int main()
{
//Use FFmpeg for building raw RGB image (used as input).
//ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1 -vcodec rawvideo -pix_fmt rgb24 -frames 1 -f rawvideo rgb_image.bin
const int width = 192;
const int height = 108;
unsigned char* rgb_in = new uint8_t[width * height * 3];
const enum AVPixelFormat out_pix_fmt = AV_PIX_FMT_YUV420P;
//Read input image for binary file (for testing)
////////////////////////////////////////////////////////////////////////////
FILE* f = fopen("rgb_image.bin", "rb"); //For using fopen in Visual Studio, define: _CRT_SECURE_NO_WARNINGS (or use fopen_s).
fread(rgb_in, 1, width * height * 3, f);
fclose(f);
////////////////////////////////////////////////////////////////////////////
//Allocate output buffers:
////////////////////////////////////////////////////////////////////////////
// YUV420p data is separated in three planes
// 1. Y - intensity plane, resolution: width x height
// 2. U - Color plane, resolution: width/2 x height/2
// 3. V - Color plane, resolution: width/2 x height/2
int out_linesize[4] = {0, 0, 0, 0};
uint8_t* out_planes[4] = { nullptr, nullptr, nullptr, nullptr };
int sts = av_image_alloc(out_planes, //uint8_t * pointers[4],
out_linesize, //int linesizes[4],
width, //int w,
height, //int h,
out_pix_fmt, //enum AVPixelFormat pix_fmt,
32); //int align); //Align to 32 bytes address may result faster execution time compared to 1 byte aligenment.
if (sts < 0)
{
printf("Error: av_image_alloc response = %d\n", sts);
return -1;
}
////////////////////////////////////////////////////////////////////////////
//Get SWS context
////////////////////////////////////////////////////////////////////////////
struct SwsContext* sws_context = nullptr;
sws_context = sws_getCachedContext(sws_context, //struct SwsContext *context,
width, //int srcW,
height, //int srcH,
AV_PIX_FMT_RGB24, //enum AVPixelFormat srcFormat,
width, //int dstW,
height, //int dstH,
out_pix_fmt, //enum AVPixelFormat dstFormat,
SWS_BILINEAR, //int flags,
nullptr, //SwsFilter *srcFilter,
nullptr, //SwsFilter *dstFilter,
nullptr); //const double *param);
if (sws_context == nullptr)
{
printf("Error: sws_getCachedContext returned nullptr\n");
return -1;
}
////////////////////////////////////////////////////////////////////////////
//Apply color conversion
////////////////////////////////////////////////////////////////////////////
const int in_linesize[1] = { 3 * width }; // RGB stride
const uint8_t* in_planes[1] = { rgb_in };
int response = sws_scale(sws_context, //struct SwsContext *c,
in_planes, //const uint8_t *const srcSlice[],
in_linesize, //const int srcStride[],
0, //int srcSliceY,
height, //int srcSliceH,
out_planes, //uint8_t *const dst[],
out_linesize); //const int dstStride[]);
if (response < 0)
{
printf("Error: sws_scale response = %d\n", response);
return -1;
}
////////////////////////////////////////////////////////////////////////////
//Write YUV420p output image to binary file (for testing)
//You may execute FFmpeg after conversion for testing the output:
//ffmpeg -y -f rawvideo -s 192x108 -pixel_format yuv420p -i yuv420p_image.bin rgb.png
////////////////////////////////////////////////////////////////////////////
f = fopen("yuv420p_image.bin", "wb");
fwrite(out_planes[0], 1, width * height, f);
fwrite(out_planes[1], 1, width * height / 4, f);
fwrite(out_planes[2], 1, width * height / 4, f);
fclose(f);
////////////////////////////////////////////////////////////////////////////
//Free allocated memory
////////////////////////////////////////////////////////////////////////////
av_freep(out_planes);
sws_freeContext(sws_context);
delete[] rgb_in;
////////////////////////////////////////////////////////////////////////////
return 0;
}
Upvotes: 2