2 Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 #include "exceptions.h"
37 #include "sound_asset.h"
38 #include "picture_asset.h"
39 #include "subtitle_asset.h"
40 #include "reel_picture_asset.h"
41 #include "reel_sound_asset.h"
42 #include "reel_subtitle_asset.h"
43 #include "subtitle_string.h"
44 #include "subtitle_image.h"
45 #include "interop_subtitle_asset.h"
46 #include "smpte_subtitle_asset.h"
47 #include "mono_picture_asset.h"
48 #include "encrypted_kdm.h"
49 #include "decrypted_kdm.h"
53 #include <boost/filesystem.hpp>
54 #include <boost/foreach.hpp>
67 using boost::shared_ptr;
68 using boost::dynamic_pointer_cast;
69 using boost::optional;
75 cerr << "Syntax: " << n << " [options] [<DCP>] [<CPL>]\n"
76 << " -s, --subtitles list all subtitles\n"
77 << " -p, --picture analyse picture\n"
78 << " -d, --decompress decompress picture when analysing (this is slow)\n"
79 << " -k, --keep-going carry on in the event of errors, if possible\n"
80 << " --kdm KDM to decrypt DCP\n"
81 << " --private-key private key for the certificate that the KDM is targeted at\n"
82 << " --ignore-missing-assets ignore missing asset files\n";
86 mbits_per_second (int size, Fraction frame_rate)
88 return size * 8 * frame_rate.as_float() / 1e6;
92 main_picture (shared_ptr<Reel> reel, bool analyse, bool decompress)
94 if (!reel->main_picture()) {
98 cout << " Picture ID: " << reel->main_picture()->id()
99 << " entry " << reel->main_picture()->entry_point()
100 << " duration " << reel->main_picture()->duration()
101 << " (" << dcp::Time(reel->main_picture()->duration(), reel->main_picture()->frame_rate().as_float(), reel->main_picture()->frame_rate().as_float()).as_string(dcp::SMPTE) << ")"
102 << " intrinsic " << reel->main_picture()->intrinsic_duration();
104 if (reel->main_picture()->asset_ref().resolved()) {
105 if (reel->main_picture()->asset()) {
106 cout << "\n Picture: "
107 << reel->main_picture()->asset()->size().width
109 << reel->main_picture()->asset()->size().height << "\n";
112 shared_ptr<MonoPictureAsset> ma = dynamic_pointer_cast<MonoPictureAsset>(reel->main_picture()->asset());
114 shared_ptr<MonoPictureAssetReader> reader = ma->start_read ();
115 pair<int, int> j2k_size_range (INT_MAX, 0);
116 for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) {
117 shared_ptr<const MonoPictureFrame> frame = reader->get_frame (i);
118 printf("Frame %" PRId64 " J2K size %7d", i, frame->j2k_size());
119 j2k_size_range.first = min(j2k_size_range.first, frame->j2k_size());
120 j2k_size_range.second = max(j2k_size_range.second, frame->j2k_size());
125 printf(" decrypted OK");
126 } catch (exception& e) {
127 printf(" decryption FAILED");
135 "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n",
136 j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()),
137 j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate())
141 cout << " - not present in this DCP.\n";
146 main_sound (shared_ptr<Reel> reel)
148 if (reel->main_sound()) {
149 cout << " Sound ID: " << reel->main_sound()->id()
150 << " entry " << reel->main_picture()->entry_point()
151 << " duration " << reel->main_picture()->duration()
152 << " intrinsic " << reel->main_picture()->intrinsic_duration();
153 if (reel->main_sound()->asset_ref().resolved()) {
154 if (reel->main_sound()->asset()) {
156 << reel->main_sound()->asset()->channels()
158 << reel->main_sound()->asset()->sampling_rate() << "Hz\n";
161 cout << " - not present in this DCP.\n";
167 main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
169 if (!reel->main_subtitle()) {
173 cout << " Subtitle ID: " << reel->main_subtitle()->id();
175 if (reel->main_subtitle()->asset_ref().resolved()) {
176 list<shared_ptr<Subtitle> > subs = reel->main_subtitle()->asset()->subtitles ();
177 cout << "\n Subtitle: " << subs.size() << " subtitles";
178 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (reel->main_subtitle()->asset());
180 cout << " in " << iop->language() << "\n";
182 shared_ptr<SMPTESubtitleAsset> smpte = dynamic_pointer_cast<SMPTESubtitleAsset> (reel->main_subtitle()->asset());
183 if (smpte && smpte->language ()) {
184 cout << " in " << smpte->language().get() << "\n";
186 if (list_subtitles) {
187 BOOST_FOREACH (shared_ptr<Subtitle> k, subs) {
188 shared_ptr<SubtitleString> ks = dynamic_pointer_cast<SubtitleString> (k);
192 shared_ptr<SubtitleImage> is = dynamic_pointer_cast<SubtitleImage> (k);
199 cout << " - not present in this DCP.\n";
204 main (int argc, char* argv[])
206 bool subtitles = false;
207 bool keep_going = false;
208 bool picture = false;
209 bool decompress = false;
210 bool ignore_missing_assets = false;
211 optional<boost::filesystem::path> kdm;
212 optional<boost::filesystem::path> private_key;
214 int option_index = 0;
216 static struct option long_options[] = {
217 { "version", no_argument, 0, 'v' },
218 { "help", no_argument, 0, 'h' },
219 { "subtitles", no_argument, 0, 's' },
220 { "keep-going", no_argument, 0, 'k' },
221 { "picture", no_argument, 0, 'p' },
222 { "decompress", no_argument, 0, 'd' },
223 { "ignore-missing-assets", no_argument, 0, 'A' },
224 { "kdm", required_argument, 0, 'B' },
225 { "private-key", required_argument, 0, 'C' },
229 int c = getopt_long (argc, argv, "vhskpdAB:C:", long_options, &option_index);
237 cout << "libdcp version " << LIBDCP_VERSION << "\n";
255 ignore_missing_assets = true;
261 private_key = optarg;
266 if (argc <= optind || argc > (optind + 1)) {
271 if (!boost::filesystem::exists (argv[optind])) {
272 cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n";
276 list<shared_ptr<CPL> > cpls;
277 if (boost::filesystem::is_directory(argv[optind])) {
279 DCP::ReadErrors errors;
281 dcp = new DCP (argv[optind]);
282 dcp->read (keep_going, &errors);
283 if (kdm && private_key) {
284 dcp->add(DecryptedKDM(EncryptedKDM(file_to_string(*kdm)), file_to_string(*private_key)));
286 } catch (FileError& e) {
287 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
289 } catch (DCPReadError& e) {
290 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
292 } catch (KDMDecryptionError& e) {
293 cerr << e.what() << "\n";
297 cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n";
299 dcp::filter_errors (errors, ignore_missing_assets);
300 for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) {
301 cerr << "Error: " << (*i)->what() << "\n";
306 cpls.push_back (shared_ptr<CPL>(new CPL(boost::filesystem::path(argv[optind]))));
308 ignore_missing_assets = true;
311 BOOST_FOREACH (shared_ptr<CPL> i, cpls) {
312 cout << " CPL: " << i->annotation_text() << "\n";
315 BOOST_FOREACH (shared_ptr<Reel> j, i->reels()) {
316 cout << " Reel " << R << "\n";
319 main_picture (j, picture, decompress);
320 } catch (UnresolvedRefError& e) {
322 if (!ignore_missing_assets) {
323 cerr << e.what() << " (for main picture)\n";
332 } catch (UnresolvedRefError& e) {
334 if (!ignore_missing_assets) {
335 cerr << e.what() << " (for main sound)\n";
343 main_subtitle (j, subtitles);
344 } catch (UnresolvedRefError& e) {
346 if (!ignore_missing_assets) {
347 cerr << e.what() << " (for main subtitle)\n";