2 Copyright (C) 2014-2020 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
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>
56 /* Centos 6 does not have this */
58 #define INT64_MAX 0x7fffffffffffffff
65 using boost::shared_ptr;
66 using boost::dynamic_pointer_cast;
69 Reel::Reel (boost::shared_ptr<const cxml::Node> node)
70 : Object (remove_urn_uuid (node->string_child ("Id")))
72 shared_ptr<cxml::Node> asset_list = node->node_child ("AssetList");
74 shared_ptr<cxml::Node> main_picture = asset_list->optional_node_child ("MainPicture");
76 _main_picture.reset (new ReelMonoPictureAsset (main_picture));
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));
84 shared_ptr<cxml::Node> main_sound = asset_list->optional_node_child ("MainSound");
86 _main_sound.reset (new ReelSoundAsset (main_sound));
89 shared_ptr<cxml::Node> main_subtitle = asset_list->optional_node_child ("MainSubtitle");
91 _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle));
94 shared_ptr<cxml::Node> main_markers = asset_list->optional_node_child ("MainMarkers");
96 _main_markers.reset (new ReelMarkersAsset (main_markers));
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");
105 BOOST_FOREACH (shared_ptr<cxml::Node> i, closed_captions) {
106 _closed_captions.push_back (shared_ptr<ReelClosedCaptionAsset>(new ReelClosedCaptionAsset(i)));
109 shared_ptr<cxml::Node> atmos = asset_list->optional_node_child ("AuxData");
111 _atmos.reset (new ReelAtmosAsset (atmos));
114 node->ignore_child ("AnnotationText");
119 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
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");
126 _main_markers->write_to_cpl (asset_list, standard);
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);
135 _main_sound->write_to_cpl (asset_list, standard);
138 if (_main_subtitle) {
139 _main_subtitle->write_to_cpl (asset_list, standard);
142 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
143 i->write_to_cpl (asset_list, standard);
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);
152 _atmos->write_to_cpl (asset_list, standard);
159 Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
161 if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
162 note (DCP_ERROR, "Reel: picture assets differ");
166 if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
170 if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
171 note (DCP_ERROR, "Reel: sound assets differ");
175 if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
179 if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
180 note (DCP_ERROR, "Reel: subtitle assets differ");
184 if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, note)) {
188 if (_main_markers && !_main_markers->equals (other->_main_markers, opt, note)) {
192 if (_closed_captions.size() != other->_closed_captions.size()) {
196 list<shared_ptr<ReelClosedCaptionAsset> >::const_iterator i = _closed_captions.begin();
197 list<shared_ptr<ReelClosedCaptionAsset> >::const_iterator j = other->_closed_captions.begin();
198 while (i != _closed_captions.end()) {
199 if (!(*i)->equals(*j, opt, note)) {
206 if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
207 note (DCP_ERROR, "Reel: atmos assets differ");
211 if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
219 Reel::encrypted () const
222 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
223 if (i->encrypted()) {
229 (_main_picture && _main_picture->encrypted ()) ||
230 (_main_sound && _main_sound->encrypted ()) ||
231 (_main_subtitle && _main_subtitle->encrypted ()) ||
233 (_atmos && _atmos->encrypted ())
238 Reel::add (DecryptedKDM const & kdm)
240 list<DecryptedKDMKey> keys = kdm.keys ();
242 for (list<DecryptedKDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) {
243 if (_main_picture && i->id() == _main_picture->key_id()) {
244 _main_picture->asset()->set_key (i->key ());
246 if (_main_sound && i->id() == _main_sound->key_id()) {
247 _main_sound->asset()->set_key (i->key ());
249 if (_main_subtitle && i->id() == _main_subtitle->key_id()) {
250 shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (_main_subtitle->asset());
252 s->set_key (i->key ());
255 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> j, _closed_captions) {
256 if (i->id() == j->key_id()) {
257 shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (j->asset());
259 s->set_key (i->key ());
263 if (_atmos && i->id() == _atmos->key_id()) {
264 _atmos->asset()->set_key (i->key ());
270 Reel::add (shared_ptr<ReelAsset> asset)
272 shared_ptr<ReelPictureAsset> p = dynamic_pointer_cast<ReelPictureAsset> (asset);
273 shared_ptr<ReelSoundAsset> so = dynamic_pointer_cast<ReelSoundAsset> (asset);
274 shared_ptr<ReelSubtitleAsset> su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
275 shared_ptr<ReelMarkersAsset> m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
276 shared_ptr<ReelClosedCaptionAsset> c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
277 shared_ptr<ReelAtmosAsset> a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
287 _closed_captions.push_back (c);
293 list<shared_ptr<ReelAsset> >
294 Reel::assets () const
296 list<shared_ptr<ReelAsset> > a;
298 a.push_back (_main_picture);
301 a.push_back (_main_sound);
303 if (_main_subtitle) {
304 a.push_back (_main_subtitle);
306 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
308 a.push_back (_atmos);
314 Reel::resolve_refs (list<shared_ptr<Asset> > assets)
317 _main_picture->asset_ref().resolve (assets);
321 _main_sound->asset_ref().resolve (assets);
324 if (_main_subtitle) {
325 _main_subtitle->asset_ref().resolve (assets);
327 /* Interop subtitle handling is all special cases */
328 if (_main_subtitle->asset_ref().resolved()) {
329 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
331 iop->resolve_fonts (assets);
336 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
337 i->asset_ref().resolve(assets);
339 /* Interop subtitle handling is all special cases */
340 if (i->asset_ref().resolved()) {
341 shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
343 iop->resolve_fonts (assets);
349 _atmos->asset_ref().resolve (assets);
354 Reel::duration () const
357 return _main_picture->actual_duration();
360 int64_t d = INT64_MAX;
363 d = min (d, _main_sound->actual_duration());
365 if (_main_subtitle) {
366 d = min (d, _main_subtitle->actual_duration());
369 d = min (d, _main_markers->actual_duration());
371 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
372 d = min (d, i->actual_duration());
375 d = min (d, _atmos->actual_duration());
378 DCP_ASSERT (d < INT64_MAX);