libdcp
reel.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
3 
4  This file is part of libdcp.
5 
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.
10 
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.
15 
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/>.
18 
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
23  including the two.
24 
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.
32 */
33 
34 
40 #include "reel.h"
41 #include "util.h"
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"
49 #include "reel_sound_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"
62 #include <libxml++/nodes/element.h>
63 #include <stdint.h>
64 
65 
66 using std::string;
67 using std::cout;
68 using std::min;
69 using std::make_shared;
70 using std::shared_ptr;
71 using std::dynamic_pointer_cast;
72 using std::vector;
73 using namespace dcp;
74 
75 
76 Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard)
77  : Object (remove_urn_uuid (node->string_child ("Id")))
78 {
79  auto asset_list = node->node_child ("AssetList");
80 
81  auto main_picture = asset_list->optional_node_child ("MainPicture");
82  if (main_picture) {
83  _main_picture = make_shared<ReelMonoPictureAsset>(main_picture);
84  }
85 
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);
89  }
90 
91  auto main_sound = asset_list->optional_node_child ("MainSound");
92  if (main_sound) {
93  _main_sound = make_shared<ReelSoundAsset>(main_sound);
94  }
95 
96  auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
97  if (main_subtitle) {
98  switch (standard) {
99  case Standard::INTEROP:
100  _main_subtitle = make_shared<ReelInteropSubtitleAsset>(main_subtitle);
101  break;
102  case Standard::SMPTE:
103  _main_subtitle = make_shared<ReelSMPTESubtitleAsset>(main_subtitle);
104  break;
105  }
106  }
107 
108  auto main_markers = asset_list->optional_node_child ("MainMarkers");
109  if (main_markers) {
110  _main_markers = make_shared<ReelMarkersAsset>(main_markers);
111  }
112 
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");
118  }
119  for (auto i: closed_captions) {
120  switch (standard) {
121  case Standard::INTEROP:
122  _closed_captions.push_back (make_shared<ReelInteropClosedCaptionAsset>(i));
123  break;
124  case Standard::SMPTE:
125  _closed_captions.push_back (make_shared<ReelSMPTEClosedCaptionAsset>(i));
126  break;
127  }
128  }
129 
130  auto atmos = asset_list->optional_node_child ("AuxData");
131  if (atmos) {
132  _atmos = make_shared<ReelAtmosAsset>(atmos);
133  }
134 
135  node->ignore_child ("AnnotationText");
136  node->done ();
137 }
138 
139 
140 xmlpp::Element *
141 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
142 {
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");
146 
147  if (_main_markers) {
148  _main_markers->write_to_cpl (asset_list, standard);
149  }
150 
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);
154  }
155 
156  if (_main_sound) {
157  _main_sound->write_to_cpl (asset_list, standard);
158  }
159 
160  if (_main_subtitle) {
161  _main_subtitle->write_to_cpl (asset_list, standard);
162  }
163 
164  for (auto i: _closed_captions) {
165  i->write_to_cpl (asset_list, standard);
166  }
167 
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);
171  }
172 
173  if (_atmos) {
174  _atmos->write_to_cpl (asset_list, standard);
175  }
176 
177  return asset_list;
178 }
179 
180 
181 bool
182 Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
183 {
184  if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
185  note (NoteType::ERROR, "Reel: picture assets differ");
186  return false;
187  }
188 
189  if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
190  return false;
191  }
192 
193  if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
194  note (NoteType::ERROR, "Reel: sound assets differ");
195  return false;
196  }
197 
198  if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
199  return false;
200  }
201 
202  if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
203  note (NoteType::ERROR, "Reel: subtitle assets differ");
204  return false;
205  }
206 
207  bool same_type = false;
208 
209  {
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) {
213  same_type = true;
214  if (!interop->equals(interop_other, opt, note)) {
215  return false;
216  }
217  }
218  }
219 
220  {
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) {
224  same_type = true;
225  if (!smpte->equals(smpte_other, opt, note)) {
226  return false;
227  }
228  }
229  }
230 
231  if ((_main_subtitle || other->_main_subtitle) && !same_type) {
232  return false;
233  }
234 
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");
237  return false;
238  }
239 
240  if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
241  note (NoteType::ERROR, "Reel: marker assets differ");
242  return false;
243  }
244 
245  if (_closed_captions.size() != other->_closed_captions.size()) {
246  return false;
247  }
248 
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)) {
253  return false;
254  }
255  ++i;
256  ++j;
257  }
258 
259  if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
260  note (NoteType::ERROR, "Reel: atmos assets differ");
261  return false;
262  }
263 
264  if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
265  return false;
266  }
267 
268  return true;
269 }
270 
271 
272 bool
273 Reel::any_encrypted () const
274 {
275  auto ecc = false;
276  for (auto i: _closed_captions) {
277  if (i->encrypted()) {
278  ecc = true;
279  }
280  }
281 
282  return (
283  (_main_picture && _main_picture->encrypted()) ||
284  (_main_sound && _main_sound->encrypted()) ||
285  (_main_subtitle && _main_subtitle->encrypted()) ||
286  ecc ||
287  (_atmos && _atmos->encrypted())
288  );
289 }
290 
291 
292 bool
293 Reel::all_encrypted () const
294 {
295  auto ecc = true;
296  for (auto i: _closed_captions) {
297  if (!i->encrypted()) {
298  ecc = false;
299  }
300  }
301 
302  return (
303  (!_main_picture || _main_picture->encrypted()) &&
304  (!_main_sound || _main_sound->encrypted()) &&
305  (!_main_subtitle || _main_subtitle->encrypted()) &&
306  ecc &&
307  (!_atmos || _atmos->encrypted())
308  );
309 }
310 
311 
312 void
313 Reel::add (DecryptedKDM const & kdm)
314 {
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.
319  */
320  _kdms.push_back (kdm);
321 }
322 
323 
324 void
325 Reel::give_kdm_to_assets (DecryptedKDM const & kdm)
326 {
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());
330  }
331  if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) {
332  _main_sound->asset()->set_key (i.key());
333  }
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());
338  }
339  }
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());
344  }
345  }
346  if (_atmos && i.id() == _atmos->key_id() && _atmos->asset_ref().resolved()) {
347  _atmos->asset()->set_key (i.key());
348  }
349  }
350 }
351 
352 
353 void
354 Reel::add (shared_ptr<ReelAsset> asset)
355 {
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);
362  if (p) {
363  _main_picture = p;
364  } else if (so) {
365  _main_sound = so;
366  } else if (su) {
367  _main_subtitle = su;
368  } else if (m) {
369  _main_markers = m;
370  } else if (c) {
371  _closed_captions.push_back (c);
372  } else if (a) {
373  _atmos = a;
374  }
375 }
376 
377 
378 vector<shared_ptr<ReelAsset>>
379 Reel::assets () const
380 {
381  vector<shared_ptr<ReelAsset>> a;
382  if (_main_picture) {
383  a.push_back (_main_picture);
384  }
385  if (_main_sound) {
386  a.push_back (_main_sound);
387  }
388  if (_main_subtitle) {
389  a.push_back (_main_subtitle);
390  }
391  std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
392  if (_atmos) {
393  a.push_back (_atmos);
394  }
395  return a;
396 }
397 
398 
399 void
400 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
401 {
402  if (_main_picture) {
403  _main_picture->asset_ref().resolve(assets);
404  }
405 
406  if (_main_sound) {
407  _main_sound->asset_ref().resolve(assets);
408  }
409 
410  if (_main_subtitle) {
411  _main_subtitle->asset_ref().resolve(assets);
412 
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());
416  if (iop) {
417  iop->resolve_fonts (assets);
418  }
419  }
420  }
421 
422  for (auto i: _closed_captions) {
423  i->asset_ref().resolve(assets);
424 
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());
428  if (iop) {
429  iop->resolve_fonts (assets);
430  }
431  }
432  }
433 
434  if (_atmos) {
435  _atmos->asset_ref().resolve (assets);
436  }
437 
438  for (auto const& i: _kdms) {
439  give_kdm_to_assets (i);
440  }
441 }
442 
443 
444 int64_t
445 Reel::duration () const
446 {
447  if (_main_picture) {
448  return _main_picture->actual_duration();
449  }
450 
451  int64_t d = INT64_MAX;
452 
453  if (_main_sound) {
454  d = min (d, _main_sound->actual_duration());
455  }
456  if (_main_subtitle) {
457  d = min (d, _main_subtitle->actual_duration());
458  }
459  if (_main_markers) {
460  d = min (d, _main_markers->actual_duration());
461  }
462  for (auto i: _closed_captions) {
463  d = min (d, i->actual_duration());
464  }
465  if (_atmos) {
466  d = min (d, _atmos->actual_duration());
467  }
468 
469  DCP_ASSERT (d < INT64_MAX);
470 
471  return d;
472 }
A decrypted KDM.
Definition: decrypted_kdm.h:76
std::vector< DecryptedKDMKey > keys() const
Some part of a DCP that has a UUID.
Definition: object.h:63
DecryptedKDM class.
DecryptedKDMKey class.
InteropSubtitleAsset class.
Namespace for everything in libdcp.
Definition: array_data.h:50
PictureAsset class.
ReelAtmosAsset class.
ReelClosedCaptionAsset class.
ReelInteropClosedCaptionAsset class.
ReelInteropSubtitleAsset class.
ReelMonoPictureAsset class.
ReelSMPTEClosedCaptionAsset class.
ReelSoundAsset class.
ReelStereoPictureAsset class.
ReelSubtitleAsset class.
SMPTESubtitleAsset class.
SoundAsset class.
StereoPictureAsset class.
A class to describe what "equality" means for a particular test.
Definition: types.h:249
SubtitleAsset class.
Utility methods and classes.