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 << " intrinsic " << reel->main_picture()->intrinsic_duration();
103 if (reel->main_picture()->asset_ref().resolved()) {
104 if (reel->main_picture()->asset()) {
105 cout << "\n Picture: "
106 << reel->main_picture()->asset()->size().width
108 << reel->main_picture()->asset()->size().height << "\n";
111 shared_ptr<MonoPictureAsset> ma = dynamic_pointer_cast<MonoPictureAsset>(reel->main_picture()->asset());
113 shared_ptr<MonoPictureAssetReader> reader = ma->start_read ();
114 pair<int, int> j2k_size_range (INT_MAX, 0);
115 for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) {
116 shared_ptr<const MonoPictureFrame> frame = reader->get_frame (i);
117 printf("Frame %" PRId64 " J2K size %7d", i, frame->j2k_size());
118 j2k_size_range.first = min(j2k_size_range.first, frame->j2k_size());
119 j2k_size_range.second = max(j2k_size_range.second, frame->j2k_size());
124 printf(" decrypted OK");
125 } catch (exception& e) {
126 printf(" decryption FAILED");
134 "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n",
135 j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()),
136 j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate())
140 cout << " - not present in this DCP.\n";
145 main_sound (shared_ptr<Reel> reel)
147 if (reel->main_sound()) {
148 cout << " Sound ID: " << reel->main_sound()->id()
149 << " entry " << reel->main_picture()->entry_point()
150 << " duration " << reel->main_picture()->duration()
151 << " intrinsic " << reel->main_picture()->intrinsic_duration();
152 if (reel->main_sound()->asset_ref().resolved()) {
153 if (reel->main_sound()->asset()) {
155 << reel->main_sound()->asset()->channels()
157 << reel->main_sound()->asset()->sampling_rate() << "Hz\n";
160 cout << " - not present in this DCP.\n";
166 main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
168 if (!reel->main_subtitle()) {
172 cout << " Subtitle ID: " << reel->main_subtitle()->id();
174 if (reel->main_subtitle()->asset_ref().resolved()) {
175 list<shared_ptr<Subtitle> > subs = reel->main_subtitle()->asset()->subtitles ();
176 cout << "\n Subtitle: " << subs.size() << " subtitles";
177 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (reel->main_subtitle()->asset());
179 cout << " in " << iop->language() << "\n";
181 shared_ptr<SMPTESubtitleAsset> smpte = dynamic_pointer_cast<SMPTESubtitleAsset> (reel->main_subtitle()->asset());
182 if (smpte && smpte->language ()) {
183 cout << " in " << smpte->language().get() << "\n";
185 if (list_subtitles) {
186 BOOST_FOREACH (shared_ptr<Subtitle> k, subs) {
187 shared_ptr<SubtitleString> ks = dynamic_pointer_cast<SubtitleString> (k);
191 shared_ptr<SubtitleImage> is = dynamic_pointer_cast<SubtitleImage> (k);
198 cout << " - not present in this DCP.\n";
203 main (int argc, char* argv[])
205 bool subtitles = false;
206 bool keep_going = false;
207 bool picture = false;
208 bool decompress = false;
209 bool ignore_missing_assets = false;
210 optional<boost::filesystem::path> kdm;
211 optional<boost::filesystem::path> private_key;
213 int option_index = 0;
215 static struct option long_options[] = {
216 { "version", no_argument, 0, 'v' },
217 { "help", no_argument, 0, 'h' },
218 { "subtitles", no_argument, 0, 's' },
219 { "keep-going", no_argument, 0, 'k' },
220 { "picture", no_argument, 0, 'p' },
221 { "decompress", no_argument, 0, 'd' },
222 { "ignore-missing-assets", no_argument, 0, 'A' },
223 { "kdm", required_argument, 0, 'B' },
224 { "private-key", required_argument, 0, 'C' },
228 int c = getopt_long (argc, argv, "vhskpdAB:C:", long_options, &option_index);
236 cout << "libdcp version " << LIBDCP_VERSION << "\n";
254 ignore_missing_assets = true;
260 private_key = optarg;
265 if (argc <= optind || argc > (optind + 1)) {
270 if (!boost::filesystem::exists (argv[optind])) {
271 cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n";
275 list<shared_ptr<CPL> > cpls;
276 if (boost::filesystem::is_directory(argv[optind])) {
278 DCP::ReadErrors errors;
280 dcp = new DCP (argv[optind]);
281 dcp->read (keep_going, &errors);
282 if (kdm && private_key) {
283 dcp->add(DecryptedKDM(EncryptedKDM(file_to_string(*kdm)), file_to_string(*private_key)));
285 } catch (FileError& e) {
286 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
288 } catch (DCPReadError& e) {
289 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
291 } catch (KDMDecryptionError& e) {
292 cerr << e.what() << "\n";
296 cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n";
298 dcp::filter_errors (errors, ignore_missing_assets);
299 for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) {
300 cerr << "Error: " << (*i)->what() << "\n";
305 cpls.push_back (shared_ptr<CPL>(new CPL(boost::filesystem::path(argv[optind]))));
307 ignore_missing_assets = true;
310 BOOST_FOREACH (shared_ptr<CPL> i, cpls) {
311 cout << " CPL: " << i->annotation_text() << "\n";
314 BOOST_FOREACH (shared_ptr<Reel> j, i->reels()) {
315 cout << " Reel " << R << "\n";
318 main_picture (j, picture, decompress);
319 } catch (UnresolvedRefError& e) {
321 if (!ignore_missing_assets) {
322 cerr << e.what() << " (for main picture)\n";
331 } catch (UnresolvedRefError& e) {
333 if (!ignore_missing_assets) {
334 cerr << e.what() << " (for main sound)\n";
342 main_subtitle (j, subtitles);
343 } catch (UnresolvedRefError& e) {
345 if (!ignore_missing_assets) {
346 cerr << e.what() << " (for main subtitle)\n";