smallpond

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/smallpond
Log | Files | Refs | README | LICENSE

commit 1cb07c131753e1343d45196407e661ef86fd22be
parent 482c0f4585e67389cd643df5de640274ee195c96
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Mon, 24 Oct 2022 10:42:28 -0500

poorly drawn video frame

Diffstat:
MMakefile | 3++-
Mmain.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);