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_subtitle_asset.h"
51 #include "reel_smpte_subtitle_asset.h"
52 #include "reel_subtitle_asset.h"
53 #include "reel_markers_asset.h"
54 #include "decrypted_kdm_key.h"
55 #include "decrypted_kdm.h"
56 #include "interop_subtitle_asset.h"
57 #include "smpte_subtitle_asset.h"
58 #include "reel_atmos_asset.h"
59 #include "reel_closed_caption_asset.h"
60 #include <libxml++/nodes/element.h>
67 using std::make_shared;
68 using std::shared_ptr;
69 using std::dynamic_pointer_cast;
74 Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard)
75 : Object (remove_urn_uuid (node->string_child ("Id")))
77 auto asset_list = node->node_child ("AssetList");
79 auto main_picture = asset_list->optional_node_child ("MainPicture");
81 _main_picture = make_shared<ReelMonoPictureAsset>(main_picture);
84 auto main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
85 if (main_stereoscopic_picture) {
86 _main_picture = make_shared<ReelStereoPictureAsset>(main_stereoscopic_picture);
89 auto main_sound = asset_list->optional_node_child ("MainSound");
91 _main_sound = make_shared<ReelSoundAsset>(main_sound);
94 auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
97 case Standard::INTEROP:
98 _main_subtitle = make_shared<ReelInteropSubtitleAsset>(main_subtitle);
100 case Standard::SMPTE:
101 _main_subtitle = make_shared<ReelSMPTESubtitleAsset>(main_subtitle);
106 auto main_markers = asset_list->optional_node_child ("MainMarkers");
108 _main_markers = make_shared<ReelMarkersAsset>(main_markers);
111 /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
112 /* XXX: not sure if Interop supports multiple closed captions */
113 auto closed_captions = asset_list->node_children ("MainClosedCaption");
114 if (closed_captions.empty()) {
115 closed_captions = asset_list->node_children ("ClosedCaption");
117 for (auto i: closed_captions) {
118 _closed_captions.push_back (make_shared<ReelClosedCaptionAsset>(i));
121 auto atmos = asset_list->optional_node_child ("AuxData");
123 _atmos = make_shared<ReelAtmosAsset>(atmos);
126 node->ignore_child ("AnnotationText");
132 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
134 auto reel = node->add_child ("Reel");
135 reel->add_child("Id")->add_child_text("urn:uuid:" + _id);
136 xmlpp::Element* asset_list = reel->add_child ("AssetList");
139 _main_markers->write_to_cpl (asset_list, standard);
142 if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
143 /* Mono pictures come before other stuff... */
144 _main_picture->write_to_cpl (asset_list, standard);
148 _main_sound->write_to_cpl (asset_list, standard);
151 if (_main_subtitle) {
152 _main_subtitle->write_to_cpl (asset_list, standard);
155 for (auto i: _closed_captions) {
156 i->write_to_cpl (asset_list, standard);
159 if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
160 /* ... but stereo pictures must come after */
161 _main_picture->write_to_cpl (asset_list, standard);
165 _atmos->write_to_cpl (asset_list, standard);
173 Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
175 if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
176 note (NoteType::ERROR, "Reel: picture assets differ");
180 if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
184 if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
185 note (NoteType::ERROR, "Reel: sound assets differ");
189 if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
193 if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
194 note (NoteType::ERROR, "Reel: subtitle assets differ");
198 bool same_type = false;
201 auto interop = dynamic_pointer_cast<ReelInteropSubtitleAsset>(_main_subtitle);
202 auto interop_other = dynamic_pointer_cast<ReelInteropSubtitleAsset>(other->_main_subtitle);
203 if (interop && interop_other) {
205 if (!interop->equals(interop_other, opt, note)) {
212 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
213 auto smpte_other = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(other->_main_subtitle);
214 if (smpte && smpte_other) {
216 if (!smpte->equals(smpte_other, opt, note)) {
222 if ((_main_subtitle || other->_main_subtitle) && !same_type) {
226 if ((_main_markers && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
227 note (NoteType::ERROR, "Reel: one has markers and the other does not");
231 if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
232 note (NoteType::ERROR, "Reel: marker assets differ");
236 if (_closed_captions.size() != other->_closed_captions.size()) {
240 auto i = _closed_captions.begin();
241 auto j = other->_closed_captions.begin();
242 while (i != _closed_captions.end()) {
243 if (!(*i)->equals(*j, opt, note)) {
250 if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
251 note (NoteType::ERROR, "Reel: atmos assets differ");
255 if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
264 Reel::any_encrypted () const
267 for (auto i: _closed_captions) {
268 if (i->encrypted()) {
274 if (_main_subtitle) {
275 if (auto enc = dynamic_pointer_cast<ReelEncryptableAsset>(_main_picture)) {
276 esub = enc->encrypted();
281 (_main_picture && _main_picture->encrypted()) ||
282 (_main_sound && _main_sound->encrypted()) ||
285 (_atmos && _atmos->encrypted())
291 Reel::all_encrypted () const
294 for (auto i: _closed_captions) {
295 if (!i->encrypted()) {
300 /* It's ok if there's no subtitle, or it's not encryptable */
302 if (_main_subtitle) {
303 if (auto enc = dynamic_pointer_cast<ReelEncryptableAsset>(_main_picture)) {
304 esub = enc->encrypted();
309 (!_main_picture || _main_picture->encrypted()) &&
310 (!_main_sound || _main_sound->encrypted()) &&
313 (!_atmos || _atmos->encrypted())
319 Reel::add (DecryptedKDM const & kdm)
321 auto keys = kdm.keys ();
323 for (auto const& i: keys) {
324 if (_main_picture && i.id() == _main_picture->key_id()) {
325 _main_picture->asset()->set_key (i.key());
327 if (_main_sound && i.id() == _main_sound->key_id()) {
328 _main_sound->asset()->set_key (i.key());
330 if (_main_subtitle) {
331 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_picture);
332 if (smpte && i.id() == smpte->key_id()) {
333 smpte->smpte_asset()->set_key(i.key());
336 for (auto j: _closed_captions) {
337 if (i.id() == j->key_id()) {
338 auto s = dynamic_pointer_cast<SMPTESubtitleAsset> (j->asset());
340 s->set_key (i.key());
344 if (_atmos && i.id() == _atmos->key_id()) {
345 _atmos->asset()->set_key (i.key());
352 Reel::add (shared_ptr<ReelAsset> asset)
354 auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
355 auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
356 auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
357 auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
358 auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
359 auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
369 _closed_captions.push_back (c);
376 vector<shared_ptr<ReelAsset>>
377 Reel::assets () const
379 vector<shared_ptr<ReelAsset>> a;
381 a.push_back (_main_picture);
384 a.push_back (_main_sound);
386 if (_main_subtitle) {
387 a.push_back (_main_subtitle);
389 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
391 a.push_back (_atmos);
398 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
401 _main_picture->asset_ref().resolve(assets);
405 _main_sound->asset_ref().resolve(assets);
408 if (_main_subtitle) {
409 _main_subtitle->asset_ref().resolve(assets);
411 /* Interop subtitle handling is all special cases */
412 if (_main_subtitle->asset_ref().resolved()) {
413 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
415 iop->resolve_fonts (assets);
420 for (auto i: _closed_captions) {
421 i->asset_ref().resolve(assets);
423 /* Interop subtitle handling is all special cases */
424 if (i->asset_ref().resolved()) {
425 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
427 iop->resolve_fonts (assets);
433 _atmos->asset_ref().resolve (assets);
439 Reel::duration () const
442 return _main_picture->actual_duration();
445 int64_t d = INT64_MAX;
448 d = min (d, _main_sound->actual_duration());
450 if (_main_subtitle) {
451 d = min (d, _main_subtitle->actual_duration());
454 d = min (d, _main_markers->actual_duration());
456 for (auto i: _closed_captions) {
457 d = min (d, i->actual_duration());
460 d = min (d, _atmos->actual_duration());
463 DCP_ASSERT (d < INT64_MAX);