Remove ReelEncryptableAsset and tidy up a bit.
[libdcp.git] / src / reel.cc
1 /*
2     Copyright (C) 2014-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 /** @file  src/reel.cc
36  *  @brief Reel class
37  */
38
39
40 #include "reel.h"
41 #include "util.h"
42 #include "picture_asset.h"
43 #include "mono_picture_asset.h"
44 #include "stereo_picture_asset.h"
45 #include "sound_asset.h"
46 #include "subtitle_asset.h"
47 #include "reel_mono_picture_asset.h"
48 #include "reel_stereo_picture_asset.h"
49 #include "reel_sound_asset.h"
50 #include "reel_interop_closed_caption_asset.h"
51 #include "reel_interop_subtitle_asset.h"
52 #include "reel_smpte_closed_caption_asset.h"
53 #include "reel_smpte_subtitle_asset.h"
54 #include "reel_subtitle_asset.h"
55 #include "reel_markers_asset.h"
56 #include "decrypted_kdm_key.h"
57 #include "decrypted_kdm.h"
58 #include "interop_subtitle_asset.h"
59 #include "smpte_subtitle_asset.h"
60 #include "reel_atmos_asset.h"
61 #include "reel_closed_caption_asset.h"
62 #include <libxml++/nodes/element.h>
63 #include <stdint.h>
64
65
66 using std::string;
67 using std::cout;
68 using std::min;
69 using std::make_shared;
70 using std::shared_ptr;
71 using std::dynamic_pointer_cast;
72 using std::vector;
73 using namespace dcp;
74
75
76 Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard)
77         : Object (remove_urn_uuid (node->string_child ("Id")))
78 {
79         auto asset_list = node->node_child ("AssetList");
80
81         auto main_picture = asset_list->optional_node_child ("MainPicture");
82         if (main_picture) {
83                 _main_picture = make_shared<ReelMonoPictureAsset>(main_picture);
84         }
85
86         auto main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
87         if (main_stereoscopic_picture) {
88                 _main_picture = make_shared<ReelStereoPictureAsset>(main_stereoscopic_picture);
89         }
90
91         auto main_sound = asset_list->optional_node_child ("MainSound");
92         if (main_sound) {
93                 _main_sound = make_shared<ReelSoundAsset>(main_sound);
94         }
95
96         auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
97         if (main_subtitle) {
98                 switch (standard) {
99                         case Standard::INTEROP:
100                                 _main_subtitle = make_shared<ReelInteropSubtitleAsset>(main_subtitle);
101                                 break;
102                         case Standard::SMPTE:
103                                 _main_subtitle = make_shared<ReelSMPTESubtitleAsset>(main_subtitle);
104                                 break;
105                 }
106         }
107
108         auto main_markers = asset_list->optional_node_child ("MainMarkers");
109         if (main_markers) {
110                 _main_markers = make_shared<ReelMarkersAsset>(main_markers);
111         }
112
113         /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
114         /* XXX: not sure if Interop supports multiple closed captions */
115         auto closed_captions = asset_list->node_children ("MainClosedCaption");
116         if (closed_captions.empty()) {
117                 closed_captions = asset_list->node_children ("ClosedCaption");
118         }
119         for (auto i: closed_captions) {
120                 switch (standard) {
121                         case Standard::INTEROP:
122                                 _closed_captions.push_back (make_shared<ReelInteropClosedCaptionAsset>(i));
123                                 break;
124                         case Standard::SMPTE:
125                                 _closed_captions.push_back (make_shared<ReelSMPTEClosedCaptionAsset>(i));
126                                 break;
127                 }
128         }
129
130         auto atmos = asset_list->optional_node_child ("AuxData");
131         if (atmos) {
132                 _atmos = make_shared<ReelAtmosAsset>(atmos);
133         }
134
135         node->ignore_child ("AnnotationText");
136         node->done ();
137 }
138
139
140 xmlpp::Element *
141 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
142 {
143         auto reel = node->add_child ("Reel");
144         reel->add_child("Id")->add_child_text("urn:uuid:" + _id);
145         xmlpp::Element* asset_list = reel->add_child ("AssetList");
146
147         if (_main_markers) {
148                 _main_markers->write_to_cpl (asset_list, standard);
149         }
150
151         if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
152                 /* Mono pictures come before other stuff... */
153                 _main_picture->write_to_cpl (asset_list, standard);
154         }
155
156         if (_main_sound) {
157                 _main_sound->write_to_cpl (asset_list, standard);
158         }
159
160         if (_main_subtitle) {
161                 _main_subtitle->write_to_cpl (asset_list, standard);
162         }
163
164         for (auto i: _closed_captions) {
165                 i->write_to_cpl (asset_list, standard);
166         }
167
168         if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
169                 /* ... but stereo pictures must come after */
170                 _main_picture->write_to_cpl (asset_list, standard);
171         }
172
173         if (_atmos) {
174                 _atmos->write_to_cpl (asset_list, standard);
175         }
176
177         return asset_list;
178 }
179
180
181 bool
182 Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
183 {
184         if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
185                 note (NoteType::ERROR, "Reel: picture assets differ");
186                 return false;
187         }
188
189         if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
190                 return false;
191         }
192
193         if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
194                 note (NoteType::ERROR, "Reel: sound assets differ");
195                 return false;
196         }
197
198         if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
199                 return false;
200         }
201
202         if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
203                 note (NoteType::ERROR, "Reel: subtitle assets differ");
204                 return false;
205         }
206
207         bool same_type = false;
208
209         {
210                 auto interop = dynamic_pointer_cast<ReelInteropSubtitleAsset>(_main_subtitle);
211                 auto interop_other = dynamic_pointer_cast<ReelInteropSubtitleAsset>(other->_main_subtitle);
212                 if (interop && interop_other) {
213                         same_type = true;
214                         if (!interop->equals(interop_other, opt, note)) {
215                                 return false;
216                         }
217                 }
218         }
219
220         {
221                 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
222                 auto smpte_other = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(other->_main_subtitle);
223                 if (smpte && smpte_other) {
224                         same_type = true;
225                         if (!smpte->equals(smpte_other, opt, note)) {
226                                 return false;
227                         }
228                 }
229         }
230
231         if ((_main_subtitle || other->_main_subtitle) && !same_type) {
232                 return false;
233         }
234
235         if ((_main_markers && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
236                 note (NoteType::ERROR, "Reel: one has markers and the other does not");
237                 return false;
238         }
239
240         if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
241                 note (NoteType::ERROR, "Reel: marker assets differ");
242                 return false;
243         }
244
245         if (_closed_captions.size() != other->_closed_captions.size()) {
246                 return false;
247         }
248
249         auto i = _closed_captions.begin();
250         auto j = other->_closed_captions.begin();
251         while (i != _closed_captions.end()) {
252                 if (!(*i)->equals(*j, opt, note)) {
253                         return false;
254                 }
255                 ++i;
256                 ++j;
257         }
258
259         if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
260                 note (NoteType::ERROR, "Reel: atmos assets differ");
261                 return false;
262         }
263
264         if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
265                 return false;
266         }
267
268         return true;
269 }
270
271
272 bool
273 Reel::any_encrypted () const
274 {
275         auto ecc = false;
276         for (auto i: _closed_captions) {
277                 if (i->encrypted()) {
278                         ecc = true;
279                 }
280         }
281
282         return (
283                 (_main_picture && _main_picture->encrypted()) ||
284                 (_main_sound && _main_sound->encrypted()) ||
285                 (_main_subtitle && _main_subtitle->encrypted()) ||
286                 ecc ||
287                 (_atmos && _atmos->encrypted())
288                 );
289 }
290
291
292 bool
293 Reel::all_encrypted () const
294 {
295         auto ecc = true;
296         for (auto i: _closed_captions) {
297                 if (!i->encrypted()) {
298                         ecc = false;
299                 }
300         }
301
302         return (
303                 (!_main_picture || _main_picture->encrypted()) &&
304                 (!_main_sound || _main_sound->encrypted()) &&
305                 (!_main_subtitle || _main_subtitle->encrypted()) &&
306                 ecc &&
307                 (!_atmos || _atmos->encrypted())
308                );
309 }
310
311
312 void
313 Reel::add (DecryptedKDM const & kdm)
314 {
315         auto keys = kdm.keys ();
316
317         for (auto const& i: keys) {
318                 if (_main_picture && i.id() == _main_picture->key_id()) {
319                         _main_picture->asset()->set_key (i.key());
320                 }
321                 if (_main_sound && i.id() == _main_sound->key_id()) {
322                         _main_sound->asset()->set_key (i.key());
323                 }
324                 if (_main_subtitle) {
325                         auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_picture);
326                         if (smpte && i.id() == smpte->key_id()) {
327                                 smpte->smpte_asset()->set_key(i.key());
328                         }
329                 }
330                 for (auto j: _closed_captions) {
331                         auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
332                         if (smpte && i.id() == smpte->key_id()) {
333                                 smpte->smpte_asset()->set_key(i.key());
334                         }
335                 }
336                 if (_atmos && i.id() == _atmos->key_id()) {
337                         _atmos->asset()->set_key (i.key());
338                 }
339         }
340 }
341
342
343 void
344 Reel::add (shared_ptr<ReelAsset> asset)
345 {
346         auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
347         auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
348         auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
349         auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
350         auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
351         auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
352         if (p) {
353                 _main_picture = p;
354         } else if (so) {
355                 _main_sound = so;
356         } else if (su) {
357                 _main_subtitle = su;
358         } else if (m) {
359                 _main_markers = m;
360         } else if (c) {
361                 _closed_captions.push_back (c);
362         } else if (a) {
363                 _atmos = a;
364         }
365 }
366
367
368 vector<shared_ptr<ReelAsset>>
369 Reel::assets () const
370 {
371         vector<shared_ptr<ReelAsset>> a;
372         if (_main_picture) {
373                 a.push_back (_main_picture);
374         }
375         if (_main_sound) {
376                 a.push_back (_main_sound);
377         }
378         if (_main_subtitle) {
379                 a.push_back (_main_subtitle);
380         }
381         std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
382         if (_atmos) {
383                 a.push_back (_atmos);
384         }
385         return a;
386 }
387
388
389 void
390 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
391 {
392         if (_main_picture) {
393                 _main_picture->asset_ref().resolve(assets);
394         }
395
396         if (_main_sound) {
397                 _main_sound->asset_ref().resolve(assets);
398         }
399
400         if (_main_subtitle) {
401                 _main_subtitle->asset_ref().resolve(assets);
402
403                 /* Interop subtitle handling is all special cases */
404                 if (_main_subtitle->asset_ref().resolved()) {
405                         auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
406                         if (iop) {
407                                 iop->resolve_fonts (assets);
408                         }
409                 }
410         }
411
412         for (auto i: _closed_captions) {
413                 i->asset_ref().resolve(assets);
414
415                 /* Interop subtitle handling is all special cases */
416                 if (i->asset_ref().resolved()) {
417                         auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
418                         if (iop) {
419                                 iop->resolve_fonts (assets);
420                         }
421                 }
422         }
423
424         if (_atmos) {
425                 _atmos->asset_ref().resolve (assets);
426         }
427 }
428
429
430 int64_t
431 Reel::duration () const
432 {
433         if (_main_picture) {
434                 return _main_picture->actual_duration();
435         }
436
437         int64_t d = INT64_MAX;
438
439         if (_main_sound) {
440                 d = min (d, _main_sound->actual_duration());
441         }
442         if (_main_subtitle) {
443                 d = min (d, _main_subtitle->actual_duration());
444         }
445         if (_main_markers) {
446                 d = min (d, _main_markers->actual_duration());
447         }
448         for (auto i: _closed_captions) {
449                 d = min (d, i->actual_duration());
450         }
451         if (_atmos) {
452                 d = min (d, _atmos->actual_duration());
453         }
454
455         DCP_ASSERT (d < INT64_MAX);
456
457         return d;
458 }