Bump waf to 2.0.27.
[libdcp.git] / src / reel.cc
1 /*
2     Copyright (C) 2014-2017 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 "decrypted_kdm_key.h"
46 #include "decrypted_kdm.h"
47 #include "interop_subtitle_asset.h"
48 #include "smpte_subtitle_asset.h"
49 #include "reel_atmos_asset.h"
50 #include "reel_closed_caption_asset.h"
51 #include <libxml++/nodes/element.h>
52 #include <boost/foreach.hpp>
53
54 using std::string;
55 using std::list;
56 using std::cout;
57 using std::max;
58 using boost::shared_ptr;
59 using boost::dynamic_pointer_cast;
60 using namespace dcp;
61
62 Reel::Reel (boost::shared_ptr<const cxml::Node> node)
63         : Object (remove_urn_uuid (node->string_child ("Id")))
64 {
65         shared_ptr<cxml::Node> asset_list = node->node_child ("AssetList");
66
67         shared_ptr<cxml::Node> main_picture = asset_list->optional_node_child ("MainPicture");
68         if (main_picture) {
69                 _main_picture.reset (new ReelMonoPictureAsset (main_picture));
70         }
71
72         shared_ptr<cxml::Node> main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
73         if (main_stereoscopic_picture) {
74                 _main_picture.reset (new ReelStereoPictureAsset (main_stereoscopic_picture));
75         }
76
77         shared_ptr<cxml::Node> main_sound = asset_list->optional_node_child ("MainSound");
78         if (main_sound) {
79                 _main_sound.reset (new ReelSoundAsset (main_sound));
80         }
81
82         shared_ptr<cxml::Node> main_subtitle = asset_list->optional_node_child ("MainSubtitle");
83         if (main_subtitle) {
84                 _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle));
85         }
86
87         /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
88         /* XXX: not sure if Interop supports multiple closed captions */
89         list<shared_ptr<cxml::Node> > closed_captions = asset_list->node_children ("MainClosedCaption");
90         if (closed_captions.empty()) {
91                 closed_captions = asset_list->node_children ("ClosedCaption");
92         }
93         BOOST_FOREACH (shared_ptr<cxml::Node> i, closed_captions) {
94                 _closed_captions.push_back (shared_ptr<ReelClosedCaptionAsset>(new ReelClosedCaptionAsset(i)));
95         }
96
97         shared_ptr<cxml::Node> atmos = asset_list->optional_node_child ("AuxData");
98         if (atmos) {
99                 _atmos.reset (new ReelAtmosAsset (atmos));
100         }
101
102         node->ignore_child ("AnnotationText");
103         node->done ();
104 }
105
106 void
107 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
108 {
109         xmlpp::Element* reel = node->add_child ("Reel");
110         reel->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid());
111         xmlpp::Element* asset_list = reel->add_child ("AssetList");
112
113         if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
114                 /* Mono pictures come before other stuff... */
115                 _main_picture->write_to_cpl (asset_list, standard);
116         }
117
118         if (_main_sound) {
119                 _main_sound->write_to_cpl (asset_list, standard);
120         }
121
122         if (_main_subtitle) {
123                 _main_subtitle->write_to_cpl (asset_list, standard);
124         }
125
126         BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
127                 i->write_to_cpl (asset_list, standard);
128         }
129
130         if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
131                 /* ... but stereo pictures must come after */
132                 _main_picture->write_to_cpl (asset_list, standard);
133         }
134
135         if (_atmos) {
136                 _atmos->write_to_cpl (asset_list, standard);
137         }
138 }
139
140 bool
141 Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
142 {
143         if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
144                 note (DCP_ERROR, "Reel: picture assets differ");
145                 return false;
146         }
147
148         if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
149                 return false;
150         }
151
152         if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
153                 note (DCP_ERROR, "Reel: sound assets differ");
154                 return false;
155         }
156
157         if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
158                 return false;
159         }
160
161         if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
162                 note (DCP_ERROR, "Reel: subtitle assets differ");
163                 return false;
164         }
165
166         if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, note)) {
167                 return false;
168         }
169
170         if (_closed_captions.size() != other->_closed_captions.size()) {
171                 return false;
172         }
173
174         list<shared_ptr<ReelClosedCaptionAsset> >::const_iterator i = _closed_captions.begin();
175         list<shared_ptr<ReelClosedCaptionAsset> >::const_iterator j = other->_closed_captions.begin();
176         while (i != _closed_captions.end()) {
177                 if (!(*i)->equals(*j, opt, note)) {
178                         return false;
179                 }
180                 ++i;
181                 ++j;
182         }
183
184         if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
185                 note (DCP_ERROR, "Reel: atmos assets differ");
186                 return false;
187         }
188
189         if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
190                 return false;
191         }
192
193         return true;
194 }
195
196 bool
197 Reel::encrypted () const
198 {
199         bool ecc = false;
200         BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
201                 if (i->encrypted()) {
202                         ecc = true;
203                 }
204         }
205
206         return (
207                 (_main_picture && _main_picture->encrypted ()) ||
208                 (_main_sound && _main_sound->encrypted ()) ||
209                 (_main_subtitle && _main_subtitle->encrypted ()) ||
210                 ecc ||
211                 (_atmos && _atmos->encrypted ())
212                 );
213 }
214
215 void
216 Reel::add (DecryptedKDM const & kdm)
217 {
218         list<DecryptedKDMKey> keys = kdm.keys ();
219
220         for (list<DecryptedKDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) {
221                 if (_main_picture && i->id() == _main_picture->key_id()) {
222                         _main_picture->asset()->set_key (i->key ());
223                 }
224                 if (_main_sound && i->id() == _main_sound->key_id()) {
225                         _main_sound->asset()->set_key (i->key ());
226                 }
227                 if (_main_subtitle && i->id() == _main_subtitle->key_id()) {
228                         shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (_main_subtitle->asset());
229                         if (s) {
230                                 s->set_key (i->key ());
231                         }
232                 }
233                 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> j, _closed_captions) {
234                         if (i->id() == j->key_id()) {
235                                 shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (j->asset());
236                                 if (s) {
237                                         s->set_key (i->key ());
238                                 }
239                         }
240                 }
241                 if (_atmos && i->id() == _atmos->key_id()) {
242                         _atmos->asset()->set_key (i->key ());
243                 }
244         }
245 }
246
247 void
248 Reel::add (shared_ptr<ReelAsset> asset)
249 {
250         shared_ptr<ReelPictureAsset> p = dynamic_pointer_cast<ReelPictureAsset> (asset);
251         shared_ptr<ReelSoundAsset> so = dynamic_pointer_cast<ReelSoundAsset> (asset);
252         shared_ptr<ReelSubtitleAsset> su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
253         shared_ptr<ReelClosedCaptionAsset> c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
254         shared_ptr<ReelAtmosAsset> a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
255         if (p) {
256                 _main_picture = p;
257         } else if (so) {
258                 _main_sound = so;
259         } else if (su) {
260                 _main_subtitle = su;
261         } else if (c) {
262                 _closed_captions.push_back (c);
263         } else if (a) {
264                 _atmos = a;
265         }
266 }
267
268 void
269 Reel::resolve_refs (list<shared_ptr<Asset> > assets)
270 {
271         if (_main_picture) {
272                 _main_picture->asset_ref().resolve (assets);
273         }
274
275         if (_main_sound) {
276                 _main_sound->asset_ref().resolve (assets);
277         }
278
279         if (_main_subtitle) {
280                 _main_subtitle->asset_ref().resolve (assets);
281
282                 /* Interop subtitle handling is all special cases */
283                 if (_main_subtitle->asset_ref().resolved()) {
284                         shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
285                         if (iop) {
286                                 iop->resolve_fonts (assets);
287                         }
288                 }
289         }
290
291         BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
292                 i->asset_ref().resolve(assets);
293
294                 /* Interop subtitle handling is all special cases */
295                 if (i->asset_ref().resolved()) {
296                         shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
297                         if (iop) {
298                                 iop->resolve_fonts (assets);
299                         }
300                 }
301         }
302
303         if (_atmos) {
304                 _atmos->asset_ref().resolve (assets);
305         }
306 }
307
308 int64_t
309 Reel::duration () const
310 {
311         int64_t d = 0;
312
313         if (_main_picture) {
314                 d = max (d, _main_picture->duration ());
315         }
316         if (_main_sound) {
317                 d = max (d, _main_sound->duration ());
318         }
319         if (_main_subtitle) {
320                 d = max (d, _main_subtitle->duration ());
321         }
322         BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
323                 d = max (d, i->duration());
324         }
325         if (_atmos) {
326                 d = max (d, _atmos->duration ());
327         }
328
329         return d;
330 }