]> git.localhorst.tv Git - ffmpeg-test.git/commitdiff
basic font rendering
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 22 Sep 2024 20:54:57 +0000 (22:54 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 22 Sep 2024 20:54:57 +0000 (22:54 +0200)
12 files changed:
Makefile
src/app/Renderer.h [new file with mode: 0644]
src/app/Stream.h
src/cairo/Context.h
src/cairo/Error.h
src/cairo/Face.h [new file with mode: 0644]
src/ffmpeg/Error.h
src/ffmpeg/Network.h [new file with mode: 0644]
src/freetype/Error.h [new file with mode: 0644]
src/freetype/Face.h [new file with mode: 0644]
src/freetype/Library.h [new file with mode: 0644]
src/main.cpp

index 48f4d5c90c410ca776bfa8b77c165fce15f0b7b1..9554743e5af61715d7a5792347058be8897fb0ea 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 CPP_SRCS = $(shell find src -name \*.cpp)
 CPP_DEPS = $(shell find src -name \*.h)
 
-LIBS = cairo libavformat libavcodec libavutil libswresample libswscale
+LIBS = cairo freetype2 libavformat libavcodec libavutil libswresample libswscale
 
 main: $(CPP_SRCS) $(CPP_DEPS)
        clang++ -g $(shell pkg-config --cflags --libs $(LIBS)) $(CPP_SRCS) -o $@
diff --git a/src/app/Renderer.h b/src/app/Renderer.h
new file mode 100644 (file)
index 0000000..41915a7
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef TEST_APP_RENDERER_H_
+#define TEST_APP_RENDERER_H_
+
+#include <cstdint>
+extern "C" {
+#include "cairo.h"
+}
+
+#include "../freetype/Face.h"
+#include "../freetype/Library.h"
+#include "../cairo/Context.h"
+#include "../cairo/Face.h"
+#include "../cairo/Surface.h"
+
+namespace app {
+
+class Renderer {
+
+public:
+       Renderer(uint8_t *plane, int linesize, int width, int height)
+       : ft()
+       , face(ft.NewFace("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 0))
+       , font(face)
+       , surface(plane, linesize, CAIRO_FORMAT_ARGB32, width, height)
+       , ctx(surface.CreateContext())
+       , width(width)
+       , height(height) {
+       }
+       ~Renderer() {
+       }
+
+       Renderer(const Renderer &) = delete;
+       Renderer &operator =(const Renderer &) = delete;
+
+public:
+       void RenderVideoFrame(int64_t num, int64_t time_ms) {
+               ctx.SetSourceRGB(0, 0, 0);
+               ctx.Paint();
+
+               ctx.MoveTo(50, 50);
+               ctx.SetFontFace(font);
+               ctx.SetFontSize(16);
+               ctx.SetSourceRGB(1, 1, 1);
+               ctx.ShowText("Hello");
+
+               surface.Flush();
+       }
+
+
+private:
+       freetype::Library ft;
+       freetype::Face face;
+       cairo::Face font;
+       cairo::Surface surface;
+       cairo::Context ctx;
+
+       int width;
+       int height;
+
+};
+
+}
+
+#endif
index 5da2a5461ce6f565ee72dce266c0f57b0aa621f4..7ad9fde73676d69e0a03b6144e432e932335ad35 100644 (file)
@@ -13,6 +13,7 @@ extern "C" {
 }
 
 #include "../ffmpeg/Encoder.h"
+#include "../ffmpeg/Network.h"
 #include "../ffmpeg/OutputContext.h"
 #include "../ffmpeg/Resampler.h"
 #include "../ffmpeg/Scaler.h"
@@ -157,6 +158,7 @@ public:
        }
 
 private:
+       ffmpeg::Network net;
        ffmpeg::OutputContext output;
        ffmpeg::Encoder audio_encoder;
        ffmpeg::Encoder video_encoder;
index 92cd24a304994ca5eb5e2c8a65c94430f0a8f62f..a65255594ba2990e8672fd69d2347f8c9f4cfbe7 100644 (file)
@@ -6,6 +6,7 @@
 #include <ostream>
 
 #include "Error.h"
+#include "Face.h"
 
 namespace cairo {
 
@@ -61,6 +62,10 @@ public:
                cairo_select_font_face(ctx, family, slant, weight);
        }
 
+       void SetFontFace(Face &face) {
+               cairo_set_font_face(ctx, face.GetFace());
+       }
+
        void SetFontSize(double size) {
                cairo_set_font_size(ctx, size);
        }
index a0b869bc68ee852da6d707971316a7926a0c5808..30e910eb70bedaaa86cedb461ff9f4178ffcf286 100644 (file)
@@ -7,7 +7,7 @@
 #include <cairo.h>
 
 namespace {
-std::string make_message(const std::string &msg, cairo_status_t status) {
+std::string cairo_make_message(const std::string &msg, cairo_status_t status) {
        return msg + ": " + std::string(cairo_status_to_string(status));
 }
 }
@@ -17,7 +17,7 @@ namespace cairo {
 class Error: public std::runtime_error {
 
 public:
-       Error(const std::string &msg, cairo_status_t status): std::runtime_error(make_message(msg, status)) {
+       Error(const std::string &msg, cairo_status_t status): std::runtime_error(cairo_make_message(msg, status)) {
        }
        ~Error() {
        }
diff --git a/src/cairo/Face.h b/src/cairo/Face.h
new file mode 100644 (file)
index 0000000..17551b6
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef TEST_CAIRO_FACE_H_
+#define TEST_CAIRO_FACE_H_
+
+extern "C" {
+#include <cairo.h>
+#include <cairo-ft.h>
+}
+
+#include "../freetype/Face.h"
+
+namespace cairo {
+
+class Face {
+
+public:
+       explicit Face(const freetype::Face &face)
+       : font(cairo_ft_font_face_create_for_ft_face(face.GetFace(), 0))
+       , face(face) {
+       }
+       ~Face() {
+               cairo_font_face_destroy(font);
+       }
+
+       Face(const Face &) = delete;
+       Face &operator =(const Face &) = delete;
+
+public:
+       cairo_font_face_t *GetFace() {
+               return font;
+       }
+
+       const cairo_font_face_t *GetFace() const {
+               return font;
+       }
+
+private:
+       cairo_font_face_t *font;
+       freetype::Face face;
+
+};
+
+}
+
+#endif
index bdcbdcef98c7beade804d0ad4d0211507899d595..f768f72e42fe0e2fcb91dd32ed5180a6792542d4 100644 (file)
@@ -9,7 +9,7 @@ extern "C" {
 }
 
 namespace {
-std::string make_message(const std::string &msg, int code) {
+std::string ffmpeg_make_message(const std::string &msg, int code) {
        char buf[128] = {0};
        av_strerror(code, buf, sizeof(buf));
        return msg + ": " + std::string(buf);
@@ -21,7 +21,7 @@ namespace ffmpeg {
 class Error: public std::runtime_error {
 
 public:
-       Error(const std::string &msg, int code): std::runtime_error(make_message(msg, code)) {
+       Error(const std::string &msg, int code): std::runtime_error(ffmpeg_make_message(msg, code)) {
        }
        ~Error() {
        }
diff --git a/src/ffmpeg/Network.h b/src/ffmpeg/Network.h
new file mode 100644 (file)
index 0000000..5a29396
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef TEST_FFMPEG_NETWORK_H_
+#define TEST_FFMPEG_NETWORK_H_
+
+extern "C" {
+#include <libavformat/avformat.h>
+}
+
+#include "Error.h"
+
+namespace ffmpeg {
+
+class Network {
+
+public:
+       Network() {
+               int res = avformat_network_init();
+               if (res != 0) {
+                       throw Error("failed to initialize network", res);
+               }
+       }
+       ~Network() {
+               avformat_network_deinit();
+       }
+
+       Network(const Network &) = delete;
+       Network &operator =(const Network &) = delete;
+
+};
+
+}
+
+#endif
diff --git a/src/freetype/Error.h b/src/freetype/Error.h
new file mode 100644 (file)
index 0000000..e8f7bac
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef TEST_FREETYPE_ERROR_H_
+#define TEST_FREETYPE_ERROR_H_
+
+#include <stdexcept>
+#include <string>
+
+extern "C" {
+#include <ft2build.h>
+#include FT_FREETYPE_H
+}
+
+
+namespace {
+std::string freetype_make_message(const std::string &msg, FT_Error status) {
+       return msg + ": " + std::string(FT_Error_String(status));
+}
+}
+
+namespace freetype {
+
+class Error: public std::runtime_error {
+
+public:
+       Error(const std::string &msg, FT_Error status): std::runtime_error(freetype_make_message(msg, status)) {
+       }
+       ~Error() {
+       }
+
+};
+
+}
+
+#endif
diff --git a/src/freetype/Face.h b/src/freetype/Face.h
new file mode 100644 (file)
index 0000000..064e4fb
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef TEST_FREETYPE_FACE_H_
+#define TEST_FREETYPE_FACE_H_
+
+extern "C" {
+#include <ft2build.h>
+#include FT_FREETYPE_H
+}
+
+#include "Error.h"
+
+namespace freetype {
+
+class Face {
+
+public:
+       Face(FT_Library lib, const char *path, FT_Long index) {
+               FT_Error err = FT_New_Face(lib, path, index, &face);
+               if (err) {
+                       throw Error("failed to open face", err);
+               }
+       }
+       ~Face() {
+               FT_Done_Face(face);
+       }
+       Face(const Face &other): face(other.face) {
+               FT_Reference_Face(face);
+       }
+       Face &operator =(const Face &other) {
+               FT_Reference_Face(other.face);
+               FT_Done_Face(face);
+               face = other.face;
+               return *this;
+       }
+
+public:
+       const FT_Face &GetFace() const {
+               return face;
+       }
+
+private:
+       FT_Face face;
+
+};
+
+}
+
+#endif
diff --git a/src/freetype/Library.h b/src/freetype/Library.h
new file mode 100644 (file)
index 0000000..8c46be1
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef TEST_FREETYPE_LIBRARY_H_
+#define TEST_FREETYPE_LIBRARY_H_
+
+extern "C" {
+#include <ft2build.h>
+#include FT_FREETYPE_H
+}
+
+#include "Error.h"
+#include "Face.h"
+
+namespace freetype {
+
+class Library {
+
+public:
+       Library() {
+               FT_Error err = FT_Init_FreeType(&lib);
+               if (err) {
+                       throw Error("failed to initialize library", err);
+               }
+       }
+       ~Library() {
+               FT_Done_FreeType(lib);
+       }
+
+       Library(const Library &) = delete;
+       Library &operator =(const Library &) = delete;
+
+public:
+       Face NewFace(const char *path, FT_Long index) {
+               return Face(lib, path, index);
+       }
+
+private:
+       FT_Library lib;
+
+};
+
+}
+
+#endif
index a77f4d00da79d0dce9de160065f35e1c2de9265d..a514c096880139b8a159acabac1994c48f377a90 100644 (file)
@@ -1,10 +1,7 @@
-#include "cairo.h"
-#include "cairo/Context.h"
 #include <chrono>
 #include <csignal>
 #include <cstdint>
 #include <iostream>
-#include <string>
 #include <thread>
 
 extern "C" {
@@ -19,12 +16,9 @@ extern "C" {
 #include <libavutil/timestamp.h>
 }
 
+#include "app/Renderer.h"
 #include "app/Stream.h"
 
-#include "cairo/Surface.h"
-
-#include "ffmpeg/Error.h"
-
 
 namespace {
 
@@ -34,11 +28,6 @@ void stop(int) {
        running = false;
 }
 
-void RenderFrame(int64_t num, int64_t time_ms, cairo::Context &c) {
-       c.SetSourceRGB(0, 0, 0);
-       c.Paint();
-}
-
 }
 
 
@@ -48,11 +37,6 @@ int main(int argc, char**argv) {
        const int FPS = 60;
        const char *URL = "rtmp://localhost/localhorsttv";
 
-       int res = avformat_network_init();
-       if (res != 0) {
-               throw ffmpeg::Error("network init failed", res);
-       }
-
        app::Stream stream(URL, WIDTH, HEIGHT, FPS);
 
        running = true;
@@ -64,8 +48,7 @@ int main(int argc, char**argv) {
        int16_t *audio_plane = stream.GetAudioPlane();
        const int audio_channels = stream.GetAudioChannels();
 
-       cairo::Surface surface(plane, linesize, CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
-       cairo::Context context(surface.CreateContext());
+       app::Renderer renderer(plane, linesize, WIDTH, HEIGHT);
 
        stream.Start();
 
@@ -81,8 +64,7 @@ int main(int argc, char**argv) {
                if (stream.GetVideoFrameCounter() > 0 && difference < 0) {
                        std::cout << (difference / 1000.0) << "s behind schedule, dropping frame" << std::endl;
                } else {
-                       RenderFrame(stream.GetVideoFrameCounter(), target, context);
-                       surface.Flush();
+                       renderer.RenderVideoFrame(stream.GetVideoFrameCounter(), target);
                }
 
                stream.PushVideoFrame();