/* Copyright (C) 2023 Grok Image Compression Inc. This file is part of DCP-o-matic. DCP-o-matic is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. DCP-o-matic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with DCP-o-matic. If not, see . */ #pragma once #include "../config.h" #include "../dcp_video.h" #include "../film.h" #include "../log.h" #include "../dcpomatic_log.h" #include "../writer.h" #include "messenger.h" #include #include static std::mutex launchMutex; namespace grk_plugin { struct GrokLogger : public MessengerLogger { explicit GrokLogger(const std::string &preamble) : MessengerLogger(preamble) {} virtual ~GrokLogger() = default; void info(const char* fmt, ...) override{ va_list arg; va_start(arg, fmt); dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_GENERAL); va_end(arg); } void warn(const char* fmt, ...) override{ va_list arg; va_start(arg, fmt); dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_WARNING); va_end(arg); } void error(const char* fmt, ...) override{ va_list arg; va_start(arg, fmt); dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_ERROR); va_end(arg); } }; struct DcpomaticContext { DcpomaticContext( std::shared_ptr film_, Writer& writer_, EventHistory& history_, boost::filesystem::path const& location_ ) : film(film_) , writer(writer_) , history(history_) , location(location_) { } void set_dimensions(uint32_t w, uint32_t h) { width = w; height = h; } std::shared_ptr film; Writer& writer; EventHistory& history; boost::filesystem::path location; uint32_t width = 0; uint32_t height = 0; }; class GrokContext { public: explicit GrokContext(DcpomaticContext* dcpomatic_context) : _dcpomatic_context(dcpomatic_context) { auto grok = Config::instance()->grok(); if (!grok.enable) { return; } boost::filesystem::path folder(_dcpomatic_context->location); boost::filesystem::path binary_path = folder / "grk_compress"; if (!boost::filesystem::exists(binary_path)) { getMessengerLogger()->error( "Invalid binary location %s", _dcpomatic_context->location.c_str() ); return; } auto proc = [this](const std::string& str) { try { Msg msg(str); auto tag = msg.next(); if (tag == GRK_MSGR_BATCH_SUBMIT_COMPRESSED) { auto clientFrameId = msg.nextUint(); msg.nextUint(); // compressed frame ID auto compressedFrameLength = msg.nextUint(); auto processor = [this](DCPVideo srcFrame, uint8_t* compressed, uint32_t compressedFrameLength) { auto compressed_data = std::make_shared(compressed, compressedFrameLength); _dcpomatic_context->writer.write(compressed_data, srcFrame.index(), srcFrame.eyes()); frame_done (); }; int const minimum_size = 16384; bool needsRecompression = compressedFrameLength < minimum_size; _messenger->processCompressed(str, processor, needsRecompression); if (needsRecompression) { auto vf = _messenger->retrieve(clientFrameId); if (!vf) { return; } auto encoded = std::make_shared(vf->encode_locally()); _dcpomatic_context->writer.write(encoded, vf->index(), vf->eyes()); frame_done (); } } } catch (std::exception& ex) { getMessengerLogger()->error("%s",ex.what()); } }; auto clientInit = MessengerInit( clientToGrokMessageBuf, clientSentSynch, grokReceiveReadySynch, grokToClientMessageBuf, grokSentSynch, clientReceiveReadySynch, proc, std::thread::hardware_concurrency() ); _messenger = new ScheduledMessenger(clientInit); } ~GrokContext() { if (!_messenger) { return; } std::unique_lock lk_global(launchMutex); if (!_messenger) { return; } if (_launched) { _messenger->shutdown(); } delete _messenger; } bool launch(DCPVideo dcpv, int device) { namespace fs = boost::filesystem; if (!_messenger) { return false; } if (_launched) { return true; } if (_launch_failed) { return false; } std::unique_lock lk_global(launchMutex); if (!_messenger) { return false; } if (_launched) { return true; } if (_launch_failed) { return false; } if (MessengerInit::firstLaunch(true)) { if (!fs::exists(_dcpomatic_context->location) || !fs::is_directory(_dcpomatic_context->location)) { getMessengerLogger()->error("Invalid directory %s", _dcpomatic_context->location.c_str()); return false; } auto s = dcpv.get_size(); _dcpomatic_context->set_dimensions(s.width, s.height); auto grok = Config::instance()->grok(); if (!_messenger->launch_grok( _dcpomatic_context->location, _dcpomatic_context->width, _dcpomatic_context->width, _dcpomatic_context->height, 3, 12, device, _dcpomatic_context->film->resolution() == Resolution::FOUR_K, _dcpomatic_context->film->video_frame_rate(), _dcpomatic_context->film->video_bit_rate(VideoEncoding::JPEG2000), grok.licence_server, grok.licence)) { _launch_failed = true; return false; } } _launched = _messenger->waitForClientInit(); _launch_failed = _launched; return _launched; } bool scheduleCompress(DCPVideo const& vf) { if (!_messenger) { return false; } auto cvt = [this, &vf](BufferSrc src) { vf.convert_to_xyz((uint16_t*)src.framePtr_); }; return _messenger->scheduleCompress(vf, cvt); } private: void frame_done() { _dcpomatic_context->history.event(); } DcpomaticContext* _dcpomatic_context; ScheduledMessenger* _messenger = nullptr; bool _launched = false; bool _launch_failed = false; }; }