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 if (reel->main_picture()->entry_point()) {
100 cout << " entry " << *reel->main_picture()->entry_point();
102 if (reel->main_picture()->duration()) {
103 cout << " duration " << *reel->main_picture()->duration()
104 << " (" << dcp::Time(*reel->main_picture()->duration(), reel->main_picture()->frame_rate().as_float(), reel->main_picture()->frame_rate().as_float()).as_string(dcp::SMPTE) << ")"
105 << " intrinsic " << reel->main_picture()->intrinsic_duration();
107 cout << " intrinsic duration " << reel->main_picture()->intrinsic_duration();
110 if (reel->main_picture()->asset_ref().resolved()) {
111 if (reel->main_picture()->asset()) {
112 cout << "\n Picture: "
113 << reel->main_picture()->asset()->size().width
115 << reel->main_picture()->asset()->size().height << "\n";
118 shared_ptr<MonoPictureAsset> ma = dynamic_pointer_cast<MonoPictureAsset>(reel->main_picture()->asset());
120 shared_ptr<MonoPictureAssetReader> reader = ma->start_read ();
121 pair<int, int> j2k_size_range (INT_MAX, 0);
122 for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) {
123 shared_ptr<const MonoPictureFrame> frame = reader->get_frame (i);
124 printf("Frame %" PRId64 " J2K size %7d", i, frame->j2k_size());
125 j2k_size_range.first = min(j2k_size_range.first, frame->j2k_size());
126 j2k_size_range.second = max(j2k_size_range.second, frame->j2k_size());
131 printf(" decrypted OK");
132 } catch (exception& e) {
133 printf(" decryption FAILED");
141 "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n",
142 j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()),
143 j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate())
147 cout << " - not present in this DCP.\n";
152 main_sound (shared_ptr<Reel> reel)
154 if (reel->main_sound()) {
155 cout << " Sound ID: " << reel->main_sound()->id();
156 if (reel->main_sound()->entry_point()) {
157 cout << " entry " << *reel->main_sound()->entry_point();
159 if (reel->main_sound()->duration()) {
160 cout << " duration " << *reel->main_sound()->duration()
161 << " intrinsic " << reel->main_sound()->intrinsic_duration();
163 cout << " intrinsic duration " << reel->main_sound()->intrinsic_duration();
166 if (reel->main_sound()->asset_ref().resolved()) {
167 if (reel->main_sound()->asset()) {
169 << reel->main_sound()->asset()->channels()
171 << reel->main_sound()->asset()->sampling_rate() << "Hz\n";
174 cout << " - not present in this DCP.\n";
180 main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
182 if (!reel->main_subtitle()) {
186 cout << " Subtitle ID: " << reel->main_subtitle()->id();
188 if (reel->main_subtitle()->asset_ref().resolved()) {
189 list<shared_ptr<Subtitle> > subs = reel->main_subtitle()->asset()->subtitles ();
190 cout << "\n Subtitle: " << subs.size() << " subtitles";
191 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (reel->main_subtitle()->asset());
193 cout << " in " << iop->language() << "\n";
195 shared_ptr<SMPTESubtitleAsset> smpte = dynamic_pointer_cast<SMPTESubtitleAsset> (reel->main_subtitle()->asset());
196 if (smpte && smpte->language ()) {
197 cout << " in " << smpte->language().get() << "\n";
199 if (list_subtitles) {
200 BOOST_FOREACH (shared_ptr<Subtitle> k, subs) {
201 shared_ptr<SubtitleString> ks = dynamic_pointer_cast<SubtitleString> (k);
205 shared_ptr<SubtitleImage> is = dynamic_pointer_cast<SubtitleImage> (k);
212 cout << " - not present in this DCP.\n";
217 main (int argc, char* argv[])
219 bool subtitles = false;
220 bool keep_going = false;
221 bool picture = false;
222 bool decompress = false;
223 bool ignore_missing_assets = false;
224 optional<boost::filesystem::path> kdm;
225 optional<boost::filesystem::path> private_key;
227 int option_index = 0;
229 static struct option long_options[] = {
230 { "version", no_argument, 0, 'v' },
231 { "help", no_argument, 0, 'h' },
232 { "subtitles", no_argument, 0, 's' },
233 { "keep-going", no_argument, 0, 'k' },
234 { "picture", no_argument, 0, 'p' },
235 { "decompress", no_argument, 0, 'd' },
236 { "ignore-missing-assets", no_argument, 0, 'A' },
237 { "kdm", required_argument, 0, 'B' },
238 { "private-key", required_argument, 0, 'C' },
242 int c = getopt_long (argc, argv, "vhskpdAB:C:", long_options, &option_index);
250 cout << "libdcp version " << LIBDCP_VERSION << "\n";
268 ignore_missing_assets = true;
274 private_key = optarg;
279 if (argc <= optind || argc > (optind + 1)) {
284 if (!boost::filesystem::exists (argv[optind])) {
285 cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n";
289 list<shared_ptr<CPL> > cpls;
290 if (boost::filesystem::is_directory(argv[optind])) {
292 DCP::ReadErrors errors;
294 dcp = new DCP (argv[optind]);
295 dcp->read (keep_going, &errors);
296 if (kdm && private_key) {
297 dcp->add(DecryptedKDM(EncryptedKDM(file_to_string(*kdm)), file_to_string(*private_key)));
299 } catch (FileError& e) {
300 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
302 } catch (DCPReadError& e) {
303 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
305 } catch (KDMDecryptionError& e) {
306 cerr << e.what() << "\n";
310 cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n";
312 dcp::filter_errors (errors, ignore_missing_assets);
313 for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) {
314 cerr << "Error: " << (*i)->what() << "\n";
319 cpls.push_back (shared_ptr<CPL>(new CPL(boost::filesystem::path(argv[optind]))));
321 ignore_missing_assets = true;
324 BOOST_FOREACH (shared_ptr<CPL> i, cpls) {
325 cout << " CPL: " << i->annotation_text() << "\n";
328 BOOST_FOREACH (shared_ptr<Reel> j, i->reels()) {
329 cout << " Reel " << R << "\n";
332 main_picture (j, picture, decompress);
333 } catch (UnresolvedRefError& e) {
335 if (!ignore_missing_assets) {
336 cerr << e.what() << " (for main picture)\n";
345 } catch (UnresolvedRefError& e) {
347 if (!ignore_missing_assets) {
348 cerr << e.what() << " (for main sound)\n";
356 main_subtitle (j, subtitles);
357 } catch (UnresolvedRefError& e) {
359 if (!ignore_missing_assets) {
360 cerr << e.what() << " (for main subtitle)\n";