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::make_shared;
66 using std::shared_ptr;
67 using std::dynamic_pointer_cast;
70 Reel::Reel (std::shared_ptr<const cxml::Node> node)
71 : Object (remove_urn_uuid (node->string_child ("Id")))
73 auto asset_list = node->node_child ("AssetList");
75 auto main_picture = asset_list->optional_node_child ("MainPicture");
77 _main_picture.reset (new ReelMonoPictureAsset (main_picture));
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));
85 auto main_sound = asset_list->optional_node_child ("MainSound");
87 _main_sound.reset (new ReelSoundAsset (main_sound));
90 auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
92 _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle));
95 auto main_markers = asset_list->optional_node_child ("MainMarkers");
97 _main_markers.reset (new ReelMarkersAsset (main_markers));
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");
106 for (auto i: closed_captions) {
107 _closed_captions.push_back (make_shared<ReelClosedCaptionAsset>(i));
110 auto atmos = asset_list->optional_node_child ("AuxData");
112 _atmos = make_shared<ReelAtmosAsset>(atmos);
115 node->ignore_child ("AnnotationText");
120 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
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");
127 _main_markers->write_to_cpl (asset_list, standard);
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);
136 _main_sound->write_to_cpl (asset_list, standard);
139 if (_main_subtitle) {
140 _main_subtitle->write_to_cpl (asset_list, standard);
143 for (auto i: _closed_captions) {
144 i->write_to_cpl (asset_list, standard);
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);
153 _atmos->write_to_cpl (asset_list, standard);
160 Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
162 if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
163 note (DCP_ERROR, "Reel: picture assets differ");
167 if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
171 if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
172 note (DCP_ERROR, "Reel: sound assets differ");
176 if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
180 if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
181 note (DCP_ERROR, "Reel: subtitle assets differ");
185 if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, note)) {
189 if ((_main_markers && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
190 note (DCP_ERROR, "Reel: one has markers and the other does not");
194 if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
195 note (DCP_ERROR, "Reel: marker assets differ");
199 if (_closed_captions.size() != other->_closed_captions.size()) {
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)) {
213 if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
214 note (DCP_ERROR, "Reel: atmos assets differ");
218 if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
226 Reel::encrypted () const
229 for (auto i: _closed_captions) {
230 if (i->encrypted()) {
236 (_main_picture && _main_picture->encrypted ()) ||
237 (_main_sound && _main_sound->encrypted ()) ||
238 (_main_subtitle && _main_subtitle->encrypted ()) ||
240 (_atmos && _atmos->encrypted ())
245 Reel::add (DecryptedKDM const & kdm)
247 auto keys = kdm.keys ();
249 for (auto const& i: keys) {
250 if (_main_picture && i.id() == _main_picture->key_id()) {
251 _main_picture->asset()->set_key (i.key());
253 if (_main_sound && i.id() == _main_sound->key_id()) {
254 _main_sound->asset()->set_key (i.key());
256 if (_main_subtitle && i.id() == _main_subtitle->key_id()) {
257 shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (_main_subtitle->asset());
259 s->set_key (i.key());
262 for (auto j: _closed_captions) {
263 if (i.id() == j->key_id()) {
264 auto s = dynamic_pointer_cast<SMPTESubtitleAsset> (j->asset());
266 s->set_key (i.key());
270 if (_atmos && i.id() == _atmos->key_id()) {
271 _atmos->asset()->set_key (i.key());
277 Reel::add (shared_ptr<ReelAsset> asset)
279 auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
280 auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
281 auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
282 auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
283 auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
284 auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
294 _closed_captions.push_back (c);
300 list<shared_ptr<ReelAsset>>
301 Reel::assets () const
303 list<shared_ptr<ReelAsset>> a;
305 a.push_back (_main_picture);
308 a.push_back (_main_sound);
310 if (_main_subtitle) {
311 a.push_back (_main_subtitle);
313 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
315 a.push_back (_atmos);
321 Reel::resolve_refs (list<shared_ptr<Asset>> assets)
324 _main_picture->asset_ref().resolve (assets);
328 _main_sound->asset_ref().resolve (assets);
331 if (_main_subtitle) {
332 _main_subtitle->asset_ref().resolve (assets);
334 /* Interop subtitle handling is all special cases */
335 if (_main_subtitle->asset_ref().resolved()) {
336 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
338 iop->resolve_fonts (assets);
343 for (auto i: _closed_captions) {
344 i->asset_ref().resolve(assets);
346 /* Interop subtitle handling is all special cases */
347 if (i->asset_ref().resolved()) {
348 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
350 iop->resolve_fonts (assets);
356 _atmos->asset_ref().resolve (assets);
361 Reel::duration () const
364 return _main_picture->actual_duration();
367 int64_t d = INT64_MAX;
370 d = min (d, _main_sound->actual_duration());
372 if (_main_subtitle) {
373 d = min (d, _main_subtitle->actual_duration());
376 d = min (d, _main_markers->actual_duration());
378 for (auto i: _closed_captions) {
379 d = min (d, i->actual_duration());
382 d = min (d, _atmos->actual_duration());
385 DCP_ASSERT (d < INT64_MAX);