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