commit 1cb07c131753e1343d45196407e661ef86fd22be
parent 482c0f4585e67389cd643df5de640274ee195c96
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Mon, 24 Oct 2022 10:42:28 -0500
poorly drawn video frame
Diffstat:
M | Makefile | | | 3 | ++- |
M | main.c | | | 137 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
2 files changed, 132 insertions(+), 8 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,5 +1,6 @@
smallpond: main.c
- gcc -o smallpond main.c $(shell pkg-config --cflags --libs lua) $(shell pkg-config --cflags --libs freetype2) $(shell pkg-config --cflags --libs cairo)
+ gcc -o smallpond main.c $(shell pkg-config --cflags --libs lua) $(shell pkg-config --cflags --libs freetype2) $(shell pkg-config --cflags --libs cairo) $(shell pkg-config --cflags --libs libavcodec) $(shell pkg-config --cflags --libs libavutil)
+
clean:
rm -f smallpond
diff --git a/main.c b/main.c
@@ -1,3 +1,4 @@
+#include <stdint.h>
#include <stdio.h>
#include <ft2build.h>
@@ -7,6 +8,9 @@
#include <cairo-pdf.h>
#include <cairo.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/frame.h>
+
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
@@ -98,12 +102,6 @@ create_surface(lua_State *L)
double width = lua_tonumber(L, -2);
double height = lua_tonumber(L, -1);
- cairo_destroy(cr);
- cairo_surface_destroy(surface);
-
- surface = cairo_pdf_surface_create("out.pdf", width, height);
- cr = cairo_create(surface);
-
cairo_set_font_face(cr, cface);
cairo_set_font_size(cr, 32.0);
@@ -111,6 +109,31 @@ create_surface(lua_State *L)
}
int
+putframe(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *out)
+{
+ int ret;
+
+ ret = avcodec_send_frame(ctx, frame);
+ if (ret < 0) {
+ fprintf(stderr, "error sending frame to encoder\n");
+ exit(1);
+ }
+
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(ctx, pkt);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ return 0;
+ else if (ret < 0) {
+ fprintf(stderr, "error encoding audio frame\n");
+ exit(1);
+ }
+
+ fwrite(pkt->data, 1, pkt->size, out);
+ av_packet_unref(pkt);
+ }
+}
+
+int
main(int argc, char *argv[])
{
lua_State *L = luaL_newstate();
@@ -146,16 +169,116 @@ main(int argc, char *argv[])
return 1;
}
- surface = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1);
+ FILE *output = fopen("out.h264", "wb");
+ if (!output) {
+ fprintf(stderr, "couldn't open output\n");
+ return 1;
+ }
+
+ AVPacket *pkt = av_packet_alloc();
+ if (!pkt) {
+ fprintf(stderr, "couldn't allocate packet!\n");
+ return 1;
+ }
+
+ AVFrame *frame = av_frame_alloc();
+ if (!frame) {
+ fprintf(stderr, "couldn't allocate frame!\n");
+ return 1;
+ }
+
+ const AVCodec *codec = avcodec_find_encoder_by_name("libx264rgb");
+ if (!codec) {
+ fprintf(stderr, "couldn't find h264 codec!\n");
+ return 1;
+ }
+
+ AVCodecContext *c = avcodec_alloc_context3(codec);
+ if (!c) {
+ fprintf(stderr, "couldn't alloc AVCodec context!\n");
+ return 1;
+ }
+
+ // suggested bitrates: https://www.videoproc.com/media-converter/bitrate-setting-for-h264.htm
+ // 720 x 480: 1800 kbps
+ // 1280 x 720: 3500 kbps
+ // 1920 x 1080: 8500 kbps
+ c->bit_rate = 1800*1000;
+ c->width = 720;
+ c->height = 480;
+ c->time_base.num = 1;
+ c->time_base.den = 30;
+ c->framerate.num = 30;
+ c->framerate.den = 1;
+ c->pix_fmt = AV_PIX_FMT_RGB24;
+ c->gop_size = 30*3;
+ AVDictionary *opts = NULL;
+
+ frame->format = c->pix_fmt;
+ frame->width = c->width;
+ frame->height = c->height;
+
+ if (avcodec_open2(c, codec, &opts) < 0) {
+ fprintf(stderr, "failed to open codec\n");
+ return 1;
+ }
+
+ if (av_frame_get_buffer(frame, 0) < 0) {
+ fprintf(stderr, "couldn't allocate frame data\n");
+ return 1;
+ }
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 720, 480);
cr = cairo_create(surface);
cairo_set_font_face(cr, cface);
cairo_set_font_size(cr, 32.0);
+ if (av_frame_make_writable(frame) < 0) {
+ fprintf(stderr, "couldn't make frame writeable\n");
+ return 1;
+ }
+ /* fill with white */
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_rectangle(cr, 0, 0, 720, 480);
+ cairo_fill(cr);
if (luaL_dofile(L, "smallpond.lua")) {
fprintf(stderr, "lua error: %s\n", lua_tostring(L, -1));
return 1;
}
+ unsigned char *image_data = cairo_image_surface_get_data(surface);
+
+ printf("avframe line size: %d\n", frame->linesize[0]);
+ for (int i = 0; i < 30; i++) {
+ if (av_frame_make_writable(frame) < 0) {
+ fprintf(stderr, "couldn't make frame writeable\n");
+ return 1;
+ }
+
+ for (int x = 0; x < 720; x++) {
+ for (int y = 0; y < 480; y++) {
+ int srcoffset = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 720) * y + x;
+ uint32_t val = ((uint32_t *)image_data)[srcoffset];
+ // we are assuming RGB24 here
+ int offset = y * frame->linesize[0] + 3*x;
+ frame->data[0][offset + 2] = (val >> 16) & 0xFF;
+ frame->data[0][offset + 1] = (val >> 8) & 0xFF;
+ frame->data[0][offset] = val & 0xFF;
+ }
+ }
+
+ fflush(stdout);
+ frame->pts = i;
+ putframe(c, frame, pkt, output);
+ }
+
+ putframe(c, NULL, pkt, output);
+ fclose(output);
+
+ avcodec_free_context(&c);
+ av_frame_free(&frame);
+ av_packet_free(&pkt);
+
cairo_destroy(cr);
cairo_surface_destroy(surface);
lua_close(L);