Allow dcpinfo to read a CPL.
[libdcp.git] / tools / dcpinfo.cc
1 /*
2     Copyright (C) 2012-2014 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 "cpl.h"
47 #include "common.h"
48 #include <getopt.h>
49 #include <boost/filesystem.hpp>
50 #include <boost/foreach.hpp>
51 #include <iostream>
52 #include <cstdlib>
53
54 using std::string;
55 using std::cerr;
56 using std::cout;
57 using std::list;
58 using boost::shared_ptr;
59 using boost::dynamic_pointer_cast;
60 using namespace dcp;
61
62 static void
63 help (string n)
64 {
65         cerr << "Syntax: " << n << " [options] [<DCP>] [<CPL>]\n"
66              << "  -s, --subtitles              list all subtitles\n"
67              << "  -k, --keep-going             carry on in the event of errors, if possible\n"
68              << "      --ignore-missing-assets  ignore missing asset files\n";
69 }
70
71 static void
72 main_picture (shared_ptr<Reel> reel)
73 {
74         if (reel->main_picture()) {
75                 cout << "      Picture ID:  " << reel->main_picture()->id()
76                      << " entry " << reel->main_picture()->entry_point()
77                      << " duration " << reel->main_picture()->duration()
78                      << " intrinsic " << reel->main_picture()->intrinsic_duration() << "\n";
79                 if (reel->main_picture()->asset()) {
80                         cout << "      Picture:     "
81                              << reel->main_picture()->asset()->size().width
82                              << "x"
83                              << reel->main_picture()->asset()->size().height << "\n";
84                 }
85         }
86 }
87
88 static void
89 main_sound (shared_ptr<Reel> reel)
90 {
91         if (reel->main_sound()) {
92                 cout << "      Sound ID:    " << reel->main_sound()->id()
93                      << " entry " << reel->main_picture()->entry_point()
94                      << " duration " << reel->main_picture()->duration()
95                      << " intrinsic " << reel->main_picture()->intrinsic_duration() << "\n";
96                 if (reel->main_sound()->asset()) {
97                         cout << "      Sound:       "
98                              << reel->main_sound()->asset()->channels()
99                              << " channels at "
100                              << reel->main_sound()->asset()->sampling_rate() << "Hz\n";
101                 }
102         }
103 }
104
105 static void
106 main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
107 {
108         if (!reel->main_subtitle()) {
109                 return;
110         }
111
112         cout << "      Subtitle ID: " << reel->main_subtitle()->id() << "\n";
113
114         list<SubtitleString> subs = reel->main_subtitle()->asset()->subtitles ();
115         cout << "      Subtitle:    " << subs.size() << " subtitles";
116         shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (reel->main_subtitle()->asset());
117         if (iop) {
118                 cout << " in " << iop->language() << "\n";
119         }
120         shared_ptr<SMPTESubtitleAsset> smpte = dynamic_pointer_cast<SMPTESubtitleAsset> (reel->main_subtitle()->asset());
121         if (smpte && smpte->language ()) {
122                 cout << " in " << smpte->language().get() << "\n";
123         }
124         if (list_subtitles) {
125                 BOOST_FOREACH (SubtitleString const& k, subs) {
126                         cout << k << "\n";
127                 }
128         }
129 }
130
131 int
132 main (int argc, char* argv[])
133 {
134         bool subtitles = false;
135         bool keep_going = false;
136         bool ignore_missing_assets = false;
137
138         int option_index = 0;
139         while (true) {
140                 static struct option long_options[] = {
141                         { "version", no_argument, 0, 'v' },
142                         { "help", no_argument, 0, 'h' },
143                         { "subtitles", no_argument, 0, 's' },
144                         { "keep-going", no_argument, 0, 'k' },
145                         { "ignore-missing-assets", no_argument, 0, 'A' },
146                         { 0, 0, 0, 0 }
147                 };
148
149                 int c = getopt_long (argc, argv, "vhskA", long_options, &option_index);
150
151                 if (c == -1) {
152                         break;
153                 }
154
155                 switch (c) {
156                 case 'v':
157                         cout << "libdcp version " << LIBDCP_VERSION << "\n";
158                         exit (EXIT_SUCCESS);
159                 case 'h':
160                         help (argv[0]);
161                         exit (EXIT_SUCCESS);
162                 case 's':
163                         subtitles = true;
164                         break;
165                 case 'k':
166                         keep_going = true;
167                         break;
168                 case 'A':
169                         ignore_missing_assets = true;
170                         break;
171                 }
172         }
173
174         if (argc <= optind || argc > (optind + 1)) {
175                 help (argv[0]);
176                 exit (EXIT_FAILURE);
177         }
178
179         if (!boost::filesystem::exists (argv[optind])) {
180                 cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n";
181                 exit (EXIT_FAILURE);
182         }
183
184         list<shared_ptr<CPL> > cpls;
185         if (boost::filesystem::is_directory(argv[optind])) {
186                 DCP* dcp = 0;
187                 DCP::ReadErrors errors;
188                 try {
189                         dcp = new DCP (argv[optind]);
190                         dcp->read (keep_going, &errors);
191                 } catch (FileError& e) {
192                         cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
193                         exit (EXIT_FAILURE);
194                 } catch (DCPReadError& e) {
195                         cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
196                         exit (EXIT_FAILURE);
197                 }
198
199                 cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n";
200
201                 dcp::filter_errors (errors, ignore_missing_assets);
202                 for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) {
203                         cerr << "Error: " << (*i)->what() << "\n";
204                 }
205
206                 cpls = dcp->cpls ();
207         } else {
208                 cpls.push_back (shared_ptr<CPL>(new CPL(boost::filesystem::path(argv[optind]))));
209                 keep_going = true;
210                 ignore_missing_assets = true;
211         }
212
213         BOOST_FOREACH (shared_ptr<CPL> i, cpls) {
214                 cout << "  CPL: " << i->annotation_text() << "\n";
215
216                 int R = 1;
217                 BOOST_FOREACH (shared_ptr<Reel> j, i->reels()) {
218                         cout << "    Reel " << R << "\n";
219
220                         try {
221                                 main_picture (j);
222                         } catch (UnresolvedRefError& e) {
223                                 if (keep_going) {
224                                         if (!ignore_missing_assets) {
225                                                 cerr << e.what() << " (for main picture)\n";
226                                         }
227                                 } else {
228                                         throw;
229                                 }
230                         }
231
232                         try {
233                                 main_sound (j);
234                         } catch (UnresolvedRefError& e) {
235                                 if (keep_going) {
236                                         if (!ignore_missing_assets) {
237                                                 cerr << e.what() << " (for main sound)\n";
238                                         }
239                                 } else {
240                                         throw;
241                                 }
242                         }
243
244                         try {
245                                 main_subtitle (j, subtitles);
246                         } catch (UnresolvedRefError& e) {
247                                 if (keep_going) {
248                                         if (!ignore_missing_assets) {
249                                                 cerr << e.what() << " (for main subtitle)\n";
250                                         }
251                                 } else {
252                                         throw;
253                                 }
254                         }
255
256                         ++R;
257                 }
258         }
259
260         return 0;
261 }