Add OK note when PKL and CPL annotation texts match.
[libdcp.git] / src / reel.cc
index 25019d359a511d5f249ea81d362806871a54e77f..def3a4ae6a18104f9860e79367286f49e9838b51 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014-2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of libdcp.
 
     files in the program, then also delete it here.
 */
 
-#include "reel.h"
-#include "util.h"
-#include "picture_asset.h"
+
+/** @file  src/reel.cc
+ *  @brief Reel class
+ */
+
+
+#include "decrypted_kdm.h"
+#include "decrypted_kdm_key.h"
+#include "equality_options.h"
+#include "interop_subtitle_asset.h"
 #include "mono_picture_asset.h"
-#include "stereo_picture_asset.h"
-#include "sound_asset.h"
-#include "subtitle_asset.h"
+#include "picture_asset.h"
+#include "reel.h"
+#include "reel_atmos_asset.h"
+#include "reel_closed_caption_asset.h"
+#include "reel_interop_closed_caption_asset.h"
+#include "reel_interop_subtitle_asset.h"
+#include "reel_markers_asset.h"
 #include "reel_mono_picture_asset.h"
-#include "reel_stereo_picture_asset.h"
+#include "reel_smpte_closed_caption_asset.h"
+#include "reel_smpte_subtitle_asset.h"
 #include "reel_sound_asset.h"
+#include "reel_stereo_picture_asset.h"
 #include "reel_subtitle_asset.h"
-#include "reel_markers_asset.h"
-#include "decrypted_kdm_key.h"
-#include "decrypted_kdm.h"
-#include "interop_subtitle_asset.h"
 #include "smpte_subtitle_asset.h"
-#include "reel_atmos_asset.h"
-#include "reel_closed_caption_asset.h"
+#include "sound_asset.h"
+#include "stereo_picture_asset.h"
+#include "subtitle_asset.h"
+#include "util.h"
 #include <libxml++/nodes/element.h>
 #include <stdint.h>
 
-/* Centos 6 does not have this */
-#ifndef INT64_MAX
-#define INT64_MAX 0x7fffffffffffffff
-#endif
 
 using std::string;
 using std::cout;
@@ -66,34 +73,42 @@ using std::dynamic_pointer_cast;
 using std::vector;
 using namespace dcp;
 
-Reel::Reel (std::shared_ptr<const cxml::Node> node)
+
+Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard)
        : Object (remove_urn_uuid (node->string_child ("Id")))
 {
        auto asset_list = node->node_child ("AssetList");
 
        auto main_picture = asset_list->optional_node_child ("MainPicture");
        if (main_picture) {
-               _main_picture.reset (new ReelMonoPictureAsset (main_picture));
+               _main_picture = make_shared<ReelMonoPictureAsset>(main_picture);
        }
 
        auto main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
        if (main_stereoscopic_picture) {
-               _main_picture.reset (new ReelStereoPictureAsset (main_stereoscopic_picture));
+               _main_picture = make_shared<ReelStereoPictureAsset>(main_stereoscopic_picture);
        }
 
        auto main_sound = asset_list->optional_node_child ("MainSound");
        if (main_sound) {
-               _main_sound.reset (new ReelSoundAsset (main_sound));
+               _main_sound = make_shared<ReelSoundAsset>(main_sound);
        }
 
        auto main_subtitle = asset_list->optional_node_child ("MainSubtitle");
        if (main_subtitle) {
-               _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle));
+               switch (standard) {
+                       case Standard::INTEROP:
+                               _main_subtitle = make_shared<ReelInteropSubtitleAsset>(main_subtitle);
+                               break;
+                       case Standard::SMPTE:
+                               _main_subtitle = make_shared<ReelSMPTESubtitleAsset>(main_subtitle);
+                               break;
+               }
        }
 
        auto main_markers = asset_list->optional_node_child ("MainMarkers");
        if (main_markers) {
-               _main_markers.reset (new ReelMarkersAsset (main_markers));
+               _main_markers = make_shared<ReelMarkersAsset>(main_markers);
        }
 
        /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */
@@ -103,7 +118,14 @@ Reel::Reel (std::shared_ptr<const cxml::Node> node)
                closed_captions = asset_list->node_children ("ClosedCaption");
        }
        for (auto i: closed_captions) {
-               _closed_captions.push_back (make_shared<ReelClosedCaptionAsset>(i));
+               switch (standard) {
+                       case Standard::INTEROP:
+                               _closed_captions.push_back (make_shared<ReelInteropClosedCaptionAsset>(i));
+                               break;
+                       case Standard::SMPTE:
+                               _closed_captions.push_back (make_shared<ReelSMPTEClosedCaptionAsset>(i));
+                               break;
+               }
        }
 
        auto atmos = asset_list->optional_node_child ("AuxData");
@@ -115,12 +137,13 @@ Reel::Reel (std::shared_ptr<const cxml::Node> node)
        node->done ();
 }
 
+
 xmlpp::Element *
 Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
 {
-       auto reel = node->add_child ("Reel");
-       reel->add_child("Id")->add_child_text("urn:uuid:" + _id);
-       xmlpp::Element* asset_list = reel->add_child ("AssetList");
+       auto reel = cxml::add_child(node, "Reel");
+       cxml::add_text_child(reel, "Id", "urn:uuid:" + _id);
+       auto asset_list = cxml::add_child(reel, "AssetList");
 
        if (_main_markers) {
                _main_markers->write_to_cpl (asset_list, standard);
@@ -155,8 +178,9 @@ Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
        return asset_list;
 }
 
+
 bool
-Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandler note) const
+Reel::equals(std::shared_ptr<const Reel> other, EqualityOptions const& opt, NoteHandler note) const
 {
        if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
                note (NoteType::ERROR, "Reel: picture assets differ");
@@ -181,7 +205,31 @@ Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandle
                return false;
        }
 
-       if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, note)) {
+       bool same_type = false;
+
+       {
+               auto interop = dynamic_pointer_cast<ReelInteropSubtitleAsset>(_main_subtitle);
+               auto interop_other = dynamic_pointer_cast<ReelInteropSubtitleAsset>(other->_main_subtitle);
+               if (interop && interop_other) {
+                       same_type = true;
+                       if (!interop->equals(interop_other, opt, note)) {
+                               return false;
+                       }
+               }
+       }
+
+       {
+               auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
+               auto smpte_other = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(other->_main_subtitle);
+               if (smpte && smpte_other) {
+                       same_type = true;
+                       if (!smpte->equals(smpte_other, opt, note)) {
+                               return false;
+                       }
+               }
+       }
+
+       if ((_main_subtitle || other->_main_subtitle) && !same_type) {
                return false;
        }
 
@@ -221,6 +269,7 @@ Reel::equals (std::shared_ptr<const Reel> other, EqualityOptions opt, NoteHandle
        return true;
 }
 
+
 bool
 Reel::any_encrypted () const
 {
@@ -260,62 +309,69 @@ Reel::all_encrypted () const
               );
 }
 
+
 void
 Reel::add (DecryptedKDM const & kdm)
 {
-       auto keys = kdm.keys ();
+       give_kdm_to_assets (kdm);
+       /* We have to keep the KDMs that we are given, as they will not be passed to unresolved assets.
+        * After we resolve some assets we will re-call give_kdm_to_assets() with all the KDMs that
+        * we have been given so far.
+        */
+       _kdms.push_back (kdm);
+}
 
-       for (auto const& i: keys) {
-               if (_main_picture && i.id() == _main_picture->key_id()) {
+
+void
+Reel::give_kdm_to_assets (DecryptedKDM const & kdm)
+{
+       for (auto const& i: kdm.keys()) {
+               if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) {
                        _main_picture->asset()->set_key (i.key());
                }
-               if (_main_sound && i.id() == _main_sound->key_id()) {
+               if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) {
                        _main_sound->asset()->set_key (i.key());
                }
-               if (_main_subtitle && i.id() == _main_subtitle->key_id()) {
-                       shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (_main_subtitle->asset());
-                       if (s) {
-                               s->set_key (i.key());
+               if (_main_subtitle) {
+                       auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
+                       if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
+                               smpte->smpte_asset()->set_key(i.key());
                        }
                }
                for (auto j: _closed_captions) {
-                       if (i.id() == j->key_id()) {
-                               auto s = dynamic_pointer_cast<SMPTESubtitleAsset> (j->asset());
-                               if (s) {
-                                       s->set_key (i.key());
-                               }
+                       auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
+                       if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
+                               smpte->smpte_asset()->set_key(i.key());
                        }
                }
-               if (_atmos && i.id() == _atmos->key_id()) {
+               if (_atmos && i.id() == _atmos->key_id() && _atmos->asset_ref().resolved()) {
                        _atmos->asset()->set_key (i.key());
                }
        }
 }
 
+
 void
 Reel::add (shared_ptr<ReelAsset> asset)
 {
-       auto p = dynamic_pointer_cast<ReelPictureAsset> (asset);
-       auto so = dynamic_pointer_cast<ReelSoundAsset> (asset);
-       auto su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
-       auto m = dynamic_pointer_cast<ReelMarkersAsset> (asset);
-       auto c = dynamic_pointer_cast<ReelClosedCaptionAsset> (asset);
-       auto a = dynamic_pointer_cast<ReelAtmosAsset> (asset);
-       if (p) {
+       if (auto p = dynamic_pointer_cast<ReelPictureAsset>(asset)) {
                _main_picture = p;
-       } else if (so) {
+       } else if (auto so = dynamic_pointer_cast<ReelSoundAsset>(asset)) {
                _main_sound = so;
-       } else if (su) {
+       } else if (auto su = dynamic_pointer_cast<ReelSubtitleAsset>(asset)) {
                _main_subtitle = su;
-       } else if (m) {
+       } else if (auto m = dynamic_pointer_cast<ReelMarkersAsset>(asset)) {
                _main_markers = m;
-       } else if (c) {
+       } else if (auto c = dynamic_pointer_cast<ReelClosedCaptionAsset>(asset)) {
                _closed_captions.push_back (c);
-       } else if (a) {
+       } else if (auto a = dynamic_pointer_cast<ReelAtmosAsset>(asset)) {
                _atmos = a;
+       } else {
+               DCP_ASSERT(false);
        }
 }
 
+
 vector<shared_ptr<ReelAsset>>
 Reel::assets () const
 {
@@ -336,19 +392,20 @@ Reel::assets () const
        return a;
 }
 
+
 void
 Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
 {
        if (_main_picture) {
-               _main_picture->asset_ref().resolve (assets);
+               _main_picture->asset_ref().resolve(assets);
        }
 
        if (_main_sound) {
-               _main_sound->asset_ref().resolve (assets);
+               _main_sound->asset_ref().resolve(assets);
        }
 
        if (_main_subtitle) {
-               _main_subtitle->asset_ref().resolve (assets);
+               _main_subtitle->asset_ref().resolve(assets);
 
                /* Interop subtitle handling is all special cases */
                if (_main_subtitle->asset_ref().resolved()) {
@@ -374,8 +431,13 @@ Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
        if (_atmos) {
                _atmos->asset_ref().resolve (assets);
        }
+
+       for (auto const& i: _kdms) {
+               give_kdm_to_assets (i);
+       }
 }
 
+
 int64_t
 Reel::duration () const
 {