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 std::shared_ptr;
66 using std::dynamic_pointer_cast;
69 Reel::Reel (std::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 (std::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 && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
189 note (DCP_ERROR, "Reel: one has markers and the other does not");
193 if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
194 note (DCP_ERROR, "Reel: marker assets differ");
198 if (_closed_captions.size() != other->_closed_captions.size()) {
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)) {
212 if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
213 note (DCP_ERROR, "Reel: atmos assets differ");
217 if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
225 Reel::encrypted () const
228 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
229 if (i->encrypted()) {
235 (_main_picture && _main_picture->encrypted ()) ||
236 (_main_sound && _main_sound->encrypted ()) ||
237 (_main_subtitle && _main_subtitle->encrypted ()) ||
239 (_atmos && _atmos->encrypted ())
244 Reel::add (DecryptedKDM const & kdm)
246 list<DecryptedKDMKey> keys = kdm.keys ();
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 ());
252 if (_main_sound && i->id() == _main_sound->key_id()) {
253 _main_sound->asset()->set_key (i->key ());
255 if (_main_subtitle && i->id() == _main_subtitle->key_id()) {
256 shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (_main_subtitle->asset());
258 s->set_key (i->key ());
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());
265 s->set_key (i->key ());
269 if (_atmos && i->id() == _atmos->key_id()) {
270 _atmos->asset()->set_key (i->key ());
276 Reel::add (shared_ptr<ReelAsset> asset)
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);
293 _closed_captions.push_back (c);
299 list<shared_ptr<ReelAsset> >
300 Reel::assets () const
302 list<shared_ptr<ReelAsset> > a;
304 a.push_back (_main_picture);
307 a.push_back (_main_sound);
309 if (_main_subtitle) {
310 a.push_back (_main_subtitle);
312 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
314 a.push_back (_atmos);
320 Reel::resolve_refs (list<shared_ptr<Asset> > assets)
323 _main_picture->asset_ref().resolve (assets);
327 _main_sound->asset_ref().resolve (assets);
330 if (_main_subtitle) {
331 _main_subtitle->asset_ref().resolve (assets);
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());
337 iop->resolve_fonts (assets);
342 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
343 i->asset_ref().resolve(assets);
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());
349 iop->resolve_fonts (assets);
355 _atmos->asset_ref().resolve (assets);
360 Reel::duration () const
363 return _main_picture->actual_duration();
366 int64_t d = INT64_MAX;
369 d = min (d, _main_sound->actual_duration());
371 if (_main_subtitle) {
372 d = min (d, _main_subtitle->actual_duration());
375 d = min (d, _main_markers->actual_duration());
377 BOOST_FOREACH (shared_ptr<ReelClosedCaptionAsset> i, _closed_captions) {
378 d = min (d, i->actual_duration());
381 d = min (d, _atmos->actual_duration());
384 DCP_ASSERT (d < INT64_MAX);