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.
40 #include "decrypted_kdm.h"
41 #include "decrypted_kdm_key.h"
42 #include "equality_options.h"
43 #include "interop_subtitle_asset.h"
44 #include "mono_picture_asset.h"
45 #include "picture_asset.h"
47 #include "reel_atmos_asset.h"
48 #include "reel_closed_caption_asset.h"
49 #include "reel_interop_closed_caption_asset.h"
50 #include "reel_interop_subtitle_asset.h"
51 #include "reel_markers_asset.h"
52 #include "reel_mono_picture_asset.h"
53 #include "reel_smpte_closed_caption_asset.h"
54 #include "reel_smpte_subtitle_asset.h"
55 #include "reel_sound_asset.h"
56 #include "reel_stereo_picture_asset.h"
57 #include "reel_subtitle_asset.h"
58 #include "smpte_subtitle_asset.h"
59 #include "sound_asset.h"
60 #include "stereo_picture_asset.h"
61 #include "subtitle_asset.h"
63 #include <libxml++/nodes/element.h>
70 using std::make_shared;
71 using std::shared_ptr;
72 using std::dynamic_pointer_cast;
77 Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard)
78 : Object (remove_urn_uuid (node->string_child ("Id")))
80 auto asset_list = node->node_child ("AssetList");
82 auto main_picture = asset_list->optional_node_child ("MainPicture");
84 _main_picture = make_shared<ReelMonoPictureAsset>(main_picture);
87 auto main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
88 if (main_stereoscopic_picture) {
89 _main_picture = make_shared<ReelStereoPictureAsset>(main_stereoscopic_picture);
92 auto main_sound = asset_list->optional_node_child ("MainSound");
94 _main_sound = make_shared<ReelSoundAsset>(main_sound);
97 auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
100 case Standard::INTEROP:
101 _main_subtitle = make_shared<ReelInteropSubtitleAsset>(main_subtitle);
103 case Standard::SMPTE:
104 _main_subtitle = make_shared<ReelSMPTESubtitleAsset>(main_subtitle);
109 auto main_markers = asset_list->optional_node_child ("MainMarkers");
111 _main_markers = make_shared<ReelMarkersAsset>(main_markers);
114 /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
115 /* XXX: not sure if Interop supports multiple closed captions */
116 auto closed_captions = asset_list->node_children ("MainClosedCaption");
117 if (closed_captions.empty()) {
118 closed_captions = asset_list->node_children ("ClosedCaption");
120 for (auto i: closed_captions) {
122 case Standard::INTEROP:
123 _closed_captions.push_back (make_shared<ReelInteropClosedCaptionAsset>(i));
125 case Standard::SMPTE:
126 _closed_captions.push_back (make_shared<ReelSMPTEClosedCaptionAsset>(i));
131 auto atmos = asset_list->optional_node_child ("AuxData");
133 _atmos = make_shared<ReelAtmosAsset>(atmos);
136 node->ignore_child ("AnnotationText");
142 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
144 auto reel = node->add_child ("Reel");
145 reel->add_child("Id")->add_child_text("urn:uuid:" + _id);
146 xmlpp::Element* asset_list = reel->add_child ("AssetList");
149 _main_markers->write_to_cpl (asset_list, standard);
152 if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
153 /* Mono pictures come before other stuff... */
154 _main_picture->write_to_cpl (asset_list, standard);
158 _main_sound->write_to_cpl (asset_list, standard);
161 if (_main_subtitle) {
162 _main_subtitle->write_to_cpl (asset_list, standard);
165 for (auto i: _closed_captions) {
166 i->write_to_cpl (asset_list, standard);
169 if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
170 /* ... but stereo pictures must come after */
171 _main_picture->write_to_cpl (asset_list, standard);
175 _atmos->write_to_cpl (asset_list, standard);
183 Reel::equals(std::shared_ptr<const Reel> other, EqualityOptions const& opt, NoteHandler note) const
185 if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
186 note (NoteType::ERROR, "Reel: picture assets differ");
190 if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
194 if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
195 note (NoteType::ERROR, "Reel: sound assets differ");
199 if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
203 if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
204 note (NoteType::ERROR, "Reel: subtitle assets differ");
208 bool same_type = false;
211 auto interop = dynamic_pointer_cast<ReelInteropSubtitleAsset>(_main_subtitle);
212 auto interop_other = dynamic_pointer_cast<ReelInteropSubtitleAsset>(other->_main_subtitle);
213 if (interop && interop_other) {
215 if (!interop->equals(interop_other, opt, note)) {
222 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
223 auto smpte_other = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(other->_main_subtitle);
224 if (smpte && smpte_other) {
226 if (!smpte->equals(smpte_other, opt, note)) {
232 if ((_main_subtitle || other->_main_subtitle) && !same_type) {
236 if ((_main_markers && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
237 note (NoteType::ERROR, "Reel: one has markers and the other does not");
241 if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
242 note (NoteType::ERROR, "Reel: marker assets differ");
246 if (_closed_captions.size() != other->_closed_captions.size()) {
250 auto i = _closed_captions.begin();
251 auto j = other->_closed_captions.begin();
252 while (i != _closed_captions.end()) {
253 if (!(*i)->equals(*j, opt, note)) {
260 if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
261 note (NoteType::ERROR, "Reel: atmos assets differ");
265 if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
274 Reel::any_encrypted () const
277 for (auto i: _closed_captions) {
278 if (i->encrypted()) {
284 (_main_picture && _main_picture->encrypted()) ||
285 (_main_sound && _main_sound->encrypted()) ||
286 (_main_subtitle && _main_subtitle->encrypted()) ||
288 (_atmos && _atmos->encrypted())
294 Reel::all_encrypted () const
297 for (auto i: _closed_captions) {
298 if (!i->encrypted()) {
304 (!_main_picture || _main_picture->encrypted()) &&
305 (!_main_sound || _main_sound->encrypted()) &&
306 (!_main_subtitle || _main_subtitle->encrypted()) &&
308 (!_atmos || _atmos->encrypted())
314 Reel::add (DecryptedKDM const & kdm)
316 give_kdm_to_assets (kdm);
317 /* We have to keep the KDMs that we are given, as they will not be passed to unresolved assets.
318 * After we resolve some assets we will re-call give_kdm_to_assets() with all the KDMs that
319 * we have been given so far.
321 _kdms.push_back (kdm);
326 Reel::give_kdm_to_assets (DecryptedKDM const & kdm)
328 for (auto const& i: kdm.keys()) {
329 if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) {
330 _main_picture->asset()->set_key (i.key());
332 if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) {
333 _main_sound->asset()->set_key (i.key());
335 if (_main_subtitle) {
336 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
337 if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
338 smpte->smpte_asset()->set_key(i.key());
341 for (auto j: _closed_captions) {
342 auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
343 if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
344 smpte->smpte_asset()->set_key(i.key());
347 if (_atmos && i.id() == _atmos->key_id() && _atmos->asset_ref().resolved()) {
348 _atmos->asset()->set_key (i.key());
355 Reel::add (shared_ptr<ReelAsset> asset)
357 if (auto p = dynamic_pointer_cast<ReelPictureAsset>(asset)) {
359 } else if (auto so = dynamic_pointer_cast<ReelSoundAsset>(asset)) {
361 } else if (auto su = dynamic_pointer_cast<ReelSubtitleAsset>(asset)) {
363 } else if (auto m = dynamic_pointer_cast<ReelMarkersAsset>(asset)) {
365 } else if (auto c = dynamic_pointer_cast<ReelClosedCaptionAsset>(asset)) {
366 _closed_captions.push_back (c);
367 } else if (auto a = dynamic_pointer_cast<ReelAtmosAsset>(asset)) {
375 vector<shared_ptr<ReelAsset>>
376 Reel::assets () const
378 vector<shared_ptr<ReelAsset>> a;
380 a.push_back (_main_picture);
383 a.push_back (_main_sound);
385 if (_main_subtitle) {
386 a.push_back (_main_subtitle);
388 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
390 a.push_back (_atmos);
397 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
400 _main_picture->asset_ref().resolve(assets);
404 _main_sound->asset_ref().resolve(assets);
407 if (_main_subtitle) {
408 _main_subtitle->asset_ref().resolve(assets);
410 /* Interop subtitle handling is all special cases */
411 if (_main_subtitle->asset_ref().resolved()) {
412 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
414 iop->resolve_fonts (assets);
419 for (auto i: _closed_captions) {
420 i->asset_ref().resolve(assets);
422 /* Interop subtitle handling is all special cases */
423 if (i->asset_ref().resolved()) {
424 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
426 iop->resolve_fonts (assets);
432 _atmos->asset_ref().resolve (assets);
435 for (auto const& i: _kdms) {
436 give_kdm_to_assets (i);
442 Reel::duration () const
445 return _main_picture->actual_duration();
448 int64_t d = INT64_MAX;
451 d = min (d, _main_sound->actual_duration());
453 if (_main_subtitle) {
454 d = min (d, _main_subtitle->actual_duration());
457 d = min (d, _main_markers->actual_duration());
459 for (auto i: _closed_captions) {
460 d = min (d, i->actual_duration());
463 d = min (d, _atmos->actual_duration());
466 DCP_ASSERT (d < INT64_MAX);