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 << " --kdm KDM to decrypt DCP\n"
80 << " --private-key private key for the certificate that the KDM is targeted at\n"
81 << " --ignore-missing-assets ignore missing asset files\n";
85 mbits_per_second (int size, Fraction frame_rate)
87 return size * 8 * frame_rate.as_float() / 1e6;
91 main_picture (shared_ptr<Reel> reel, bool analyse, bool decompress)
93 if (!reel->main_picture()) {
97 cout << " Picture ID: " << reel->main_picture()->id();
98 if (reel->main_picture()->entry_point()) {
99 cout << " entry " << *reel->main_picture()->entry_point();
101 if (reel->main_picture()->duration()) {
102 cout << " duration " << *reel->main_picture()->duration()
103 << " (" << dcp::Time(*reel->main_picture()->duration(), reel->main_picture()->frame_rate().as_float(), reel->main_picture()->frame_rate().as_float()).as_string(dcp::SMPTE) << ")"
104 << " intrinsic " << reel->main_picture()->intrinsic_duration();
106 cout << " intrinsic duration " << reel->main_picture()->intrinsic_duration();
109 if (reel->main_picture()->asset_ref().resolved()) {
110 if (reel->main_picture()->asset()) {
111 cout << "\n Picture: "
112 << reel->main_picture()->asset()->size().width
114 << reel->main_picture()->asset()->size().height << "\n";
117 shared_ptr<MonoPictureAsset> ma = dynamic_pointer_cast<MonoPictureAsset>(reel->main_picture()->asset());
119 shared_ptr<MonoPictureAssetReader> reader = ma->start_read ();
120 pair<int, int> j2k_size_range (INT_MAX, 0);
121 for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) {
122 shared_ptr<const MonoPictureFrame> frame = reader->get_frame (i);
123 printf("Frame %" PRId64 " J2K size %7d", i, frame->j2k_size());
124 j2k_size_range.first = min(j2k_size_range.first, frame->j2k_size());
125 j2k_size_range.second = max(j2k_size_range.second, frame->j2k_size());
130 printf(" decrypted OK");
131 } catch (exception& e) {
132 printf(" decryption FAILED");
140 "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n",
141 j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()),
142 j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate())
146 cout << " - not present in this DCP.\n";
151 main_sound (shared_ptr<Reel> reel)
153 if (reel->main_sound()) {
154 cout << " Sound ID: " << reel->main_sound()->id();
155 if (reel->main_sound()->entry_point()) {
156 cout << " entry " << *reel->main_sound()->entry_point();
158 if (reel->main_sound()->duration()) {
159 cout << " duration " << *reel->main_sound()->duration()
160 << " intrinsic " << reel->main_sound()->intrinsic_duration();
162 cout << " intrinsic duration " << reel->main_sound()->intrinsic_duration();
165 if (reel->main_sound()->asset_ref().resolved()) {
166 if (reel->main_sound()->asset()) {
168 << reel->main_sound()->asset()->channels()
170 << reel->main_sound()->asset()->sampling_rate() << "Hz\n";
173 cout << " - not present in this DCP.\n";
179 main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
181 if (!reel->main_subtitle()) {
185 cout << " Subtitle ID: " << reel->main_subtitle()->id();
187 if (reel->main_subtitle()->asset_ref().resolved()) {
188 list<shared_ptr<Subtitle> > subs = reel->main_subtitle()->asset()->subtitles ();
189 cout << "\n Subtitle: " << subs.size() << " subtitles";
190 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (reel->main_subtitle()->asset());
192 cout << " in " << iop->language() << "\n";
194 shared_ptr<SMPTESubtitleAsset> smpte = dynamic_pointer_cast<SMPTESubtitleAsset> (reel->main_subtitle()->asset());
195 if (smpte && smpte->language ()) {
196 cout << " in " << smpte->language().get() << "\n";
198 if (list_subtitles) {
199 BOOST_FOREACH (shared_ptr<Subtitle> k, subs) {
200 shared_ptr<SubtitleString> ks = dynamic_pointer_cast<SubtitleString> (k);
204 shared_ptr<SubtitleImage> is = dynamic_pointer_cast<SubtitleImage> (k);
211 cout << " - not present in this DCP.\n";
216 main (int argc, char* argv[])
218 bool subtitles = false;
219 bool picture = false;
220 bool decompress = false;
221 bool ignore_missing_assets = false;
222 optional<boost::filesystem::path> kdm;
223 optional<boost::filesystem::path> private_key;
225 int option_index = 0;
227 static struct option long_options[] = {
228 { "version", no_argument, 0, 'v' },
229 { "help", no_argument, 0, 'h' },
230 { "subtitles", no_argument, 0, 's' },
231 { "picture", no_argument, 0, 'p' },
232 { "decompress", no_argument, 0, 'd' },
233 { "ignore-missing-assets", no_argument, 0, 'A' },
234 { "kdm", required_argument, 0, 'B' },
235 { "private-key", required_argument, 0, 'C' },
239 int c = getopt_long (argc, argv, "vhspdAB:C:", long_options, &option_index);
247 cout << "libdcp version " << LIBDCP_VERSION << "\n";
262 ignore_missing_assets = true;
268 private_key = optarg;
273 if (argc <= optind || argc > (optind + 1)) {
278 if (!boost::filesystem::exists (argv[optind])) {
279 cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n";
283 list<shared_ptr<CPL> > cpls;
284 if (boost::filesystem::is_directory(argv[optind])) {
286 list<dcp::VerificationNote> notes;
288 dcp = new DCP (argv[optind]);
290 if (kdm && private_key) {
291 dcp->add(DecryptedKDM(EncryptedKDM(file_to_string(*kdm)), file_to_string(*private_key)));
293 } catch (FileError& e) {
294 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
296 } catch (DCPReadError& e) {
297 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
299 } catch (KDMDecryptionError& e) {
300 cerr << e.what() << "\n";
304 cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n";
306 dcp::filter_notes (notes, ignore_missing_assets);
307 BOOST_FOREACH (dcp::VerificationNote i, notes) {
308 cerr << "Error: " << note_to_string(i) << "\n";
313 cpls.push_back (shared_ptr<CPL>(new CPL(boost::filesystem::path(argv[optind]))));
314 ignore_missing_assets = true;
317 BOOST_FOREACH (shared_ptr<CPL> i, cpls) {
318 cout << " CPL: " << i->annotation_text() << "\n";
321 BOOST_FOREACH (shared_ptr<Reel> j, i->reels()) {
322 cout << " Reel " << R << "\n";
325 main_picture (j, picture, decompress);
326 } catch (UnresolvedRefError& e) {
327 if (!ignore_missing_assets) {
328 cerr << e.what() << " (for main picture)\n";
334 } catch (UnresolvedRefError& e) {
335 if (!ignore_missing_assets) {
336 cerr << e.what() << " (for main sound)\n";
341 main_subtitle (j, subtitles);
342 } catch (UnresolvedRefError& e) {
343 if (!ignore_missing_assets) {
344 cerr << e.what() << " (for main subtitle)\n";