Missing finalize() in dcpdecryptmxf.
[libdcp.git] / tools / dcpdecryptmxf.cc
1 /*
2     Copyright (C) 2016-2021 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
35 #include "atmos_asset.h"
36 #include "atmos_asset_reader.h"
37 #include "atmos_asset_writer.h"
38 #include "atmos_frame.h"
39 #include "crypto_context.h"
40 #include "decrypted_kdm.h"
41 #include "encrypted_kdm.h"
42 #include "exceptions.h"
43 #include "key.h"
44 #include "mono_picture_asset.h"
45 #include "mono_picture_asset_writer.h"
46 #include "util.h"
47 #include <asdcp/AS_DCP.h>
48 #include <getopt.h>
49 #include <iostream>
50 #include <string>
51
52
53 using std::cerr;
54 using std::cout;
55 using std::shared_ptr;
56 using std::string;
57 using boost::optional;
58
59
60 static void
61 help (string n)
62 {
63         cerr << "Re-write a MXF (decrypting it if required)\n"
64              << "Syntax: " << n << " [OPTION] <MXF>]\n"
65              << "  --version          show libdcp version\n"
66              << "  -v, --verbose      be verbose\n"
67              << "  -h, --help         show this help\n"
68              << "  -o, --output       output filename\n"
69              << "  -k, --kdm          KDM file\n"
70              << "  -p, --private-key  private key file\n"
71              << "  -t, --type         MXF type: picture or atmos\n";
72 }
73
74 template <class T, class U>
75 void copy (T const& in, shared_ptr<U> writer)
76 {
77         auto reader = in.start_read();
78         for (int64_t i = 0; i < in.intrinsic_duration(); ++i) {
79                 auto frame = reader->get_frame (i);
80                 writer->write (frame->data(), frame->size());
81         }
82         writer->finalize();
83 };
84
85
86 int
87 main (int argc, char* argv[])
88 {
89         dcp::init ();
90
91         bool verbose = false;
92         optional<boost::filesystem::path> output_file;
93         optional<boost::filesystem::path> kdm_file;
94         optional<boost::filesystem::path> private_key_file;
95
96         enum class Type {
97                 PICTURE,
98                 ATMOS,
99         };
100
101         optional<Type> type;
102
103         int option_index = 0;
104         while (true) {
105                 struct option long_options[] = {
106                         { "version", no_argument, 0, 'A' },
107                         { "verbose", no_argument, 0, 'v' },
108                         { "help", no_argument, 0, 'h' },
109                         { "output", required_argument, 0, 'o'},
110                         { "kdm", required_argument, 0, 'k'},
111                         { "private-key", required_argument, 0, 'p'},
112                         { "type", required_argument, 0, 't' },
113                         { 0, 0, 0, 0 }
114                 };
115
116                 int c = getopt_long (argc, argv, "Avho:k:p:t:", long_options, &option_index);
117
118                 if (c == -1) {
119                         break;
120                 }
121
122                 switch (c) {
123                 case 'A':
124                         cout << "libdcp version " << LIBDCP_VERSION << "\n";
125                         exit (EXIT_SUCCESS);
126                 case 'v':
127                         verbose = true;
128                         break;
129                 case 'h':
130                         help (argv[0]);
131                         exit (EXIT_SUCCESS);
132                 case 'o':
133                         output_file = optarg;
134                         break;
135                 case 'k':
136                         kdm_file = optarg;
137                         break;
138                 case 'p':
139                         private_key_file = optarg;
140                         break;
141                 case 't':
142                         if (strcmp(optarg, "picture") == 0) {
143                                 type = Type::PICTURE;
144                         } else if (strcmp(optarg, "atmos") == 0) {
145                                 type = Type::ATMOS;
146                         } else {
147                                 cerr << "Unknown MXF type " << optarg << "\n";
148                                 exit (EXIT_FAILURE);
149                         }
150                         break;
151                 }
152         }
153
154         if (optind >= argc) {
155                 help (argv[0]);
156                 exit (EXIT_FAILURE);
157         }
158
159         boost::filesystem::path input_file = argv[optind];
160
161         if (!output_file) {
162                 cerr << "You must specify -o or --output\n";
163                 exit (EXIT_FAILURE);
164         }
165
166         if (!kdm_file) {
167                 cerr << "You must specify -k or --kdm\n";
168                 exit (EXIT_FAILURE);
169         }
170
171         if (!private_key_file) {
172                 cerr << "You must specify -p or --private-key\n";
173                 exit (EXIT_FAILURE);
174         }
175
176         if (!type) {
177                 cerr << "You must specify -t or --type\n";
178                 exit (EXIT_FAILURE);
179         }
180
181         dcp::EncryptedKDM encrypted_kdm (dcp::file_to_string (kdm_file.get ()));
182         dcp::DecryptedKDM decrypted_kdm (encrypted_kdm, dcp::file_to_string (private_key_file.get()));
183
184         auto add_key = [verbose](dcp::MXF& mxf, dcp::DecryptedKDM const& kdm) {
185                 auto key_id = mxf.key_id();
186                 if (key_id) {
187                         if (verbose) {
188                                 cout << "Asset is encrypted.\n";
189                         }
190                         auto keys = kdm.keys();
191                         auto key = std::find_if (keys.begin(), keys.end(), [key_id](dcp::DecryptedKDMKey const& k) { return k.id() == *key_id; });
192                         if (key == keys.end()) {
193                                 cout << "No key found in KDM.\n";
194                                 exit(EXIT_FAILURE);
195                         }
196                         if (verbose) {
197                                 cout << "Key found in KDM.\n";
198                         }
199                         mxf.set_key (key->key());
200                 }
201         };
202
203         try {
204                 switch (*type) {
205                 case Type::ATMOS:
206                 {
207                         dcp::AtmosAsset in (input_file);
208                         add_key (in, decrypted_kdm);
209                         dcp::AtmosAsset out (
210                                 in.edit_rate(),
211                                 in.first_frame(),
212                                 in.max_channel_count(),
213                                 in.max_object_count(),
214                                 in.atmos_version()
215                                 );
216                         auto writer = out.start_write (output_file.get());
217                         copy (in, writer);
218                         break;
219                 }
220                 case Type::PICTURE:
221                 {
222                         dcp::MonoPictureAsset in (input_file);
223                         add_key (in, decrypted_kdm);
224                         dcp::MonoPictureAsset out (in.edit_rate(), dcp::Standard::SMPTE);
225                         auto writer = out.start_write (output_file.get(), false);
226                         copy (in, writer);
227                         break;
228                 }
229                 }
230         } catch (dcp::ReadError& e) {
231                 cerr << "Read error: " << e.what() << "\n";
232                 return EXIT_FAILURE;
233         }
234
235         return 0;
236 }