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