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 "decrypted_kdm.h"
41 #include "decrypted_kdm_key.h"
42 #include "equality_options.h"
43 #include "interop_text_asset.h"
44 #include "mono_j2k_picture_asset.h"
45 #include "j2k_picture_asset.h"
46 #include "reel.h"
47 #include "reel_atmos_asset.h"
49 #include "reel_markers_asset.h"
51 #include "reel_smpte_text_asset.h"
52 #include "reel_sound_asset.h"
54 #include "reel_text_asset.h"
55 #include "smpte_text_asset.h"
56 #include "sound_asset.h"
58 #include "text_asset.h"
59 #include "util.h"
60 #include <libxml++/nodes/element.h>
61 #include <stdint.h>
62 
63 
64 using std::string;
65 using std::cout;
66 using std::min;
67 using std::make_shared;
68 using std::shared_ptr;
69 using std::dynamic_pointer_cast;
70 using std::vector;
71 using namespace dcp;
72 
73 
74 Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard)
75  : Object (remove_urn_uuid (node->string_child ("Id")))
76 {
77  auto asset_list = node->node_child ("AssetList");
78 
79  if (auto main_picture = asset_list->optional_node_child("MainPicture")) {
80  _main_picture = make_shared<ReelMonoPictureAsset>(main_picture);
81  }
82 
83  if (auto main_stereoscopic_picture = asset_list->optional_node_child("MainStereoscopicPicture")) {
84  _main_picture = make_shared<ReelStereoPictureAsset>(main_stereoscopic_picture);
85  }
86 
87  if (auto main_sound = asset_list->optional_node_child("MainSound")) {
88  _main_sound = make_shared<ReelSoundAsset>(main_sound);
89  }
90 
91  if (auto main_subtitle = asset_list->optional_node_child("MainSubtitle")) {
92  switch (standard) {
93  case Standard::INTEROP:
94  _main_subtitle = make_shared<ReelInteropTextAsset>(main_subtitle);
95  break;
96  case Standard::SMPTE:
97  _main_subtitle = make_shared<ReelSMPTETextAsset>(main_subtitle);
98  break;
99  }
100  }
101 
102  if (auto main_caption = asset_list->optional_node_child("MainCaption")) {
103  switch (standard) {
104  case Standard::INTEROP:
105  DCP_ASSERT(false);
106  break;
107  case Standard::SMPTE:
108  _main_caption = make_shared<ReelSMPTETextAsset>(main_caption);
109  break;
110  }
111  }
112 
113  if (auto main_markers = asset_list->optional_node_child("MainMarkers")) {
114  _main_markers = make_shared<ReelMarkersAsset>(main_markers);
115  }
116 
117  auto closed_subtitles = asset_list->node_children("ClosedSubtitle");
118  for (auto i: closed_subtitles) {
119  switch (standard) {
120  case Standard::INTEROP:
121  DCP_ASSERT(false);
122  break;
123  case Standard::SMPTE:
124  _closed_subtitles.push_back(make_shared<ReelSMPTETextAsset>(i));
125  break;
126  }
127  }
128 
129  /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
130  /* XXX: not sure if Interop supports multiple closed captions */
131  auto closed_captions = asset_list->node_children ("MainClosedCaption");
132  if (closed_captions.empty()) {
133  closed_captions = asset_list->node_children ("ClosedCaption");
134  }
135  for (auto i: closed_captions) {
136  switch (standard) {
137  case Standard::INTEROP:
138  _closed_captions.push_back(make_shared<ReelInteropTextAsset>(i));
139  break;
140  case Standard::SMPTE:
141  _closed_captions.push_back(make_shared<ReelSMPTETextAsset>(i));
142  break;
143  }
144  }
145 
146  if (auto atmos = asset_list->optional_node_child("AuxData")) {
147  _atmos = make_shared<ReelAtmosAsset>(atmos);
148  }
149 
150  node->ignore_child ("AnnotationText");
151  node->done ();
152 }
153 
154 
155 xmlpp::Element *
156 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
157 {
158  auto reel = cxml::add_child(node, "Reel");
159  cxml::add_text_child(reel, "Id", "urn:uuid:" + _id);
160  auto asset_list = cxml::add_child(reel, "AssetList");
161 
162  if (_main_markers) {
163  _main_markers->write_to_cpl (asset_list, standard);
164  }
165 
166  if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
167  /* Mono pictures come before other stuff... */
168  _main_picture->write_to_cpl (asset_list, standard);
169  }
170 
171  if (_main_sound) {
172  _main_sound->write_to_cpl (asset_list, standard);
173  }
174 
175  if (_main_subtitle) {
176  _main_subtitle->write_to_cpl (asset_list, standard);
177  }
178 
179  if (_main_caption) {
180  _main_caption->write_to_cpl(asset_list, standard);
181  }
182 
183  for (auto i: _closed_subtitles) {
184  i->write_to_cpl(asset_list, standard);
185  }
186 
187  for (auto i: _closed_captions) {
188  i->write_to_cpl (asset_list, standard);
189  }
190 
191  if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
192  /* ... but stereo pictures must come after */
193  _main_picture->write_to_cpl (asset_list, standard);
194  }
195 
196  if (_atmos) {
197  _atmos->write_to_cpl (asset_list, standard);
198  }
199 
200  return asset_list;
201 }
202 
203 
204 bool
205 Reel::equals(std::shared_ptr<const Reel> other, EqualityOptions const& opt, NoteHandler note) const
206 {
207  if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
208  note (NoteType::ERROR, "Reel: picture assets differ");
209  return false;
210  }
211 
212  if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
213  return false;
214  }
215 
216  if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
217  note (NoteType::ERROR, "Reel: sound assets differ");
218  return false;
219  }
220 
221  if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
222  return false;
223  }
224 
225  if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
226  note (NoteType::ERROR, "Reel: subtitle assets differ");
227  return false;
228  }
229 
230  bool same_type = false;
231 
232  {
233  auto interop = dynamic_pointer_cast<ReelInteropTextAsset>(_main_subtitle);
234  auto interop_other = dynamic_pointer_cast<ReelInteropTextAsset>(other->_main_subtitle);
235  if (interop && interop_other) {
236  same_type = true;
237  if (!interop->equals(interop_other, opt, note)) {
238  return false;
239  }
240  }
241  }
242 
243  {
244  auto smpte = dynamic_pointer_cast<ReelSMPTETextAsset>(_main_subtitle);
245  auto smpte_other = dynamic_pointer_cast<ReelSMPTETextAsset>(other->_main_subtitle);
246  if (smpte && smpte_other) {
247  same_type = true;
248  if (!smpte->equals(smpte_other, opt, note)) {
249  return false;
250  }
251  }
252  }
253 
254  if ((_main_subtitle || other->_main_subtitle) && !same_type) {
255  return false;
256  }
257 
258  if ((_main_markers && !other->_main_markers) || (!_main_markers && other->_main_markers)) {
259  note (NoteType::ERROR, "Reel: one has markers and the other does not");
260  return false;
261  }
262 
263  if (_main_markers && !_main_markers->equals(other->_main_markers, opt, note)) {
264  note (NoteType::ERROR, "Reel: marker assets differ");
265  return false;
266  }
267 
268  if (_closed_captions.size() != other->_closed_captions.size()) {
269  return false;
270  }
271 
272  auto i = _closed_captions.begin();
273  auto j = other->_closed_captions.begin();
274  while (i != _closed_captions.end()) {
275  if (!(*i)->equals(*j, opt, note)) {
276  return false;
277  }
278  ++i;
279  ++j;
280  }
281 
282  if ((_atmos && !other->_atmos) || (!_atmos && other->_atmos)) {
283  note (NoteType::ERROR, "Reel: atmos assets differ");
284  return false;
285  }
286 
287  if (_atmos && !_atmos->equals (other->_atmos, opt, note)) {
288  return false;
289  }
290 
291  return true;
292 }
293 
294 
295 bool
296 Reel::any_encrypted () const
297 {
298  auto ecc = false;
299  for (auto i: _closed_captions) {
300  if (i->encrypted()) {
301  ecc = true;
302  }
303  }
304 
305  return (
306  (_main_picture && _main_picture->encrypted()) ||
307  (_main_sound && _main_sound->encrypted()) ||
308  (_main_subtitle && _main_subtitle->encrypted()) ||
309  ecc ||
310  (_atmos && _atmos->encrypted())
311  );
312 }
313 
314 
315 bool
316 Reel::all_encrypted () const
317 {
318  auto ecc = true;
319  for (auto i: _closed_captions) {
320  if (!i->encrypted()) {
321  ecc = false;
322  }
323  }
324 
325  return (
326  (!_main_picture || _main_picture->encrypted()) &&
327  (!_main_sound || _main_sound->encrypted()) &&
328  (!_main_subtitle || _main_subtitle->encrypted()) &&
329  ecc &&
330  (!_atmos || _atmos->encrypted())
331  );
332 }
333 
334 
335 void
336 Reel::add (DecryptedKDM const & kdm)
337 {
338  give_kdm_to_assets (kdm);
339  /* We have to keep the KDMs that we are given, as they will not be passed to unresolved assets.
340  * After we resolve some assets we will re-call give_kdm_to_assets() with all the KDMs that
341  * we have been given so far.
342  */
343  _kdms.push_back (kdm);
344 }
345 
346 
347 void
348 Reel::give_kdm_to_assets (DecryptedKDM const & kdm)
349 {
350  for (auto const& i: kdm.keys()) {
351  if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) {
352  _main_picture->j2k_asset()->set_key(i.key());
353  }
354  if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) {
355  _main_sound->asset()->set_key (i.key());
356  }
357  if (_main_subtitle) {
358  auto smpte = dynamic_pointer_cast<ReelSMPTETextAsset>(_main_subtitle);
359  if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
360  smpte->smpte_asset()->set_key(i.key());
361  }
362  }
363  for (auto j: _closed_captions) {
364  auto smpte = dynamic_pointer_cast<ReelSMPTETextAsset>(j);
365  if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
366  smpte->smpte_asset()->set_key(i.key());
367  }
368  }
369  if (_atmos && i.id() == _atmos->key_id() && _atmos->asset_ref().resolved()) {
370  _atmos->asset()->set_key (i.key());
371  }
372  }
373 }
374 
375 
376 void
377 Reel::add (shared_ptr<ReelAsset> asset)
378 {
379  if (auto p = dynamic_pointer_cast<ReelPictureAsset>(asset)) {
380  _main_picture = p;
381  } else if (auto so = dynamic_pointer_cast<ReelSoundAsset>(asset)) {
382  _main_sound = so;
383  } else if (auto te = dynamic_pointer_cast<ReelTextAsset>(asset)) {
384  switch (te->type()) {
385  case TextType::OPEN_SUBTITLE:
386  _main_subtitle = te;
387  break;
388  case TextType::OPEN_CAPTION:
389  _main_caption = te;
390  break;
391  case TextType::CLOSED_SUBTITLE:
392  _closed_subtitles.push_back(te);
393  break;
394  case TextType::CLOSED_CAPTION:
395  _closed_captions.push_back(te);
396  break;
397  }
398  } else if (auto m = dynamic_pointer_cast<ReelMarkersAsset>(asset)) {
399  _main_markers = m;
400  } else if (auto a = dynamic_pointer_cast<ReelAtmosAsset>(asset)) {
401  _atmos = a;
402  } else {
403  DCP_ASSERT(false);
404  }
405 }
406 
407 
408 vector<shared_ptr<ReelAsset>>
409 Reel::assets () const
410 {
411  vector<shared_ptr<ReelAsset>> a;
412  if (_main_picture) {
413  a.push_back (_main_picture);
414  }
415  if (_main_sound) {
416  a.push_back (_main_sound);
417  }
418  if (_main_subtitle) {
419  a.push_back (_main_subtitle);
420  }
421  if (_main_caption) {
422  a.push_back(_main_caption);
423  }
424  std::copy (_closed_subtitles.begin(), _closed_subtitles.end(), back_inserter(a));
425  std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a));
426  if (_atmos) {
427  a.push_back (_atmos);
428  }
429  return a;
430 }
431 
432 
433 void
434 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
435 {
436  if (_main_picture) {
437  _main_picture->asset_ref().resolve(assets);
438  }
439 
440  if (_main_sound) {
441  _main_sound->asset_ref().resolve(assets);
442  }
443 
444  auto resolve_interop_fonts = [&assets](shared_ptr<ReelTextAsset>(asset)) {
445  /* Interop subtitle handling is all special cases */
446  if (asset->asset_ref().resolved()) {
447  if (auto iop = dynamic_pointer_cast<InteropTextAsset>(asset->asset_ref().asset())) {
448  iop->resolve_fonts(assets);
449  }
450  }
451 
452  };
453 
454  if (_main_subtitle) {
455  _main_subtitle->asset_ref().resolve(assets);
456  resolve_interop_fonts(_main_subtitle);
457  }
458 
459  if (_main_caption) {
460  _main_caption->asset_ref().resolve(assets);
461  }
462 
463  for (auto i: _closed_subtitles) {
464  i->asset_ref().resolve(assets);
465  resolve_interop_fonts(i);
466  }
467 
468  for (auto i: _closed_captions) {
469  i->asset_ref().resolve(assets);
470  resolve_interop_fonts(i);
471  }
472 
473  if (_atmos) {
474  _atmos->asset_ref().resolve (assets);
475  }
476 
477  for (auto const& i: _kdms) {
478  give_kdm_to_assets (i);
479  }
480 }
481 
482 
483 int64_t
484 Reel::duration () const
485 {
486  if (_main_picture) {
487  return _main_picture->actual_duration();
488  }
489 
490  int64_t d = INT64_MAX;
491 
492  if (_main_sound) {
493  d = min (d, _main_sound->actual_duration());
494  }
495  if (_main_subtitle) {
496  d = min (d, _main_subtitle->actual_duration());
497  }
498  if (_main_caption) {
499  d = min(d, _main_caption->actual_duration());
500  }
501  if (_main_markers) {
502  d = min (d, _main_markers->actual_duration());
503  }
504  for (auto i: _closed_subtitles) {
505  d = min(d, i->actual_duration());
506  }
507  for (auto i: _closed_captions) {
508  d = min (d, i->actual_duration());
509  }
510  if (_atmos) {
511  d = min (d, _atmos->actual_duration());
512  }
513 
514  DCP_ASSERT (d < INT64_MAX);
515 
516  return d;
517 }
A decrypted KDM.
Definition: decrypted_kdm.h:75
std::vector< DecryptedKDMKey > keys() const
A class to describe what "equality" means for a particular test.
Some part of a DCP that has a UUID.
Definition: object.h:63
DecryptedKDM class.
DecryptedKDMKey class.
Class to describe what equality means when calling Asset::equals().
InteropTextAsset class.
J2KPictureAsset class.
MonoJ2KPictureAsset class.
Namespace for everything in libdcp.
Definition: array_data.h:50
Reel class.
ReelAtmosAsset class.
ReelInteropTextAsset class.
ReelMarkersAsset class.
ReelMonoPictureAsset class.
ReelSMPTETextAsset class.
ReelSoundAsset class.
ReelStereoPictureAsset class.
ReelTextAsset class.
SMPTETextAsset class.
SoundAsset class.
StereoJ2KPictureAsset class.
TextAsset class.
Utility methods and classes.