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