72e62c4fad592d3afd938520a3e099f44d8fa69b
[dcpomatic.git] / src / lib / grok / context.h
1 /*
2     Copyright (C) 2023 Grok Image Compression Inc.
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 #pragma once
22
23 #include "../config.h"
24 #include "../dcp_video.h"
25 #include "../film.h"
26 #include "../log.h"
27 #include "../dcpomatic_log.h"
28 #include "../writer.h"
29 #include "messenger.h"
30 #include <dcp/array_data.h>
31
32
33 static std::mutex launchMutex;
34
35 namespace grk_plugin
36 {
37
38 struct GrokLogger : public MessengerLogger {
39         explicit GrokLogger(const std::string &preamble) : MessengerLogger(preamble)
40         {}
41         virtual ~GrokLogger() = default;
42         void info(const char* fmt, ...) override{
43                 va_list arg;
44                 va_start(arg, fmt);
45                 dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_GENERAL);
46                 va_end(arg);
47         }
48         void warn(const char* fmt, ...) override{
49                 va_list arg;
50                 va_start(arg, fmt);
51                 dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_WARNING);
52                 va_end(arg);
53         }
54         void error(const char* fmt, ...) override{
55                 va_list arg;
56                 va_start(arg, fmt);
57                 dcpomatic_log->log(preamble_ + log_message(fmt, arg),LogEntry::TYPE_ERROR);
58                 va_end(arg);
59         }
60 };
61
62 struct FrameProxy {
63         FrameProxy(int index, Eyes eyes, DCPVideo dcpv) : index_(index), eyes_(eyes), vf(dcpv)
64         {}
65         int index() const {
66                 return index_;
67         }
68         Eyes eyes(void) const {
69                 return eyes_;
70         }
71         int index_;
72         Eyes eyes_;
73         DCPVideo vf;
74 };
75
76 struct DcpomaticContext {
77         DcpomaticContext(std::shared_ptr<const Film> film, Writer& writer,
78                                                 EventHistory &history, const std::string &location) :
79                                                                         film_(film), writer_(writer),
80                                                                         history_(history), location_(location),
81                                                                         width_(0), height_(0)
82         {}
83         void setDimensions(uint32_t w, uint32_t h) {
84                 width_ = w;
85                 height_ = h;
86         }
87         std::shared_ptr<const Film> film_;
88         Writer& writer_;
89         EventHistory &history_;
90         std::string location_;
91         uint32_t width_;
92         uint32_t height_;
93 };
94
95 class GrokContext {
96 public:
97         explicit GrokContext(const DcpomaticContext &dcpomaticContext) :
98                                                                 dcpomaticContext_(dcpomaticContext),
99                                                                 messenger_(nullptr),
100                                                                 launched_(false)
101         {
102                 if (Config::instance()->enable_gpu ())  {
103                     boost::filesystem::path folder(dcpomaticContext_.location_);
104                     boost::filesystem::path binaryPath = folder / "grk_compress";
105                     if (!boost::filesystem::exists(binaryPath)) {
106                         getMessengerLogger()->error("Invalid binary location %s",
107                                         dcpomaticContext_.location_.c_str());
108                                 return;
109                     }
110                         auto proc = [this](const std::string& str) {
111                                 try {
112                                         Msg msg(str);
113                                         auto tag = msg.next();
114                                         if(tag == GRK_MSGR_BATCH_SUBMIT_COMPRESSED)
115                                         {
116                                                 auto clientFrameId = msg.nextUint();
117                                                 auto compressedFrameId = msg.nextUint();
118                                                 (void)compressedFrameId;
119                                                 auto compressedFrameLength = msg.nextUint();
120                                                 auto  processor =
121                                                                 [this](FrameProxy srcFrame, uint8_t* compressed, uint32_t compressedFrameLength)
122                                                 {
123                                                         auto compressed_data = std::make_shared<dcp::ArrayData>(compressed, compressedFrameLength);
124                                                         dcpomaticContext_.writer_.write(compressed_data, srcFrame.index(), srcFrame.eyes());
125                                                         frame_done ();
126                                                 };
127                                                 int const minimum_size = 16384;
128                                                 bool needsRecompression = compressedFrameLength < minimum_size;
129                                                 messenger_->processCompressed(str, processor, needsRecompression);
130                                                 if (needsRecompression) {
131                                                         auto fp = messenger_->retrieve(clientFrameId);
132                                                         if (!fp) {
133                                                                 return;
134                                                         }
135
136                                                         auto encoded = std::make_shared<dcp::ArrayData>(fp->vf.encode_locally());
137                                                         dcpomaticContext_.writer_.write(encoded, fp->vf.index(), fp->vf.eyes());
138                                                         frame_done ();
139                                                 }
140                                         }
141                                 } catch (std::exception &ex){
142                                         getMessengerLogger()->error("%s",ex.what());
143                                 }
144                         };
145                         auto clientInit =
146                                 MessengerInit(clientToGrokMessageBuf, clientSentSynch, grokReceiveReadySynch,
147                                                           grokToClientMessageBuf, grokSentSynch, clientReceiveReadySynch, proc,
148                                                           std::thread::hardware_concurrency());
149                         messenger_ = new ScheduledMessenger<FrameProxy>(clientInit);
150                 }
151         }
152         ~GrokContext(void) {
153                 shutdown();
154         }
155         bool launch(DCPVideo dcpv, int device){
156                 if (!messenger_ )
157                         return false;
158                 if (launched_)
159                         return true;
160                 std::unique_lock<std::mutex> lk_global(launchMutex);
161                 if (!messenger_)
162                         return false;
163                 if (launched_)
164                         return true;
165                 if (MessengerInit::firstLaunch(true)) {
166                         auto s = dcpv.get_size();
167                         dcpomaticContext_.setDimensions(s.width, s.height);
168                         auto config = Config::instance();
169                         messenger_->launchGrok(dcpomaticContext_.location_,
170                                         dcpomaticContext_.width_,dcpomaticContext_.width_,
171                                         dcpomaticContext_.height_,
172                                         3, 12, device,
173                                         dcpomaticContext_.film_->resolution() == Resolution::FOUR_K,
174                                         dcpomaticContext_.film_->video_frame_rate(),
175                                         dcpomaticContext_.film_->j2k_bandwidth(),
176                                         config->gpu_license_server(),
177                                         config->gpu_license_port(),
178                                         config->gpu_license());
179                 }
180                 launched_ =  messenger_->waitForClientInit();
181
182                 return launched_;
183         }
184         bool scheduleCompress(DCPVideo const& vf){
185                 if (!messenger_)
186                         return false;
187
188                 auto fp = FrameProxy(vf.index(), vf.eyes(), vf);
189                 auto cvt = [this, &fp](BufferSrc src){
190                         // xyz conversion
191                         fp.vf.convert_to_xyz((uint16_t*)src.framePtr_);
192                 };
193                 return messenger_->scheduleCompress(fp, cvt);
194         }
195         void shutdown(void){
196                 if (!messenger_)
197                         return;
198
199                 std::unique_lock<std::mutex> lk_global(launchMutex);
200                 if (!messenger_)
201                         return;
202                 if (launched_)
203                         messenger_->shutdown();
204                 delete messenger_;
205                 messenger_ = nullptr;
206         }
207         void frame_done () {
208                 dcpomaticContext_.history_.event ();
209         }
210 private:
211         DcpomaticContext dcpomaticContext_;
212         ScheduledMessenger<FrameProxy> *messenger_;
213         bool launched_;
214 };
215
216 }
217