+uint8_t *vfd = nullptr;
+uint8_t cefimg[1280 * 720 * 4];
+SwsContext *sws = nullptr;
+
+extern "C" {
+#include <libswscale/swscale.h>
+}
+
+void convert_stuff(const VideoFormat &video_format, const uint8_t *ptr)
+{
+ if (sws == nullptr) {
+ sws = sws_getContext(video_format.width, video_format.height, AV_PIX_FMT_BGRA,
+ video_format.width, video_format.height, AV_PIX_FMT_NV12,
+ SWS_BICUBIC, nullptr, nullptr, nullptr);
+ vfd = new uint8_t[video_format.width * video_format.height * 2];
+ }
+
+ uint8_t *src_pic_data[4] = { nullptr, nullptr, nullptr, nullptr };
+ int src_linesizes[4] = { 0, 0, 0, 0 };
+ src_pic_data[0] = (uint8_t *)ptr;
+ src_linesizes[0] = video_format.width * 4;
+
+ uint8_t *dst_pic_data[4] = { nullptr, nullptr, nullptr, nullptr };
+ int dst_linesizes[4] = { 0, 0, 0, 0 };
+ dst_pic_data[0] = vfd;
+ dst_linesizes[0] = video_format.width;
+ dst_pic_data[1] = vfd + video_format.width * video_format.height;
+ dst_linesizes[1] = video_format.width;
+
+ sws_scale(sws, src_pic_data, src_linesizes, 0, video_format.height, dst_pic_data, dst_linesizes);
+}
+
+#include <cef_app.h>
+#include <cef_browser.h>
+#include <cef_client.h>
+#include "nageru_cef_app.h"
+
+recursive_mutex browser_mutex;
+int browser_ready = 0;
+
+class KaeruCEFClient : public CefClient, public CefRenderHandler, public CefLoadHandler
+{
+public:
+ KaeruCEFClient() {}
+
+ CefRefPtr<CefRenderHandler> GetRenderHandler() override
+ {
+ return this;
+ }
+
+ CefRefPtr<CefLoadHandler> GetLoadHandler() override
+ {
+ return this;
+ }
+
+ // CefRenderHandler.
+
+ void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height) override
+ {
+ // fprintf(stderr, "onpaint %dx%d\n", width, height);
+ memcpy(cefimg, buffer, width * height * 4); // FIXME lock?
+
+ lock_guard<recursive_mutex> lock(browser_mutex);
+ if (browser_ready == 1)
+ browser_ready = 2;
+ }
+
+ void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override
+ {
+ fprintf(stderr, "getviewrect\n");
+ rect = CefRect(0, 0, 1280, 720);
+ }
+
+ // CefLoadHandler.
+
+ void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) override
+ {
+ fprintf(stderr, "onload\n");
+
+ CefString script_url("<theme eval>");
+ int start_line = 1;
+ browser->GetMainFrame()->ExecuteJavaScript("play();", script_url, start_line);
+
+ lock_guard<recursive_mutex> lock(browser_mutex);
+ browser_ready = 1;
+ }
+
+private:
+ CEFCapture *parent;
+
+ IMPLEMENT_REFCOUNTING(KaeruCEFClient);
+};
+
+CefRefPtr<NageruCefApp> cef_app;
+CefRefPtr<CefBrowser> browser;
+unique_ptr<KaeruCEFClient> cef_client;
+
+ int parse_digit(char ch)
+ {
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ }
+ return 0;
+ }
+
+ int parse_clock(char ch1, char ch2)
+ {
+ int s1 = parse_digit(ch1);
+ int s2 = parse_digit(ch2);
+ return s1 * 10 + s2;
+ }
+
+
+ int parse_score_weird(char ch1, char ch2, char ch3)
+ {
+ char str[4];
+ char *ptr = str;
+ if (ch1 != ' ') *ptr++ = ch1;
+ if (ch2 != ' ') *ptr++ = ch2;
+ if (ch3 != ' ') *ptr++ = ch3;
+ *ptr++ = 0;
+
+ return atoi(str);
+ }
+
+void add_cef(uint8_t *data, unsigned width, unsigned height, int64_t video_pts, AVRational video_timebase)
+{
+ if (cef_client == nullptr) {
+ cef_client.reset(new KaeruCEFClient);
+
+ //cef_app.reset(new NageruCefApp);
+ cef_app->initialize_cef();
+
+ CefPostTask(TID_UI, new CEFTaskAdapter([&]{
+ lock_guard<recursive_mutex> lock(browser_mutex);
+
+ CefBrowserSettings browser_settings;
+ // browser_settings.web_security = cef_state_t::STATE_DISABLED;
+ browser_settings.webgl = cef_state_t::STATE_DISABLED;
+ browser_settings.windowless_frame_rate = 60.00;
+
+ CefWindowInfo window_info;
+ window_info.SetAsWindowless(0);
+ browser = CefBrowserHost::CreateBrowserSync(window_info, cef_client.get(), "file:///home/sesse/dev/ultimatescore/score.html", browser_settings, nullptr, nullptr);
+ }));
+ }
+
+ {
+ CefString script_url("<theme eval>");
+ int start_line = 1;
+ char buf[256];
+
+ int old_bodet_clock = bodet_clock;
+
+ //fprintf(stderr, "video_pts=%ld timebase = %ld/%ld\n", video_pts, video_timebase.num, video_timebase.den);
+ //double cur_time = video_start_time + video_pts * double(video_timebase.num) / double(video_timebase.den);
+ double cur_time = video_start_time + video_pts / double(TIMEBASE);
+ //double cur_time = video_start_time + (frameno++) / fps;
+ while (cur_msg < bodet_msgs.size() && cur_time > bodet_msgs[cur_msg].t) {
+ const string &m = bodet_msgs[cur_msg].msg;
+ if (m.size() >= 10 && m[0] == 'G' && m[1] == '1' && m[2] == '0') {
+ int min = parse_clock(m[4], m[5]);
+ int sec = parse_clock(m[6], m[7]);
+ bodet_clock = min * 60 + sec;
+ score1 = parse_score_weird(m[8], m[9], m[10]);
+ score2 = parse_score_weird(m[11], m[12], m[13]);
+ }
+ ++cur_msg;
+ }
+
+ string str = "update('{";
+ snprintf(buf, 256, "\"score1\": %d", score1);
+ str += buf;
+ snprintf(buf, 256, ",\"score2\": %d", score2);
+ str += buf;
+
+ if (false) {
+ int doh = uint64_t(cur_time + 7200) % 86400;
+ snprintf(buf, 256, "%02d:%02d:%02d", doh / 3600, (doh % 3600) / 60, doh % 60);
+ team1 = buf;
+ }
+
+ str += ",\"team1\": \"" + team1 + "\"";
+ str += ",\"team2\": \"" + team2 + "\"";
+ str += ",\"team1color\": \"" + team1color + "\"";
+ str += ",\"team2color\": \"" + team2color + "\"";
+ str += "}');setteams();setcolors();setscore();";
+
+ snprintf(buf, 256, "update_given_clock(%d,'clock');", bodet_clock);
+ str += buf;
+
+ if (old_bodet_clock == 0 && bodet_clock != 0) {
+ str += "showclock();";
+ } else if (old_bodet_clock != 0 && bodet_clock == 0) {
+ str += "hideclock();";
+ }
+
+ //printf("%s\n", str.c_str());
+
+ bool ok = false;
+ do {
+ browser_mutex.lock();
+ if (browser_ready >= 2) {
+ browser->GetMainFrame()->ExecuteJavaScript(str, script_url, start_line);
+ browser_mutex.unlock();
+ ok = true;
+ } else {
+ browser_mutex.unlock();
+ printf("Waiting for CEF...\n");
+ usleep(100000);
+ }
+ } while (!ok);
+ }
+
+ unsigned char r0, g0, b0;
+ unsigned char a1, r1, g1, b1;
+ unsigned char *sptr = cefimg;
+ unsigned char *dptr = data;
+ for (int i = 0; i < 1280 * 720; ++i) {
+ //a0 = dptr[3];
+ r0 = dptr[2];
+ g0 = dptr[1];
+ b0 = dptr[0];
+
+ a1 = sptr[3];
+ r1 = sptr[2];
+ g1 = sptr[1];
+ b1 = sptr[0];
+
+ unsigned a = 255;
+ unsigned r = r0 + ((r1 - r0) * a1) / 255;
+ unsigned g = g0 + ((g1 - g0) * a1) / 255;
+ unsigned b = b0 + ((b1 - b0) * a1) / 255;
+
+ sptr += 4;
+ *dptr++ = b;
+ *dptr++ = g;
+ *dptr++ = r;
+ *dptr++ = a;
+ }
+ //memcpy(data, cefimg, 1280*720*4);
+}
+
+double crop_start = 0.0;
+double crop_end = HUGE_VAL;
+
+bool within(double t)
+{
+ return t >= crop_start && t < crop_end;
+}
+
+string last_ts;
+
+int64_t video_pts_offset = 0, audio_pts_offset = 0;
+int64_t next_video_pts = 0, next_audio_pts = 0;
+