Try to fix Windows build.
[libdcp.git] / tools / dcpinfo.cc
1 /*
2     Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
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.
10
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.
15
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/>.
18
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
23     including the two.
24
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.
32 */
33
34 #include "dcp.h"
35 #include "exceptions.h"
36 #include "reel.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 "interop_subtitle_asset.h"
45 #include "smpte_subtitle_asset.h"
46 #include "mono_picture_asset.h"
47 #include "encrypted_kdm.h"
48 #include "decrypted_kdm.h"
49 #include "cpl.h"
50 #include "common.h"
51 #include <getopt.h>
52 #include <boost/filesystem.hpp>
53 #include <boost/foreach.hpp>
54 #include <iostream>
55 #include <cstdlib>
56 #include <inttypes.h>
57
58 using std::string;
59 using std::cerr;
60 using std::cout;
61 using std::list;
62 using std::pair;
63 using std::min;
64 using std::max;
65 using std::exception;
66 using boost::shared_ptr;
67 using boost::dynamic_pointer_cast;
68 using boost::optional;
69 using namespace dcp;
70
71 static void
72 help (string n)
73 {
74         cerr << "Syntax: " << n << " [options] [<DCP>] [<CPL>]\n"
75              << "  -s, --subtitles              list all subtitles\n"
76              << "  -p, --picture                analyse picture\n"
77              << "  -d, --decompress             decompress picture when analysing (this is slow)\n"
78              << "  -k, --keep-going             carry on in the event of errors, if possible\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";
82 }
83
84 static double
85 mbits_per_second (int size, Fraction frame_rate)
86 {
87         return size * 8 * frame_rate.as_float() / 1e6;
88 }
89
90 static void
91 main_picture (shared_ptr<Reel> reel, bool analyse, bool decompress)
92 {
93         if (!reel->main_picture()) {
94                 return;
95         }
96
97         cout << "      Picture ID:  " << reel->main_picture()->id()
98              << " entry " << reel->main_picture()->entry_point()
99              << " duration " << reel->main_picture()->duration()
100              << " intrinsic " << reel->main_picture()->intrinsic_duration();
101
102         if (reel->main_picture()->asset_ref().resolved()) {
103                 if (reel->main_picture()->asset()) {
104                         cout << "\n      Picture:     "
105                              << reel->main_picture()->asset()->size().width
106                              << "x"
107                              << reel->main_picture()->asset()->size().height << "\n";
108                 }
109
110                 shared_ptr<MonoPictureAsset> ma = dynamic_pointer_cast<MonoPictureAsset>(reel->main_picture()->asset());
111                 if (analyse && ma) {
112                         shared_ptr<MonoPictureAssetReader> reader = ma->start_read ();
113                         pair<int, int> j2k_size_range (INT_MAX, 0);
114                         for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) {
115                                 shared_ptr<const MonoPictureFrame> frame = reader->get_frame (i);
116                                 printf("Frame %" PRId64 " J2K size %7d", i, frame->j2k_size());
117                                 j2k_size_range.first = min(j2k_size_range.first, frame->j2k_size());
118                                 j2k_size_range.second = max(j2k_size_range.second, frame->j2k_size());
119
120                                 if (decompress) {
121                                         try {
122                                                 frame->xyz_image();
123                                                 printf(" decrypted OK");
124                                         } catch (exception& e) {
125                                                 printf(" decryption FAILED");
126                                         }
127                                 }
128
129                                 printf("\n");
130
131                         }
132                         printf(
133                                 "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n",
134                                 j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()),
135                                 j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate())
136                                 );
137                 }
138         } else {
139                 cout << " - not present in this DCP.\n";
140         }
141 }
142
143 static void
144 main_sound (shared_ptr<Reel> reel)
145 {
146         if (reel->main_sound()) {
147                 cout << "      Sound ID:    " << reel->main_sound()->id()
148                      << " entry " << reel->main_picture()->entry_point()
149                      << " duration " << reel->main_picture()->duration()
150                      << " intrinsic " << reel->main_picture()->intrinsic_duration();
151                 if (reel->main_sound()->asset_ref().resolved()) {
152                         if (reel->main_sound()->asset()) {
153                                 cout << "\n      Sound:       "
154                                      << reel->main_sound()->asset()->channels()
155                                      << " channels at "
156                                      << reel->main_sound()->asset()->sampling_rate() << "Hz\n";
157                         }
158                 } else {
159                         cout << " - not present in this DCP.\n";
160                 }
161         }
162 }
163
164 static void
165 main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
166 {
167         if (!reel->main_subtitle()) {
168                 return;
169         }
170
171         cout << "      Subtitle ID: " << reel->main_subtitle()->id();
172
173         if (reel->main_subtitle()->asset_ref().resolved()) {
174                 list<SubtitleString> subs = reel->main_subtitle()->asset()->subtitles ();
175                 cout << "\n      Subtitle:    " << subs.size() << " subtitles";
176                 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (reel->main_subtitle()->asset());
177                 if (iop) {
178                         cout << " in " << iop->language() << "\n";
179                 }
180                 shared_ptr<SMPTESubtitleAsset> smpte = dynamic_pointer_cast<SMPTESubtitleAsset> (reel->main_subtitle()->asset());
181                 if (smpte && smpte->language ()) {
182                         cout << " in " << smpte->language().get() << "\n";
183                 }
184                 if (list_subtitles) {
185                         BOOST_FOREACH (SubtitleString const& k, subs) {
186                                 cout << k << "\n";
187                         }
188                 }
189         } else {
190                 cout << " - not present in this DCP.\n";
191         }
192 }
193
194 int
195 main (int argc, char* argv[])
196 {
197         bool subtitles = false;
198         bool keep_going = false;
199         bool picture = false;
200         bool decompress = false;
201         bool ignore_missing_assets = false;
202         optional<boost::filesystem::path> kdm;
203         optional<boost::filesystem::path> private_key;
204
205         int option_index = 0;
206         while (true) {
207                 static struct option long_options[] = {
208                         { "version", no_argument, 0, 'v' },
209                         { "help", no_argument, 0, 'h' },
210                         { "subtitles", no_argument, 0, 's' },
211                         { "keep-going", no_argument, 0, 'k' },
212                         { "picture", no_argument, 0, 'p' },
213                         { "decompress", no_argument, 0, 'd' },
214                         { "ignore-missing-assets", no_argument, 0, 'A' },
215                         { "kdm", required_argument, 0, 'B' },
216                         { "private-key", required_argument, 0, 'C' },
217                         { 0, 0, 0, 0 }
218                 };
219
220                 int c = getopt_long (argc, argv, "vhskpdAB:C:", long_options, &option_index);
221
222                 if (c == -1) {
223                         break;
224                 }
225
226                 switch (c) {
227                 case 'v':
228                         cout << "libdcp version " << LIBDCP_VERSION << "\n";
229                         exit (EXIT_SUCCESS);
230                 case 'h':
231                         help (argv[0]);
232                         exit (EXIT_SUCCESS);
233                 case 's':
234                         subtitles = true;
235                         break;
236                 case 'k':
237                         keep_going = true;
238                         break;
239                 case 'p':
240                         picture = true;
241                         break;
242                 case 'd':
243                         decompress = true;
244                         break;
245                 case 'A':
246                         ignore_missing_assets = true;
247                         break;
248                 case 'B':
249                         kdm = optarg;
250                         break;
251                 case 'C':
252                         private_key = optarg;
253                         break;
254                 }
255         }
256
257         if (argc <= optind || argc > (optind + 1)) {
258                 help (argv[0]);
259                 exit (EXIT_FAILURE);
260         }
261
262         if (!boost::filesystem::exists (argv[optind])) {
263                 cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n";
264                 exit (EXIT_FAILURE);
265         }
266
267         list<shared_ptr<CPL> > cpls;
268         if (boost::filesystem::is_directory(argv[optind])) {
269                 DCP* dcp = 0;
270                 DCP::ReadErrors errors;
271                 try {
272                         dcp = new DCP (argv[optind]);
273                         dcp->read (keep_going, &errors);
274                         if (kdm && private_key) {
275                                 dcp->add(DecryptedKDM(EncryptedKDM(file_to_string(*kdm)), file_to_string(*private_key)));
276                         }
277                 } catch (FileError& e) {
278                         cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
279                         exit (EXIT_FAILURE);
280                 } catch (DCPReadError& e) {
281                         cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
282                         exit (EXIT_FAILURE);
283                 } catch (KDMDecryptionError& e) {
284                         cerr << e.what() << "\n";
285                         exit (EXIT_FAILURE);
286                 }
287
288                 cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n";
289
290                 dcp::filter_errors (errors, ignore_missing_assets);
291                 for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) {
292                         cerr << "Error: " << (*i)->what() << "\n";
293                 }
294
295                 cpls = dcp->cpls ();
296         } else {
297                 cpls.push_back (shared_ptr<CPL>(new CPL(boost::filesystem::path(argv[optind]))));
298                 keep_going = true;
299                 ignore_missing_assets = true;
300         }
301
302         BOOST_FOREACH (shared_ptr<CPL> i, cpls) {
303                 cout << "  CPL: " << i->annotation_text() << "\n";
304
305                 int R = 1;
306                 BOOST_FOREACH (shared_ptr<Reel> j, i->reels()) {
307                         cout << "    Reel " << R << "\n";
308
309                         try {
310                                 main_picture (j, picture, decompress);
311                         } catch (UnresolvedRefError& e) {
312                                 if (keep_going) {
313                                         if (!ignore_missing_assets) {
314                                                 cerr << e.what() << " (for main picture)\n";
315                                         }
316                                 } else {
317                                         throw;
318                                 }
319                         }
320
321                         try {
322                                 main_sound (j);
323                         } catch (UnresolvedRefError& e) {
324                                 if (keep_going) {
325                                         if (!ignore_missing_assets) {
326                                                 cerr << e.what() << " (for main sound)\n";
327                                         }
328                                 } else {
329                                         throw;
330                                 }
331                         }
332
333                         try {
334                                 main_subtitle (j, subtitles);
335                         } catch (UnresolvedRefError& e) {
336                                 if (keep_going) {
337                                         if (!ignore_missing_assets) {
338                                                 cerr << e.what() << " (for main subtitle)\n";
339                                         }
340                                 } else {
341                                         throw;
342                                 }
343                         }
344
345                         ++R;
346                 }
347         }
348
349         return 0;
350 }