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