2 Copyright (C) 2014-2021 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.
42 #include "picture_asset.h"
43 #include "mono_picture_asset.h"
44 #include "stereo_picture_asset.h"
45 #include "sound_asset.h"
46 #include "subtitle_asset.h"
47 #include "reel_mono_picture_asset.h"
48 #include "reel_stereo_picture_asset.h"
49 #include "reel_sound_asset.h"
50 #include "reel_interop_closed_caption_asset.h"
51 #include "reel_interop_subtitle_asset.h"
52 #include "reel_smpte_closed_caption_asset.h"
53 #include "reel_smpte_subtitle_asset.h"
54 #include "reel_subtitle_asset.h"
55 #include "reel_markers_asset.h"
56 #include "decrypted_kdm_key.h"
57 #include "decrypted_kdm.h"
58 #include "interop_subtitle_asset.h"
59 #include "smpte_subtitle_asset.h"
60 #include "reel_atmos_asset.h"
61 #include "reel_closed_caption_asset.h"
62 #include <libxml++/nodes/element.h>
69 using std::make_shared;
70 using std::shared_ptr;
71 using std::dynamic_pointer_cast;
76 Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard)
77 : Object (remove_urn_uuid (node->string_child ("Id")))
79 auto asset_list = node->node_child ("AssetList");
81 auto main_picture = asset_list->optional_node_child ("MainPicture");
83 _main_picture = make_shared<ReelMonoPictureAsset>(main_picture);
86 auto main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
87 if (main_stereoscopic_picture) {
88 _main_picture = make_shared<ReelStereoPictureAsset>(main_stereoscopic_picture);
91 auto main_sound = asset_list->optional_node_child ("MainSound");
93 _main_sound = make_shared<ReelSoundAsset>(main_sound);
96 auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
99 case Standard::INTEROP:
100 _main_subtitle = make_shared<ReelInteropSubtitleAsset>(main_subtitle);
102 case Standard::SMPTE:
103 _main_subtitle = make_shared<ReelSMPTESubtitleAsset>(main_subtitle);
108 auto main_markers = asset_list->optional_node_child ("MainMarkers");
110 _main_markers = make_shared<ReelMarkersAsset>(main_markers);
113 /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
114 /* XXX: not sure if Interop supports multiple closed captions */
115 auto closed_captions = asset_list->node_children ("MainClosedCaption");
116 if (closed_captions.empty()) {
117 closed_captions = asset_list->node_children ("ClosedCaption");
119 for (auto i: closed_captions) {
121 case Standard::INTEROP:
122 _closed_captions.push_back (make_shared<ReelInteropClosedCaptionAsset>(i));
124 case Standard::SMPTE:
125 _closed_captions.push_back (make_shared<ReelSMPTEClosedCaptionAsset>(i));
130 auto atmos = asset_list->optional_node_child ("AuxData");
132 _atmos = make_shared<ReelAtmosAsset>(atmos);
135 node->ignore_child ("AnnotationText");
141 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
143 auto reel = node->add_child ("Reel");
144 reel->add_child("Id")->add_child_text("urn:uuid:" + _id);
145 xmlpp::Element* asset_list = reel->add_child ("AssetList");
148 _main_markers->write_to_cpl (asset_list, standard);
151 if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
152 /* Mono pictures come before other stuff... */
153 _main_picture->write_to_cpl (asset_list, standard);
157 _main_sound->write_to_cpl (asset_list, standard);
160 if (_main_subtitle) {
161 _main_subtitle->write_to_cpl (asset_list, standard);
164 for (auto i: _closed_captions) {
165 i->write_to_cpl (asset_list, standard);
168 if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
169 /* ... but stereo pictures must come after */
170 _main_picture->write_to_cpl (asset_list, standard);
174 _atmos->write_to_cpl (asset_list, standard);
182 Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
184 if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
185 note (NoteType::ERROR, "Reel: picture assets differ");
189 if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
193 if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
194 note (NoteType::ERROR, "Reel: sound assets differ");
198 if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
202 if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
203 note (NoteType::ERROR, "Reel: subtitle assets differ");
207 bool same_type = false;
210 auto interop = dynamic_pointer_cast<ReelInteropSubtitleAsset>(_main_subtitle);
211 auto interop_other = dynamic_pointer_cast<ReelInteropSubtitleAsset>(other->_main_subtitle);
212 if (interop && interop_other) {
214 if (!interop->equals(interop_other, opt, note)) {
221 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
222 auto smpte_other = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(other->_main_subtitle);
223 if (smpte && smpte_other) {
225 if (!smpte->equals(smpte_other, opt, note)) {
231 if ((_main_subtitle || other->_main_subtitle) && !same_type) {
235 if ((_main_markers && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
236 note (NoteType::ERROR, "Reel: one has markers and the other does not");
240 if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
241 note (NoteType::ERROR, "Reel: marker assets differ");
245 if (_closed_captions.size() != other->_closed_captions.size()) {
249 auto i = _closed_captions.begin();
250 auto j = other->_closed_captions.begin();
251 while (i != _closed_captions.end()) {
252 if (!(*i)->equals(*j, opt, note)) {
259 if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
260 note (NoteType::ERROR, "Reel: atmos assets differ");
264 if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
273 Reel::any_encrypted () const
276 for (auto i: _closed_captions) {
277 if (i->encrypted()) {
283 (_main_picture && _main_picture->encrypted()) ||
284 (_main_sound && _main_sound->encrypted()) ||
285 (_main_subtitle && _main_subtitle->encrypted()) ||
287 (_atmos && _atmos->encrypted())
293 Reel::all_encrypted () const
296 for (auto i: _closed_captions) {
297 if (!i->encrypted()) {
303 (!_main_picture || _main_picture->encrypted()) &&
304 (!_main_sound || _main_sound->encrypted()) &&
305 (!_main_subtitle || _main_subtitle->encrypted()) &&
307 (!_atmos || _atmos->encrypted())
313 Reel::add (DecryptedKDM const & kdm)
315 auto keys = kdm.keys ();
317 for (auto const& i: keys) {
318 if (_main_picture && i.id() == _main_picture->key_id()) {
319 _main_picture->asset()->set_key (i.key());
321 if (_main_sound && i.id() == _main_sound->key_id()) {
322 _main_sound->asset()->set_key (i.key());
324 if (_main_subtitle) {
325 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_picture);
326 if (smpte && i.id() == smpte->key_id()) {
327 smpte->smpte_asset()->set_key(i.key());
330 for (auto j: _closed_captions) {
331 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
332 if (smpte && i.id() == smpte->key_id()) {
333 smpte->smpte_asset()->set_key(i.key());
336 if (_atmos && i.id() == _atmos->key_id()) {
337 _atmos->asset()->set_key (i.key());
344 Reel::add (shared_ptr<ReelAsset> asset)
346 auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
347 auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
348 auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
349 auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
350 auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
351 auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
361 _closed_captions.push_back (c);
368 vector<shared_ptr<ReelAsset>>
369 Reel::assets () const
371 vector<shared_ptr<ReelAsset>> a;
373 a.push_back (_main_picture);
376 a.push_back (_main_sound);
378 if (_main_subtitle) {
379 a.push_back (_main_subtitle);
381 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
383 a.push_back (_atmos);
390 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
393 _main_picture->asset_ref().resolve(assets);
397 _main_sound->asset_ref().resolve(assets);
400 if (_main_subtitle) {
401 _main_subtitle->asset_ref().resolve(assets);
403 /* Interop subtitle handling is all special cases */
404 if (_main_subtitle->asset_ref().resolved()) {
405 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
407 iop->resolve_fonts (assets);
412 for (auto i: _closed_captions) {
413 i->asset_ref().resolve(assets);
415 /* Interop subtitle handling is all special cases */
416 if (i->asset_ref().resolved()) {
417 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
419 iop->resolve_fonts (assets);
425 _atmos->asset_ref().resolve (assets);
431 Reel::duration () const
434 return _main_picture->actual_duration();
437 int64_t d = INT64_MAX;
440 d = min (d, _main_sound->actual_duration());
442 if (_main_subtitle) {
443 d = min (d, _main_subtitle->actual_duration());
446 d = min (d, _main_markers->actual_duration());
448 for (auto i: _closed_captions) {
449 d = min (d, i->actual_duration());
452 d = min (d, _atmos->actual_duration());
455 DCP_ASSERT (d < INT64_MAX);