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