2 Copyright (C) 2023 Grok Image Compression Inc.
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
24 #include "../config.h"
25 #include "../dcp_video.h"
28 #include "../dcpomatic_log.h"
29 #include "../writer.h"
30 #include "messenger.h"
31 #include <dcp/array_data.h>
32 #include <boost/filesystem.hpp>
35 static std::mutex launchMutex;
40 struct GrokLogger : public MessengerLogger {
41 explicit GrokLogger(const std::string &preamble) : MessengerLogger(preamble)
43 virtual ~GrokLogger() = default;
44 void info(const char* fmt, ...) override{
47 dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_GENERAL);
50 void warn(const char* fmt, ...) override{
53 dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_WARNING);
56 void error(const char* fmt, ...) override{
59 dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_ERROR);
65 FrameProxy(int index, Eyes eyes, DCPVideo dcpv) : index_(index), eyes_(eyes), vf(dcpv)
70 Eyes eyes(void) const {
78 struct DcpomaticContext
81 std::shared_ptr<const Film> film_,
83 EventHistory& history_,
84 boost::filesystem::path const& location_
94 void set_dimensions(uint32_t w, uint32_t h)
100 std::shared_ptr<const Film> film;
102 EventHistory& history;
103 boost::filesystem::path location;
112 explicit GrokContext(DcpomaticContext* dcpomatic_context)
113 : _dcpomatic_context(dcpomatic_context)
115 auto grok = Config::instance()->grok().get_value_or({});
120 boost::filesystem::path folder(_dcpomatic_context->location);
121 boost::filesystem::path binary_path = folder / "grk_compress";
122 if (!boost::filesystem::exists(binary_path)) {
123 getMessengerLogger()->error(
124 "Invalid binary location %s", _dcpomatic_context->location.c_str()
129 auto proc = [this](const std::string& str) {
132 auto tag = msg.next();
133 if (tag == GRK_MSGR_BATCH_SUBMIT_COMPRESSED) {
134 auto clientFrameId = msg.nextUint();
135 msg.nextUint(); // compressed frame ID
136 auto compressedFrameLength = msg.nextUint();
137 auto processor = [this](FrameProxy srcFrame, uint8_t* compressed, uint32_t compressedFrameLength) {
138 auto compressed_data = std::make_shared<dcp::ArrayData>(compressed, compressedFrameLength);
139 _dcpomatic_context->writer.write(compressed_data, srcFrame.index(), srcFrame.eyes());
143 int const minimum_size = 16384;
145 bool needsRecompression = compressedFrameLength < minimum_size;
146 _messenger->processCompressed(str, processor, needsRecompression);
148 if (needsRecompression) {
149 auto fp = _messenger->retrieve(clientFrameId);
154 auto encoded = std::make_shared<dcp::ArrayData>(fp->vf.encode_locally());
155 _dcpomatic_context->writer.write(encoded, fp->vf.index(), fp->vf.eyes());
159 } catch (std::exception& ex) {
160 getMessengerLogger()->error("%s",ex.what());
164 auto clientInit = MessengerInit(
165 clientToGrokMessageBuf,
167 grokReceiveReadySynch,
168 grokToClientMessageBuf,
170 clientReceiveReadySynch,
172 std::thread::hardware_concurrency()
175 _messenger = new ScheduledMessenger<FrameProxy>(clientInit);
183 bool launch(DCPVideo dcpv, int device)
185 namespace fs = boost::filesystem;
193 if (_launch_failed) {
197 std::unique_lock<std::mutex> lk_global(launchMutex);
205 if (_launch_failed) {
209 if (MessengerInit::firstLaunch(true)) {
211 if (!fs::exists(_dcpomatic_context->location) || !fs::is_directory(_dcpomatic_context->location)) {
212 getMessengerLogger()->error("Invalid directory %s", _dcpomatic_context->location.c_str());
216 auto s = dcpv.get_size();
217 _dcpomatic_context->set_dimensions(s.width, s.height);
218 auto grok = Config::instance()->grok().get_value_or({});
219 if (!_messenger->launchGrok(
220 _dcpomatic_context->location,
221 _dcpomatic_context->width,
222 _dcpomatic_context->width,
223 _dcpomatic_context->height,
227 _dcpomatic_context->film->resolution() == Resolution::FOUR_K,
228 _dcpomatic_context->film->video_frame_rate(),
229 _dcpomatic_context->film->j2k_bandwidth(),
233 _launch_failed = true;
238 _launched = _messenger->waitForClientInit();
239 _launch_failed = _launched;
244 bool scheduleCompress(DCPVideo const& vf)
250 auto fp = FrameProxy(vf.index(), vf.eyes(), vf);
251 auto cvt = [this, &fp](BufferSrc src) {
252 fp.vf.convert_to_xyz((uint16_t*)src.framePtr_);
255 return _messenger->scheduleCompress(fp, cvt);
264 std::unique_lock<std::mutex> lk_global(launchMutex);
271 _messenger->shutdown();
275 _messenger = nullptr;
280 _dcpomatic_context->history.event();
284 DcpomaticContext* _dcpomatic_context;
285 ScheduledMessenger<FrameProxy>* _messenger = nullptr;
286 bool _launched = false;
287 bool _launch_failed = false;