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 give_kdm_to_assets (kdm);
316 /* We have to keep the KDMs that we are given, as they will not be passed to unresolved assets.
317 * After we resolve some assets we will re-call give_kdm_to_assets() with all the KDMs that
318 * we have been given so far.
320 _kdms.push_back (kdm);
325 Reel::give_kdm_to_assets (DecryptedKDM const & kdm)
327 for (auto const& i: kdm.keys()) {
328 if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) {
329 _main_picture->asset()->set_key (i.key());
331 if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) {
332 _main_sound->asset()->set_key (i.key());
334 if (_main_subtitle) {
335 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
336 if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
337 smpte->smpte_asset()->set_key(i.key());
340 for (auto j: _closed_captions) {
341 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
342 if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
343 smpte->smpte_asset()->set_key(i.key());
346 if (_atmos && i.id() == _atmos->key_id() && _atmos->asset_ref().resolved()) {
347 _atmos->asset()->set_key (i.key());
354 Reel::add (shared_ptr<ReelAsset> asset)
356 auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
357 auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
358 auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
359 auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
360 auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
361 auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
371 _closed_captions.push_back (c);
378 vector<shared_ptr<ReelAsset>>
379 Reel::assets () const
381 vector<shared_ptr<ReelAsset>> a;
383 a.push_back (_main_picture);
386 a.push_back (_main_sound);
388 if (_main_subtitle) {
389 a.push_back (_main_subtitle);
391 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
393 a.push_back (_atmos);
400 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
403 _main_picture->asset_ref().resolve(assets);
407 _main_sound->asset_ref().resolve(assets);
410 if (_main_subtitle) {
411 _main_subtitle->asset_ref().resolve(assets);
413 /* Interop subtitle handling is all special cases */
414 if (_main_subtitle->asset_ref().resolved()) {
415 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
417 iop->resolve_fonts (assets);
422 for (auto i: _closed_captions) {
423 i->asset_ref().resolve(assets);
425 /* Interop subtitle handling is all special cases */
426 if (i->asset_ref().resolved()) {
427 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
429 iop->resolve_fonts (assets);
435 _atmos->asset_ref().resolve (assets);
438 for (auto const& i: _kdms) {
439 give_kdm_to_assets (i);
445 Reel::duration () const
448 return _main_picture->actual_duration();
451 int64_t d = INT64_MAX;
454 d = min (d, _main_sound->actual_duration());
456 if (_main_subtitle) {
457 d = min (d, _main_subtitle->actual_duration());
460 d = min (d, _main_markers->actual_duration());
462 for (auto i: _closed_captions) {
463 d = min (d, i->actual_duration());
466 d = min (d, _atmos->actual_duration());
469 DCP_ASSERT (d < INT64_MAX);