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 (auto enc = dynamic_pointer_cast<ReelEncryptableAsset>(i)) {
278 if (enc->encrypted()) {
285 if (_main_subtitle) {
286 if (auto enc = dynamic_pointer_cast<ReelEncryptableAsset>(_main_picture)) {
287 esub = enc->encrypted();
292 (_main_picture && _main_picture->encrypted()) ||
293 (_main_sound && _main_sound->encrypted()) ||
296 (_atmos && _atmos->encrypted())
302 Reel::all_encrypted () const
305 for (auto i: _closed_captions) {
306 if (auto enc = dynamic_pointer_cast<ReelEncryptableAsset>(i)) {
307 if (!enc->encrypted()) {
313 /* It's ok if there's no subtitle, or it's not encryptable */
315 if (_main_subtitle) {
316 if (auto enc = dynamic_pointer_cast<ReelEncryptableAsset>(_main_picture)) {
317 esub = enc->encrypted();
322 (!_main_picture || _main_picture->encrypted()) &&
323 (!_main_sound || _main_sound->encrypted()) &&
326 (!_atmos || _atmos->encrypted())
332 Reel::add (DecryptedKDM const & kdm)
334 auto keys = kdm.keys ();
336 for (auto const& i: keys) {
337 if (_main_picture && i.id() == _main_picture->key_id()) {
338 _main_picture->asset()->set_key (i.key());
340 if (_main_sound && i.id() == _main_sound->key_id()) {
341 _main_sound->asset()->set_key (i.key());
343 if (_main_subtitle) {
344 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_picture);
345 if (smpte && i.id() == smpte->key_id()) {
346 smpte->smpte_asset()->set_key(i.key());
349 for (auto j: _closed_captions) {
350 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
351 if (smpte && i.id() == smpte->key_id()) {
352 smpte->smpte_asset()->set_key(i.key());
355 if (_atmos && i.id() == _atmos->key_id()) {
356 _atmos->asset()->set_key (i.key());
363 Reel::add (shared_ptr<ReelAsset> asset)
365 auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
366 auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
367 auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
368 auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
369 auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
370 auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
380 _closed_captions.push_back (c);
387 vector<shared_ptr<ReelAsset>>
388 Reel::assets () const
390 vector<shared_ptr<ReelAsset>> a;
392 a.push_back (_main_picture);
395 a.push_back (_main_sound);
397 if (_main_subtitle) {
398 a.push_back (_main_subtitle);
400 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
402 a.push_back (_atmos);
409 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
412 _main_picture->asset_ref().resolve(assets);
416 _main_sound->asset_ref().resolve(assets);
419 if (_main_subtitle) {
420 _main_subtitle->asset_ref().resolve(assets);
422 /* Interop subtitle handling is all special cases */
423 if (_main_subtitle->asset_ref().resolved()) {
424 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
426 iop->resolve_fonts (assets);
431 for (auto i: _closed_captions) {
432 i->asset_ref().resolve(assets);
434 /* Interop subtitle handling is all special cases */
435 if (i->asset_ref().resolved()) {
436 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
438 iop->resolve_fonts (assets);
444 _atmos->asset_ref().resolve (assets);
450 Reel::duration () const
453 return _main_picture->actual_duration();
456 int64_t d = INT64_MAX;
459 d = min (d, _main_sound->actual_duration());
461 if (_main_subtitle) {
462 d = min (d, _main_subtitle->actual_duration());
465 d = min (d, _main_markers->actual_duration());
467 for (auto i: _closed_captions) {
468 d = min (d, i->actual_duration());
471 d = min (d, _atmos->actual_duration());
474 DCP_ASSERT (d < INT64_MAX);