Fix problems when adding KDMs to a VF, before adding the OV.
[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         give_kdm_to_assets (kdm);
316         /* We have to keep the KDMs that we are given, as they will not be passed to unresolved assets.
317          * After we resolve some assets we will re-call give_kdm_to_assets() with all the KDMs that
318          * we have been given so far.
319          */
320         _kdms.push_back (kdm);
321 }
322
323
324 void
325 Reel::give_kdm_to_assets (DecryptedKDM const & kdm)
326 {
327         for (auto const& i: kdm.keys()) {
328                 if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) {
329                         _main_picture->asset()->set_key (i.key());
330                 }
331                 if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) {
332                         _main_sound->asset()->set_key (i.key());
333                 }
334                 if (_main_subtitle) {
335                         auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
336                         if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
337                                 smpte->smpte_asset()->set_key(i.key());
338                         }
339                 }
340                 for (auto j: _closed_captions) {
341                         auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
342                         if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
343                                 smpte->smpte_asset()->set_key(i.key());
344                         }
345                 }
346                 if (_atmos && i.id() == _atmos->key_id() && _atmos->asset_ref().resolved()) {
347                         _atmos->asset()->set_key (i.key());
348                 }
349         }
350 }
351
352
353 void
354 Reel::add (shared_ptr<ReelAsset> asset)
355 {
356         auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
357         auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
358         auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
359         auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
360         auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
361         auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
362         if (p) {
363                 _main_picture = p;
364         } else if (so) {
365                 _main_sound = so;
366         } else if (su) {
367                 _main_subtitle = su;
368         } else if (m) {
369                 _main_markers = m;
370         } else if (c) {
371                 _closed_captions.push_back (c);
372         } else if (a) {
373                 _atmos = a;
374         }
375 }
376
377
378 vector<shared_ptr<ReelAsset>>
379 Reel::assets () const
380 {
381         vector<shared_ptr<ReelAsset>> a;
382         if (_main_picture) {
383                 a.push_back (_main_picture);
384         }
385         if (_main_sound) {
386                 a.push_back (_main_sound);
387         }
388         if (_main_subtitle) {
389                 a.push_back (_main_subtitle);
390         }
391         std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
392         if (_atmos) {
393                 a.push_back (_atmos);
394         }
395         return a;
396 }
397
398
399 void
400 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
401 {
402         if (_main_picture) {
403                 _main_picture->asset_ref().resolve(assets);
404         }
405
406         if (_main_sound) {
407                 _main_sound->asset_ref().resolve(assets);
408         }
409
410         if (_main_subtitle) {
411                 _main_subtitle->asset_ref().resolve(assets);
412
413                 /* Interop subtitle handling is all special cases */
414                 if (_main_subtitle->asset_ref().resolved()) {
415                         auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
416                         if (iop) {
417                                 iop->resolve_fonts (assets);
418                         }
419                 }
420         }
421
422         for (auto i: _closed_captions) {
423                 i->asset_ref().resolve(assets);
424
425                 /* Interop subtitle handling is all special cases */
426                 if (i->asset_ref().resolved()) {
427                         auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
428                         if (iop) {
429                                 iop->resolve_fonts (assets);
430                         }
431                 }
432         }
433
434         if (_atmos) {
435                 _atmos->asset_ref().resolve (assets);
436         }
437
438         for (auto const& i: _kdms) {
439                 give_kdm_to_assets (i);
440         }
441 }
442
443
444 int64_t
445 Reel::duration () const
446 {
447         if (_main_picture) {
448                 return _main_picture->actual_duration();
449         }
450
451         int64_t d = INT64_MAX;
452
453         if (_main_sound) {
454                 d = min (d, _main_sound->actual_duration());
455         }
456         if (_main_subtitle) {
457                 d = min (d, _main_subtitle->actual_duration());
458         }
459         if (_main_markers) {
460                 d = min (d, _main_markers->actual_duration());
461         }
462         for (auto i: _closed_captions) {
463                 d = min (d, i->actual_duration());
464         }
465         if (_atmos) {
466                 d = min (d, _atmos->actual_duration());
467         }
468
469         DCP_ASSERT (d < INT64_MAX);
470
471         return d;
472 }