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