2 Copyright (C) 2012-2019 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;
92 main_picture (shared_ptr<Reel> reel, bool analyse, bool decompress)
94 shared_ptr<dcp::ReelPictureAsset> mp = reel->main_picture ();
99 cout << " Picture ID: " << mp->id();
100 if (mp->entry_point()) {
101 cout << " entry " << *mp->entry_point();
103 if (mp->duration()) {
104 cout << " duration " << *mp->duration()
105 << " (" << dcp::Time(*mp->duration(), mp->frame_rate().as_float(), mp->frame_rate().as_float()).as_string(dcp::SMPTE) << ")"
106 << " intrinsic " << mp->intrinsic_duration();
108 cout << " intrinsic duration " << mp->intrinsic_duration();
111 if (mp->asset_ref().resolved()) {
113 cout << "\n Picture: "
114 << mp->asset()->size().width
116 << mp->asset()->size().height << "\n";
119 shared_ptr<MonoPictureAsset> ma = dynamic_pointer_cast<MonoPictureAsset>(mp->asset());
121 shared_ptr<MonoPictureAssetReader> reader = ma->start_read ();
122 pair<int, int> j2k_size_range (INT_MAX, 0);
123 for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) {
124 shared_ptr<const MonoPictureFrame> frame = reader->get_frame (i);
125 printf("Frame %" PRId64 " J2K size %7d", i, frame->j2k_size());
126 j2k_size_range.first = min(j2k_size_range.first, frame->j2k_size());
127 j2k_size_range.second = max(j2k_size_range.second, frame->j2k_size());
132 printf(" decrypted OK");
133 } catch (exception& e) {
134 printf(" decryption FAILED");
142 "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n",
143 j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()),
144 j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate())
148 cout << " - not present in this DCP.\n";
152 mp->duration().get_value_or(mp->intrinsic_duration()),
153 mp->frame_rate().as_float(),
154 mp->frame_rate().as_float()
160 main_sound (shared_ptr<Reel> reel)
162 shared_ptr<dcp::ReelSoundAsset> ms = reel->main_sound ();
167 cout << " Sound ID: " << ms->id();
168 if (ms->entry_point()) {
169 cout << " entry " << *ms->entry_point();
171 if (ms->duration()) {
172 cout << " duration " << *ms->duration()
173 << " intrinsic " << ms->intrinsic_duration();
175 cout << " intrinsic duration " << ms->intrinsic_duration();
178 if (ms->asset_ref().resolved()) {
181 << ms->asset()->channels()
183 << ms->asset()->sampling_rate() << "Hz\n";
186 cout << " - not present in this DCP.\n";
192 main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
194 shared_ptr<dcp::ReelSubtitleAsset> ms = reel->main_subtitle ();
199 cout << " Subtitle ID: " << ms->id();
201 if (ms->asset_ref().resolved()) {
202 list<shared_ptr<Subtitle> > subs = ms->asset()->subtitles ();
203 cout << "\n Subtitle: " << subs.size() << " subtitles";
204 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (ms->asset());
206 cout << " in " << iop->language() << "\n";
208 shared_ptr<SMPTESubtitleAsset> smpte = dynamic_pointer_cast<SMPTESubtitleAsset> (ms->asset());
209 if (smpte && smpte->language ()) {
210 cout << " in " << smpte->language().get() << "\n";
212 if (list_subtitles) {
213 BOOST_FOREACH (shared_ptr<Subtitle> k, subs) {
214 shared_ptr<SubtitleString> ks = dynamic_pointer_cast<SubtitleString> (k);
218 shared_ptr<SubtitleImage> is = dynamic_pointer_cast<SubtitleImage> (k);
225 cout << " - not present in this DCP.\n";
230 main (int argc, char* argv[])
232 bool subtitles = false;
233 bool picture = false;
234 bool decompress = false;
235 bool ignore_missing_assets = false;
236 optional<boost::filesystem::path> kdm;
237 optional<boost::filesystem::path> private_key;
239 int option_index = 0;
241 static struct option long_options[] = {
242 { "version", no_argument, 0, 'v' },
243 { "help", no_argument, 0, 'h' },
244 { "subtitles", no_argument, 0, 's' },
245 { "picture", no_argument, 0, 'p' },
246 { "decompress", no_argument, 0, 'd' },
247 { "ignore-missing-assets", no_argument, 0, 'A' },
248 { "kdm", required_argument, 0, 'B' },
249 { "private-key", required_argument, 0, 'C' },
253 int c = getopt_long (argc, argv, "vhspdAB:C:", long_options, &option_index);
261 cout << "libdcp version " << LIBDCP_VERSION << "\n";
276 ignore_missing_assets = true;
282 private_key = optarg;
287 if (argc <= optind || argc > (optind + 1)) {
292 if (!boost::filesystem::exists (argv[optind])) {
293 cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n";
297 list<shared_ptr<CPL> > cpls;
298 if (boost::filesystem::is_directory(argv[optind])) {
300 list<dcp::VerificationNote> notes;
302 dcp = new DCP (argv[optind]);
304 if (kdm && private_key) {
305 dcp->add(DecryptedKDM(EncryptedKDM(file_to_string(*kdm)), file_to_string(*private_key)));
307 } catch (FileError& e) {
308 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
310 } catch (DCPReadError& e) {
311 cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
313 } catch (KDMDecryptionError& e) {
314 cerr << e.what() << "\n";
318 cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n";
320 dcp::filter_notes (notes, ignore_missing_assets);
321 BOOST_FOREACH (dcp::VerificationNote i, notes) {
322 cerr << "Error: " << note_to_string(i) << "\n";
327 cpls.push_back (shared_ptr<CPL>(new CPL(boost::filesystem::path(argv[optind]))));
328 ignore_missing_assets = true;
331 dcp::Time total_time;
333 BOOST_FOREACH (shared_ptr<CPL> i, cpls) {
334 cout << " CPL: " << i->annotation_text() << "\n";
337 BOOST_FOREACH (shared_ptr<Reel> j, i->reels()) {
338 cout << " Reel " << R << "\n";
341 total_time += main_picture(j, picture, decompress);
342 } catch (UnresolvedRefError& e) {
343 if (!ignore_missing_assets) {
344 cerr << e.what() << " (for main picture)\n";
350 } catch (UnresolvedRefError& e) {
351 if (!ignore_missing_assets) {
352 cerr << e.what() << " (for main sound)\n";
357 main_subtitle (j, subtitles);
358 } catch (UnresolvedRefError& e) {
359 if (!ignore_missing_assets) {
360 cerr << e.what() << " (for main subtitle)\n";
368 cout << "Total: " << total_time.as_string(dcp::SMPTE) << "\n";