summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-03-08 00:12:29 +0100
committerCarl Hetherington <cth@carlh.net>2025-03-08 00:12:29 +0100
commit182a0b48fb456355a139c21533a7d6ca0bbe42eb (patch)
tree19722a22f8667a1fe029af2ec34efbbddea3c851 /src/lib
parentd4bb9572785a19c394cff3242e2b9ebab7c5d31c (diff)
parent42b92cbe4518a170b217ab54a26b51f56246a50f (diff)
Merge branch '2981-headless-grok'
This makes it possible to configure Grok for encoding from the command line.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/config.cc6
-rw-r--r--src/lib/config.h4
-rw-r--r--src/lib/encode_cli.cc105
-rw-r--r--src/lib/grok/context.h4
-rw-r--r--src/lib/grok/util.cc53
-rw-r--r--src/lib/grok/util.h27
-rw-r--r--src/lib/grok_j2k_encoder_thread.cc2
-rw-r--r--src/lib/j2k_encoder.cc10
-rw-r--r--src/lib/util.cc4
-rw-r--r--src/lib/wscript2
10 files changed, 194 insertions, 23 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index 3227ea433..49c64e5b6 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -238,7 +238,7 @@ Config::set_defaults()
set_cover_sheet_to_default();
#ifdef DCPOMATIC_GROK
- _grok = boost::none;
+ _grok = {};
#endif
_main_divider_sash_position = {};
@@ -1151,9 +1151,7 @@ Config::write_config() const
cxml::add_text_child(root, "LayoutForShortScreen", _layout_for_short_screen ? "1" : "0");
#ifdef DCPOMATIC_GROK
- if (_grok) {
- _grok->as_xml(cxml::add_child(root, "Grok"));
- }
+ _grok.as_xml(cxml::add_child(root, "Grok"));
#endif
_export.write(cxml::add_child(root, "Export"));
diff --git a/src/lib/config.h b/src/lib/config.h
index d9a95ebfd..b2a979ffa 100644
--- a/src/lib/config.h
+++ b/src/lib/config.h
@@ -666,7 +666,7 @@ public:
std::string licence;
};
- boost::optional<Grok> grok() const {
+ Grok grok() const {
return _grok;
}
#endif
@@ -1495,7 +1495,7 @@ private:
bool _layout_for_short_screen;
#ifdef DCPOMATIC_GROK
- boost::optional<Grok> _grok;
+ Grok _grok;
#endif
ExportConfig _export;
diff --git a/src/lib/encode_cli.cc b/src/lib/encode_cli.cc
index a682fa4e3..fa304b6e0 100644
--- a/src/lib/encode_cli.cc
+++ b/src/lib/encode_cli.cc
@@ -30,6 +30,7 @@
#include "filter.h"
#ifdef DCPOMATIC_GROK
#include "grok/context.h"
+#include "grok/util.h"
#endif
#include "hints.h"
#include "job_manager.h"
@@ -43,6 +44,7 @@
#include "version.h"
#include "video_content.h"
#include <dcp/filesystem.h>
+#include <dcp/raw_convert.h>
#include <dcp/version.h>
#include <fmt/format.h>
#include <getopt.h>
@@ -66,7 +68,19 @@ using boost::optional;
static void
help(function <void (string)> out)
{
- out(fmt::format("Syntax: {} [OPTION] [<FILM>]\n", program_name));
+ out(fmt::format("Syntax: {} [OPTION] [COMMAND] [<PARAMETER>]\n", program_name));
+
+ out("\nCommands:\n\n");
+ out(" make-dcp <FILM> make DCP from the given film; default if no other command is specified\n");
+ out(variant::insert_dcpomatic(" list-servers display a list of encoding servers that %1 can use (until Ctrl-C)\n"));
+ out(" dump <FILM> show a summary of the film's settings\n");
+#ifdef DCPOMATIC_GROK
+ out(" config-params list the parameters that can be set with `config`\n");
+ out(" config <PARAMETER> <VALUE> set a DCP-o-matic configuration value\n");
+ out(" list-gpus list available GPUs\n");
+#endif
+
+ out("\nOptions:\n\n");
out(variant::insert_dcpomatic(" -v, --version show %1 version\n"));
out(" -h, --help show this help\n");
out(" -f, --flags show flags passed to C++ compiler on build\n");
@@ -77,15 +91,19 @@ help(function <void (string)> out)
out(" -k, --keep-going keep running even when the job is complete\n");
out(" -s, --servers <file> specify servers to use in a text file\n");
out(variant::insert_dcpomatic(" -l, --list-servers just display a list of encoding servers that %1 is configured to use; don't encode\n"));
+ out(" (deprecated - use the list-servers command instead)\n");
out(" -d, --dcp-path echo DCP's path to stdout on successful completion (implies -n)\n");
out(" -c, --config <dir> directory containing config.xml and cinemas.xml\n");
out(" --dump just dump a summary of the film's settings; don't encode\n");
+ out(" (deprecated - use the dump command instead)\n");
out(" --no-check don't check project's content files for changes before making the DCP\n");
out(" --export-format <format> export project to a file, rather than making a DCP: specify mov or mp4\n");
out(" --export-filename <filename> filename to export to with --export-format\n");
out(" --hints analyze film for hints before encoding and abort if any are found\n");
+ out("\ne.g.\n");
+ out(fmt::format("\n {} -t 4 make-dcp my_great_movie\n", program_name));
+ out(fmt::format("\n {} config grok-licence 12345ABCD\n", program_name));
out("\n");
- out("<FILM> is the film directory.\n");
}
@@ -270,6 +288,10 @@ encode_cli(int argc, char* argv[], function<void (string)> out, function<void ()
optional<string> export_format;
optional<boost::filesystem::path> export_filename;
bool hints = false;
+ string command = "make-dcp";
+
+ /* This makes it possible to call getopt several times in the same executable, for tests */
+ optind = 0;
int option_index = 0;
while (true) {
@@ -357,6 +379,77 @@ encode_cli(int argc, char* argv[], function<void (string)> out, function<void ()
}
}
+ vector<string> commands = {
+ "make-dcp",
+ "list-servers",
+#ifdef DCPOMATIC_GROK
+ "dump",
+ "config-params",
+ "config",
+ "list-gpus"
+#else
+ "dump"
+#endif
+ };
+
+ if (optind < argc - 1) {
+ /* Command with a film specified afterwards */
+ command = argv[optind++];
+ } else if (optind < argc) {
+ /* Look for a valid command, hoping that it's not the name of a film */
+ if (std::find(commands.begin(), commands.end(), argv[optind]) != commands.end()) {
+ command = argv[optind++];
+ }
+ }
+
+
+#ifdef DCPOMATIC_GROK
+ if (command == "config-params") {
+ out("Configurable parameters:\n\n");
+ out(" grok-licence licence string for using the Grok JPEG2000 encoder\n");
+ out(" grok-enable 1 to enable the Grok encoder, 0 to disable it\n");
+ out(" grok-binary-location directory containing Grok binaries\n");
+ out(" grok-gpu-index index of GPU to use (from 0, see list-gpus)\n");
+ return {};
+ }
+
+ if (command == "config") {
+ if (optind < argc - 1) {
+ string const parameter = argv[optind++];
+ string const value = argv[optind++];
+ auto grok = Config::instance()->grok();
+ if (parameter == "grok-licence") {
+ grok.licence = value;
+ } else if (parameter == "grok-enable") {
+ if (value == "1") {
+ grok.enable = true;
+ } else if (value == "0") {
+ grok.enable = false;
+ } else {
+ return fmt::format("Invalid value {} for grok-enable (use 1 to enable, 0 to disable)", value);
+ }
+ } else if (parameter == "grok-binary-location") {
+ grok.binary_location = value;
+ } else if (parameter == "grok-gpu-index") {
+ grok.selected = dcp::raw_convert<int>(value);
+ } else {
+ return fmt::format("Unrecognised configuration parameter `{}'", parameter);
+ }
+ Config::instance()->set_grok(grok);
+ Config::instance()->write();
+ } else {
+ return fmt::format("Missing configuration parameter: use {} config <parameter> <value>", program_name);
+ }
+ return {};
+ } else if (command == "list-gpus") {
+ int N = 0;
+ for (auto gpu: get_gpu_names()) {
+ out(fmt::format("{}: {}\n", N++, gpu));
+ }
+ return {};
+ }
+#endif
+
if (config) {
State::override_path = *config;
}
@@ -376,7 +469,7 @@ encode_cli(int argc, char* argv[], function<void (string)> out, function<void ()
Config::instance()->set_servers(servers);
}
- if (list_servers_) {
+ if (command == "list-servers" || list_servers_) {
list_servers(out);
return {};
}
@@ -420,7 +513,7 @@ encode_cli(int argc, char* argv[], function<void (string)> out, function<void ()
return fmt::format("{}: error reading film `{}' ({})\n", program_name, film_dir.string(), e.what());
}
- if (dump) {
+ if (command == "dump" || dump) {
print_dump(out, film);
return {};
}
@@ -489,9 +582,9 @@ encode_cli(int argc, char* argv[], function<void (string)> out, function<void ()
if (progress) {
if (export_format) {
- out(fmt::format("\nExporting {}\n", film->name()));
+ out(fmt::format("Exporting {}\n", film->name()));
} else {
- out(fmt::format("\nMaking DCP for {}\n", film->name()));
+ out(fmt::format("Making DCP for {}\n", film->name()));
}
}
diff --git a/src/lib/grok/context.h b/src/lib/grok/context.h
index 602c8b13f..b31867cf6 100644
--- a/src/lib/grok/context.h
+++ b/src/lib/grok/context.h
@@ -99,7 +99,7 @@ public:
explicit GrokContext(DcpomaticContext* dcpomatic_context)
: _dcpomatic_context(dcpomatic_context)
{
- auto grok = Config::instance()->grok().get_value_or({});
+ auto grok = Config::instance()->grok();
if (!grok.enable) {
return;
}
@@ -216,7 +216,7 @@ public:
auto s = dcpv.get_size();
_dcpomatic_context->set_dimensions(s.width, s.height);
- auto grok = Config::instance()->grok().get_value_or({});
+ auto grok = Config::instance()->grok();
if (!_messenger->launch_grok(
_dcpomatic_context->location,
_dcpomatic_context->width,
diff --git a/src/lib/grok/util.cc b/src/lib/grok/util.cc
new file mode 100644
index 000000000..3cbc55678
--- /dev/null
+++ b/src/lib/grok/util.cc
@@ -0,0 +1,53 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "util.h"
+#include "../config.h"
+#include <boost/process.hpp>
+#include <future>
+
+
+using std::string;
+using std::vector;
+
+
+vector<string>
+get_gpu_names()
+{
+ namespace bp = boost::process;
+
+ auto binary = Config::instance()->grok().binary_location / "gpu_lister";
+
+ bp::ipstream stream;
+ bp::child child(binary, bp::std_out > stream);
+
+ string line;
+ vector<string> gpu_names;
+ while (child.running() && std::getline(stream, line) && !line.empty()) {
+ gpu_names.push_back(line);
+ }
+
+ child.wait();
+
+ return gpu_names;
+}
+
+
diff --git a/src/lib/grok/util.h b/src/lib/grok/util.h
new file mode 100644
index 000000000..a78ecabca
--- /dev/null
+++ b/src/lib/grok/util.h
@@ -0,0 +1,27 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include <boost/filesystem.hpp>
+#include <string>
+#include <vector>
+
+
+extern std::vector<std::string> get_gpu_names();
diff --git a/src/lib/grok_j2k_encoder_thread.cc b/src/lib/grok_j2k_encoder_thread.cc
index e6c256f11..d6825113c 100644
--- a/src/lib/grok_j2k_encoder_thread.cc
+++ b/src/lib/grok_j2k_encoder_thread.cc
@@ -62,7 +62,7 @@ try
LOG_TIMING("encoder-pop thread=%1 frame=%2 eyes=%3", thread_id(), frame.index(), static_cast<int>(frame.eyes()));
- auto grok = Config::instance()->grok().get_value_or({});
+ auto grok = Config::instance()->grok();
if (_context->launch(frame, grok.selected) && _context->scheduleCompress(frame)) {
frame_guard.cancel();
diff --git a/src/lib/j2k_encoder.cc b/src/lib/j2k_encoder.cc
index 309fce0b3..50452fbad 100644
--- a/src/lib/j2k_encoder.cc
+++ b/src/lib/j2k_encoder.cc
@@ -98,7 +98,7 @@ J2KEncoder::J2KEncoder(shared_ptr<const Film> film, Writer& writer)
#endif
{
#ifdef DCPOMATIC_GROK
- auto grok = Config::instance()->grok().get_value_or({});
+ auto grok = Config::instance()->grok();
_dcpomatic_context = new grk_plugin::DcpomaticContext(film, writer, _history, grok.binary_location);
if (grok.enable) {
_context = new grk_plugin::GrokContext(_dcpomatic_context);
@@ -135,7 +135,7 @@ J2KEncoder::servers_list_changed()
{
auto config = Config::instance();
#ifdef DCPOMATIC_GROK
- auto const grok_enable = config->grok().get_value_or({}).enable;
+ auto const grok_enable = config->grok().enable;
#else
auto const grok_enable = false;
#endif
@@ -162,7 +162,7 @@ void
J2KEncoder::pause()
{
#ifdef DCPOMATIC_GROK
- if (!Config::instance()->grok().get_value_or({}).enable) {
+ if (!Config::instance()->grok().enable) {
return;
}
return;
@@ -183,7 +183,7 @@ J2KEncoder::pause()
void J2KEncoder::resume()
{
#ifdef DCPOMATIC_GROK
- if (!Config::instance()->grok().get_value_or({}).enable) {
+ if (!Config::instance()->grok().enable) {
return;
}
@@ -226,7 +226,7 @@ J2KEncoder::end()
*/
for (auto & i: _queue) {
#ifdef DCPOMATIC_GROK
- if (Config::instance()->grok().get_value_or({}).enable) {
+ if (Config::instance()->grok().enable) {
if (!_context->scheduleCompress(i)){
LOG_GENERAL (N_("[%1] J2KEncoder thread pushes frame %2 back onto queue after failure"), thread_id(), i.index());
// handle error
diff --git a/src/lib/util.cc b/src/lib/util.cc
index df15e1abb..1e2f7d61f 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -1146,7 +1146,7 @@ setup_grok_library_path()
}
}
auto const grok = Config::instance()->grok();
- if (!grok || grok->binary_location.empty()) {
+ if (grok.binary_location.empty()) {
setenv("LD_LIRARY_PATH", old_path.c_str(), 1);
return;
}
@@ -1155,7 +1155,7 @@ setup_grok_library_path()
if (!new_path.empty()) {
new_path += ":";
}
- new_path += grok->binary_location.string();
+ new_path += grok.binary_location.string();
setenv("LD_LIBRARY_PATH", new_path.c_str(), 1);
}
diff --git a/src/lib/wscript b/src/lib/wscript
index 79f0d563e..cae78fc4a 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -267,7 +267,7 @@ def build(bld):
obj.uselib += ' POLKIT'
if bld.env.ENABLE_GROK:
- obj.source += ' grok_j2k_encoder_thread.cc'
+ obj.source += ' grok_j2k_encoder_thread.cc grok/util.cc'
if bld.env.TARGET_WINDOWS_64 or bld.env.TARGET_WINDOWS_32:
obj.uselib += ' WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE SETUPAPI OLE32 UUID'