From: Carl Hetherington Date: Thu, 27 Feb 2020 22:50:10 +0000 (+0100) Subject: Move swaroop playlist editor stuff into its own swaroop_ X-Git-Tag: v2.15.45~8 X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=adeab682b76138d7a05bab3943af23cd57e2bc57 Move swaroop playlist editor stuff into its own swaroop_ files. --- diff --git a/src/lib/spl.cc b/src/lib/spl.cc deleted file mode 100644 index e8e86f89c..000000000 --- a/src/lib/spl.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* - Copyright (C) 2018-2019 Carl Hetherington - - 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 . - -*/ - -#include "spl.h" -#include "content_store.h" -#include -#include -#include -#include -#include - -using std::cout; -using std::string; -using boost::shared_ptr; -using dcp::raw_convert; - -void -SPL::read (boost::filesystem::path path, ContentStore* store) -{ - _path = path; - - _spl.clear (); - _missing = false; - cxml::Document doc ("SPL"); - doc.read_file (path); - _id = doc.string_child("Id"); - BOOST_FOREACH (cxml::ConstNodePtr i, doc.node_children("Entry")) { - shared_ptr c = store->get(i->string_child("Digest")); - if (c) { - add (SPLEntry(c, i)); - } else { - _missing = true; - } - } - - _allowed_shows = doc.optional_number_child("AllowedShows"); -} - -void -SPL::write (boost::filesystem::path path) const -{ - _path = path; - - xmlpp::Document doc; - xmlpp::Element* root = doc.create_root_node ("SPL"); - root->add_child("Id")->add_child_text (_id); - BOOST_FOREACH (SPLEntry i, _spl) { - i.as_xml (root->add_child("Entry")); - } - if (_allowed_shows) { - root->add_child("AllowedShows")->add_child_text(raw_convert(*_allowed_shows)); - } - doc.write_to_file_formatted (path.string()); -} diff --git a/src/lib/spl.h b/src/lib/spl.h deleted file mode 100644 index 18892993f..000000000 --- a/src/lib/spl.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - Copyright (C) 2018 Carl Hetherington - - 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 . - -*/ - -#ifndef DCPOMATIC_SPL_H -#define DCPOMATIC_SPL_H - -#include "spl_entry.h" -#include - -class ContentStore; - -class SPL -{ -public: - SPL () - : _id (dcp::make_uuid()) - , _missing (false) - {} - - void add (SPLEntry e) { - _spl.push_back (e); - } - - void remove (std::size_t index) { - _spl.erase (_spl.begin() + index); - } - - std::vector const & get () const { - return _spl; - } - - SPLEntry & operator[] (std::size_t index) { - return _spl[index]; - } - - SPLEntry const & operator[] (std::size_t index) const { - return _spl[index]; - } - - void read (boost::filesystem::path path, ContentStore* store); - void write (boost::filesystem::path path) const; - - std::string id () const { - return _id; - } - - boost::optional path () const { - return _path; - } - - std::string name () const { - if (!_path) { - return ""; - } - return _path->filename().string(); - } - - bool missing () const { - return _missing; - } - - boost::optional allowed_shows () const { - return _allowed_shows; - } - - bool have_allowed_shows () const { - return !_allowed_shows || *_allowed_shows > 0; - } - - void set_allowed_shows (int s) { - _allowed_shows = s; - } - - void unset_allowed_shows () { - _allowed_shows = boost::optional(); - } - - void decrement_allowed_shows () { - if (_allowed_shows) { - (*_allowed_shows)--; - } - - } - -private: - std::string _id; - mutable boost::optional _path; - std::vector _spl; - /** true if any content was missing when read() was last called on this SPL */ - bool _missing; - /** number of times left that the player will allow this playlist to be played (unset means infinite shows) */ - boost::optional _allowed_shows; -}; - -#endif diff --git a/src/lib/spl_entry.cc b/src/lib/spl_entry.cc deleted file mode 100644 index a2f36317e..000000000 --- a/src/lib/spl_entry.cc +++ /dev/null @@ -1,72 +0,0 @@ -/* - Copyright (C) 2018 Carl Hetherington - - 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 . - -*/ - -#include "spl_entry.h" -#include "dcp_content.h" -#include "dcpomatic_assert.h" -#include - -using boost::shared_ptr; -using boost::dynamic_pointer_cast; - -SPLEntry::SPLEntry (shared_ptr content) - : skippable (false) - , disable_timeline (false) - , stop_after_play (false) -{ - construct (content); -} - -SPLEntry::SPLEntry (shared_ptr content, cxml::ConstNodePtr node) - : skippable (node->bool_child("Skippable")) - , disable_timeline (node->bool_child("DisableTimeline")) - , stop_after_play (node->bool_child("StopAfterPlay")) -{ - construct (content); -} - -void -SPLEntry::construct (shared_ptr c) -{ - content = c; - shared_ptr dcp = dynamic_pointer_cast (content); - digest = content->digest (); - if (dcp) { - name = dcp->name (); - DCPOMATIC_ASSERT (dcp->cpl()); - id = *dcp->cpl(); - kind = dcp->content_kind().get_value_or(dcp::FEATURE); - type = DCP; - encrypted = dcp->encrypted (); - } else { - name = content->path(0).filename().string(); - type = ECINEMA; - kind = dcp::FEATURE; - } -} - -void -SPLEntry::as_xml (xmlpp::Element* e) -{ - e->add_child("Digest")->add_child_text(digest); - e->add_child("Skippable")->add_child_text(skippable ? "1" : "0"); - e->add_child("DisableTimeline")->add_child_text(disable_timeline ? "1" : "0"); - e->add_child("StopAfterPlay")->add_child_text(stop_after_play ? "1" : "0"); -} diff --git a/src/lib/spl_entry.h b/src/lib/spl_entry.h deleted file mode 100644 index d939ec66a..000000000 --- a/src/lib/spl_entry.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright (C) 2018 Carl Hetherington - - 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 . - -*/ - -#ifndef DCPOMATIC_SPL_ENTRY_H -#define DCPOMATIC_SPL_ENTRY_H - -#include -#include -#include - -namespace xmlpp { - class Element; -} - -class Content; - -class SPLEntry -{ -public: - SPLEntry (boost::shared_ptr content); - SPLEntry (boost::shared_ptr content, cxml::ConstNodePtr node); - - void as_xml (xmlpp::Element* e); - - boost::shared_ptr content; - std::string name; - /** Digest of this content */ - std::string digest; - /** CPL ID or something else for MP4 (?) */ - std::string id; - dcp::ContentKind kind; - enum Type { - DCP, - ECINEMA - }; - Type type; - bool encrypted; - bool skippable; - bool disable_timeline; - bool stop_after_play; - -private: - void construct (boost::shared_ptr content); -}; - -#endif diff --git a/src/lib/swaroop_spl.cc b/src/lib/swaroop_spl.cc new file mode 100644 index 000000000..02ed966a4 --- /dev/null +++ b/src/lib/swaroop_spl.cc @@ -0,0 +1,71 @@ +/* + Copyright (C) 2018-2019 Carl Hetherington + + 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 . + +*/ + +#include "swaroop_spl.h" +#include "content_store.h" +#include +#include +#include +#include +#include + +using std::cout; +using std::string; +using boost::shared_ptr; +using dcp::raw_convert; + +void +SPL::read (boost::filesystem::path path, ContentStore* store) +{ + _path = path; + + _spl.clear (); + _missing = false; + cxml::Document doc ("SPL"); + doc.read_file (path); + _id = doc.string_child("Id"); + BOOST_FOREACH (cxml::ConstNodePtr i, doc.node_children("Entry")) { + shared_ptr c = store->get(i->string_child("Digest")); + if (c) { + add (SPLEntry(c, i)); + } else { + _missing = true; + } + } + + _allowed_shows = doc.optional_number_child("AllowedShows"); +} + +void +SPL::write (boost::filesystem::path path) const +{ + _path = path; + + xmlpp::Document doc; + xmlpp::Element* root = doc.create_root_node ("SPL"); + root->add_child("Id")->add_child_text (_id); + BOOST_FOREACH (SPLEntry i, _spl) { + i.as_xml (root->add_child("Entry")); + } + if (_allowed_shows) { + root->add_child("AllowedShows")->add_child_text(raw_convert(*_allowed_shows)); + } + doc.write_to_file_formatted (path.string()); +} diff --git a/src/lib/swaroop_spl.h b/src/lib/swaroop_spl.h new file mode 100644 index 000000000..308f5286d --- /dev/null +++ b/src/lib/swaroop_spl.h @@ -0,0 +1,112 @@ +/* + Copyright (C) 2018 Carl Hetherington + + 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 . + +*/ + +#ifndef DCPOMATIC_SPL_H +#define DCPOMATIC_SPL_H + +#include "swaroop_spl_entry.h" +#include + +class ContentStore; + +class SPL +{ +public: + SPL () + : _id (dcp::make_uuid()) + , _missing (false) + {} + + void add (SPLEntry e) { + _spl.push_back (e); + } + + void remove (std::size_t index) { + _spl.erase (_spl.begin() + index); + } + + std::vector const & get () const { + return _spl; + } + + SPLEntry & operator[] (std::size_t index) { + return _spl[index]; + } + + SPLEntry const & operator[] (std::size_t index) const { + return _spl[index]; + } + + void read (boost::filesystem::path path, ContentStore* store); + void write (boost::filesystem::path path) const; + + std::string id () const { + return _id; + } + + boost::optional path () const { + return _path; + } + + std::string name () const { + if (!_path) { + return ""; + } + return _path->filename().string(); + } + + bool missing () const { + return _missing; + } + + boost::optional allowed_shows () const { + return _allowed_shows; + } + + bool have_allowed_shows () const { + return !_allowed_shows || *_allowed_shows > 0; + } + + void set_allowed_shows (int s) { + _allowed_shows = s; + } + + void unset_allowed_shows () { + _allowed_shows = boost::optional(); + } + + void decrement_allowed_shows () { + if (_allowed_shows) { + (*_allowed_shows)--; + } + + } + +private: + std::string _id; + mutable boost::optional _path; + std::vector _spl; + /** true if any content was missing when read() was last called on this SPL */ + bool _missing; + /** number of times left that the player will allow this playlist to be played (unset means infinite shows) */ + boost::optional _allowed_shows; +}; + +#endif diff --git a/src/lib/swaroop_spl_entry.cc b/src/lib/swaroop_spl_entry.cc new file mode 100644 index 000000000..ed5a469ac --- /dev/null +++ b/src/lib/swaroop_spl_entry.cc @@ -0,0 +1,72 @@ +/* + Copyright (C) 2018 Carl Hetherington + + 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 . + +*/ + +#include "swaroop_spl_entry.h" +#include "dcp_content.h" +#include "dcpomatic_assert.h" +#include + +using boost::shared_ptr; +using boost::dynamic_pointer_cast; + +SPLEntry::SPLEntry (shared_ptr content) + : skippable (false) + , disable_timeline (false) + , stop_after_play (false) +{ + construct (content); +} + +SPLEntry::SPLEntry (shared_ptr content, cxml::ConstNodePtr node) + : skippable (node->bool_child("Skippable")) + , disable_timeline (node->bool_child("DisableTimeline")) + , stop_after_play (node->bool_child("StopAfterPlay")) +{ + construct (content); +} + +void +SPLEntry::construct (shared_ptr c) +{ + content = c; + shared_ptr dcp = dynamic_pointer_cast (content); + digest = content->digest (); + if (dcp) { + name = dcp->name (); + DCPOMATIC_ASSERT (dcp->cpl()); + id = *dcp->cpl(); + kind = dcp->content_kind().get_value_or(dcp::FEATURE); + type = DCP; + encrypted = dcp->encrypted (); + } else { + name = content->path(0).filename().string(); + type = ECINEMA; + kind = dcp::FEATURE; + } +} + +void +SPLEntry::as_xml (xmlpp::Element* e) +{ + e->add_child("Digest")->add_child_text(digest); + e->add_child("Skippable")->add_child_text(skippable ? "1" : "0"); + e->add_child("DisableTimeline")->add_child_text(disable_timeline ? "1" : "0"); + e->add_child("StopAfterPlay")->add_child_text(stop_after_play ? "1" : "0"); +} diff --git a/src/lib/swaroop_spl_entry.h b/src/lib/swaroop_spl_entry.h new file mode 100644 index 000000000..75e0b5223 --- /dev/null +++ b/src/lib/swaroop_spl_entry.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2018 Carl Hetherington + + 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 . + +*/ + +#ifndef DCPOMATIC_SWAROOP_SPL_ENTRY_H +#define DCPOMATIC_SWAROOP_SPL_ENTRY_H + +#include +#include +#include + +namespace xmlpp { + class Element; +} + +class Content; + +class SPLEntry +{ +public: + SPLEntry (boost::shared_ptr content); + SPLEntry (boost::shared_ptr content, cxml::ConstNodePtr node); + + void as_xml (xmlpp::Element* e); + + boost::shared_ptr content; + std::string name; + /** Digest of this content */ + std::string digest; + /** CPL ID or something else for MP4 (?) */ + std::string id; + dcp::ContentKind kind; + enum Type { + DCP, + ECINEMA + }; + Type type; + bool encrypted; + bool skippable; + bool disable_timeline; + bool stop_after_play; + +private: + void construct (boost::shared_ptr content); +}; + +#endif diff --git a/src/lib/wscript b/src/lib/wscript index 31071bf7a..6ef41e91e 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -150,8 +150,6 @@ sources = """ server.cc shuffler.cc state.cc - spl.cc - spl_entry.cc string_log_entry.cc string_text_file.cc string_text_file_content.cc @@ -201,6 +199,9 @@ def build(bld): obj.source = sources + ' version.cc' + if bld.env.VARIANT == 'swaroop-theater' or bld.env.VARIANT == 'swaroop-studio': + obj.source += ' swaroop_spl.cc swaroop_spl_entry.cc' + if bld.env.TARGET_WINDOWS: obj.uselib += ' WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE' if bld.env.STATIC_DCPOMATIC: diff --git a/src/tools/dcpomatic_ecinema.cc b/src/tools/dcpomatic_ecinema.cc deleted file mode 100644 index 152194c48..000000000 --- a/src/tools/dcpomatic_ecinema.cc +++ /dev/null @@ -1,312 +0,0 @@ -/* - Copyright (C) 2018-2019 Carl Hetherington - - 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 . - -*/ - -#include "lib/version.h" -#include "lib/decrypted_ecinema_kdm.h" -#include "lib/config.h" -#include "lib/util.h" -#include "lib/film.h" -#include "lib/dcp_content.h" -#include "lib/job_manager.h" -#include "lib/cross.h" -#include "lib/transcode_job.h" -#include "lib/ffmpeg_encoder.h" -#include "lib/signal_manager.h" -#include "lib/video_content.h" -#include "lib/ratio.h" -#include -extern "C" { -#include -#include -} -#include -#include -#include -#include -#include -#include -#include -#include - -using std::string; -using std::cerr; -using std::cout; -using std::ofstream; -using boost::optional; -using boost::shared_ptr; - -static void convert_dcp ( - boost::filesystem::path input, - boost::filesystem::path output_file, - boost::optional kdm, - int crf - ); -static void convert_ffmpeg (boost::filesystem::path input, boost::filesystem::path output_file, string format); -static void write_kdm (string id, boost::filesystem::path name, dcp::Key key); - -static void -help (string n) -{ - cerr << "Syntax: " << n << " [OPTION] |\n" - << " -v, --version show DCP-o-matic version\n" - << " -h, --help show this help\n" - << " -o, --output output directory\n" - << " -f, --format output format (mov or mp4; defaults to mov)\n" - << " -k, --kdm DCP KDM filename (if required)\n" - << " -c, --crf quality (CRF) when transcoding from DCP (0 is best, 51 is worst, defaults to 23)\n" - << "\n" - << " is an unencrypted .mp4 file; is a DCP directory.\n"; -} - -int -main (int argc, char* argv[]) -{ - optional output; - optional format; - optional kdm; - int crf = 23; - - int option_index = 0; - while (true) { - static struct option long_options[] = { - { "version", no_argument, 0, 'v' }, - { "help", no_argument, 0, 'h' }, - { "output", required_argument, 0, 'o' }, - { "format", required_argument, 0, 'f' }, - { "kdm", required_argument, 0, 'k' }, - { "crf", required_argument, 0, 'c' }, - }; - - int c = getopt_long (argc, argv, "vho:f:k:c:", long_options, &option_index); - - if (c == -1) { - break; - } - - switch (c) { - case 'v': - cout << "dcpomatic version " << dcpomatic_version << " " << dcpomatic_git_commit << "\n"; - exit (EXIT_SUCCESS); - case 'h': - help (argv[0]); - exit (EXIT_SUCCESS); - case 'o': - output = optarg; - break; - case 'f': - format = optarg; - break; - case 'k': - kdm = optarg; - break; - case 'c': - crf = atoi(optarg); - break; - } - } - - if (optind >= argc) { - help (argv[0]); - exit (EXIT_FAILURE); - } - - if (!output) { - cerr << "You must specify --output-media or -o\n"; - exit (EXIT_FAILURE); - } - - if (!format) { - format = "mov"; - } - - if (*format != "mov" && *format != "mp4") { - cerr << "Invalid format specified: must be mov or mp4\n"; - exit (EXIT_FAILURE); - } - - dcpomatic_setup_path_encoding (); - dcpomatic_setup (); - signal_manager = new SignalManager (); - - boost::filesystem::path input = argv[optind]; - boost::filesystem::path output_file; - if (boost::filesystem::is_directory(input)) { - output_file = *output / (input.parent_path().filename().string() + ".ecinema"); - } else { - output_file = *output / (input.filename().string() + ".ecinema"); - } - - if (!boost::filesystem::is_directory(*output)) { - boost::filesystem::create_directory (*output); - } - - av_register_all (); - - if (boost::filesystem::is_directory(input)) { - /* Assume input is a DCP */ - convert_dcp (input, output_file, kdm, crf); - } else { - convert_ffmpeg (input, output_file, *format); - } -} - -static void -convert_ffmpeg (boost::filesystem::path input, boost::filesystem::path output_file, string format) -{ - AVFormatContext* input_fc = avformat_alloc_context (); - if (avformat_open_input(&input_fc, input.string().c_str(), 0, 0) < 0) { - cerr << "Could not open input file\n"; - exit (EXIT_FAILURE); - } - - if (avformat_find_stream_info (input_fc, 0) < 0) { - cerr << "Could not read stream information\n"; - exit (EXIT_FAILURE); - } - - AVFormatContext* output_fc; - avformat_alloc_output_context2 (&output_fc, av_guess_format(format.c_str(), 0, 0), 0, 0); - - for (uint32_t i = 0; i < input_fc->nb_streams; ++i) { - AVStream* is = input_fc->streams[i]; - AVStream* os = avformat_new_stream (output_fc, is->codec->codec); - if (avcodec_parameters_copy (os->codecpar, is->codecpar) < 0) { - cerr << "Could not set up output stream.\n"; - exit (EXIT_FAILURE); - } - - os->avg_frame_rate = is->avg_frame_rate; - - switch (is->codec->codec_type) { - case AVMEDIA_TYPE_VIDEO: - os->time_base = is->time_base; - os->r_frame_rate = is->r_frame_rate; - os->sample_aspect_ratio = is->sample_aspect_ratio; - os->codec->time_base = is->codec->time_base; - os->codec->framerate = is->codec->framerate; - os->codec->pix_fmt = is->codec->pix_fmt; - break; - case AVMEDIA_TYPE_AUDIO: - os->codec->sample_fmt = is->codec->sample_fmt; - os->codec->bits_per_raw_sample = is->codec->bits_per_raw_sample; - os->codec->sample_rate = is->codec->sample_rate; - os->codec->channel_layout = is->codec->channel_layout; - os->codec->channels = is->codec->channels; - if (is->codecpar->codec_id == AV_CODEC_ID_PCM_S24LE) { - /* XXX: fix incoming 24-bit files labelled lpcm, which apparently isn't allowed */ - os->codecpar->codec_tag = MKTAG('i', 'n', '2', '4'); - } - break; - default: - /* XXX */ - break; - } - } - - if (avio_open2 (&output_fc->pb, output_file.string().c_str(), AVIO_FLAG_WRITE, 0, 0) < 0) { - cerr << "Could not open output file `" << output_file.string() << "'\n"; - exit (EXIT_FAILURE); - } - - dcp::Key key (AES_CTR_KEY_SIZE); - AVDictionary* options = 0; - av_dict_set (&options, "encryption_key", key.hex().c_str(), 0); - /* XXX: is this OK? */ - av_dict_set (&options, "encryption_kid", "00000000000000000000000000000000", 0); - av_dict_set (&options, "encryption_scheme", "cenc-aes-ctr", 0); - - string id = dcp::make_uuid (); - if (av_dict_set(&output_fc->metadata, SWAROOP_ID_TAG, id.c_str(), 0) < 0) { - cerr << "Could not write ID to output.\n"; - exit (EXIT_FAILURE); - } - - if (avformat_write_header (output_fc, &options) < 0) { - cerr << "Could not write header to output.\n"; - exit (EXIT_FAILURE); - } - - AVPacket packet; - while (av_read_frame(input_fc, &packet) >= 0) { - AVStream* is = input_fc->streams[packet.stream_index]; - AVStream* os = output_fc->streams[packet.stream_index]; - packet.pts = av_rescale_q_rnd(packet.pts, is->time_base, os->time_base, (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); - packet.dts = av_rescale_q_rnd(packet.dts, is->time_base, os->time_base, (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); - packet.duration = av_rescale_q(packet.duration, is->time_base, os->time_base); - packet.pos = -1; - if (av_interleaved_write_frame (output_fc, &packet) < 0) { - cerr << "Could not write frame to output.\n"; - exit (EXIT_FAILURE); - } - } - - av_write_trailer (output_fc); - - avformat_free_context (input_fc); - avformat_free_context (output_fc); - - write_kdm (id, output_file, key); -} - -static void -write_kdm (string id, boost::filesystem::path name, dcp::Key key) -{ - DecryptedECinemaKDM decrypted_kdm (id, name.filename().string(), key, optional(), optional()); - EncryptedECinemaKDM encrypted_kdm = decrypted_kdm.encrypt (Config::instance()->decryption_chain()->leaf()); - - ofstream f(string(name.string() + ".xml").c_str()); - f << encrypted_kdm.as_xml() << "\n"; -} - -static void -convert_dcp ( - boost::filesystem::path input, boost::filesystem::path output_file, optional kdm, int crf - ) -{ - shared_ptr film (new Film(boost::optional())); - shared_ptr dcp (new DCPContent(input)); - film->examine_and_add_content (dcp); - if (kdm) { - dcp->add_kdm (dcp::EncryptedKDM(dcp::file_to_string(*kdm))); - } - - JobManager* jm = JobManager::instance (); - while (jm->work_to_do ()) { - while (signal_manager->ui_idle ()) {} - dcpomatic_sleep_seconds (1); - } - DCPOMATIC_ASSERT (!jm->errors()); - - film->set_container (Ratio::nearest_from_ratio(dcp->video->size().ratio())); - - string id = dcp::make_uuid (); - dcp::Key key (AES_CTR_KEY_SIZE); - - shared_ptr job (new TranscodeJob(film)); - job->set_encoder ( - shared_ptr( - new FFmpegEncoder(film, job, output_file, EXPORT_FORMAT_H264_PCM, false, false, crf, key, id) - ) - ); - jm->add (job); - show_jobs_on_console (true); - - write_kdm (id, output_file, key); -} diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc deleted file mode 100644 index de6ae107b..000000000 --- a/src/tools/dcpomatic_playlist.cc +++ /dev/null @@ -1,478 +0,0 @@ -/* - Copyright (C) 2018 Carl Hetherington - - 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 . - -*/ - -#include "../wx/wx_util.h" -#include "../wx/wx_signal_manager.h" -#include "../wx/content_view.h" -#include "../wx/dcpomatic_button.h" -#include "../lib/util.h" -#include "../lib/config.h" -#include "../lib/cross.h" -#include "../lib/film.h" -#include "../lib/dcp_content.h" -#include "../lib/spl_entry.h" -#include "../lib/spl.h" -#include -#include -#include -#include -#ifdef __WXOSX__ -#include -#endif - -using std::exception; -using std::cout; -using std::string; -using boost::optional; -using boost::shared_ptr; -using boost::weak_ptr; -using boost::bind; -using boost::dynamic_pointer_cast; - -class ContentDialog : public wxDialog, public ContentStore -{ -public: - ContentDialog (wxWindow* parent) - : wxDialog (parent, wxID_ANY, _("Add content"), wxDefaultPosition, wxSize(800, 640)) - , _content_view (new ContentView(this)) - { - _content_view->update (); - - wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); - SetSizer (overall_sizer); - - overall_sizer->Add (_content_view, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); - - wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL); - if (buttons) { - overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); - } - - overall_sizer->Layout (); - } - - shared_ptr selected () const - { - return _content_view->selected (); - } - - shared_ptr get (string digest) const - { - return _content_view->get (digest); - } - -private: - ContentView* _content_view; -}; - -class DOMFrame : public wxFrame -{ -public: - explicit DOMFrame (wxString const & title) - : wxFrame (0, -1, title) - , _content_dialog (new ContentDialog(this)) - { - /* Use a panel as the only child of the Frame so that we avoid - the dark-grey background on Windows. - */ - wxPanel* overall_panel = new wxPanel (this, wxID_ANY); - wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL); - - _list = new wxListCtrl ( - overall_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL - ); - - _list->AppendColumn (_("Name"), wxLIST_FORMAT_LEFT, 400); - _list->AppendColumn (_("CPL"), wxLIST_FORMAT_LEFT, 350); - _list->AppendColumn (_("Type"), wxLIST_FORMAT_CENTRE, 100); - _list->AppendColumn (_("Format"), wxLIST_FORMAT_CENTRE, 75); - _list->AppendColumn (_("Encrypted"), wxLIST_FORMAT_CENTRE, 90); - _list->AppendColumn (_("Skippable"), wxLIST_FORMAT_CENTRE, 90); - _list->AppendColumn (_("Disable timeline"), wxLIST_FORMAT_CENTRE, 125); - _list->AppendColumn (_("Stop after play"), wxLIST_FORMAT_CENTRE, 125); - - wxImageList* images = new wxImageList (16, 16); - wxIcon tick_icon; - wxIcon no_tick_icon; -#ifdef DCPOMATIX_OSX - tick_icon.LoadFile ("tick.png", wxBITMAP_TYPE_PNG_RESOURCE); - no_tick_icon.LoadFile ("no_tick.png", wxBITMAP_TYPE_PNG_RESOURCE); -#else - boost::filesystem::path tick_path = shared_path() / "tick.png"; - tick_icon.LoadFile (std_to_wx(tick_path.string())); - boost::filesystem::path no_tick_path = shared_path() / "no_tick.png"; - no_tick_icon.LoadFile (std_to_wx(no_tick_path.string())); -#endif - images->Add (tick_icon); - images->Add (no_tick_icon); - - _list->SetImageList (images, wxIMAGE_LIST_SMALL); - - h_sizer->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP); - - wxBoxSizer* button_sizer = new wxBoxSizer (wxVERTICAL); - _up = new Button (overall_panel, _("Up")); - _down = new Button (overall_panel, _("Down")); - _add = new Button (overall_panel, _("Add")); - _remove = new Button (overall_panel, _("Remove")); - _save = new Button (overall_panel, _("Save playlist")); - _load = new Button (overall_panel, _("Load playlist")); - button_sizer->Add (_up, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - button_sizer->Add (_down, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - button_sizer->Add (_add, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - button_sizer->Add (_remove, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - button_sizer->Add (_save, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - button_sizer->Add (_load, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - - h_sizer->Add (button_sizer, 0, wxALL, DCPOMATIC_SIZER_GAP); - - wxBoxSizer* v_sizer = new wxBoxSizer (wxVERTICAL); - - wxBoxSizer* allowed_shows_sizer = new wxBoxSizer (wxHORIZONTAL); - _allowed_shows_enable = new wxCheckBox (overall_panel, wxID_ANY, _("Limit number of shows with this playlist to")); - allowed_shows_sizer->Add (_allowed_shows_enable, 0, wxRIGHT, DCPOMATIC_SIZER_GAP); - _allowed_shows = new wxSpinCtrl (overall_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 65536, 100); - allowed_shows_sizer->Add (_allowed_shows); - - v_sizer->Add (allowed_shows_sizer, 0, wxALL, DCPOMATIC_SIZER_GAP); - v_sizer->Add (h_sizer); - - overall_panel->SetSizer (v_sizer); - - _list->Bind (wxEVT_LEFT_DOWN, bind(&DOMFrame::list_left_click, this, _1)); - _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&DOMFrame::selection_changed, this)); - _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&DOMFrame::selection_changed, this)); - _up->Bind (wxEVT_BUTTON, bind(&DOMFrame::up_clicked, this)); - _down->Bind (wxEVT_BUTTON, bind(&DOMFrame::down_clicked, this)); - _add->Bind (wxEVT_BUTTON, bind(&DOMFrame::add_clicked, this)); - _remove->Bind (wxEVT_BUTTON, bind(&DOMFrame::remove_clicked, this)); - _save->Bind (wxEVT_BUTTON, bind(&DOMFrame::save_clicked, this)); - _load->Bind (wxEVT_BUTTON, bind(&DOMFrame::load_clicked, this)); - _allowed_shows_enable->Bind (wxEVT_CHECKBOX, bind(&DOMFrame::allowed_shows_changed, this)); - _allowed_shows->Bind (wxEVT_SPINCTRL, bind(&DOMFrame::allowed_shows_changed, this)); - - setup_sensitivity (); - } - -private: - - void allowed_shows_changed () - { - if (_allowed_shows_enable->GetValue()) { - _playlist.set_allowed_shows (_allowed_shows->GetValue()); - } else { - _playlist.unset_allowed_shows (); - } - setup_sensitivity (); - } - - void add (SPLEntry e) - { - wxListItem item; - item.SetId (_list->GetItemCount()); - long const N = _list->InsertItem (item); - set_item (N, e); - } - - void selection_changed () - { - setup_sensitivity (); - } - - void set_item (long N, SPLEntry e) - { - _list->SetItem (N, 0, std_to_wx(e.name)); - _list->SetItem (N, 1, std_to_wx(e.id)); - _list->SetItem (N, 2, std_to_wx(dcp::content_kind_to_string(e.kind))); - _list->SetItem (N, 3, e.type == SPLEntry::DCP ? _("DCP") : _("E-cinema")); - _list->SetItem (N, 4, e.encrypted ? S_("Question|Y") : S_("Question|N")); - _list->SetItem (N, COLUMN_SKIPPABLE, wxEmptyString, e.skippable ? 0 : 1); - _list->SetItem (N, COLUMN_DISABLE_TIMELINE, wxEmptyString, e.disable_timeline ? 0 : 1); - _list->SetItem (N, COLUMN_STOP_AFTER_PLAY, wxEmptyString, e.stop_after_play ? 0 : 1); - } - - void setup_sensitivity () - { - int const num_selected = _list->GetSelectedItemCount (); - long int selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - _up->Enable (selected > 0); - _down->Enable (selected != -1 && selected < (_list->GetItemCount() - 1)); - _remove->Enable (num_selected > 0); - _allowed_shows->Enable (_allowed_shows_enable->GetValue()); - } - - void list_left_click (wxMouseEvent& ev) - { - int flags; - long item = _list->HitTest (ev.GetPosition(), flags, 0); - int x = ev.GetPosition().x; - optional column; - for (int i = 0; i < _list->GetColumnCount(); ++i) { - x -= _list->GetColumnWidth (i); - if (x < 0) { - column = i; - break; - } - } - - if (item != -1 && column) { - switch (*column) { - case COLUMN_SKIPPABLE: - _playlist[item].skippable = !_playlist[item].skippable; - break; - case COLUMN_DISABLE_TIMELINE: - _playlist[item].disable_timeline = !_playlist[item].disable_timeline; - break; - case COLUMN_STOP_AFTER_PLAY: - _playlist[item].stop_after_play = !_playlist[item].stop_after_play; - break; - default: - ev.Skip (); - } - set_item (item, _playlist[item]); - } else { - ev.Skip (); - } - } - - void add_clicked () - { - int const r = _content_dialog->ShowModal (); - if (r == wxID_OK) { - shared_ptr content = _content_dialog->selected (); - if (content) { - SPLEntry e (content); - add (e); - _playlist.add (e); - } - } - } - - void up_clicked () - { - long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (s < 1) { - return; - } - - SPLEntry tmp = _playlist[s]; - _playlist[s] = _playlist[s-1]; - _playlist[s-1] = tmp; - - set_item (s - 1, _playlist[s-1]); - set_item (s, _playlist[s]); - } - - void down_clicked () - { - long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (s > (_list->GetItemCount() - 1)) { - return; - } - - SPLEntry tmp = _playlist[s]; - _playlist[s] = _playlist[s+1]; - _playlist[s+1] = tmp; - - set_item (s + 1, _playlist[s+1]); - set_item (s, _playlist[s]); - } - - void remove_clicked () - { - long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (s == -1) { - return; - } - - _playlist.remove (s); - _list->DeleteItem (s); - } - - void save_clicked () - { - Config* c = Config::instance (); - wxString default_dir = c->player_playlist_directory() ? std_to_wx(c->player_playlist_directory()->string()) : wxString(wxEmptyString); - wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), default_dir, wxEmptyString, wxT("XML files (*.xml)|*.xml"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (d->ShowModal() == wxID_OK) { - boost::filesystem::path file = wx_to_std (d->GetPath()); - file.replace_extension (".xml"); - _playlist.write (file); - } - } - - void load_clicked () - { - Config* c = Config::instance (); - wxString default_dir = c->player_playlist_directory() ? std_to_wx(c->player_playlist_directory()->string()) : wxString(wxEmptyString); - wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), default_dir, wxEmptyString, wxT("XML files (*.xml)|*.xml")); - if (d->ShowModal() == wxID_OK) { - _list->DeleteAllItems (); - _playlist.read (wx_to_std(d->GetPath()), _content_dialog); - if (!_playlist.missing()) { - _list->DeleteAllItems (); - BOOST_FOREACH (SPLEntry i, _playlist.get()) { - add (i); - } - } else { - error_dialog (this, _("Some content in this playlist was not found.")); - } - optional allowed_shows = _playlist.allowed_shows (); - _allowed_shows_enable->SetValue (static_cast(allowed_shows)); - if (allowed_shows) { - _allowed_shows->SetValue (*allowed_shows); - } else { - _allowed_shows->SetValue (65536); - } - setup_sensitivity (); - } - } - - wxListCtrl* _list; - wxButton* _up; - wxButton* _down; - wxButton* _add; - wxButton* _remove; - wxButton* _save; - wxButton* _load; - wxCheckBox* _allowed_shows_enable; - wxSpinCtrl* _allowed_shows; - SPL _playlist; - ContentDialog* _content_dialog; - - enum { - COLUMN_SKIPPABLE = 5, - COLUMN_DISABLE_TIMELINE = 6, - COLUMN_STOP_AFTER_PLAY = 7 - }; -}; - -/** @class App - * @brief The magic App class for wxWidgets. - */ -class App : public wxApp -{ -public: - App () - : wxApp () - , _frame (0) - {} - -private: - - bool OnInit () - try - { - SetAppName (_("DCP-o-matic KDM Creator")); - - if (!wxApp::OnInit()) { - return false; - } - -#ifdef DCPOMATIC_LINUX - unsetenv ("UBUNTU_MENUPROXY"); -#endif - -#ifdef __WXOSX__ - ProcessSerialNumber serial; - GetCurrentProcess (&serial); - TransformProcessType (&serial, kProcessTransformToForegroundApplication); -#endif - - dcpomatic_setup_path_encoding (); - - /* Enable i18n; this will create a Config object - to look for a force-configured language. This Config - object will be wrong, however, because dcpomatic_setup - hasn't yet been called and there aren't any filters etc. - set up yet. - */ - dcpomatic_setup_i18n (); - - /* Set things up, including filters etc. - which will now be internationalised correctly. - */ - dcpomatic_setup (); - - /* Force the configuration to be re-loaded correctly next - time it is needed. - */ - Config::drop (); - - _frame = new DOMFrame (_("DCP-o-matic Playlist Editor")); - SetTopWindow (_frame); - _frame->Maximize (); - _frame->Show (); - - signal_manager = new wxSignalManager (this); - Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); - - return true; - } - catch (exception& e) - { - error_dialog (0, _("DCP-o-matic could not start"), std_to_wx(e.what())); - return true; - } - - /* An unhandled exception has occurred inside the main event loop */ - bool OnExceptionInMainLoop () - { - try { - throw; - } catch (FileError& e) { - error_dialog ( - 0, - wxString::Format ( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str ()) - ) - ); - } catch (exception& e) { - error_dialog ( - 0, - wxString::Format ( - _("An exception occurred: %s.\n\n") + " " + REPORT_PROBLEM, - std_to_wx (e.what ()) - ) - ); - } catch (...) { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); - } - - /* This will terminate the program */ - return false; - } - - void OnUnhandledException () - { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); - } - - void idle () - { - signal_manager->ui_idle (); - } - - DOMFrame* _frame; -}; - -IMPLEMENT_APP (App) diff --git a/src/tools/dcpomatic_uuid.cc b/src/tools/dcpomatic_uuid.cc deleted file mode 100644 index 65582ac0c..000000000 --- a/src/tools/dcpomatic_uuid.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright (C) 2018 Carl Hetherington - - 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 . - -*/ - -#include - -int main () -{ -#ifdef DCPOMATIC_LINUX - FILE* f = fopen ("/sys/class/dmi/id/product_uuid", "r"); - if (!f) { - printf ("unknown"); - return 1; - } - char buffer[256]; - int const N = fread (buffer, 1, 255, f); - buffer[N] = '\0'; - printf ("%s", buffer); - fclose (f); - return 0; -#endif - printf ("unknown"); - return 1; -} diff --git a/src/tools/swaroop_dcpomatic_ecinema.cc b/src/tools/swaroop_dcpomatic_ecinema.cc new file mode 100644 index 000000000..152194c48 --- /dev/null +++ b/src/tools/swaroop_dcpomatic_ecinema.cc @@ -0,0 +1,312 @@ +/* + Copyright (C) 2018-2019 Carl Hetherington + + 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 . + +*/ + +#include "lib/version.h" +#include "lib/decrypted_ecinema_kdm.h" +#include "lib/config.h" +#include "lib/util.h" +#include "lib/film.h" +#include "lib/dcp_content.h" +#include "lib/job_manager.h" +#include "lib/cross.h" +#include "lib/transcode_job.h" +#include "lib/ffmpeg_encoder.h" +#include "lib/signal_manager.h" +#include "lib/video_content.h" +#include "lib/ratio.h" +#include +extern "C" { +#include +#include +} +#include +#include +#include +#include +#include +#include +#include +#include + +using std::string; +using std::cerr; +using std::cout; +using std::ofstream; +using boost::optional; +using boost::shared_ptr; + +static void convert_dcp ( + boost::filesystem::path input, + boost::filesystem::path output_file, + boost::optional kdm, + int crf + ); +static void convert_ffmpeg (boost::filesystem::path input, boost::filesystem::path output_file, string format); +static void write_kdm (string id, boost::filesystem::path name, dcp::Key key); + +static void +help (string n) +{ + cerr << "Syntax: " << n << " [OPTION] |\n" + << " -v, --version show DCP-o-matic version\n" + << " -h, --help show this help\n" + << " -o, --output output directory\n" + << " -f, --format output format (mov or mp4; defaults to mov)\n" + << " -k, --kdm DCP KDM filename (if required)\n" + << " -c, --crf quality (CRF) when transcoding from DCP (0 is best, 51 is worst, defaults to 23)\n" + << "\n" + << " is an unencrypted .mp4 file; is a DCP directory.\n"; +} + +int +main (int argc, char* argv[]) +{ + optional output; + optional format; + optional kdm; + int crf = 23; + + int option_index = 0; + while (true) { + static struct option long_options[] = { + { "version", no_argument, 0, 'v' }, + { "help", no_argument, 0, 'h' }, + { "output", required_argument, 0, 'o' }, + { "format", required_argument, 0, 'f' }, + { "kdm", required_argument, 0, 'k' }, + { "crf", required_argument, 0, 'c' }, + }; + + int c = getopt_long (argc, argv, "vho:f:k:c:", long_options, &option_index); + + if (c == -1) { + break; + } + + switch (c) { + case 'v': + cout << "dcpomatic version " << dcpomatic_version << " " << dcpomatic_git_commit << "\n"; + exit (EXIT_SUCCESS); + case 'h': + help (argv[0]); + exit (EXIT_SUCCESS); + case 'o': + output = optarg; + break; + case 'f': + format = optarg; + break; + case 'k': + kdm = optarg; + break; + case 'c': + crf = atoi(optarg); + break; + } + } + + if (optind >= argc) { + help (argv[0]); + exit (EXIT_FAILURE); + } + + if (!output) { + cerr << "You must specify --output-media or -o\n"; + exit (EXIT_FAILURE); + } + + if (!format) { + format = "mov"; + } + + if (*format != "mov" && *format != "mp4") { + cerr << "Invalid format specified: must be mov or mp4\n"; + exit (EXIT_FAILURE); + } + + dcpomatic_setup_path_encoding (); + dcpomatic_setup (); + signal_manager = new SignalManager (); + + boost::filesystem::path input = argv[optind]; + boost::filesystem::path output_file; + if (boost::filesystem::is_directory(input)) { + output_file = *output / (input.parent_path().filename().string() + ".ecinema"); + } else { + output_file = *output / (input.filename().string() + ".ecinema"); + } + + if (!boost::filesystem::is_directory(*output)) { + boost::filesystem::create_directory (*output); + } + + av_register_all (); + + if (boost::filesystem::is_directory(input)) { + /* Assume input is a DCP */ + convert_dcp (input, output_file, kdm, crf); + } else { + convert_ffmpeg (input, output_file, *format); + } +} + +static void +convert_ffmpeg (boost::filesystem::path input, boost::filesystem::path output_file, string format) +{ + AVFormatContext* input_fc = avformat_alloc_context (); + if (avformat_open_input(&input_fc, input.string().c_str(), 0, 0) < 0) { + cerr << "Could not open input file\n"; + exit (EXIT_FAILURE); + } + + if (avformat_find_stream_info (input_fc, 0) < 0) { + cerr << "Could not read stream information\n"; + exit (EXIT_FAILURE); + } + + AVFormatContext* output_fc; + avformat_alloc_output_context2 (&output_fc, av_guess_format(format.c_str(), 0, 0), 0, 0); + + for (uint32_t i = 0; i < input_fc->nb_streams; ++i) { + AVStream* is = input_fc->streams[i]; + AVStream* os = avformat_new_stream (output_fc, is->codec->codec); + if (avcodec_parameters_copy (os->codecpar, is->codecpar) < 0) { + cerr << "Could not set up output stream.\n"; + exit (EXIT_FAILURE); + } + + os->avg_frame_rate = is->avg_frame_rate; + + switch (is->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + os->time_base = is->time_base; + os->r_frame_rate = is->r_frame_rate; + os->sample_aspect_ratio = is->sample_aspect_ratio; + os->codec->time_base = is->codec->time_base; + os->codec->framerate = is->codec->framerate; + os->codec->pix_fmt = is->codec->pix_fmt; + break; + case AVMEDIA_TYPE_AUDIO: + os->codec->sample_fmt = is->codec->sample_fmt; + os->codec->bits_per_raw_sample = is->codec->bits_per_raw_sample; + os->codec->sample_rate = is->codec->sample_rate; + os->codec->channel_layout = is->codec->channel_layout; + os->codec->channels = is->codec->channels; + if (is->codecpar->codec_id == AV_CODEC_ID_PCM_S24LE) { + /* XXX: fix incoming 24-bit files labelled lpcm, which apparently isn't allowed */ + os->codecpar->codec_tag = MKTAG('i', 'n', '2', '4'); + } + break; + default: + /* XXX */ + break; + } + } + + if (avio_open2 (&output_fc->pb, output_file.string().c_str(), AVIO_FLAG_WRITE, 0, 0) < 0) { + cerr << "Could not open output file `" << output_file.string() << "'\n"; + exit (EXIT_FAILURE); + } + + dcp::Key key (AES_CTR_KEY_SIZE); + AVDictionary* options = 0; + av_dict_set (&options, "encryption_key", key.hex().c_str(), 0); + /* XXX: is this OK? */ + av_dict_set (&options, "encryption_kid", "00000000000000000000000000000000", 0); + av_dict_set (&options, "encryption_scheme", "cenc-aes-ctr", 0); + + string id = dcp::make_uuid (); + if (av_dict_set(&output_fc->metadata, SWAROOP_ID_TAG, id.c_str(), 0) < 0) { + cerr << "Could not write ID to output.\n"; + exit (EXIT_FAILURE); + } + + if (avformat_write_header (output_fc, &options) < 0) { + cerr << "Could not write header to output.\n"; + exit (EXIT_FAILURE); + } + + AVPacket packet; + while (av_read_frame(input_fc, &packet) >= 0) { + AVStream* is = input_fc->streams[packet.stream_index]; + AVStream* os = output_fc->streams[packet.stream_index]; + packet.pts = av_rescale_q_rnd(packet.pts, is->time_base, os->time_base, (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); + packet.dts = av_rescale_q_rnd(packet.dts, is->time_base, os->time_base, (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); + packet.duration = av_rescale_q(packet.duration, is->time_base, os->time_base); + packet.pos = -1; + if (av_interleaved_write_frame (output_fc, &packet) < 0) { + cerr << "Could not write frame to output.\n"; + exit (EXIT_FAILURE); + } + } + + av_write_trailer (output_fc); + + avformat_free_context (input_fc); + avformat_free_context (output_fc); + + write_kdm (id, output_file, key); +} + +static void +write_kdm (string id, boost::filesystem::path name, dcp::Key key) +{ + DecryptedECinemaKDM decrypted_kdm (id, name.filename().string(), key, optional(), optional()); + EncryptedECinemaKDM encrypted_kdm = decrypted_kdm.encrypt (Config::instance()->decryption_chain()->leaf()); + + ofstream f(string(name.string() + ".xml").c_str()); + f << encrypted_kdm.as_xml() << "\n"; +} + +static void +convert_dcp ( + boost::filesystem::path input, boost::filesystem::path output_file, optional kdm, int crf + ) +{ + shared_ptr film (new Film(boost::optional())); + shared_ptr dcp (new DCPContent(input)); + film->examine_and_add_content (dcp); + if (kdm) { + dcp->add_kdm (dcp::EncryptedKDM(dcp::file_to_string(*kdm))); + } + + JobManager* jm = JobManager::instance (); + while (jm->work_to_do ()) { + while (signal_manager->ui_idle ()) {} + dcpomatic_sleep_seconds (1); + } + DCPOMATIC_ASSERT (!jm->errors()); + + film->set_container (Ratio::nearest_from_ratio(dcp->video->size().ratio())); + + string id = dcp::make_uuid (); + dcp::Key key (AES_CTR_KEY_SIZE); + + shared_ptr job (new TranscodeJob(film)); + job->set_encoder ( + shared_ptr( + new FFmpegEncoder(film, job, output_file, EXPORT_FORMAT_H264_PCM, false, false, crf, key, id) + ) + ); + jm->add (job); + show_jobs_on_console (true); + + write_kdm (id, output_file, key); +} diff --git a/src/tools/swaroop_dcpomatic_playlist.cc b/src/tools/swaroop_dcpomatic_playlist.cc new file mode 100644 index 000000000..73fcbf802 --- /dev/null +++ b/src/tools/swaroop_dcpomatic_playlist.cc @@ -0,0 +1,478 @@ +/* + Copyright (C) 2018 Carl Hetherington + + 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 . + +*/ + +#include "../wx/wx_util.h" +#include "../wx/wx_signal_manager.h" +#include "../wx/content_view.h" +#include "../wx/dcpomatic_button.h" +#include "../lib/util.h" +#include "../lib/config.h" +#include "../lib/cross.h" +#include "../lib/film.h" +#include "../lib/dcp_content.h" +#include "../lib/swaroop_spl_entry.h" +#include "../lib/swaroop_spl.h" +#include +#include +#include +#include +#ifdef __WXOSX__ +#include +#endif + +using std::exception; +using std::cout; +using std::string; +using boost::optional; +using boost::shared_ptr; +using boost::weak_ptr; +using boost::bind; +using boost::dynamic_pointer_cast; + +class ContentDialog : public wxDialog, public ContentStore +{ +public: + ContentDialog (wxWindow* parent) + : wxDialog (parent, wxID_ANY, _("Add content"), wxDefaultPosition, wxSize(800, 640)) + , _content_view (new ContentView(this)) + { + _content_view->update (); + + wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); + SetSizer (overall_sizer); + + overall_sizer->Add (_content_view, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL); + if (buttons) { + overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); + } + + overall_sizer->Layout (); + } + + shared_ptr selected () const + { + return _content_view->selected (); + } + + shared_ptr get (string digest) const + { + return _content_view->get (digest); + } + +private: + ContentView* _content_view; +}; + +class DOMFrame : public wxFrame +{ +public: + explicit DOMFrame (wxString const & title) + : wxFrame (0, -1, title) + , _content_dialog (new ContentDialog(this)) + { + /* Use a panel as the only child of the Frame so that we avoid + the dark-grey background on Windows. + */ + wxPanel* overall_panel = new wxPanel (this, wxID_ANY); + wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL); + + _list = new wxListCtrl ( + overall_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL + ); + + _list->AppendColumn (_("Name"), wxLIST_FORMAT_LEFT, 400); + _list->AppendColumn (_("CPL"), wxLIST_FORMAT_LEFT, 350); + _list->AppendColumn (_("Type"), wxLIST_FORMAT_CENTRE, 100); + _list->AppendColumn (_("Format"), wxLIST_FORMAT_CENTRE, 75); + _list->AppendColumn (_("Encrypted"), wxLIST_FORMAT_CENTRE, 90); + _list->AppendColumn (_("Skippable"), wxLIST_FORMAT_CENTRE, 90); + _list->AppendColumn (_("Disable timeline"), wxLIST_FORMAT_CENTRE, 125); + _list->AppendColumn (_("Stop after play"), wxLIST_FORMAT_CENTRE, 125); + + wxImageList* images = new wxImageList (16, 16); + wxIcon tick_icon; + wxIcon no_tick_icon; +#ifdef DCPOMATIX_OSX + tick_icon.LoadFile ("tick.png", wxBITMAP_TYPE_PNG_RESOURCE); + no_tick_icon.LoadFile ("no_tick.png", wxBITMAP_TYPE_PNG_RESOURCE); +#else + boost::filesystem::path tick_path = shared_path() / "tick.png"; + tick_icon.LoadFile (std_to_wx(tick_path.string())); + boost::filesystem::path no_tick_path = shared_path() / "no_tick.png"; + no_tick_icon.LoadFile (std_to_wx(no_tick_path.string())); +#endif + images->Add (tick_icon); + images->Add (no_tick_icon); + + _list->SetImageList (images, wxIMAGE_LIST_SMALL); + + h_sizer->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP); + + wxBoxSizer* button_sizer = new wxBoxSizer (wxVERTICAL); + _up = new Button (overall_panel, _("Up")); + _down = new Button (overall_panel, _("Down")); + _add = new Button (overall_panel, _("Add")); + _remove = new Button (overall_panel, _("Remove")); + _save = new Button (overall_panel, _("Save playlist")); + _load = new Button (overall_panel, _("Load playlist")); + button_sizer->Add (_up, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_down, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_add, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_remove, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_save, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_load, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + + h_sizer->Add (button_sizer, 0, wxALL, DCPOMATIC_SIZER_GAP); + + wxBoxSizer* v_sizer = new wxBoxSizer (wxVERTICAL); + + wxBoxSizer* allowed_shows_sizer = new wxBoxSizer (wxHORIZONTAL); + _allowed_shows_enable = new wxCheckBox (overall_panel, wxID_ANY, _("Limit number of shows with this playlist to")); + allowed_shows_sizer->Add (_allowed_shows_enable, 0, wxRIGHT, DCPOMATIC_SIZER_GAP); + _allowed_shows = new wxSpinCtrl (overall_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 65536, 100); + allowed_shows_sizer->Add (_allowed_shows); + + v_sizer->Add (allowed_shows_sizer, 0, wxALL, DCPOMATIC_SIZER_GAP); + v_sizer->Add (h_sizer); + + overall_panel->SetSizer (v_sizer); + + _list->Bind (wxEVT_LEFT_DOWN, bind(&DOMFrame::list_left_click, this, _1)); + _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&DOMFrame::selection_changed, this)); + _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&DOMFrame::selection_changed, this)); + _up->Bind (wxEVT_BUTTON, bind(&DOMFrame::up_clicked, this)); + _down->Bind (wxEVT_BUTTON, bind(&DOMFrame::down_clicked, this)); + _add->Bind (wxEVT_BUTTON, bind(&DOMFrame::add_clicked, this)); + _remove->Bind (wxEVT_BUTTON, bind(&DOMFrame::remove_clicked, this)); + _save->Bind (wxEVT_BUTTON, bind(&DOMFrame::save_clicked, this)); + _load->Bind (wxEVT_BUTTON, bind(&DOMFrame::load_clicked, this)); + _allowed_shows_enable->Bind (wxEVT_CHECKBOX, bind(&DOMFrame::allowed_shows_changed, this)); + _allowed_shows->Bind (wxEVT_SPINCTRL, bind(&DOMFrame::allowed_shows_changed, this)); + + setup_sensitivity (); + } + +private: + + void allowed_shows_changed () + { + if (_allowed_shows_enable->GetValue()) { + _playlist.set_allowed_shows (_allowed_shows->GetValue()); + } else { + _playlist.unset_allowed_shows (); + } + setup_sensitivity (); + } + + void add (SPLEntry e) + { + wxListItem item; + item.SetId (_list->GetItemCount()); + long const N = _list->InsertItem (item); + set_item (N, e); + } + + void selection_changed () + { + setup_sensitivity (); + } + + void set_item (long N, SPLEntry e) + { + _list->SetItem (N, 0, std_to_wx(e.name)); + _list->SetItem (N, 1, std_to_wx(e.id)); + _list->SetItem (N, 2, std_to_wx(dcp::content_kind_to_string(e.kind))); + _list->SetItem (N, 3, e.type == SPLEntry::DCP ? _("DCP") : _("E-cinema")); + _list->SetItem (N, 4, e.encrypted ? S_("Question|Y") : S_("Question|N")); + _list->SetItem (N, COLUMN_SKIPPABLE, wxEmptyString, e.skippable ? 0 : 1); + _list->SetItem (N, COLUMN_DISABLE_TIMELINE, wxEmptyString, e.disable_timeline ? 0 : 1); + _list->SetItem (N, COLUMN_STOP_AFTER_PLAY, wxEmptyString, e.stop_after_play ? 0 : 1); + } + + void setup_sensitivity () + { + int const num_selected = _list->GetSelectedItemCount (); + long int selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + _up->Enable (selected > 0); + _down->Enable (selected != -1 && selected < (_list->GetItemCount() - 1)); + _remove->Enable (num_selected > 0); + _allowed_shows->Enable (_allowed_shows_enable->GetValue()); + } + + void list_left_click (wxMouseEvent& ev) + { + int flags; + long item = _list->HitTest (ev.GetPosition(), flags, 0); + int x = ev.GetPosition().x; + optional column; + for (int i = 0; i < _list->GetColumnCount(); ++i) { + x -= _list->GetColumnWidth (i); + if (x < 0) { + column = i; + break; + } + } + + if (item != -1 && column) { + switch (*column) { + case COLUMN_SKIPPABLE: + _playlist[item].skippable = !_playlist[item].skippable; + break; + case COLUMN_DISABLE_TIMELINE: + _playlist[item].disable_timeline = !_playlist[item].disable_timeline; + break; + case COLUMN_STOP_AFTER_PLAY: + _playlist[item].stop_after_play = !_playlist[item].stop_after_play; + break; + default: + ev.Skip (); + } + set_item (item, _playlist[item]); + } else { + ev.Skip (); + } + } + + void add_clicked () + { + int const r = _content_dialog->ShowModal (); + if (r == wxID_OK) { + shared_ptr content = _content_dialog->selected (); + if (content) { + SPLEntry e (content); + add (e); + _playlist.add (e); + } + } + } + + void up_clicked () + { + long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s < 1) { + return; + } + + SPLEntry tmp = _playlist[s]; + _playlist[s] = _playlist[s-1]; + _playlist[s-1] = tmp; + + set_item (s - 1, _playlist[s-1]); + set_item (s, _playlist[s]); + } + + void down_clicked () + { + long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s > (_list->GetItemCount() - 1)) { + return; + } + + SPLEntry tmp = _playlist[s]; + _playlist[s] = _playlist[s+1]; + _playlist[s+1] = tmp; + + set_item (s + 1, _playlist[s+1]); + set_item (s, _playlist[s]); + } + + void remove_clicked () + { + long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s == -1) { + return; + } + + _playlist.remove (s); + _list->DeleteItem (s); + } + + void save_clicked () + { + Config* c = Config::instance (); + wxString default_dir = c->player_playlist_directory() ? std_to_wx(c->player_playlist_directory()->string()) : wxString(wxEmptyString); + wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), default_dir, wxEmptyString, wxT("XML files (*.xml)|*.xml"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (d->ShowModal() == wxID_OK) { + boost::filesystem::path file = wx_to_std (d->GetPath()); + file.replace_extension (".xml"); + _playlist.write (file); + } + } + + void load_clicked () + { + Config* c = Config::instance (); + wxString default_dir = c->player_playlist_directory() ? std_to_wx(c->player_playlist_directory()->string()) : wxString(wxEmptyString); + wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), default_dir, wxEmptyString, wxT("XML files (*.xml)|*.xml")); + if (d->ShowModal() == wxID_OK) { + _list->DeleteAllItems (); + _playlist.read (wx_to_std(d->GetPath()), _content_dialog); + if (!_playlist.missing()) { + _list->DeleteAllItems (); + BOOST_FOREACH (SPLEntry i, _playlist.get()) { + add (i); + } + } else { + error_dialog (this, _("Some content in this playlist was not found.")); + } + optional allowed_shows = _playlist.allowed_shows (); + _allowed_shows_enable->SetValue (static_cast(allowed_shows)); + if (allowed_shows) { + _allowed_shows->SetValue (*allowed_shows); + } else { + _allowed_shows->SetValue (65536); + } + setup_sensitivity (); + } + } + + wxListCtrl* _list; + wxButton* _up; + wxButton* _down; + wxButton* _add; + wxButton* _remove; + wxButton* _save; + wxButton* _load; + wxCheckBox* _allowed_shows_enable; + wxSpinCtrl* _allowed_shows; + SPL _playlist; + ContentDialog* _content_dialog; + + enum { + COLUMN_SKIPPABLE = 5, + COLUMN_DISABLE_TIMELINE = 6, + COLUMN_STOP_AFTER_PLAY = 7 + }; +}; + +/** @class App + * @brief The magic App class for wxWidgets. + */ +class App : public wxApp +{ +public: + App () + : wxApp () + , _frame (0) + {} + +private: + + bool OnInit () + try + { + SetAppName (_("DCP-o-matic KDM Creator")); + + if (!wxApp::OnInit()) { + return false; + } + +#ifdef DCPOMATIC_LINUX + unsetenv ("UBUNTU_MENUPROXY"); +#endif + +#ifdef __WXOSX__ + ProcessSerialNumber serial; + GetCurrentProcess (&serial); + TransformProcessType (&serial, kProcessTransformToForegroundApplication); +#endif + + dcpomatic_setup_path_encoding (); + + /* Enable i18n; this will create a Config object + to look for a force-configured language. This Config + object will be wrong, however, because dcpomatic_setup + hasn't yet been called and there aren't any filters etc. + set up yet. + */ + dcpomatic_setup_i18n (); + + /* Set things up, including filters etc. + which will now be internationalised correctly. + */ + dcpomatic_setup (); + + /* Force the configuration to be re-loaded correctly next + time it is needed. + */ + Config::drop (); + + _frame = new DOMFrame (_("DCP-o-matic Playlist Editor")); + SetTopWindow (_frame); + _frame->Maximize (); + _frame->Show (); + + signal_manager = new wxSignalManager (this); + Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); + + return true; + } + catch (exception& e) + { + error_dialog (0, _("DCP-o-matic could not start"), std_to_wx(e.what())); + return true; + } + + /* An unhandled exception has occurred inside the main event loop */ + bool OnExceptionInMainLoop () + { + try { + throw; + } catch (FileError& e) { + error_dialog ( + 0, + wxString::Format ( + _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, + std_to_wx (e.what()), + std_to_wx (e.file().string().c_str ()) + ) + ); + } catch (exception& e) { + error_dialog ( + 0, + wxString::Format ( + _("An exception occurred: %s.\n\n") + " " + REPORT_PROBLEM, + std_to_wx (e.what ()) + ) + ); + } catch (...) { + error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + } + + /* This will terminate the program */ + return false; + } + + void OnUnhandledException () + { + error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + } + + void idle () + { + signal_manager->ui_idle (); + } + + DOMFrame* _frame; +}; + +IMPLEMENT_APP (App) diff --git a/src/tools/swaroop_dcpomatic_uuid.cc b/src/tools/swaroop_dcpomatic_uuid.cc new file mode 100644 index 000000000..65582ac0c --- /dev/null +++ b/src/tools/swaroop_dcpomatic_uuid.cc @@ -0,0 +1,40 @@ +/* + Copyright (C) 2018 Carl Hetherington + + 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 . + +*/ + +#include + +int main () +{ +#ifdef DCPOMATIC_LINUX + FILE* f = fopen ("/sys/class/dmi/id/product_uuid", "r"); + if (!f) { + printf ("unknown"); + return 1; + } + char buffer[256]; + int const N = fread (buffer, 1, 255, f); + buffer[N] = '\0'; + printf ("%s", buffer); + fclose (f); + return 0; +#endif + printf ("unknown"); + return 1; +} diff --git a/src/tools/wscript b/src/tools/wscript index 3b2c0a04c..710f49eeb 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -37,9 +37,9 @@ def build(bld): cli_tools = [] if bld.env.VARIANT == 'swaroop-theater': - cli_tools = [ 'dcpomatic_uuid'] - elif bld.env.VARIANT == "swaroop-studio": - cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create', 'dcpomatic_ecinema', 'dcpomatic_uuid'] + cli_tools = ['swaroop_dcpomatic_uuid'] + elif bld.env.VARIANT == 'swaroop-studio': + cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create', 'swaroop_dcpomatic_ecinema', 'swaroop_dcpomatic_uuid'] else: cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create'] @@ -49,16 +49,18 @@ def build(bld): obj.includes = ['..'] obj.use = ['libdcpomatic2'] obj.source = '%s.cc' % t - obj.target = t.replace('dcpomatic', 'dcpomatic2') + obj.target = t.replace('dcpomatic', 'dcpomatic2').replace('swaroop_', '') if t == 'server_test': obj.install_path = None gui_tools = [] if not bld.env.DISABLE_GUI: if bld.env.VARIANT == 'swaroop-theater': - gui_tools = ['dcpomatic_player', 'dcpomatic_playlist'] + gui_tools = ['dcpomatic_player', 'swaroop_dcpomatic_playlist'] + elif bld.env.VARIANT == 'swaroop-studio': + gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'swaroop_dcpomatic_playlist'] else: - gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'dcpomatic_playlist'] + gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player'] for t in gui_tools: obj = bld(features='cxx cxxprogram') @@ -75,7 +77,7 @@ def build(bld): obj.source = '%s.cc' % t if bld.env.TARGET_WINDOWS: obj.source += ' ../../platform/windows/%s.rc' % t - obj.target = t.replace('dcpomatic', 'dcpomatic2') + obj.target = t.replace('dcpomatic', 'dcpomatic2').replace('swaroop_', '') i18n.po_to_mo(os.path.join('src', 'tools'), 'dcpomatic2', bld) diff --git a/src/wx/controls.h b/src/wx/controls.h index 1b6a379cc..c11d6e8e3 100644 --- a/src/wx/controls.h +++ b/src/wx/controls.h @@ -24,7 +24,6 @@ #include "lib/dcpomatic_time.h" #include "lib/types.h" #include "lib/film.h" -#include "lib/spl.h" #include #include #include diff --git a/src/wx/swaroop_controls.h b/src/wx/swaroop_controls.h index a8bb41dea..f773bf901 100644 --- a/src/wx/swaroop_controls.h +++ b/src/wx/swaroop_controls.h @@ -19,6 +19,7 @@ */ #include "controls.h" +#include "lib/swaroop_spl.h" class DCPContent; class EncryptedECinemaKDM;