2 Copyright (C) 2014-2020 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.
36 #include "picture_asset.h"
37 #include "mono_picture_asset.h"
38 #include "stereo_picture_asset.h"
39 #include "sound_asset.h"
40 #include "subtitle_asset.h"
41 #include "reel_mono_picture_asset.h"
42 #include "reel_stereo_picture_asset.h"
43 #include "reel_sound_asset.h"
44 #include "reel_subtitle_asset.h"
45 #include "reel_markers_asset.h"
46 #include "decrypted_kdm_key.h"
47 #include "decrypted_kdm.h"
48 #include "interop_subtitle_asset.h"
49 #include "smpte_subtitle_asset.h"
50 #include "reel_atmos_asset.h"
51 #include "reel_closed_caption_asset.h"
52 #include <libxml++/nodes/element.h>
53 #include <boost/foreach.hpp>
56 /* Centos 6 does not have this */
58 #define INT64_MAX 0x7fffffffffffffff
64 using std::make_shared;
65 using std::shared_ptr;
66 using std::dynamic_pointer_cast;
70 Reel::Reel (std::shared_ptr<const cxml::Node> node)
71 : Object (remove_urn_uuid (node->string_child ("Id")))
73 auto asset_list = node->node_child ("AssetList");
75 auto main_picture = asset_list->optional_node_child ("MainPicture");
77 _main_picture.reset (new ReelMonoPictureAsset (main_picture));
80 auto main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
81 if (main_stereoscopic_picture) {
82 _main_picture.reset (new ReelStereoPictureAsset (main_stereoscopic_picture));
85 auto main_sound = asset_list->optional_node_child ("MainSound");
87 _main_sound.reset (new ReelSoundAsset (main_sound));
90 auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
92 _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle));
95 auto main_markers = asset_list->optional_node_child ("MainMarkers");
97 _main_markers.reset (new ReelMarkersAsset (main_markers));
100 /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
101 /* XXX: not sure if Interop supports multiple closed captions */
102 auto closed_captions = asset_list->node_children ("MainClosedCaption");
103 if (closed_captions.empty()) {
104 closed_captions = asset_list->node_children ("ClosedCaption");
106 for (auto i: closed_captions) {
107 _closed_captions.push_back (make_shared<ReelClosedCaptionAsset>(i));
110 auto atmos = asset_list->optional_node_child ("AuxData");
112 _atmos = make_shared<ReelAtmosAsset>(atmos);
115 node->ignore_child ("AnnotationText");
120 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
122 auto reel = node->add_child ("Reel");
123 reel->add_child("Id")->add_child_text("urn:uuid:" + _id);
124 xmlpp::Element* asset_list = reel->add_child ("AssetList");
127 _main_markers->write_to_cpl (asset_list, standard);
130 if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
131 /* Mono pictures come before other stuff... */
132 _main_picture->write_to_cpl (asset_list, standard);
136 _main_sound->write_to_cpl (asset_list, standard);
139 if (_main_subtitle) {
140 _main_subtitle->write_to_cpl (asset_list, standard);
143 for (auto i: _closed_captions) {
144 i->write_to_cpl (asset_list, standard);
147 if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
148 /* ... but stereo pictures must come after */
149 _main_picture->write_to_cpl (asset_list, standard);
153 _atmos->write_to_cpl (asset_list, standard);
160 Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
162 if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
163 note (NoteType::ERROR, "Reel: picture assets differ");
167 if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
171 if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
172 note (NoteType::ERROR, "Reel: sound assets differ");
176 if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
180 if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
181 note (NoteType::ERROR, "Reel: subtitle assets differ");
185 if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, note)) {
189 if ((_main_markers && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
190 note (NoteType::ERROR, "Reel: one has markers and the other does not");
194 if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
195 note (NoteType::ERROR, "Reel: marker assets differ");
199 if (_closed_captions.size() != other->_closed_captions.size()) {
203 auto i = _closed_captions.begin();
204 auto j = other->_closed_captions.begin();
205 while (i != _closed_captions.end()) {
206 if (!(*i)->equals(*j, opt, note)) {
213 if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
214 note (NoteType::ERROR, "Reel: atmos assets differ");
218 if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
226 Reel::any_encrypted () const
229 for (auto i: _closed_captions) {
230 if (i->encrypted()) {
236 (_main_picture && _main_picture->encrypted()) ||
237 (_main_sound && _main_sound->encrypted()) ||
238 (_main_subtitle && _main_subtitle->encrypted()) ||
240 (_atmos && _atmos->encrypted())
246 Reel::all_encrypted () const
249 for (auto i: _closed_captions) {
250 if (!i->encrypted()) {
256 (!_main_picture || _main_picture->encrypted()) &&
257 (!_main_sound || _main_sound->encrypted()) &&
258 (!_main_subtitle || _main_subtitle->encrypted()) &&
260 (!_atmos || _atmos->encrypted())
265 Reel::add (DecryptedKDM const & kdm)
267 auto keys = kdm.keys ();
269 for (auto const& i: keys) {
270 if (_main_picture && i.id() == _main_picture->key_id()) {
271 _main_picture->asset()->set_key (i.key());
273 if (_main_sound && i.id() == _main_sound->key_id()) {
274 _main_sound->asset()->set_key (i.key());
276 if (_main_subtitle && i.id() == _main_subtitle->key_id()) {
277 shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (_main_subtitle->asset());
279 s->set_key (i.key());
282 for (auto j: _closed_captions) {
283 if (i.id() == j->key_id()) {
284 auto s = dynamic_pointer_cast<SMPTESubtitleAsset> (j->asset());
286 s->set_key (i.key());
290 if (_atmos && i.id() == _atmos->key_id()) {
291 _atmos->asset()->set_key (i.key());
297 Reel::add (shared_ptr<ReelAsset> asset)
299 auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
300 auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
301 auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
302 auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
303 auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
304 auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
314 _closed_captions.push_back (c);
320 vector<shared_ptr<ReelAsset>>
321 Reel::assets () const
323 vector<shared_ptr<ReelAsset>> a;
325 a.push_back (_main_picture);
328 a.push_back (_main_sound);
330 if (_main_subtitle) {
331 a.push_back (_main_subtitle);
333 std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
335 a.push_back (_atmos);
341 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
344 _main_picture->asset_ref().resolve (assets);
348 _main_sound->asset_ref().resolve (assets);
351 if (_main_subtitle) {
352 _main_subtitle->asset_ref().resolve (assets);
354 /* Interop subtitle handling is all special cases */
355 if (_main_subtitle->asset_ref().resolved()) {
356 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset());
358 iop->resolve_fonts (assets);
363 for (auto i: _closed_captions) {
364 i->asset_ref().resolve(assets);
366 /* Interop subtitle handling is all special cases */
367 if (i->asset_ref().resolved()) {
368 auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset());
370 iop->resolve_fonts (assets);
376 _atmos->asset_ref().resolve (assets);
381 Reel::duration () const
384 return _main_picture->actual_duration();
387 int64_t d = INT64_MAX;
390 d = min (d, _main_sound->actual_duration());
392 if (_main_subtitle) {
393 d = min (d, _main_subtitle->actual_duration());
396 d = min (d, _main_markers->actual_duration());
398 for (auto i: _closed_captions) {
399 d = min (d, i->actual_duration());
402 d = min (d, _atmos->actual_duration());
405 DCP_ASSERT (d < INT64_MAX);