Skip to content

Commit 018b98d

Browse files
committed
OpenGL ffmpeg
1 parent 1904469 commit 018b98d

3 files changed

Lines changed: 90 additions & 86 deletions

File tree

‎ffmpeg/encode.c‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/*
2+
Basis for:
3+
24
http://stackoverflow.com/questions/16667687/how-to-convert-rgb-from-yuv420p-for-ffmpeg-encoder
35
http://stackoverflow.com/questions/2940671/how-does-one-encode-a-series-of-images-into-h264-using-the-x264-c-api/36405714#36405714
6+
http://stackoverflow.com/questions/16667687/how-to-convert-rgb-from-yuv420p-for-ffmpeg-encoder
47
58
Transform a bunch of raw pixels into compressed video formats.
69
@@ -27,7 +30,7 @@ static AVCodecContext *c = NULL;
2730
static AVFrame *frame;
2831
static AVPacket pkt;
2932
static FILE *file;
30-
struct SwsContext *sws_context = NULL;
33+
static struct SwsContext *sws_context = NULL;
3134

3235
/*
3336
Convert RGB24 array to YUV. Save directly to the `frame`,
@@ -58,8 +61,9 @@ Image 2:
5861
-------+-----
5962
green | white
6063
*/
61-
uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb) {
64+
void generate_rgb(int width, int height, int pts, uint8_t **rgbp) {
6265
int x, y, cur;
66+
uint8_t *rgb = *rgbp;
6367
rgb = realloc(rgb, 3 * sizeof(uint8_t) * height * width);
6468
for (y = 0; y < height; y++) {
6569
for (x = 0; x < width; x++) {
@@ -102,13 +106,14 @@ uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb) {
102106
}
103107
}
104108
}
105-
return rgb;
109+
*rgbp = rgb;
106110
}
107111

108112
/* Allocate resources and write header data to the output file. */
109113
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) {
110114
AVCodec *codec;
111115
int ret;
116+
avcodec_register_all();
112117
codec = avcodec_find_encoder(codec_id);
113118
if (!codec) {
114119
fprintf(stderr, "Codec not found\n");
@@ -211,14 +216,14 @@ static void encode_example(const char *filename, int codec_id) {
211216
ffmpeg_encoder_start(filename, codec_id, 25, width, height);
212217
for (pts = 0; pts < 100; pts++) {
213218
frame->pts = pts;
214-
rgb = generate_rgb(width, height, pts, rgb);
219+
generate_rgb(width, height, pts, &rgb);
215220
ffmpeg_encoder_encode_frame(rgb);
216221
}
217222
ffmpeg_encoder_finish();
223+
free(rgb);
218224
}
219225

220226
int main(void) {
221-
avcodec_register_all();
222227
encode_example("tmp.h264", AV_CODEC_ID_H264);
223228
encode_example("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO);
224229
/* TODO: is this encoded correctly? Possible to view it without container? */

‎opengl/Makefile_params‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
LIBS := -lm -lGL -lGLU -lglut -lglfw -lGLEW -lX11 \
22
-lpng \
33
-lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml -lopencv_video -lopencv_features2d -lopencv_calib3d -lopencv_objdetect -lopencv_contrib -lopencv_legacy -lopencv_flann \
4-
-lavformat -lavcodec -lswscale -lavutil
4+
-lavcodec -lswscale -lavutil

‎opengl/offscreen.c‎

Lines changed: 79 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
http://stackoverflow.com/questions/3191978/how-to-use-glut-opengl-to-render-to-a-file/14324292#14324292
33
*/
44

5-
#define LIBPNG 0
5+
/* Turn output methods on and off. */
6+
#define PPM 1
7+
#define LIBPNG 1
68
#define FFMPEG 1
79

810
#include <assert.h>
@@ -21,17 +23,17 @@
2123
#endif
2224

2325
#if FFMPEG
24-
#include <libavutil/opt.h> /* av_opt_set */
2526
#include <libavcodec/avcodec.h>
26-
#include <libavutil/imgutils.h> /* av_image_alloc */
27+
#include <libavutil/imgutils.h>
28+
#include <libavutil/opt.h>
29+
#include <libswscale/swscale.h>
2730
#endif
2831

2932
enum Constants { SCREENSHOT_MAX_FILENAME = 256 };
33+
static GLubyte *pixels = NULL;
3034
static GLuint fbo;
3135
static GLuint rbo_color;
3236
static GLuint rbo_depth;
33-
static const GLenum FORMAT = GL_RGBA;
34-
static const GLuint format_nchannels = 4;
3537
static const unsigned int HEIGHT = 100;
3638
static const unsigned int WIDTH = 100;
3739
static int offscreen = 1;
@@ -43,27 +45,24 @@ static unsigned int time0;
4345
static double angle;
4446
static double delta_angle;
4547

48+
#if PPM
4649
/*
4750
Take screenshot with glReadPixels and save to a file in PPM format.
4851
- filename: file path to save to, without extension
4952
- width: screen width in pixels
5053
- height: screen height in pixels
51-
- format: glReadPixelsFormat
52-
- format_nchannels: number of channels per pixel (e.g. R, G, B, A)
53-
This is implied by format, but we haven't found a built-in way to get this
54-
information automatically without hard-coding a large switch.
5554
- pixels: intermediate buffer to avoid repeated mallocs across multiple calls.
5655
Contents of this buffer do not matter. May be NULL, in which case it is initialized.
5756
You must `free` it when you won't be calling this function anymore.
5857
*/
59-
static GLubyte *pixels = NULL;
60-
static void screenshot_ppm(const char *filename, unsigned int width, unsigned int height,
61-
GLenum format, unsigned int format_nchannels, GLubyte **pixels) {
58+
static void screenshot_ppm(const char *filename, unsigned int width,
59+
unsigned int height, GLubyte **pixels) {
6260
size_t i, j, k, cur;
61+
const size_t format_nchannels = 3;
6362
FILE *f = fopen(filename, "w");
6463
fprintf(f, "P3\n%d %d\n%d\n", width, height, 255);
6564
*pixels = realloc(*pixels, format_nchannels * sizeof(GLubyte) * width * height);
66-
glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, *pixels);
65+
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, *pixels);
6766
for (i = 0; i < height; i++) {
6867
for (j = 0; j < width; j++) {
6968
cur = format_nchannels * ((height - i - 1) * width + j);
@@ -73,20 +72,22 @@ static void screenshot_ppm(const char *filename, unsigned int width, unsigned in
7372
}
7473
fclose(f);
7574
}
75+
#endif
7676

7777
#if LIBPNG
78+
/* Adapted from https://github.com/cirosantilli/cpp-cheat/blob/19044698f91fefa9cb75328c44f7a487d336b541/png/open_manipulate_write.c */
7879
static png_byte *png_bytes = NULL;
7980
static png_byte **png_rows = NULL;
80-
static void screenshot_png(const char *filename, unsigned int width,
81-
unsigned int height, GLenum format, unsigned int format_nchannels,
81+
static void screenshot_png(const char *filename, unsigned int width, unsigned int height,
8282
GLubyte **pixels, png_byte **png_bytes, png_byte ***png_rows) {
83-
size_t i, j, cur, cur_png, nvals;
83+
size_t i, nvals;
84+
const size_t format_nchannels = 4;
8485
FILE *f = fopen(filename, "wb");
8586
nvals = format_nchannels * width * height;
8687
*pixels = realloc(*pixels, nvals * sizeof(GLubyte));
8788
*png_bytes = realloc(*png_bytes, nvals * sizeof(png_byte));
8889
*png_rows = realloc(*png_rows, height * sizeof(png_byte*));
89-
glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, *pixels);
90+
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, *pixels);
9091
for (i = 0; i < nvals; i++)
9192
(*png_bytes)[i] = (*pixels)[i];
9293
for (i = 0; i < height; i++)
@@ -116,16 +117,28 @@ static void screenshot_png(const char *filename, unsigned int width,
116117
#endif
117118

118119
#if FFMPEG
120+
/* Adapted from: https://github.com/cirosantilli/cpp-cheat/blob/19044698f91fefa9cb75328c44f7a487d336b541/ffmpeg/encode.c */
121+
119122
static AVCodecContext *c = NULL;
120123
static AVFrame *frame;
121-
static FILE *f;
122-
int64_t pts = 0;
124+
static AVPacket pkt;
125+
static FILE *file;
126+
static struct SwsContext *sws_context = NULL;
127+
static uint8_t *rgb = NULL;
128+
129+
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) {
130+
const int in_linesize[1] = { 3 * c->width };
131+
sws_context = sws_getCachedContext(sws_context,
132+
c->width, c->height, AV_PIX_FMT_RGB24,
133+
c->width, c->height, AV_PIX_FMT_YUV420P,
134+
0, 0, 0, 0);
135+
sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0,
136+
c->height, frame->data, frame->linesize);
137+
}
123138

124-
static void screenshot_mpg_init(const char *filename, int width, int height) {
139+
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) {
125140
AVCodec *codec;
126-
int codec_id = AV_CODEC_ID_MPEG1VIDEO;
127141
int ret;
128-
129142
avcodec_register_all();
130143
codec = avcodec_find_encoder(codec_id);
131144
if (!codec) {
@@ -140,7 +153,8 @@ static void screenshot_mpg_init(const char *filename, int width, int height) {
140153
c->bit_rate = 400000;
141154
c->width = width;
142155
c->height = height;
143-
c->time_base = (AVRational){1, 25};
156+
c->time_base.num = 1;
157+
c->time_base.den = fps;
144158
c->gop_size = 10;
145159
c->max_b_frames = 1;
146160
c->pix_fmt = AV_PIX_FMT_YUV420P;
@@ -150,8 +164,8 @@ static void screenshot_mpg_init(const char *filename, int width, int height) {
150164
fprintf(stderr, "Could not open codec\n");
151165
exit(1);
152166
}
153-
f = fopen(filename, "wb");
154-
if (!f) {
167+
file = fopen(filename, "wb");
168+
if (!file) {
155169
fprintf(stderr, "Could not open %s\n", filename);
156170
exit(1);
157171
}
@@ -170,75 +184,61 @@ static void screenshot_mpg_init(const char *filename, int width, int height) {
170184
}
171185
}
172186

173-
static void screenshot_mpg_deinit(void) {
174-
AVPacket pkt;
175-
int ret, x, y, got_output;
187+
void ffmpeg_encoder_finish(void) {
176188
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
177-
178-
av_init_packet(&pkt);
179-
pkt.data = NULL;
180-
pkt.size = 0;
181-
for (got_output = 1; got_output; pts++) {
189+
int got_output, ret;
190+
do {
182191
fflush(stdout);
183192
ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
184193
if (ret < 0) {
185194
fprintf(stderr, "Error encoding frame\n");
186195
exit(1);
187196
}
188197
if (got_output) {
189-
fwrite(pkt.data, 1, pkt.size, f);
198+
fwrite(pkt.data, 1, pkt.size, file);
190199
av_packet_unref(&pkt);
191200
}
192-
}
193-
fwrite(endcode, 1, sizeof(endcode), f);
194-
fclose(f);
201+
} while (got_output);
202+
fwrite(endcode, 1, sizeof(endcode), file);
203+
fclose(file);
195204
avcodec_close(c);
196205
av_free(c);
197206
av_freep(&frame->data[0]);
198207
av_frame_free(&frame);
199208
}
200209

201-
/*
202-
Compress one frame.
203-
screenshot_mpg_init must have been called previously,
204-
and screenshot_mpg_deinit must be called to clean this afterwards.
205-
Adapted from https://github.com/FFmpeg/FFmpeg/blob/4b150fbe1f3905f8245f63d74ff72f2ef92d9717/doc/examples/decoding_encoding.c
206-
*/
207-
static void screenshot_mpg(unsigned int width, unsigned int height, GLenum format,
208-
unsigned int format_nchannels, GLubyte **pixels) {
209-
AVPacket pkt;
210-
int ret, x, y, got_output;
211-
size_t nvals;
212-
213-
nvals = format_nchannels * width * height;
214-
*pixels = realloc(*pixels, nvals * sizeof(GLubyte));
215-
glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, *pixels);
216-
210+
void ffmpeg_encoder_encode_frame(uint8_t *rgb) {
211+
int ret, got_output;
212+
ffmpeg_encoder_set_frame_yuv_from_rgb(rgb);
217213
av_init_packet(&pkt);
218214
pkt.data = NULL;
219215
pkt.size = 0;
220-
frame->pts = pts;
221-
for (y = 0; y < c->height; y++) {
222-
for (x = 0; x < c->width; x++) {
223-
frame->data[0][y * frame->linesize[0] + x] = x + y + pts * 3;
224-
}
225-
}
226-
for (y = 0; y < c->height / 2; y++) {
227-
for (x = 0; x < c->width / 2; x++) {
228-
frame->data[1][y * frame->linesize[1] + x] = 128 + y + pts * 2;
229-
frame->data[2][y * frame->linesize[2] + x] = 64 + x + pts * 5;
230-
}
231-
}
232216
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
233217
if (ret < 0) {
234218
fprintf(stderr, "Error encoding frame\n");
235219
exit(1);
236220
}
237221
if (got_output) {
238-
fwrite(pkt.data, 1, pkt.size, f);
222+
fwrite(pkt.data, 1, pkt.size, file);
239223
av_packet_unref(&pkt);
240224
}
241-
pts++;
225+
}
226+
227+
void ffmpeg_encoder_glread_rgb(uint8_t **rgb, GLubyte **pixels, unsigned int width, unsigned int height) {
228+
size_t i, j, k, cur_gl, cur_rgb, nvals;
229+
const size_t format_nchannels = 3;
230+
nvals = format_nchannels * width * height;
231+
*pixels = realloc(*pixels, nvals * sizeof(GLubyte));
232+
*rgb = realloc(*rgb, nvals * sizeof(uint8_t));
233+
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, *pixels);
234+
for (i = 0; i < height; i++) {
235+
for (j = 0; j < width; j++) {
236+
cur_gl = format_nchannels * (width * (height - i - 1) + j);
237+
cur_rgb = format_nchannels * (width * i + j);
238+
for (k = 0; k < format_nchannels; k++)
239+
(*rgb)[cur_rgb + k] = (*pixels)[cur_gl + k];
240+
}
241+
}
242242
}
243243
#endif
244244

@@ -299,7 +299,7 @@ static void init(void) {
299299
time0 = glutGet(GLUT_ELAPSED_TIME);
300300
model_init();
301301
#if FFMPEG
302-
screenshot_mpg_init("tmp.mpg", WIDTH, HEIGHT);
302+
ffmpeg_encoder_start("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO, 25, WIDTH, HEIGHT);
303303
#endif
304304
}
305305

@@ -311,7 +311,8 @@ static void deinit(void) {
311311
free(png_rows);
312312
#endif
313313
#if FFMPEG
314-
screenshot_mpg_deinit();
314+
ffmpeg_encoder_finish();
315+
free(rgb);
315316
#endif
316317
if (offscreen) {
317318
glDeleteFramebuffers(1, &fbo);
@@ -343,20 +344,18 @@ static void display(void) {
343344
} else {
344345
glutSwapBuffers();
345346
}
346-
#if LIBPNG
347-
strcpy(extension, ".png");
348-
#else
349-
strcpy(extension, ".ppm");
347+
#if PPM
348+
snprintf(filename, SCREENSHOT_MAX_FILENAME, "tmp%d.ppm", nframes);
349+
screenshot_ppm(filename, WIDTH, HEIGHT, &pixels);
350350
#endif
351-
snprintf(filename, SCREENSHOT_MAX_FILENAME, "tmp%d%s", nframes, extension);
352351
#if LIBPNG
353-
screenshot_png(filename, WIDTH, HEIGHT, FORMAT, format_nchannels, &pixels, &png_bytes, &png_rows);
354-
#else
352+
snprintf(filename, SCREENSHOT_MAX_FILENAME, "tmp%d.png", nframes);
353+
screenshot_png(filename, WIDTH, HEIGHT, &pixels, &png_bytes, &png_rows);
354+
#endif
355355
# if FFMPEG
356-
screenshot_mpg(WIDTH, HEIGHT, FORMAT, format_nchannels, &pixels);
357-
# else
358-
screenshot_ppm(filename, WIDTH, HEIGHT, FORMAT, format_nchannels, &pixels);
359-
# endif
356+
frame->pts = nframes;
357+
ffmpeg_encoder_glread_rgb(&rgb, &pixels, WIDTH, HEIGHT);
358+
ffmpeg_encoder_encode_frame(rgb);
360359
#endif
361360
nframes++;
362361
if (model_finished())

0 commit comments

Comments
 (0)