]> git.localhorst.tv Git - ffmpeg-test.git/commitdiff
websockets
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 25 Sep 2024 22:12:03 +0000 (00:12 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 25 Sep 2024 22:12:03 +0000 (00:12 +0200)
Makefile
src/app/Renderer.h
src/cairo/Context.h
src/main.cpp
src/uv/Error.h [new file with mode: 0644]
src/uv/Loop.h [new file with mode: 0644]
src/uv/Signal.h [new file with mode: 0644]
src/ws/Connection.cpp [new file with mode: 0644]
src/ws/Connection.h [new file with mode: 0644]
src/ws/Context.h [new file with mode: 0644]
src/ws/io.h [new file with mode: 0644]

index 9554743e5af61715d7a5792347058be8897fb0ea..d2d0188035b9c6969ca1e37742eb8d4c429907a5 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 freetype2 libavformat libavcodec libavutil libswresample libswscale
+LIBS = cairo freetype2 jsoncpp libavformat libavcodec libavutil libswresample libswscale libuv libwebsockets
 
 main: $(CPP_SRCS) $(CPP_DEPS)
        clang++ -g $(shell pkg-config --cflags --libs $(LIBS)) $(CPP_SRCS) -o $@
index 41915a7056d59d62b00e8472faeab48aa1f6a143..d32587fe2d0e4cdb4ec52ae0bb44a82a6b84e288 100644 (file)
@@ -24,7 +24,8 @@ public:
        , surface(plane, linesize, CAIRO_FORMAT_ARGB32, width, height)
        , ctx(surface.CreateContext())
        , width(width)
-       , height(height) {
+       , height(height)
+       , text("Hello") {
        }
        ~Renderer() {
        }
@@ -41,11 +42,15 @@ public:
                ctx.SetFontFace(font);
                ctx.SetFontSize(16);
                ctx.SetSourceRGB(1, 1, 1);
-               ctx.ShowText("Hello");
+               ctx.ShowText(text.c_str());
 
                surface.Flush();
        }
 
+       void SetText(const std::string &t) {
+               text = t;
+       }
+
 
 private:
        freetype::Library ft;
@@ -57,6 +62,8 @@ private:
        int width;
        int height;
 
+       std::string text;
+
 };
 
 }
index a65255594ba2990e8672fd69d2347f8c9f4cfbe7..0eaa600affd3ed8e71850233f871cdd0a704c6cd 100644 (file)
@@ -10,6 +10,8 @@
 
 namespace cairo {
 
+using TextExtends = cairo_text_extents_t;
+
 class Context {
 
 public:
@@ -38,6 +40,10 @@ public:
                cairo_fill(ctx);
        }
 
+       void GetTextExtends(const char *text, TextExtends &extends) {
+               cairo_text_extents(ctx, text, &extends);
+       }
+
        void MoveTo(double x, double y) {
                cairo_move_to(ctx, x, y);
        }
index a514c096880139b8a159acabac1994c48f377a90..33833f3bec89760a1f927a621d2227a2530934a8 100644 (file)
@@ -3,6 +3,7 @@
 #include <cstdint>
 #include <iostream>
 #include <thread>
+#include <json/json.h>
 
 extern "C" {
 #include <libavcodec/codec_id.h>
@@ -18,6 +19,9 @@ extern "C" {
 
 #include "app/Renderer.h"
 #include "app/Stream.h"
+#include "uv/Loop.h"
+#include "ws/Connection.h"
+#include "ws/Context.h"
 
 
 namespace {
@@ -28,6 +32,15 @@ void stop(int) {
        running = false;
 }
 
+void ws_handler(void *user, const Json::Value &json) {
+       const std::string data_string = json["data"].asString();
+       Json::Value data;
+       Json::Reader json_reader;
+       json_reader.parse(data_string, data);
+       app::Renderer *renderer = static_cast<app::Renderer *>(user);
+       renderer->SetText(data["model"]["text"].asString());
+}
+
 }
 
 
@@ -37,6 +50,11 @@ int main(int argc, char**argv) {
        const int FPS = 60;
        const char *URL = "rtmp://localhost/localhorsttv";
 
+       uv::Loop loop;
+
+       ws::Context wsctx(loop);
+       ws::Connection wsconn(wsctx.GetContext());
+
        app::Stream stream(URL, WIDTH, HEIGHT, FPS);
 
        running = true;
@@ -50,11 +68,15 @@ int main(int argc, char**argv) {
 
        app::Renderer renderer(plane, linesize, WIDTH, HEIGHT);
 
+       wsconn.Subscribe("ChatBotLog", &ws_handler, &renderer);
+
        stream.Start();
 
        std::cout << std::endl;
 
        while (running) {
+               loop.TryStep();
+
                const int64_t target = stream.VideoElapsedMS();
                const int64_t elapsed = stream.TimeElapsedMS();
                const int64_t difference = target - elapsed;
@@ -62,7 +84,7 @@ int main(int argc, char**argv) {
                stream.PrepareVideoFrame();
 
                if (stream.GetVideoFrameCounter() > 0 && difference < 0) {
-                       std::cout << (difference / 1000.0) << "s behind schedule, dropping frame" << std::endl;
+                       std::cout << (difference / -1000.0) << "s behind schedule, dropping frame" << std::endl;
                } else {
                        renderer.RenderVideoFrame(stream.GetVideoFrameCounter(), target);
                }
@@ -79,9 +101,9 @@ int main(int argc, char**argv) {
                        stream.PushAudioFrame();
                }
 
-               if (stream.GetVideoFrameCounter() % 60 == 59) {
-                       std::cout << "rendered: " << (target / 1000.0) << "s, elapsed: " << (elapsed / 1000.0) << "s, difference: " << (difference / 1000.0) << 's' << std::endl;
-               }
+               //if (stream.GetVideoFrameCounter() % 60 == 59) {
+               //      std::cout << "rendered: " << (target / 1000.0) << "s, elapsed: " << (elapsed / 1000.0) << "s, difference: " << (difference / 1000.0) << 's' << std::endl;
+               //}
                if (difference > 3000) {
                        std::this_thread::sleep_for(std::chrono::milliseconds(10));
                }
@@ -89,6 +111,7 @@ int main(int argc, char**argv) {
 
        std::cout << std::endl;
 
+       wsctx.Shutdown();
        stream.Finish();
 
        return 0;
diff --git a/src/uv/Error.h b/src/uv/Error.h
new file mode 100644 (file)
index 0000000..9e600db
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef TEST_UV_ERROR_H_
+#define TEST_UV_ERROR_H_
+
+#include <stdexcept>
+#include <string>
+
+#include <uv.h>
+
+namespace {
+std::string uv_make_message(const std::string &msg, int status) {
+       return msg + ": " + std::string(uv_strerror(status));
+}
+}
+
+namespace uv {
+
+class Error: public std::runtime_error {
+
+public:
+       Error(const std::string &msg, int status): std::runtime_error(uv_make_message(msg, status)) {
+       }
+       ~Error() {
+       }
+
+};
+
+}
+
+#endif
diff --git a/src/uv/Loop.h b/src/uv/Loop.h
new file mode 100644 (file)
index 0000000..de9d91f
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef TEST_UV_LOOP_H_
+#define TEST_UV_LOOP_H_
+
+#include <uv.h>
+
+#include "Error.h"
+
+namespace uv {
+
+class Loop {
+
+public:
+       Loop() {
+               int res = uv_loop_init(&loop);
+               if (res < 0) {
+                       throw Error("failed to initialize loop", res);
+               }
+       }
+       ~Loop() {
+               uv_loop_close(&loop);
+       }
+
+       Loop(const Loop &) = delete;
+       Loop &operator =(const Loop &) = delete;
+
+public:
+       uv_loop_t *GetLoop() {
+               return &loop;
+       }
+
+public:
+       void Run() {
+               uv_run(&loop, UV_RUN_DEFAULT);
+       }
+       void Step() {
+               uv_run(&loop, UV_RUN_ONCE);
+       }
+       void TryStep() {
+               uv_run(&loop, UV_RUN_NOWAIT);
+       }
+
+private:
+       uv_loop_t loop;
+
+};
+
+}
+
+#endif
diff --git a/src/uv/Signal.h b/src/uv/Signal.h
new file mode 100644 (file)
index 0000000..35cd88d
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef TEST_UV_LOOP_H_
+#define TEST_UV_LOOP_H_
+
+#include <uv.h>
+
+#include "Error.h"
+
+namespace uv {
+
+class Signal {
+
+public:
+       Signal(uv_loop_t &loop, int sig, void *data, void (*handler)(void *, int)): handler(handler), data(data) {
+               int res = uv_signal_init(&loop, &signal);
+               if (res < 0) {
+                       throw Error("failed to initialize loop", res);
+               }
+               res = uv_signal_start(&signal, &signal_handler, sig);
+               if (res < 0) {
+                       throw Error("failed to install signal handler", res);
+               }
+               signal.data = this;
+       }
+       ~Signal() {
+               uv_signal_stop(&signal);
+       }
+
+       Signal(const Signal &) = delete;
+       Signal &operator =(const Signal &) = delete;
+
+private:
+       static void signal_handler(uv_signal_t *sig, int signum) {
+               Signal *signal = static_cast<Signal *>(sig->data);
+               signal->handler(signal->data, signum);
+       }
+
+private:
+       uv_signal_t signal;
+       void (*handler)(void *, int);
+       void *data;
+
+};
+
+}
+
+#endif
diff --git a/src/ws/Connection.cpp b/src/ws/Connection.cpp
new file mode 100644 (file)
index 0000000..6debd28
--- /dev/null
@@ -0,0 +1,88 @@
+#include "Connection.h"
+
+#include <iostream>
+#include <libwebsockets.h>
+#include <stdexcept>
+
+#include "io.h"
+
+namespace ws {
+
+Connection::Connection(lws_context *ctx)
+: info{0}, wsi(nullptr), connected(false) {
+       info.context = ctx;
+       info.opaque_user_data = this;
+       // wss://alttp.localhorst.tv/app/nkmbiabdrtqnd8t19txs?protocol=7&client=js&version=8.3.0&flash=false
+       info.address = "alttp.localhorst.tv";
+       info.port = 443;
+       info.ssl_connection = 1;
+       info.path = "/app/nkmbiabdrtqnd8t19txs?protocol=7&client=js&version=8.3.0&flash=false";
+       info.host = "alttp.localhorst.tv";
+       info.origin = "test";
+       info.protocol = "pusher";
+       info.ietf_version_or_minus_one = -1;
+       info.userdata = this;
+       info.pwsi = &wsi;
+       wsi = lws_client_connect_via_info(&info);
+       if (!wsi) {
+               throw std::runtime_error("failed to connect client");
+       }
+       lws_set_timer_usecs(wsi, 30000000);
+       out_buffer.insert(0, LWS_PRE, '\0');
+}
+
+int Connection::ProtoCallback(lws_callback_reasons reason, void *in, size_t len) {
+       switch (reason) {
+               case LWS_CALLBACK_CLIENT_ESTABLISHED:
+                       connected = true;
+                       if (out_buffer.length() > LWS_PRE) {
+                               lws_callback_on_writable(wsi);
+                       }
+                       break;
+               case LWS_CALLBACK_CLIENT_CLOSED:
+                       connected = false;
+                       break;
+               case LWS_CALLBACK_CLIENT_RECEIVE:
+                       if (lws_is_first_fragment(wsi)) {
+                               in_buffer.clear();
+                       }
+                       in_buffer.append(static_cast<const char *>(in), len);
+                       if (lws_is_final_fragment(wsi)) {
+                               HandleMessage(in_buffer);
+                       }
+                       break;
+               case LWS_CALLBACK_CLIENT_WRITEABLE:
+                       if (out_buffer.length() > LWS_PRE) {
+                               int res = lws_write(wsi, reinterpret_cast<unsigned char *>(&out_buffer[0]) + LWS_PRE, out_buffer.length() - LWS_PRE, LWS_WRITE_TEXT);
+                               if (res > 0) {
+                                       out_buffer.erase(LWS_PRE, res);
+                               }
+                               break;
+                       }
+               case LWS_CALLBACK_TIMER:
+                       Ping();
+                       lws_set_timer_usecs(wsi, 30000000);
+                       break;
+               case LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL:
+               case LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL:
+               case LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL:
+               case LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL:
+               case LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION:
+               case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
+               case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
+               case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
+               case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
+               case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:
+               case LWS_CALLBACK_WSI_CREATE:
+                       break;
+               default:
+                       std::cout << "unhandled connection proto callback, reason: " << reason << ", in: " << in << ", len: " << len << std::endl;
+                       if (in && len) {
+                               std::cout << " DATA: \"" << std::string(static_cast<const char *>(in), len) << '"' << std::endl;
+                       }
+                       break;
+       }
+       return 0;
+}
+
+}
diff --git a/src/ws/Connection.h b/src/ws/Connection.h
new file mode 100644 (file)
index 0000000..76ec576
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef TEST_WS_CONNECTION_H_
+#define TEST_WS_CONNECTION_H_
+
+#include "json/reader.h"
+#include "json/value.h"
+#include "json/writer.h"
+#include <cstring>
+#include <map>
+#include <string>
+
+#include <json/json.h>
+#include <libwebsockets.h>
+#include <vector>
+
+namespace ws {
+
+class Connection {
+
+public:
+       explicit Connection(lws_context *ctx);
+       ~Connection() {
+       }
+
+       Connection(const Connection &) = delete;
+       Connection &operator =(const Connection &) = delete;
+
+private:
+       struct Callback {
+               void *user;
+               void (*callback)(void *, const Json::Value &);
+               void Call(const Json::Value &val) const {
+                       (*callback)(user, val);
+               }
+       };
+
+public:
+       void Ping() {
+               SendMessage("{\"event\":\"pusher:ping\"}");
+       }
+
+       void Subscribe(const std::string &chan, void (*callback)(void *, const Json::Value &), void *user = nullptr) {
+               callbacks[chan].push_back({ user, callback });
+               Json::Value json;
+               json["event"] = "pusher:subscribe";
+               json["data"]["channel"] = chan;
+               SendMessage(json);
+       }
+
+       void SendMessage(const Json::Value &json) {
+               SendMessage(json_writer.write(json));
+       }
+
+       void SendMessage(const std::string &msg) {
+               out_buffer.append(msg);
+               lws_callback_on_writable(wsi);
+       }
+
+       void SendMessage(const char *msg) {
+               out_buffer.append(msg);
+               lws_callback_on_writable(wsi);
+       }
+
+public:
+       int ProtoCallback(lws_callback_reasons reason, void *in, size_t len);
+
+       void HandleMessage(const std::string &msg) {
+               Json::Value json;
+               json_reader.parse(msg, json);
+               const std::string channel = json["channel"].asString();
+               for (const Callback &callback : callbacks[channel]) {
+                       callback.Call(json);
+               }
+       }
+
+private:
+       lws_client_connect_info info;
+       lws *wsi;
+       bool connected;
+
+       std::string in_buffer;
+       std::string out_buffer;
+
+       Json::Reader json_reader;
+       Json::FastWriter json_writer;
+       std::map<std::string, std::vector<Callback>> callbacks;
+
+};
+
+}
+
+#endif
diff --git a/src/ws/Context.h b/src/ws/Context.h
new file mode 100644 (file)
index 0000000..7d69981
--- /dev/null
@@ -0,0 +1,92 @@
+#ifndef TEST_WS_CONTEXT_H_
+#define TEST_WS_CONTEXT_H_
+
+#include <cstdio>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+
+#include <libwebsockets.h>
+
+#include "Connection.h"
+#include "io.h"
+#include "../uv/Loop.h"
+
+namespace ws {
+
+class Context {
+
+public:
+       explicit Context(uv::Loop &loop): info{0}, ctx(nullptr), proto{0}, protos{0}, loops{0} {
+               //lws_set_log_level(LLL_USER|LLL_ERR|LLL_WARN|LLL_NOTICE|LLL_INFO|LLL_DEBUG, nullptr);
+               proto.name = "pusher";
+               proto.callback = &proto_callback;
+               proto.user = this;
+               proto.rx_buffer_size = BUFSIZ;
+               proto.tx_packet_size = BUFSIZ;
+               protos[0] = &proto;
+               info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_LIBUV;
+               info.port = CONTEXT_PORT_NO_LISTEN;
+               info.pprotocols = protos;
+               loops[0] = loop.GetLoop();
+               info.foreign_loops = loops;
+               ctx = lws_create_context(&info);
+               if (!ctx) {
+                       throw std::runtime_error("failed to create context");
+               }
+       }
+       ~Context() {
+               lws_context_destroy(ctx);
+       }
+
+       Context(const Context &) = delete;
+       Context &operator =(const Context &) = delete;
+
+public:
+       lws_context *GetContext() {
+               return ctx;
+       }
+
+       void Shutdown() {
+               lws_context_deprecate(ctx, nullptr);
+       }
+
+private:
+       static int proto_callback(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) {
+               void *user_data = lws_wsi_user(wsi);
+               if (user_data) {
+                       Connection *conn = static_cast<Connection *>(user_data);
+                       return conn->ProtoCallback(reason, in, len);
+               }
+               Context *c = static_cast<Context *>(user);
+               return c->ProtoCallback(reason, in, len);
+       }
+
+       int ProtoCallback(lws_callback_reasons reason, void *in, size_t len) {
+               switch (reason) {
+                       case LWS_CALLBACK_PROTOCOL_INIT:
+                       case LWS_CALLBACK_PROTOCOL_DESTROY:
+                       case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
+                               break;
+                       default:
+                               std::cout << "unhandled generic proto callback, reason: " << reason << ", in: " << in << ", len: " << len << std::endl;
+                               if (in && len) {
+                                       std::cout << " DATA: \"" << std::string(static_cast<const char *>(in), len) << '"' << std::endl;
+                               }
+                               break;
+               }
+               return 0;
+       }
+
+private:
+       lws_context_creation_info info;
+       lws_protocols proto;
+       const lws_protocols *protos[2];
+       void *loops[2];
+       lws_context *ctx;
+
+};
+
+}
+
+#endif
diff --git a/src/ws/io.h b/src/ws/io.h
new file mode 100644 (file)
index 0000000..b60d852
--- /dev/null
@@ -0,0 +1,370 @@
+#ifndef TEST_WS_IO_H_
+#define TEST_WS_IO_H_
+
+#include <ostream>
+
+extern "C" {
+#include <libwebsockets.h>
+}
+
+namespace std {
+inline std::ostream &operator <<(std::ostream &out, lws_callback_reasons r) {
+       switch (r) {
+               case LWS_CALLBACK_PROTOCOL_INIT:
+                       out << "protocol init";
+                       break;
+               case LWS_CALLBACK_PROTOCOL_DESTROY:
+                       out << "protocol destroy";
+                       break;
+               case LWS_CALLBACK_WSI_CREATE:
+                       out << "wsi create";
+                       break;
+               case LWS_CALLBACK_WSI_DESTROY:
+                       out << "wsi destroy";
+                       break;
+               case LWS_CALLBACK_WSI_TX_CREDIT_GET:
+                       out << "wsi tx credit get";
+                       break;
+
+               case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
+                       out << "load extra client certs";
+                       break;
+               case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
+                       out << "load extra server certs";
+                       break;
+               case LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION:
+                       out << "perform server cert verify";
+                       break;
+               case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
+                       out << "perform client cert verify";
+                       break;
+               case LWS_CALLBACK_SSL_INFO:
+                       out << "ssl info";
+                       break;
+
+               case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
+                       out << "new client instance";
+                       break;
+               case LWS_CALLBACK_HTTP:
+                       out << "http request";
+                       break;
+               case LWS_CALLBACK_HTTP_BODY:
+                       out << "http body";
+                       break;
+               case LWS_CALLBACK_HTTP_BODY_COMPLETION:
+                       out << "http body complete";
+                       break;
+               case LWS_CALLBACK_HTTP_FILE_COMPLETION:
+                       out << "http file complete";
+                       break;
+               case LWS_CALLBACK_HTTP_WRITEABLE:
+                       out << "http writable";
+                       break;
+               case LWS_CALLBACK_CLOSED_HTTP:
+                       out << "http closed";
+                       break;
+               case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
+                       out << "filter http connection";
+                       break;
+               case LWS_CALLBACK_ADD_HEADERS:
+                       out << "add headers";
+                       break;
+               case LWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION:
+                       out << "verify basic auth";
+                       break;
+               case LWS_CALLBACK_CHECK_ACCESS_RIGHTS:
+                       out << "check access rights";
+                       break;
+               case LWS_CALLBACK_PROCESS_HTML:
+                       out << "process html";
+                       break;
+               case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
+                       out << "http bind protocol";
+                       break;
+               case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
+                       out << "http drop protocol";
+                       break;
+               case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE:
+                       out << "http confirm upgrade";
+                       break;
+
+               case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
+                       out << "http client connection established";
+                       break;
+               case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
+                       out << "http client connection closed";
+                       break;
+               case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
+                       out << "http client read";
+                       break;
+               case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
+                       out << "http client completed";
+                       break;
+               case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
+                       out << "http client writable";
+                       break;
+               case LWS_CALLBACK_CLIENT_HTTP_REDIRECT:
+                       out << "http client redirect";
+                       break;
+               case LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL:
+                       out << "http client bind protocol";
+                       break;
+               case LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL:
+                       out << "http client drop protocol";
+                       break;
+
+               case LWS_CALLBACK_ESTABLISHED:
+                       out << "connection established";
+                       break;
+               case LWS_CALLBACK_CLOSED:
+                       out << "connection closed";
+                       break;
+               case LWS_CALLBACK_SERVER_WRITEABLE:
+                       out << "server writable";
+                       break;
+               case LWS_CALLBACK_RECEIVE:
+                       out << "receive";
+                       break;
+               case LWS_CALLBACK_RECEIVE_PONG:
+                       out << "receive pong";
+                       break;
+               case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
+                       out << "peer initiated close";
+                       break;
+               case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+                       out << "filter protocol connection";
+                       break;
+               case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
+                       out << "confirm extension ok";
+                       break;
+               case LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL:
+                       out << "ws server bind protocol";
+                       break;
+               case LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL:
+                       out << "ws server drop protocol";
+                       break;
+
+               case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+                       out << "client connection error";
+                       break;
+               case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:
+                       out << "client filter pre establish";
+                       break;
+               case LWS_CALLBACK_CLIENT_ESTABLISHED:
+                       out << "client connection established";
+                       break;
+               case LWS_CALLBACK_CLIENT_CLOSED:
+                       out << "client connection closed";
+                       break;
+               case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
+                       out << "client append handshake header";
+                       break;
+               case LWS_CALLBACK_CLIENT_RECEIVE:
+                       out << "client receive";
+                       break;
+               case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
+                       out << "client receive pong";
+                       break;
+               case LWS_CALLBACK_CLIENT_WRITEABLE:
+                       out << "client writable";
+                       break;
+               case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
+                       out << "client confirm extension suppported";
+                       break;
+               case LWS_CALLBACK_WS_EXT_DEFAULTS:
+                       out << "ws ext defaults";
+                       break;
+               case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+                       out << "filter network connection";
+                       break;
+               case LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL:
+                       out << "ws client bind protocol";
+                       break;
+               case LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL:
+                       out << "ws client drop protocol";
+                       break;
+
+               case LWS_CALLBACK_GET_THREAD_ID:
+                       out << "get thread id";
+                       break;
+               case LWS_CALLBACK_ADD_POLL_FD:
+                       out << "add poll fd";
+                       break;
+               case LWS_CALLBACK_DEL_POLL_FD:
+                       out << "del poll fd";
+                       break;
+               case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
+                       out << "change mode poll fd";
+                       break;
+               case LWS_CALLBACK_LOCK_POLL:
+                       out << "lock poll";
+                       break;
+               case LWS_CALLBACK_UNLOCK_POLL:
+                       out << "unlock poll";
+                       break;
+
+               case LWS_CALLBACK_CGI:
+                       out << "cgi";
+                       break;
+               case LWS_CALLBACK_CGI_TERMINATED:
+                       out << "cgi terminated";
+                       break;
+               case LWS_CALLBACK_CGI_STDIN_DATA:
+                       out << "cgi stdin data";
+                       break;
+               case LWS_CALLBACK_CGI_STDIN_COMPLETED:
+                       out << "cgi stdin completed";
+                       break;
+               case LWS_CALLBACK_CGI_PROCESS_ATTACH:
+                       out << "cgi process attach";
+                       break;
+
+               case LWS_CALLBACK_SESSION_INFO:
+                       out << "session info";
+                       break;
+               case LWS_CALLBACK_GS_EVENT:
+                       out << "gs event";
+                       break;
+               case LWS_CALLBACK_HTTP_PMO:
+                       out << "http pmo";
+                       break;
+
+               case LWS_CALLBACK_RAW_PROXY_CLI_RX:
+                       out << "raw proxy cli rx";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_SRV_RX:
+                       out << "raw proxy srv rx";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_CLI_CLOSE:
+                       out << "raw proxy cli close";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_SRV_CLOSE:
+                       out << "raw proxy srv close";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE:
+                       out << "raw proxy cli writable";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE:
+                       out << "raw proxy srv writable";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_CLI_ADOPT:
+                       out << "raw proxy cli adopt";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_SRV_ADOPT:
+                       out << "raw proxy srv adopt";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL:
+                       out << "raw proxy cli bind protocol";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL:
+                       out << "raw proxy srv bind protocol";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL:
+                       out << "raw proxy cli drop protocol";
+                       break;
+               case LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL:
+                       out << "raw proxy srv drop protocol";
+                       break;
+
+               case LWS_CALLBACK_RAW_RX:
+                       out << "raw rx";
+                       break;
+               case LWS_CALLBACK_RAW_CLOSE:
+                       out << "raw close";
+                       break;
+               case LWS_CALLBACK_RAW_WRITEABLE:
+                       out << "raw writable";
+                       break;
+               case LWS_CALLBACK_RAW_ADOPT:
+                       out << "raw adopt";
+                       break;
+               case LWS_CALLBACK_RAW_CONNECTED:
+                       out << "raw connected";
+                       break;
+               case LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL:
+                       out << "raw skt bind protocol";
+                       break;
+               case LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL:
+                       out << "raw skt drop protocol";
+                       break;
+
+               case LWS_CALLBACK_RAW_ADOPT_FILE:
+                       out << "raw adopt file";
+                       break;
+               case LWS_CALLBACK_RAW_RX_FILE:
+                       out << "raw rx file";
+                       break;
+               case LWS_CALLBACK_RAW_WRITEABLE_FILE:
+                       out << "raw writable file";
+                       break;
+               case LWS_CALLBACK_RAW_CLOSE_FILE:
+                       out << "raw close file";
+                       break;
+               case LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL:
+                       out << "raw file bind protocol";
+                       break;
+               case LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL:
+                       out << "raw file drop protocol";
+                       break;
+
+               case LWS_CALLBACK_TIMER:
+                       out << "timer";
+                       break;
+               case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
+                       out << "event wait cancelled";
+                       break;
+               case LWS_CALLBACK_CHILD_CLOSING:
+                       out << "child closing";
+                       break;
+
+               case LWS_CALLBACK_VHOST_CERT_AGING:
+                       out << "vhost cert aging";
+                       break;
+               case LWS_CALLBACK_VHOST_CERT_UPDATE:
+                       out << "vhost cert update";
+                       break;
+
+               case LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED:
+                       out << "mqtt new client instantiated";
+                       break;
+               case LWS_CALLBACK_MQTT_IDLE:
+                       out << "mqtt idle";
+                       break;
+               case LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED:
+                       out << "mqtt client established";
+                       break;
+               case LWS_CALLBACK_MQTT_SUBSCRIBED:
+                       out << "mqtt subscribed";
+                       break;
+               case LWS_CALLBACK_MQTT_CLIENT_WRITEABLE:
+                       out << "mqtt client writable";
+                       break;
+               case LWS_CALLBACK_MQTT_CLIENT_RX:
+                       out << "mqtt client rx";
+                       break;
+               case LWS_CALLBACK_MQTT_UNSUBSCRIBED:
+                       out << "mqtt unsubscribed";
+                       break;
+               case LWS_CALLBACK_MQTT_DROP_PROTOCOL:
+                       out << "mqtt drop protocol";
+                       break;
+               case LWS_CALLBACK_MQTT_CLIENT_CLOSED:
+                       out << "mqtt client closed";
+                       break;
+               case LWS_CALLBACK_MQTT_ACK:
+                       out << "mqtt ack";
+                       break;
+               case LWS_CALLBACK_MQTT_RESEND:
+                       out << "mqtt resend";
+                       break;
+
+               default:
+                       out << "unknown reason";
+                       break;
+       }
+       out << " (" << int(r) << ')';
+       return out;
+}
+}
+
+#endif